From 856547be238fded2396eaf82e7c7d9502836bb22 Mon Sep 17 00:00:00 2001 From: Garux Date: Mon, 5 Apr 2021 17:06:49 +0300 Subject: [PATCH 001/232] Fix Q1 MDL group frame loading, e.g. Q1 progs/flame2.mdl --- code/AssetLib/MDL/MDLFileData.h | 4 +++- code/AssetLib/MDL/MDLLoader.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/MDL/MDLFileData.h b/code/AssetLib/MDL/MDLFileData.h index bbb6f7728..872cee7f8 100644 --- a/code/AssetLib/MDL/MDLFileData.h +++ b/code/AssetLib/MDL/MDLFileData.h @@ -696,6 +696,8 @@ struct GroupFrame //! 0 = simple frame, !0 = group frame int32_t type; + int32_t numframes; + //! Minimum vertex for all single frames Vertex min; @@ -703,7 +705,7 @@ struct GroupFrame Vertex max; //! Time for all single frames - float *time; + float time; // float[numframes] //! List of single frames SimpleFrame *frames; diff --git a/code/AssetLib/MDL/MDLLoader.cpp b/code/AssetLib/MDL/MDLLoader.cpp index a4286a716..b5010a37f 100644 --- a/code/AssetLib/MDL/MDLLoader.cpp +++ b/code/AssetLib/MDL/MDLLoader.cpp @@ -428,7 +428,7 @@ void MDLImporter::InternReadFile_Quake1() { } else { // get the first frame in the group BE_NCONST MDL::GroupFrame *pcFrames2 = (BE_NCONST MDL::GroupFrame *)pcFrames; - pcFirstFrame = &(pcFrames2->frames[0]); + pcFirstFrame = (MDL::SimpleFrame *)( &pcFrames2->time + pcFrames2->numframes ); } BE_NCONST MDL::Vertex *pcVertices = (BE_NCONST MDL::Vertex *)((pcFirstFrame->name) + sizeof(pcFirstFrame->name)); VALIDATE_FILE_SIZE((const unsigned char *)(pcVertices + pcHeader->num_verts)); From 8ad9c937f171bd716bbd5692af1c501acbaded88 Mon Sep 17 00:00:00 2001 From: Krishty Date: Tue, 4 May 2021 19:10:24 +0200 Subject: [PATCH 002/232] enabled debug information in MSVC release build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No effect on runtime speed/size. Slightly slower link time, but debugging experience improves by a million times. - /Zi – Store debug information in a .pdb file, not directly in the DLL/EXE - /DEBUG:FULL – generate debug information during link - /PDBALTPATH:%_PDB% – do not store the file system path of the .pdb, just the filename and hash (no disclose paths on distribution) - /OPT:REF /OPT:ICF – remove unreferenced functions and fold identical functions (this was enabled before, but requires explicit enabling if /DEBUG:FULL is specified) --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97a3641f5..f1b60936d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,6 +268,8 @@ ELSEIF(MSVC) ADD_COMPILE_OPTIONS(/wd4351) ENDIF() SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /Zi /Od") + SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi") + SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FULL /PDBALTPATH:%_PDB% /OPT:REF /OPT:ICF") ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) IF(NOT ASSIMP_HUNTER_ENABLED) SET(CMAKE_CXX_STANDARD 11) From 2a126f9f62a8174448a5ac31d94a3534eefd909f Mon Sep 17 00:00:00 2001 From: Krishty Date: Mon, 3 May 2021 21:46:53 +0200 Subject: [PATCH 003/232] reduced Ogre string bloat The Ogre importer used std::string where a string literal would have been sufficient. Saves another 600 B of code and data. --- code/AssetLib/Ogre/OgreBinarySerializer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/AssetLib/Ogre/OgreBinarySerializer.cpp b/code/AssetLib/Ogre/OgreBinarySerializer.cpp index 0fc18feb9..68b1cf1ed 100644 --- a/code/AssetLib/Ogre/OgreBinarySerializer.cpp +++ b/code/AssetLib/Ogre/OgreBinarySerializer.cpp @@ -55,9 +55,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { namespace Ogre { -const std::string MESH_VERSION_1_8 = "[MeshSerializer_v1.8]"; -const std::string SKELETON_VERSION_1_8 = "[Serializer_v1.80]"; -const std::string SKELETON_VERSION_1_1 = "[Serializer_v1.10]"; +static constexpr auto MESH_VERSION_1_8 = "[MeshSerializer_v1.8]"; +static constexpr auto SKELETON_VERSION_1_8 = "[Serializer_v1.80]"; +static constexpr auto SKELETON_VERSION_1_1 = "[Serializer_v1.10]"; const unsigned short HEADER_CHUNK_ID = 0x1000; From 7b6dab5e209257a17b4ca3bcc7834e2c8cc402b1 Mon Sep 17 00:00:00 2001 From: Krishty Date: Mon, 3 May 2021 21:52:48 +0200 Subject: [PATCH 004/232] reduced DXF string bloat The DXF importer defined a global std::string constant, only to convert it back to a C string on use. This commit defines the constant as a C string right away, thus saving 340 B of code and data. --- code/AssetLib/DXF/DXFLoader.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/DXF/DXFLoader.cpp b/code/AssetLib/DXF/DXFLoader.cpp index d4a6be4ad..b42d497f4 100644 --- a/code/AssetLib/DXF/DXFLoader.cpp +++ b/code/AssetLib/DXF/DXFLoader.cpp @@ -63,11 +63,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; // AutoCAD Binary DXF -const std::string AI_DXF_BINARY_IDENT = std::string("AutoCAD Binary DXF\r\n\x1a\0"); -const size_t AI_DXF_BINARY_IDENT_LEN = 24u; +static constexpr char AI_DXF_BINARY_IDENT[] = "AutoCAD Binary DXF\r\n\x1a"; +static constexpr size_t AI_DXF_BINARY_IDENT_LEN = sizeof AI_DXF_BINARY_IDENT; // default vertex color that all uncolored vertices will receive -const aiColor4D AI_DXF_DEFAULT_COLOR(aiColor4D(0.6f, 0.6f, 0.6f, 0.6f)); +static const aiColor4D AI_DXF_DEFAULT_COLOR(aiColor4D(0.6f, 0.6f, 0.6f, 0.6f)); // color indices for DXF - 16 are supported, the table is // taken directly from the DXF spec. @@ -156,10 +156,10 @@ void DXFImporter::InternReadFile( const std::string& filename, aiScene* pScene, } // Check whether this is a binary DXF file - we can't read binary DXF files :-( - char buff[AI_DXF_BINARY_IDENT_LEN+1] = {0}; + char buff[AI_DXF_BINARY_IDENT_LEN] = {0}; file->Read(buff,AI_DXF_BINARY_IDENT_LEN,1); - if (0 == strncmp(AI_DXF_BINARY_IDENT.c_str(),buff,AI_DXF_BINARY_IDENT_LEN)) { + if (0 == memcmp(AI_DXF_BINARY_IDENT,buff,AI_DXF_BINARY_IDENT_LEN)) { throw DeadlyImportError("DXF: Binary files are not supported at the moment"); } @@ -549,7 +549,7 @@ void DXFImporter::ParseEntities(DXF::LineReader& reader, DXF::FileData& output) ++reader; } - ASSIMP_LOG_VERBOSE_DEBUG_F( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), + ASSIMP_LOG_VERBOSE_DEBUG_F( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), " inserted blocks in ENTITIES" ); } From f3c18556d1331e43ac5828fa82bf10fee966df96 Mon Sep 17 00:00:00 2001 From: Krishty Date: Tue, 4 May 2021 22:03:44 +0200 Subject: [PATCH 005/232] reduced OpenGEX string bloat The OpenGEX importer defined a few global std::string constants, only to convert them back to C strings on use. This commit defines them as C strings from the beginning. strncmp() was used to compare these strings to other strings, but the length limit was set to string length, which made it equivalent to strcmp(), just slower. Fixed that as well. --- code/AssetLib/OpenGEX/OpenGEXImporter.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp index b29aeeeb1..7ee278521 100644 --- a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp +++ b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp @@ -735,22 +735,22 @@ enum MeshAttribute { TexCoord }; -static const std::string PosToken = "position"; -static const std::string ColToken = "color"; -static const std::string NormalToken = "normal"; -static const std::string TexCoordToken = "texcoord"; +constexpr auto PosToken = "position"; +constexpr auto ColToken = "color"; +constexpr auto NormalToken = "normal"; +constexpr auto TexCoordToken = "texcoord"; //------------------------------------------------------------------------------------------------ static MeshAttribute getAttributeByName(const char *attribName) { ai_assert(nullptr != attribName); - if (0 == strncmp(PosToken.c_str(), attribName, PosToken.size())) { + if (0 == strcmp(PosToken, attribName)) { return Position; - } else if (0 == strncmp(ColToken.c_str(), attribName, ColToken.size())) { + } else if (0 == strcmp(ColToken, attribName)) { return Color; - } else if (0 == strncmp(NormalToken.c_str(), attribName, NormalToken.size())) { + } else if (0 == strcmp(NormalToken, attribName)) { return Normal; - } else if (0 == strncmp(TexCoordToken.c_str(), attribName, TexCoordToken.size())) { + } else if (0 == strcmp(TexCoordToken, attribName)) { return TexCoord; } From b57ce004f805f133cf731311c3c8a6f08b0d7db9 Mon Sep 17 00:00:00 2001 From: Krishty Date: Tue, 4 May 2021 09:45:26 +0200 Subject: [PATCH 006/232] reduced FBX string bloat The FBX importer used two std::strings where string literals would have been sufficient. --- code/AssetLib/FBX/FBXMeshGeometry.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/AssetLib/FBX/FBXMeshGeometry.cpp b/code/AssetLib/FBX/FBXMeshGeometry.cpp index 2bca8dff2..4f87c5a2c 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.cpp +++ b/code/AssetLib/FBX/FBXMeshGeometry.cpp @@ -604,15 +604,15 @@ void MeshGeometry::ReadVertexDataTangents(std::vector& tangents_out, } // ------------------------------------------------------------------------------------------------ -static const std::string BinormalIndexToken = "BinormalIndex"; -static const std::string BinormalsIndexToken = "BinormalsIndex"; +static const char * BinormalIndexToken = "BinormalIndex"; +static const char * BinormalsIndexToken = "BinormalsIndex"; void MeshGeometry::ReadVertexDataBinormals(std::vector& binormals_out, const Scope& source, const std::string& MappingInformationType, const std::string& ReferenceInformationType) { const char * str = source.Elements().count( "Binormals" ) > 0 ? "Binormals" : "Binormal"; - const char * strIdx = source.Elements().count( "Binormals" ) > 0 ? BinormalsIndexToken.c_str() : BinormalIndexToken.c_str(); + const char * strIdx = source.Elements().count( "Binormals" ) > 0 ? BinormalsIndexToken : BinormalIndexToken; ResolveVertexDataArray(binormals_out,source,MappingInformationType,ReferenceInformationType, str, strIdx, From 153b890b02efaea35f5a73d3bb10f7576bce57a1 Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Wed, 5 May 2021 14:09:43 +0100 Subject: [PATCH 007/232] Prevent accessing nullpointers --- code/AssetLib/glTF2/glTF2Importer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index f34b1b451..e6265946b 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1263,7 +1263,7 @@ aiMeshMorphAnim *CreateMeshMorphAnim(glTF2::Asset&, Node &node, AnimationSampler static const float kMillisecondsFromSeconds = 1000.f; - if (nullptr != samplers.weight) { + if (samplers.weight && samplers.weight->input && samplers.weight->output) { float *times = nullptr; samplers.weight->input->ExtractData(times); float *values = nullptr; From 24df52647f4432d0bb97be468799e31082dd4dde Mon Sep 17 00:00:00 2001 From: Krishty Date: Wed, 5 May 2021 23:05:44 +0200 Subject: [PATCH 008/232] merge and update all copies of stb_image.h --- code/AssetLib/M3D/M3DWrapper.h | 4 + code/Common/Assimp.cpp | 33 + code/Pbrt/PbrtExporter.cpp | 3 +- code/Pbrt/stb_image.h | 7756 -------------------------------- contrib/stb_image/stb_image.h | 652 ++- 5 files changed, 514 insertions(+), 7934 deletions(-) delete mode 100644 code/Pbrt/stb_image.h diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index 5c370e607..34bec49b2 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -56,6 +56,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //#define ASSIMP_USE_M3D_READFILECB //#define M3D_ASCII +// Share stb_image's PNG loader with other importers/exporters instead of bringing our own copy. +#define STBI_ONLY_PNG +#include + #include "m3d.h" namespace Assimp { diff --git a/code/Common/Assimp.cpp b/code/Common/Assimp.cpp index 4e2c7117c..6074b84bc 100644 --- a/code/Common/Assimp.cpp +++ b/code/Common/Assimp.cpp @@ -1251,3 +1251,36 @@ ASSIMP_API void aiQuaternionInterpolate( ai_assert(nullptr != end); aiQuaternion::Interpolate(*dst, *start, *end, factor); } + + +// stb_image is a lightweight image loader. It is shared by: +// - M3D import +// - PBRT export +// Since it's a header-only library, its implementation must be instantiated in some cpp file. +// Don't scatter this task over multiple importers/exporters. Maintain it in a central place (here!). + +#define ASSIMP_HAS_PBRT_EXPORT (!ASSIMP_BUILD_NO_EXPORT && !ASSIMP_BUILD_NO_PBRT_EXPORTER) +#define ASSIMP_HAS_M3D ((!ASSIMP_BUILD_NO_EXPORT && !ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER) + +#if ASSIMP_HAS_PBRT_EXPORT +# define ASSIMP_NEEDS_STB_IMAGE 1 +#elif ASSIMP_HAS_M3D +# define ASSIMP_NEEDS_STB_IMAGE 1 +# define STBI_ONLY_PNG +#endif + +#if ASSIMP_NEEDS_STB_IMAGE + +# if _MSC_VER // "unreferenced function has been removed" (SSE2 detection routine in x64 builds) +# pragma warning(push) +# pragma warning(disable: 4505) +# endif + +# define STB_IMAGE_IMPLEMENTATION +# include "../contrib/stb_image/stb_image.h" + +# if _MSC_VER +# pragma warning(pop) +# endif + +#endif diff --git a/code/Pbrt/PbrtExporter.cpp b/code/Pbrt/PbrtExporter.cpp index b793c37f9..35b108702 100644 --- a/code/Pbrt/PbrtExporter.cpp +++ b/code/Pbrt/PbrtExporter.cpp @@ -83,8 +83,7 @@ Other: #include #include -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" +#include "../contrib/stb_image/stb_image.h" using namespace Assimp; diff --git a/code/Pbrt/stb_image.h b/code/Pbrt/stb_image.h deleted file mode 100644 index 65a205f6e..000000000 --- a/code/Pbrt/stb_image.h +++ /dev/null @@ -1,7756 +0,0 @@ -/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define STB_IMAGE_IMPLEMENTATION - #include "stb_image.h" - - You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. - And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free - - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8/16-bit-per-channel - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels, 8/16 bit-per-channel) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - PNM (PPM and PGM binary only) - - Animated GIF still needs a proper API, but here's one way to do it: - http://gist.github.com/urraka/685d9a6340b26b830d49 - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - - Full documentation under "DOCUMENTATION" below. - - -LICENSE - - See end of file for license information. - -RECENT REVISION HISTORY: - - 2.26 (2020-07-13) many minor fixes - 2.25 (2020-02-02) fix warnings - 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically - 2.23 (2019-08-11) fix clang static analysis warning - 2.22 (2019-03-04) gif fixes, fix warnings - 2.21 (2019-02-25) fix typo in comment - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings - 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes - 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 - RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; - correct channel count for PNG & BMP - 2.10 (2016-01-22) avoid warning introduced in 2.09 - 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED - - See end of file for full revision history. - - - ============================ Contributors ========================= - - Image formats Extensions, features - Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - github:urraka (animated gif) Junggon Kim (PNM comments) - Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) - socks-the-fox (16-bit PNG) - Jeremy Sawicki (handle all ImageNet JPGs) - Optimizations & bugfixes Mikhail Morozov (1-bit BMP) - Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine - John-Mark Allen - Carmelo J Fdez-Aguera - - Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski - Phil Jordan Dave Moore Roy Eltham - Hayaki Saito Nathan Reed Won Chun - Luke Graham Johan Duparc Nick Verigakis the Horde3D community - Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton github:snagar - Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex - Cass Everitt Ryamond Barbiero github:grim210 - Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw - Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus - Josh Tobin Matthew Gregan github:poppolopoppo - Julian Raschke Gregory Mullen Christian Floisand github:darealshinji - Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 - Brad Weinberger Matvey Cherevko [reserved] - Luca Sas Alexander Veselov Zack Middleton [reserved] - Ryan C. Gordon [reserved] [reserved] - DO NOT ADD YOUR NAME HERE - - To add your name to the credits, pick a random blank space in the middle and fill it. - 80% of merge conflicts on stb PRs are due to people adding their name at the end - of the credits. -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below for HDR usage): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *channels_in_file -- outputs # of image components in image file -// int desired_channels -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data, or NULL on an allocation failure or if the image is -// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'desired_channels' if desired_channels is non-zero, or -// *channels_in_file otherwise. If desired_channels is non-zero, -// *channels_in_file has the number of components that _would_ have been -// output otherwise. E.g. if you set desired_channels to 4, you will always -// get RGBA output, but you can check *channels_in_file to see if it's trivially -// opaque because e.g. there were only 3 channels in the source image. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *channels_in_file will be unchanged. The function -// stbi_failure_reason() can be queried for an extremely brief, end-user -// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS -// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// =========================================================================== -// -// UNICODE: -// -// If compiling for Windows and you wish to use Unicode filenames, compile -// with -// #define STBI_WINDOWS_UTF8 -// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert -// Windows wchar_t filenames to utf8. -// -// =========================================================================== -// -// Philosophy -// -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Sometimes I let "good performance" creep up in priority over "easy to maintain", -// and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy-to-use ones. Nevertheless, it's important -// to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. -// -// Some secondary priorities arise directly from the first two, some of which -// provide more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small source code footprint ("easy to maintain") -// - No dependencies ("ease of use") -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). -// -// =========================================================================== -// -// SIMD support -// -// The JPEG decoder will try to automatically use SIMD kernels on x86 when -// supported by the compiler. For ARM Neon support, you must explicitly -// request it. -// -// (The old do-it-yourself SIMD API is no longer supported in the current -// code.) -// -// On x86, SSE2 will automatically be used when available based on a run-time -// test; if not, the generic C versions are used as a fall-back. On ARM targets, -// the typical path is to have separate builds for NEON and non-NEON devices -// (at least this is true for iOS and Android). Therefore, the NEON support is -// toggled by a build flag: define STBI_NEON to get NEON loops. -// -// If for some reason you do not want to use any of SIMD code, or if -// you have issues compiling it, you can disable it entirely by -// defining STBI_NO_SIMD. -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image supports loading HDR images in general, and currently the Radiance -// .HDR file format specifically. You can still load any file through the existing -// interface; if you attempt to load an HDR file, it will be automatically remapped -// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// iPhone PNG support: -// -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// -// =========================================================================== -// -// ADDITIONAL CONFIGURATION -// -// - You can suppress implementation of any of the decoders to reduce -// your code footprint by #defining one or more of the following -// symbols before creating the implementation. -// -// STBI_NO_JPEG -// STBI_NO_PNG -// STBI_NO_BMP -// STBI_NO_PSD -// STBI_NO_TGA -// STBI_NO_GIF -// STBI_NO_HDR -// STBI_NO_PIC -// STBI_NO_PNM (.ppm and .pgm) -// -// - You can request *only* certain decoders and suppress all other ones -// (this will be more forward-compatible, as addition of new decoders -// doesn't require you to disable them explicitly): -// -// STBI_ONLY_JPEG -// STBI_ONLY_PNG -// STBI_ONLY_BMP -// STBI_ONLY_PSD -// STBI_ONLY_TGA -// STBI_ONLY_GIF -// STBI_ONLY_HDR -// STBI_ONLY_PIC -// STBI_ONLY_PNM (.ppm and .pgm) -// -// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still -// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB -// -// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater -// than that size (in either width or height) without further processing. -// This is to let programs in the wild set an upper bound to prevent -// denial-of-service attacks on untrusted data, as one could generate a -// valid image of gigantic dimensions and force stb_image to allocate a -// huge block of memory and spend disproportionate time decoding it. By -// default this is set to (1 << 24), which is 16777216, but that's still -// very big. - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for desired_channels - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -#include -typedef unsigned char stbi_uc; -typedef unsigned short stbi_us; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef STBIDEF -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -//////////////////////////////////// -// -// 8-bits-per-channel interface -// - -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - -#ifdef STBI_WINDOWS_UTF8 -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); -#endif - -//////////////////////////////////// -// -// 16-bits-per-channel interface -// - -STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -#endif - -//////////////////////////////////// -// -// float-per-channel interface -// -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); - #endif -#endif - -#ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR - -#ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR - -// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// on most compilers (and ALL modern mainstream compilers) this is threadsafe -STBIDEF const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit (char const *filename); -STBIDEF int stbi_is_16_bit_from_file(FILE *f); -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - -// flip the image vertically, so the first pixel in the output array is the bottom left -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); - -// as above, but only applies to images loaded on the thread that calls the function -// this function is only available if your compiler supports thread-local variables; -// calling it will fail to link if your compiler doesn't -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); - -// ZLIB client - used by PNG, available for other purposes - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ - || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ - || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ - || defined(STBI_ONLY_ZLIB) - #ifndef STBI_ONLY_JPEG - #define STBI_NO_JPEG - #endif - #ifndef STBI_ONLY_PNG - #define STBI_NO_PNG - #endif - #ifndef STBI_ONLY_BMP - #define STBI_NO_BMP - #endif - #ifndef STBI_ONLY_PSD - #define STBI_NO_PSD - #endif - #ifndef STBI_ONLY_TGA - #define STBI_NO_TGA - #endif - #ifndef STBI_ONLY_GIF - #define STBI_NO_GIF - #endif - #ifndef STBI_ONLY_HDR - #define STBI_NO_HDR - #endif - #ifndef STBI_ONLY_PIC - #define STBI_NO_PIC - #endif - #ifndef STBI_ONLY_PNM - #define STBI_NO_PNM - #endif -#endif - -#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) -#define STBI_NO_ZLIB -#endif - - -#include -#include // ptrdiff_t on osx -#include -#include -#include - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp, pow -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - -#ifdef __cplusplus -#define STBI_EXTERN extern "C" -#else -#define STBI_EXTERN extern -#endif - - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - -#ifndef STBI_NO_THREAD_LOCALS - #if defined(__cplusplus) && __cplusplus >= 201103L - #define STBI_THREAD_LOCAL thread_local - #elif defined(__GNUC__) && __GNUC__ < 5 - #define STBI_THREAD_LOCAL __thread - #elif defined(_MSC_VER) - #define STBI_THREAD_LOCAL __declspec(thread) - #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) - #define STBI_THREAD_LOCAL _Thread_local - #endif - - #ifndef STBI_THREAD_LOCAL - #if defined(__GNUC__) - #define STBI_THREAD_LOCAL __thread - #endif - #endif -#endif - -#ifdef _MSC_VER -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) -#endif - -#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) -// ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." -#endif - -#ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) -#endif - -#ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) -#endif - -// x86/x64 detection -#if defined(__x86_64__) || defined(_M_X64) -#define STBI__X64_TARGET -#elif defined(__i386) || defined(_M_IX86) -#define STBI__X86_TARGET -#endif - -#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// which in turn means it gets to use SSE2 everywhere. This is unfortunate, -// but previous attempts to provide the SSE2 functions with runtime -// detection caused numerous issues. The way architecture extensions are -// exposed in GCC/Clang is, sadly, not really suited for one-file libs. -// New behavior: if compiled with -msse2, we use SSE2 without any -// detection; if not, we don't use it at all. -#define STBI_NO_SIMD -#endif - -#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) -// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET -// -// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the -// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. -// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not -// simultaneously enabling "-mstackrealign". -// -// See https://github.com/nothings/stb/issues/81 for more information. -// -// So default to no SSE2 on 32-bit MinGW. If you've read this far and added -// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. -#define STBI_NO_SIMD -#endif - -#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) -#define STBI_SSE2 -#include - -#ifdef _MSC_VER - -#if _MSC_VER >= 1400 // not VC6 -#include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; -} -#else -static int stbi__cpuid3(void) -{ - int res; - __asm { - mov eax,1 - cpuid - mov res,edx - } - return res; -} -#endif - -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#endif - -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - // If we're even attempting to compile this on GCC/Clang, that means - // -msse2 is on, which means the compiler is allowed to use SSE2 - // instructions at will, and so are we. - return 1; -} -#endif - -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -// assume GCC or Clang on ARM targets -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#endif - -#ifndef STBI_MAX_DIMENSIONS -#define STBI_MAX_DIMENSIONS (1 << 24) -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - int callback_already_read; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - int ch; - fseek((FILE*) user, n, SEEK_CUR); - ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ - if (ch != EOF) { - ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ - } -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user) || ferror((FILE *) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; -} - -enum -{ - STBI_ORDER_RGB, - STBI_ORDER_BGR -}; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} stbi__result_info; - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__png_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__psd_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -static -#ifdef STBI_THREAD_LOCAL -STBI_THREAD_LOCAL -#endif -const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -#ifndef STBI_NO_FAILURE_STRINGS -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} -#endif - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// stb_image uses ints pervasively, including for offset calculations. -// therefore the largest decoded image size we can support with the -// current code, even on 64-bit targets, is INT_MAX. this is not a -// significant limitation for the intended use case. -// -// we do, however, need to make sure our size calculations don't -// overflow. hence a few helper functions for size calculations that -// multiply integers together, making sure that they're non-negative -// and no overflow occurs. - -// return 1 if the sum is valid, 0 on overflow. -// negative terms are considered invalid. -static int stbi__addsizes_valid(int a, int b) -{ - if (b < 0) return 0; - // now 0 <= b <= INT_MAX, hence also - // 0 <= INT_MAX - b <= INTMAX. - // And "a + b <= INT_MAX" (which might overflow) is the - // same as a <= INT_MAX - b (no overflow) - return a <= INT_MAX - b; -} - -// returns 1 if the product is valid, 0 on overflow. -// negative factors are considered invalid. -static int stbi__mul2sizes_valid(int a, int b) -{ - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; // mul-by-0 is always safe - // portable way to check for no overflows in a*b - return a <= INT_MAX/b; -} - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow -static int stbi__mad2sizes_valid(int a, int b, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); -} -#endif - -// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow -static int stbi__mad3sizes_valid(int a, int b, int c, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__addsizes_valid(a*b*c, add); -} - -// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); -} -#endif - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// mallocs with size overflow checking -static void *stbi__malloc_mad2(int a, int b, int add) -{ - if (!stbi__mad2sizes_valid(a, b, add)) return NULL; - return stbi__malloc(a*b + add); -} -#endif - -static void *stbi__malloc_mad3(int a, int b, int c, int add) -{ - if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; - return stbi__malloc(a*b*c + add); -} - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) -{ - if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; - return stbi__malloc(a*b*c*d + add); -} -#endif - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) -#else - #define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static int stbi__vertically_flip_on_load_global = 0; - -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_global = flag_true_if_should_flip; -} - -#ifndef STBI_THREAD_LOCAL -#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global -#else -static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; - -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_local = flag_true_if_should_flip; - stbi__vertically_flip_on_load_set = 1; -} - -#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ - ? stbi__vertically_flip_on_load_local \ - : stbi__vertically_flip_on_load_global) -#endif // STBI_THREAD_LOCAL - -static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields - ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed - ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order - ri->num_channels = 0; - - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); - #else - STBI_NOTUSED(bpc); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp, ri); - #endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi_uc *reduced; - - reduced = (stbi_uc *) stbi__malloc(img_len); - if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling - - STBI_FREE(orig); - return reduced; -} - -static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi__uint16 *enlarged; - - enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); - if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff - - STBI_FREE(orig); - return enlarged; -} - -static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) -{ - int row; - size_t bytes_per_row = (size_t)w * bytes_per_pixel; - stbi_uc temp[2048]; - stbi_uc *bytes = (stbi_uc *)image; - - for (row = 0; row < (h>>1); row++) { - stbi_uc *row0 = bytes + row*bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; - } - } -} - -#ifndef STBI_NO_GIF -static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) -{ - int slice; - int slice_size = w * h * bytes_per_pixel; - - stbi_uc *bytes = (stbi_uc *)image; - for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; - } -} -#endif - -static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 8) { - result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; - } - - // @TODO: move stbi__convert_format to here - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); - } - - return (unsigned char *) result; -} - -static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 16) { - result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; - } - - // @TODO: move stbi__convert_format16 to here - // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); - } - - return (stbi__uint16 *) result; -} - -#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); - } -} -#endif - -#ifndef STBI_NO_STDIO - -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) -STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); -STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); -#endif - -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) -{ - return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); -} -#endif - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) - wchar_t wMode[64]; - wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) - return 0; - - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) - return 0; - -#if _MSC_VER >= 1400 - if (0 != _wfopen_s(&f, wFilename, wMode)) - f = 0; -#else - f = _wfopen(wFilename, wMode); -#endif - -#elif defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint16 *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - stbi__uint16 *result; - if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file_16(f,x,y,comp,req_comp); - fclose(f); - return result; -} - - -#endif //!STBI_NO_STDIO - -STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - - result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); - if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); - } - - return result; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } - #endif - data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_LINEAR - -// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is -// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always -// reports false! - -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - long pos = ftell(f); - int res; - stbi__context s; - stbi__start_file(&s,f); - res = stbi__hdr_test(&s); - fseek(f, pos, SEEK_SET); - return res; - #else - STBI_NOTUSED(f); - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; - #endif -} - -#ifndef STBI_NO_LINEAR -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; - -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) -// nothing -#else -static void stbi__skip(stbi__context *s, int n) -{ - if (n == 0) return; // already there! - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) -// nothing -#else -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} -#endif - -#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) -// nothing -#else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} -#endif - -#ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); -} -#endif - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 stbi__compute_y_16(int r, int g, int b) -{ - return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - stbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - stbi__uint16 *src = data + j * x * img_n ; - stbi__uint16 *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output; - if (!data) return NULL; - output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - } - if (n < comp) { - for (i=0; i < x*y; ++i) { - output[i*comp + n] = data[i*comp + n]/255.0f; - } - } - STBI_FREE(data); - return output; -} -#endif - -#ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output; - if (!data) return NULL; - output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder -// -// simple implementation -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - some SIMD kernels for common paths on targets with SSE2/NEON -// - uses a lot of intermediate memory, could cache poorly - -#ifndef STBI_NO_JPEG - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi__uint16 dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - int jfif; - int app14_color_transform; // Adobe APP14 tag - int rgb; - - int scan_n, order[4]; - int restart_interval, todo; - -// kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0; - unsigned int code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -// build a table that decodes both magnitude and value of small ACs in -// one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; - - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (~0U << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB - k = stbi_lrot(j->code_buffer, n); - if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); -} - -// get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; -} - -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static const stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) -{ - int diff,dc,k; - int t; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * dequant[0]); - - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; -} - -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - diff = t ? stbi__extend_receive(j, t) : 0; - - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; -} - -// @OPTIMIZE: store non-zigzagged during the decode passes, -// and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->succ_high == 0) { - int shift = j->succ_low; - - if (j->eob_run) { - --j->eob_run; - return 1; - } - - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients - - short bit = (short) (1 << j->succ_low); - - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } - - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) * 4096) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0]*4; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SSE2 -// sse2 integer IDCT. not the fastest possible implementation but it -// produces bit-identical results to the generic C version so it's -// fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; - - // dot product constant: even elems=x, odd elems=y - #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) - - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y - #define dct_rot(out0,out1, x,y,c0,c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - - // out = in << 12 (in 16-bit, out 32-bit) - #define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - - // wide add - #define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) - - // wide sub - #define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - - // butterfly a/b, add bias, then shift by "s" and pack - #define dct_bfly32o(out0, out1, a,b,bias,s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } - - // 8-bit interleave step (for transposes) - #define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) - - // 16-bit interleave step (for transposes) - #define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) - - #define dct_pass(bias,shift) \ - { \ - /* even part */ \ - dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ - dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0,row7, x0,x7,bias,shift); \ - dct_bfly32o(row1,row6, x1,x6,bias,shift); \ - dct_bfly32o(row2,row5, x2,x5,bias,shift); \ - dct_bfly32o(row3,row4, x3,x4,bias,shift); \ - } - - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); - - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); - - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); - - // column pass - dct_pass(bias_0, 10); - - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); - - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); - - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } - - // row pass - dct_pass(bias_1, 17); - - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); - - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... - - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... - - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... - - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } - -#undef dct_const -#undef dct_rot -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_interleave8 -#undef dct_interleave16 -#undef dct_pass -} - -#endif // STBI_SSE2 - -#ifdef STBI_NEON - -// NEON integer IDCT. should produce bit-identical -// results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); - -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) - -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) - -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) - -// wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) - -// wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) - -// butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } - -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ - dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ - dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ - dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ - } - - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); - - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - - // column pass - dct_pass(vrshrn_n_s32, 10); - - // 16bit 8x8 transpose - { -// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. -// whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); - - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); - - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); - -#undef dct_trn16 -#undef dct_trn32 -#undef dct_trn64 - } - - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); - - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! - - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); - - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); - - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); - - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); - -#undef dct_trn8_8 -#undef dct_trn8_16 -#undef dct_trn8_32 - } - -#undef dct_long_mul -#undef dct_long_mac -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_pass -} - -#endif // STBI_NEON - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); - int t = q & 15,i; - if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } - - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - L = stbi__get16be(z->s); - if (L < 2) { - if (m == 0xFE) - return stbi__err("bad COM len","Corrupt JPEG"); - else - return stbi__err("bad APP len","Corrupt JPEG"); - } - L -= 2; - - if (m == 0xE0 && L >= 5) { // JFIF APP0 segment - static const unsigned char tag[5] = {'J','F','I','F','\0'}; - int ok = 1; - int i; - for (i=0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment - static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; - int ok = 1; - int i; - for (i=0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform - L -= 6; - } - } - - stbi__skip(z->s, L); - return 1; - } - - return stbi__err("unknown marker","Corrupt JPEG"); -} - -// after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } - - return 1; -} - -static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) -{ - int i; - for (i=0; i < ncomp; ++i) { - if (z->img_comp[i].raw_data) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } - } - return why; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - - z->rgb = 0; - for (i=0; i < s->img_n; ++i) { - static const unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != STBI__SCAN_load) return 1; - - if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - // these sizes can't be more than 17 bits - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - if (z->progressive) { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) - -#define stbi__SOF_progressive(x) ((x) == 0xc2) - -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->jfif = 0; - z->app14_color_transform = -1; // valid values are 0,1,2 - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -// decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else if (stbi__DNL(m)) { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; - - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { -#if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); - - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); - - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); -#elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); - - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); -#endif - - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } - - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); - - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} -#endif - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -// this is a reduced-precision calculation of YCbCr-to-RGB introduced -// to make sure the code produces the same results in both SIMD and scalar -#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; - -#ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel - - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); - - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); - - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); - - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); - - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } -#endif - -#ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); - - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); - - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); - - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } -#endif - - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -// set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; - -#ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; -#endif -} - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - stbi__free_jpeg_components(j, j->s->img_n, 0); -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -// fast 0..255 * 0..255 => 0..255 rounded multiplication -static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) -{ - unsigned int t = x*y + 128; - return (stbi_uc) ((t + (t >>8)) >> 8); -} - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n, is_rgb; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; - - is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - - if (z->s->img_n == 3 && n < 3 && !is_rgb) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - if (is_rgb) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else if (z->s->img_n == 4) { - if (z->app14_color_transform == 0) { // CMYK - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } else if (z->app14_color_transform == 2) { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } else { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - if (is_rgb) { - if (n == 1) - for (i=0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else { - for (i=0; i < z->s->img_x; ++i, out += 2) { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } - } - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output - return output; - } -} - -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - STBI_NOTUSED(ri); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return result; -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); - stbi__rewind(s); - STBI_FREE(j); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int result; - stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; -} -#endif - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -#ifndef STBI_NO_ZLIB - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int j = stbi__bit_reverse(next_code[s],s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static int stbi__zeof(stbi__zbuf *z) -{ - return (z->zbuffer >= z->zbuffer_end); -} - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - return stbi__zeof(z) ? 0 : *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - if (z->code_buffer >= (1U << z->num_bits)) { - z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ - return; - } - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s >= 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - if ((unsigned int)b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! - if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) { - if (stbi__zeof(a)) { - return -1; /* report error for unexpected end of data. */ - } - stbi__fill_bits(a); - } - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); -} - -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes -{ - char *q; - unsigned int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (unsigned int) (z->zout - z->zout_start); - limit = old_limit = (unsigned) (z->zout_end - z->zout_start); - if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); - while (cur + n > limit) { - if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); - limit *= 2; - } - q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static const int stbi__zlength_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static const int stbi__zlength_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, -257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static const int stbi__zdist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - if (len) { do *zout++ = v; while (--len); } - } else { - if (len) { do *zout++ = *p++; while (--len); } - } - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else { - stbi_uc fill = 0; - if (c == 16) { - c = stbi__zreceive(a,2)+3; - if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n-1]; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - } else if (c == 18) { - c = stbi__zreceive(a,7)+11; - } else { - return stbi__err("bad codelengths", "Corrupt PNG"); - } - if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes+n, fill, c); - n += c; - } - } - if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncompressed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -static const stbi_uc stbi__zdefault_length[288] = -{ - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 -}; -static const stbi_uc stbi__zdefault_distance[32] = -{ - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 -}; -/* -Init algorithm: -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} -*/ - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} -#endif - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - -#ifndef STBI_NO_PNG -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; - int depth; -} stbi__png; - - -enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int stbi__paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - int bytes = (depth == 16? 2 : 1); - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n*bytes; - stbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; // copy it into a local for later - - int output_bytes = out_n*bytes; - int filter_bytes = img_n*bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - - // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, - // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), - // so just check for raw_len < img_len always. - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; - int filter = *raw++; - - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } - - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - - if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4) ); - } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; - if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; - } - } - } - } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } - - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, - a->out + (j*x+i)*out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16*) z->out; - - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag = flag_true_if_should_convert; -} - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = ( t * 255 + half) / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]={0}; - stbi__uint16 tc16[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); - s->img_y = stbi__get32be(s); - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } - - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; - } - STBI_FREE(z->expanded); z->expanded = NULL; - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) -{ - void *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth <= 8) - ri->bits_per_channel = 8; - else if (p->depth == 16) - ri->bits_per_channel = 16; - else - return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; - - return result; -} - -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp, ri); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} - -static int stbi__png_is16(stbi__context *s) -{ - stbi__png p; - p.s = s; - if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) - return 0; - if (p.depth != 16) { - stbi__rewind(p.s); - return 0; - } - return 1; -} -#endif - -// Microsoft/Windows BMP image - -#ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) { n += 16; z >>= 16; } - if (z >= 0x00100) { n += 8; z >>= 8; } - if (z >= 0x00010) { n += 4; z >>= 4; } - if (z >= 0x00004) { n += 2; z >>= 2; } - if (z >= 0x00002) { n += 1;/* >>= 1;*/ } - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -// extract an arbitrarily-aligned N-bit value (N=bits) -// from v, and then make it 8-bits long and fractionally -// extend it to full full range. -static int stbi__shiftsigned(unsigned int v, int shift, int bits) -{ - static unsigned int mul_table[9] = { - 0, - 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, - 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, - }; - static unsigned int shift_table[9] = { - 0, 0,0,1,0,2,4,6,0, - }; - if (shift < 0) - v <<= -shift; - else - v >>= shift; - STBI_ASSERT(v < 256); - v >>= (8-bits); - STBI_ASSERT(bits >= 0 && bits <= 8); - return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; -} - -typedef struct -{ - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; - int extra_read; -} stbi__bmp_data; - -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - info->extra_read = 14; - - if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); - - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->extra_read += 12; - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *) 1; -} - - -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, all_a; - stbi_uc pal[256][4]; - int psize=0,i,j,width; - int flip_vertically, pad, target; - stbi__bmp_data info; - STBI_NOTUSED(ri); - - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set - - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; - - if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - info.extra_read - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - info.extra_read - info.hsz) >> 2; - } - if (psize == 0) { - STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); - if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) { - return stbi__errpuc("bad offset", "Corrupt BMP"); - } - } - - if (info.bpp == 24 && ma == 0xff000000) - s->img_n = 3; - else - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - - // sanity-check size - if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); - - out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - if (info.bpp == 1) { - for (j=0; j < (int) s->img_y; ++j) { - int bit_offset = 7, v = stbi__get8(s); - for (i=0; i < (int) s->img_x; ++i) { - int color = (v>>bit_offset)&0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - if((--bit_offset) < 0) { - bit_offset = 7; - v = stbi__get8(s); - } - } - stbi__skip(s, pad); - } - } else { - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - info.extra_read - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } - - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; - - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i]; p1[i] = p2[i]; p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} -#endif - -// Targa Truevision - TGA -// by Jonathan Dummer -#ifndef STBI_NO_TGA -// returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if (is_rgb16) *is_rgb16 = 0; - switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // fallthrough - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough - case 32: return bits_per_pixel/8; - default: return 0; - } -} - -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( tga_color_type == 1 ) { // colormapped (paletted) image - if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - stbi__skip(s,4); // skip image x and y origin - } else { // "normal" image w/o colormap - if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s,9); // skip colormap specification and image x/y origin - } - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 - -errorEnd: - stbi__rewind(s); - return res; -} - -// read 16bit value and convert to 24bit RGB -static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = (stbi__uint16)stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (stbi_uc)((r * 255)/31); - out[1] = (stbi_uc)((g * 255)/31); - out[2] = (stbi_uc)((b * 255)/31); - - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. -} - -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16=0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4] = {0}; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - STBI_NOTUSED(ri); - STBI_NOTUSED(tga_x_origin); // @TODO - STBI_NOTUSED(tga_y_origin); // @TODO - - if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); - - tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { - for (i=0; i < tga_height; ++i) { - int row = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - if (tga_palette_len == 0) { /* you have to have at least one entry! */ - STBI_FREE(tga_data); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } - - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - STBI_NOTUSED(tga_palette_start); - // OK, done - return tga_data; -} -#endif - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) -{ - int count, nleft, len; - - count = 0; - while ((nleft = pixelCount - count) > 0) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) return 0; // corrupt data - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - - return 1; -} - -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - int pixelCount; - int channelCount, compression; - int channel, i; - int bitdepth; - int w,h; - stbi_uc *out; - STBI_NOTUSED(ri); - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Check size - if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); - - // Create the destination image. - - if (!compression && bitdepth == 16 && bpc == 16) { - out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; - } else - out = (stbi_uc *) stbi__malloc(4 * w*h); - - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - if (channel >= channelCount) { - // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) { - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; - } else { - stbi_uc *p = out+channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } - } else { - if (ri->bits_per_channel == 16) { // output bpc - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16) stbi__get16be(s); - } else { - stbi_uc *p = out+channel; - if (bitdepth == 16) { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - } - - // remove weird white matte from PSD - if (channelCount >= 4) { - if (ri->bits_per_channel == 16) { - for (i=0; i < w*h; ++i) { - stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; - if (pixel[3] != 0 && pixel[3] != 65535) { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); - pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); - pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); - } - } - } else { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); - } - } - } - } - - // convert to desired output format - if (req_comp && req_comp != 4) { - if (ri->bits_per_channel == 16) - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = 4; - *y = h; - *x = w; - - return out; -} -#endif - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -#ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) -{ - stbi_uc *result; - int i, x,y, internal_comp; - STBI_NOTUSED(ri); - - if (!comp) comp = &internal_comp; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - - if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} -#endif - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb - -#ifndef STBI_NO_GIF -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[8192]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; - int delay; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); - stbi__rewind( s ); - return 0; - } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - int idx; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - idx = g->cur_x + g->cur_y; - p = &g->out[idx]; - g->history[idx / 4] = 1; - - c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc) init_code; - g->codes[init_code].suffix = (stbi_uc) init_code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) { - return stbi__errpuc("no clear code", "Corrupt GIF"); - } - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 8192) { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } - - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -// two back is the image from two frames ago, used for a very specific disposal format -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) -{ - int dispose; - int first_frame; - int pi; - int pcount; - STBI_NOTUSED(req_comp); - - // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) - return stbi__errpuc("too large", "GIF image is too large"); - pcount = g->w * g->h; - g->out = (stbi_uc *) stbi__malloc(4 * pcount); - g->background = (stbi_uc *) stbi__malloc(4 * pcount); - g->history = (stbi_uc *) stbi__malloc(pcount); - if (!g->out || !g->background || !g->history) - return stbi__errpuc("outofmem", "Out of memory"); - - // image is treated as "transparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to the color that was there the previous frame. - memset(g->out, 0x00, 4 * pcount); - memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) - memset(g->history, 0x00, pcount); // pixels that were affected previous frame - first_frame = 1; - } else { - // second frame - how do we dispose of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; - - if ((dispose == 3) && (two_back == 0)) { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } - - if (dispose == 3) { // use previous graphic - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); - } - } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); - } - } - } else { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } - - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); - } - - // clear my history; - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - - for (;;) { - int tag = stbi__get8(s); - switch (tag) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - // if the width of the specified rectangle is 0, that means - // we may not see *any* pixels or the image is malformed; - // to make sure this is caught, move the current y down to - // max_y (which is what out_gif_code checks). - if (w == 0) - g->cur_y = g->max_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (!o) return NULL; - - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); - } - } - } - - return o; - } - - case 0x21: // Comment Extension. - { - int len; - int ext = stbi__get8(s); - if (ext == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. - - // unset old transparent - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; - } - } else { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) { - stbi__skip(s, len); - } - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } -} - -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - if (stbi__gif_test(s)) { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - memset(&g, 0, sizeof(g)); - if (delays) { - *delays = 0; - } - - do { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - - if (u) { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; - - if (out) { - void *tmp = (stbi_uc*) STBI_REALLOC( out, layers * stride ); - if (NULL == tmp) { - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - return stbi__errpuc("outofmem", "Out of memory"); - } - else { - out = (stbi_uc*) tmp; - } - - if (delays) { - *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); - } - } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - } - } - memcpy( out + ((layers - 1) * stride), u, stride ); - if (layers >= 2) { - two_back = out - 2 * stride; - } - - if (delays) { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); - - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - - *z = layers; - return out; - } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); - } -} - -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - STBI_NOTUSED(ri); - - u = stbi__gif_load_next(s, &g, comp, req_comp, 0); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - - // moved conversion to after successful load so that the same - // can be done for multiple frames. - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g.w, g.h); - } else if (g.out) { - // if there was an error and we allocated an image buffer, free it! - STBI_FREE(g.out); - } - - // free buffers needed for multiple frame loading; - STBI_FREE(g.history); - STBI_FREE(g.background); - - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s, const char *signature) -{ - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - stbi__rewind(s); - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); - stbi__rewind(s); - if(!r) { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); - } - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - const char *headerToken; - STBI_NOTUSED(ri); - - // Check identifier - headerToken = stbi__hdr_gettoken(s,buffer); - if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); - - // Read data - hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); - if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); - if (!scanline) { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); - } - } - - for (k = 0; k < 4; ++k) { - int nleft; - i = 0; - while ((nleft = width - i) > 0) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - if (scanline) - STBI_FREE(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int dummy; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -#ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; - - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) - return 0; - if (x) *x = s->img_x; - if (y) *y = s->img_y; - if (comp) { - if (info.bpp == 24 && info.ma == 0xff000000) - *comp = 3; - else - *comp = info.ma ? 4 : 3; - } - return 1; -} -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount, dummy, depth; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 8 && depth != 16) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} - -static int stbi__psd_is16(stbi__context *s) -{ - int channelCount, depth; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - (void) stbi__get32be(s); - (void) stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 16) { - stbi__rewind( s ); - return 0; - } - return 1; -} -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained,dummy; - stbi__pic_packet packets[10]; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; - } - - stbi__skip(s, 88); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; - } - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} -#endif - -// ************************************************************************************************* -// Portable Gray Map and Portable Pixel Map loader -// by Ken Miller -// -// PGM: http://netpbm.sourceforge.net/doc/pgm.html -// PPM: http://netpbm.sourceforge.net/doc/ppm.html -// -// Known limitations: -// Does not support comments in the header section -// Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel - -#ifndef STBI_NO_PNM - -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; -} - -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - STBI_NOTUSED(ri); - - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) - return 0; - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "PNM too large"); - - out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); - - if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; -} - -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; -} - -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); - - if (stbi__at_eof(s) || *c != '#') - break; - - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); - } -} - -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; - - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - } - - return value; -} - -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv, dummy; - char c, p, t; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - stbi__rewind(s); - - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind(s); - return 0; - } - - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); - - *x = stbi__pnm_getinteger(s, &c); // read width - stbi__pnm_skip_whitespace(s, &c); - - *y = stbi__pnm_getinteger(s, &c); // read height - stbi__pnm_skip_whitespace(s, &c); - - maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); - else - return 1; -} -#endif - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; - #endif - - // test tga last because it's a crappy test! - #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; - #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -static int stbi__is_16_main(stbi__context *s) -{ - #ifndef STBI_NO_PNG - if (stbi__png_is16(s)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_is16(s)) return 1; - #endif - - return 0; -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} - -STBIDEF int stbi_is_16_bit(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; -} - -STBIDEF int stbi_is_16_bit_from_file(FILE *f) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__is_16_main(&s); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__is_16_main(&s); -} - -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__is_16_main(&s); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug - 1-bit BMP - *_is_16_bit api - avoid warnings - 2.16 (2017-07-23) all functions have 16-bit variants; - STBI_NO_STDIO works again; - compilation fixes; - fix rounding in unpremultiply; - optimize vertical flip; - disable raw_len validation; - documentation fixes - 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; - warning fixes; disable run-time SSE detection on gcc; - uniform handling of optional "return" values; - thread-safe initialization of zlib tables - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) allocate large structures on the stack - remove white matting for transparent PSD - fix reported channel count for PNG & BMP - re-enable SSE2 in non-gcc 64-bit - support RGB-formatted JPEG - read 16-bit PNGs (only as 8-bit) - 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED - 2.09 (2016-01-16) allow comments in PNM files - 16-bit-per-pixel TGA (not bit-per-component) - info() for TGA could break due to .hdr handling - info() for BMP to shares code instead of sloppy parse - can use STBI_REALLOC_SIZED if allocator doesn't support realloc - code cleanup - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) fix compiler warnings - partial animated GIF support - limited 16-bpc PSD support - #ifdef unused functions - bug with < 92 byte PIC,PNM,HDR,TGA - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) extra corruption checking (mmozeiko) - stbi_set_flip_vertically_on_load (nguillemot) - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 (2008-08-02) - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 (2006-11-19) - first released version -*/ - - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/contrib/stb_image/stb_image.h b/contrib/stb_image/stb_image.h index d9c21bc81..accef4839 100644 --- a/contrib/stb_image/stb_image.h +++ b/contrib/stb_image/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.19 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: @@ -48,6 +48,13 @@ LICENSE RECENT REVISION HISTORY: + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings @@ -84,23 +91,33 @@ RECENT REVISION HISTORY: Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) Arseny Kapoulkine John-Mark Allen + Carmelo J Fdez-Aguera Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan - Dave Moore Roy Eltham Hayaki Saito Nathan Reed - Won Chun Luke Graham Johan Duparc Nick Verigakis - the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar - Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex - Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 - Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw - Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus - Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo - Christian Floisand Kevin Schmidt github:darealshinji - Blazej Dariusz Roszkowski github:Michaelangel007 + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko [reserved] + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. */ #ifndef STBI_INCLUDE_STB_IMAGE_H @@ -161,6 +178,16 @@ RECENT REVISION HISTORY: // // =========================================================================== // +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// // Philosophy // // stb libraries are designed with the following priorities: @@ -171,12 +198,12 @@ RECENT REVISION HISTORY: // // Sometimes I let "good performance" creep up in priority over "easy to maintain", // and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy to use ones. Nevertheless, it's important +// performance, in addition to the easy-to-use ones. Nevertheless, it's important // to keep in mind that from the standpoint of you, a client of this library, // all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. // // Some secondary priorities arise directly from the first two, some of which -// make more explicit reasons why performance can't be emphasized. +// provide more explicit reasons why performance can't be emphasized. // // - Portable ("ease of use") // - Small source code footprint ("easy to maintain") @@ -219,11 +246,10 @@ RECENT REVISION HISTORY: // // HDR image support (disable by defining STBI_NO_HDR) // -// stb_image now supports loading HDR images in general, and currently -// the Radiance .HDR file format, although the support is provided -// generically. You can still load any file through the existing interface; -// if you attempt to load an HDR file, it will be automatically remapped to -// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; // both of these constants can be reconfigured through this interface: // // stbi_hdr_to_ldr_gamma(2.2f); @@ -257,7 +283,7 @@ RECENT REVISION HISTORY: // // By default we convert iphone-formatted PNGs back to RGB, even though // they are internally encoded differently. You can disable this conversion -// by by calling stbi_convert_iphone_png_to_rgb(0), in which case +// by calling stbi_convert_iphone_png_to_rgb(0), in which case // you will always just get the native iphone "format" through (which // is BGR stored in RGB). // @@ -301,7 +327,14 @@ RECENT REVISION HISTORY: // - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still // want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB // - +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. #ifndef STBI_NO_STDIO #include @@ -319,6 +352,7 @@ enum STBI_rgb_alpha = 4 }; +#include typedef unsigned char stbi_uc; typedef unsigned short stbi_us; @@ -326,11 +360,13 @@ typedef unsigned short stbi_us; extern "C" { #endif +#ifndef STBIDEF #ifdef STB_IMAGE_STATIC #define STBIDEF static #else #define STBIDEF extern #endif +#endif ////////////////////////////////////////////////////////////////////////////// // @@ -355,10 +391,6 @@ typedef struct STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - #ifndef STBI_NO_STDIO STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); @@ -366,6 +398,14 @@ STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in // for stbi_load_from_file, file pointer is left pointing immediately after image #endif +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + //////////////////////////////////// // // 16-bits-per-channel interface @@ -413,7 +453,7 @@ STBIDEF int stbi_is_hdr_from_file(FILE *f); // get a VERY brief reason for failure -// NOT THREADSAFE +// on most compilers (and ALL modern mainstream compilers) this is threadsafe STBIDEF const char *stbi_failure_reason (void); // free the loaded image -- this is just free() @@ -446,6 +486,11 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); // flip the image vertically, so the first pixel in the output array is the bottom left STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + // ZLIB client - used by PNG, available for other purposes STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); @@ -525,6 +570,12 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #define STBI_ASSERT(x) assert(x) #endif +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + #ifndef _MSC_VER #ifdef __cplusplus @@ -536,6 +587,23 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #define stbi_inline __forceinline #endif +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif #ifdef _MSC_VER typedef unsigned short stbi__uint16; @@ -649,14 +717,18 @@ static int stbi__cpuid3(void) #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { int info3 = stbi__cpuid3(); return ((info3 >> 26) & 1) != 0; } +#endif + #else // assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { // If we're even attempting to compile this on GCC/Clang, that means @@ -664,6 +736,8 @@ static int stbi__sse2_available(void) // instructions at will, and so are we. return 1; } +#endif + #endif #endif @@ -682,6 +756,10 @@ static int stbi__sse2_available(void) #define STBI_SIMD_ALIGN(type, name) type name #endif +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + /////////////////////////////////////////////// // // stbi__context struct and start_xxx functions @@ -699,6 +777,7 @@ typedef struct int read_from_callbacks; int buflen; stbi_uc buffer_start[128]; + int callback_already_read; stbi_uc *img_buffer, *img_buffer_end; stbi_uc *img_buffer_original, *img_buffer_original_end; @@ -712,6 +791,7 @@ static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) { s->io.read = NULL; s->read_from_callbacks = 0; + s->callback_already_read = 0; s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; } @@ -723,7 +803,8 @@ static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void * s->io_user_data = user; s->buflen = sizeof(s->buffer_start); s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; stbi__refill_buffer(s); s->img_buffer_original_end = s->img_buffer_end; } @@ -737,12 +818,17 @@ static int stbi__stdio_read(void *user, char *data, int size) static void stbi__stdio_skip(void *user, int n) { + int ch; fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } } static int stbi__stdio_eof(void *user) { - return feof((FILE*) user); + return feof((FILE*) user) || ferror((FILE *) user); } static stbi_io_callbacks stbi__stdio_callbacks = @@ -840,19 +926,24 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); #endif -// this is not threadsafe -static const char *stbi__g_failure_reason; +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; STBIDEF const char *stbi_failure_reason(void) { return stbi__g_failure_reason; } +#ifndef STBI_NO_FAILURE_STRINGS static int stbi__err(const char *str) { stbi__g_failure_reason = str; return 0; } +#endif static void *stbi__malloc(size_t size) { @@ -891,11 +982,13 @@ static int stbi__mul2sizes_valid(int a, int b) return a <= INT_MAX/b; } +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow static int stbi__mad2sizes_valid(int a, int b, int add) { return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); } +#endif // returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow static int stbi__mad3sizes_valid(int a, int b, int c, int add) @@ -913,12 +1006,14 @@ static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) } #endif +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // mallocs with size overflow checking static void *stbi__malloc_mad2(int a, int b, int add) { if (!stbi__mad2sizes_valid(a, b, add)) return NULL; return stbi__malloc(a*b + add); } +#endif static void *stbi__malloc_mad3(int a, int b, int c, int add) { @@ -962,13 +1057,29 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); #endif -static int stbi__vertically_flip_on_load = 0; +static int stbi__vertically_flip_on_load_global = 0; STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { - stbi__vertically_flip_on_load = flag_true_if_should_flip; + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; } +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields @@ -990,6 +1101,8 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re #endif #ifndef STBI_NO_PSD if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); #endif #ifndef STBI_NO_PIC if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); @@ -1070,6 +1183,7 @@ static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) } } +#ifndef STBI_NO_GIF static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) { int slice; @@ -1077,10 +1191,11 @@ static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int byt stbi_uc *bytes = (stbi_uc *)image; for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; } } +#endif static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { @@ -1090,8 +1205,10 @@ static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, if (result == NULL) return NULL; + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + if (ri.bits_per_channel != 8) { - STBI_ASSERT(ri.bits_per_channel == 16); result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 8; } @@ -1114,8 +1231,10 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, if (result == NULL) return NULL; + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + if (ri.bits_per_channel != 16) { - STBI_ASSERT(ri.bits_per_channel == 8); result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 16; } @@ -1131,7 +1250,7 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, return (stbi__uint16 *) result; } -#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR) +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { @@ -1143,10 +1262,38 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in #ifndef STBI_NO_STDIO +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + static FILE *stbi__fopen(char const *filename, char const *mode) { FILE *f; -#if defined(_MSC_VER) && _MSC_VER >= 1400 +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != fopen_s(&f, filename, mode)) f=0; #else @@ -1237,15 +1384,15 @@ STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *u STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - + stbi__context s; + stbi__start_mem(&s,buffer,len); + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); } - return result; + return result; } #endif @@ -1390,6 +1537,7 @@ enum static void stbi__refill_buffer(stbi__context *s) { int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); if (n == 0) { // at end of file, treat same as if from memory, but need to handle case // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file @@ -1414,6 +1562,9 @@ stbi_inline static stbi_uc stbi__get8(stbi__context *s) return 0; } +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else stbi_inline static int stbi__at_eof(stbi__context *s) { if (s->io.read) { @@ -1425,9 +1576,14 @@ stbi_inline static int stbi__at_eof(stbi__context *s) return s->img_buffer >= s->img_buffer_end; } +#endif +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else static void stbi__skip(stbi__context *s, int n) { + if (n == 0) return; // already there! if (n < 0) { s->img_buffer = s->img_buffer_end; return; @@ -1442,7 +1598,11 @@ static void stbi__skip(stbi__context *s, int n) } s->img_buffer += n; } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) { if (s->io.read) { @@ -1466,18 +1626,27 @@ static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) } else return 0; } +#endif +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else static int stbi__get16be(stbi__context *s) { int z = stbi__get8(s); return (z << 8) + stbi__get8(s); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else static stbi__uint32 stbi__get32be(stbi__context *s) { stbi__uint32 z = stbi__get16be(s); return (z << 16) + stbi__get16be(s); } +#endif #if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) // nothing @@ -1499,7 +1668,9 @@ static stbi__uint32 stbi__get32le(stbi__context *s) #define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else ////////////////////////////////////////////////////////////////////////////// // // generic converter from built-in img_n to req_comp @@ -1515,7 +1686,11 @@ static stbi_uc stbi__compute_y(int r, int g, int b) { return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; @@ -1539,19 +1714,19 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } @@ -1559,12 +1734,20 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r STBI_FREE(data); return good; } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else static stbi__uint16 stbi__compute_y_16(int r, int g, int b) { return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; @@ -1588,19 +1771,19 @@ static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int r // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } @@ -1608,6 +1791,7 @@ static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int r STBI_FREE(data); return good; } +#endif #ifndef STBI_NO_LINEAR static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) @@ -1623,7 +1807,11 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) for (k=0; k < n; ++k) { output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } } STBI_FREE(data); return output; @@ -1904,7 +2092,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB k = stbi_lrot(j->code_buffer, n); - STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; @@ -2015,6 +2203,7 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__ // first scan for DC coefficient, must be first memset(data,0,64*sizeof(data[0])); // 0 all the ac values now t = stbi__jpeg_huff_decode(j, hdc); + if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); diff = t ? stbi__extend_receive(j, t) : 0; dc = j->img_comp[b].dc_pred + diff; @@ -3005,6 +3194,8 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); c = stbi__get8(s); if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); s->img_n = c; @@ -3596,7 +3787,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp int k; unsigned int i,j; stbi_uc *output; - stbi_uc *coutput[4]; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; stbi__resample res_comp[4]; @@ -3717,7 +3908,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp if (n == 1) for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } } } } @@ -3885,16 +4076,23 @@ typedef struct stbi__zhuffman z_length, z_distance; } stbi__zbuf; +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) { - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; + return stbi__zeof(z) ? 0 : *z->zbuffer++; } static void stbi__fill_bits(stbi__zbuf *z) { do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; z->num_bits += 8; } while (z->num_bits <= 24); @@ -3919,10 +4117,11 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) for (s=STBI__ZFAST_BITS+1; ; ++s) if (k < z->maxcode[s]) break; - if (s == 16) return -1; // invalid code! + if (s >= 16) return -1; // invalid code! // code size is s, so: b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); + if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. a->code_buffer >>= s; a->num_bits -= s; return z->value[b]; @@ -3931,7 +4130,12 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { int b,s; - if (a->num_bits < 16) stbi__fill_bits(a); + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; if (b) { s = b >> 9; @@ -3945,13 +4149,16 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes { char *q; - int cur, limit, old_limit; + unsigned int cur, limit, old_limit; z->zout = zout; if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = old_limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); limit *= 2; + } q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); STBI_NOTUSED(old_limit); if (q == NULL) return stbi__err("outofmem", "Out of memory"); @@ -4049,11 +4256,12 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) c = stbi__zreceive(a,2)+3; if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); fill = lencodes[n-1]; - } else if (c == 17) + } else if (c == 17) { c = stbi__zreceive(a,3)+3; - else { - STBI_ASSERT(c == 18); + } else if (c == 18) { c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); } if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); memset(lencodes+n, fill, c); @@ -4079,7 +4287,7 @@ static int stbi__parse_uncompressed_block(stbi__zbuf *a) a->code_buffer >>= 8; a->num_bits -= 8; } - STBI_ASSERT(a->num_bits == 0); + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); // now fill header the normal way while (k < 4) header[k++] = stbi__zget8(a); @@ -4101,6 +4309,7 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) int cm = cmf & 15; /* int cinfo = cmf >> 4; */ int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png @@ -4362,7 +4571,7 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r return stbi__err("invalid filter","Corrupt PNG"); if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place filter_bytes = 1; width = img_width_bytes; @@ -4731,7 +4940,7 @@ static void stbi__de_iphone(stbi__png *z) static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]; + stbi_uc has_trans=0, tc[3]={0}; stbi__uint16 tc16[3]; stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; int first=1,k,interlace=0, color=0, is_iphone=0; @@ -4757,8 +4966,10 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); first = 0; if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); @@ -4875,6 +5086,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) ++s->img_n; } STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); return 1; } @@ -4905,10 +5118,12 @@ static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, st void *result=NULL; if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth < 8) + if (p->depth <= 8) ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; else - ri->bits_per_channel = p->depth; + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { @@ -5009,11 +5224,11 @@ static int stbi__high_bit(unsigned int z) { int n=0; if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } return n; } @@ -5030,7 +5245,7 @@ static int stbi__bitcount(unsigned int a) // extract an arbitrarily-aligned N-bit value (N=bits) // from v, and then make it 8-bits long and fractionally // extend it to full full range. -static int stbi__shiftsigned(int v, int shift, int bits) +static int stbi__shiftsigned(unsigned int v, int shift, int bits) { static unsigned int mul_table[9] = { 0, @@ -5044,7 +5259,7 @@ static int stbi__shiftsigned(int v, int shift, int bits) v <<= -shift; else v >>= shift; - STBI_ASSERT(v >= 0 && v < 256); + STBI_ASSERT(v < 256); v >>= (8-bits); STBI_ASSERT(bits >= 0 && bits <= 8); return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; @@ -5054,6 +5269,7 @@ typedef struct { int bpp, offset, hsz; unsigned int mr,mg,mb,ma, all_a; + int extra_read; } stbi__bmp_data; static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) @@ -5066,6 +5282,9 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->offset = stbi__get32le(s); info->hsz = hsz = stbi__get32le(s); info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); if (hsz == 12) { @@ -5109,6 +5328,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); + info->extra_read += 12; // not documented, but generated by photoshop and handled by mspaint if (info->mr == info->mg && info->mg == info->mb) { // ?!?!? @@ -5157,6 +5377,9 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req flip_vertically = ((int) s->img_y) > 0; s->img_y = abs((int) s->img_y); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + mr = info.mr; mg = info.mg; mb = info.mb; @@ -5165,13 +5388,22 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req if (info.hsz == 12) { if (info.bpp < 24) - psize = (info.offset - 14 - 24) / 3; + psize = (info.offset - info.extra_read - 24) / 3; } else { if (info.bpp < 16) - psize = (info.offset - 14 - info.hsz) >> 2; + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); + if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } } - s->img_n = ma ? 4 : 3; + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else @@ -5193,7 +5425,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req if (info.hsz != 12) stbi__get8(s); pal[i][3] = 255; } - stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); if (info.bpp == 1) width = (s->img_x + 7) >> 3; else if (info.bpp == 4) width = (s->img_x + 1) >> 1; else if (info.bpp == 8) width = s->img_x; @@ -5207,6 +5439,8 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req out[z++] = pal[color][0]; out[z++] = pal[color][1]; out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; if((--bit_offset) < 0) { bit_offset = 7; v = stbi__get8(s); @@ -5240,7 +5474,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; int z = 0; int easy=0; - stbi__skip(s, info.offset - 14 - info.hsz); + stbi__skip(s, info.offset - info.extra_read - info.hsz); if (info.bpp == 24) width = 3 * s->img_x; else if (info.bpp == 16) width = 2*s->img_x; else /* bpp = 32 and pad = 0 */ width=0; @@ -5258,6 +5492,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } } for (j=0; j < (int) s->img_y; ++j) { if (easy) { @@ -5299,7 +5534,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req stbi_uc *p1 = out + j *s->img_x*target; stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; + t = p1[i]; p1[i] = p2[i]; p2[i] = t; } } } @@ -5479,6 +5714,11 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req int RLE_repeating = 0; int read_next_pixel = 1; STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); // do a tiny bit of precessing if ( tga_image_type >= 8 ) @@ -5519,6 +5759,11 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // do I need to load a palette? if ( tga_indexed) { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + // any data to skip? (offset usually = 0) stbi__skip(s, tga_palette_start ); // load the palette @@ -5642,6 +5887,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // Microsoft's C compilers happy... [8^( tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); // OK, done return tga_data; } @@ -5726,6 +5972,9 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req h = stbi__get32be(s); w = stbi__get32be(s); + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + // Make sure the depth is 8 bits. bitdepth = stbi__get16be(s); if (bitdepth != 8 && bitdepth != 16) @@ -5789,7 +6038,7 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req // Else if n is 128, noop. // Endloop - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, // which we're going to just skip. stbi__skip(s, h * channelCount * 2 ); @@ -6080,6 +6329,10 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c x = stbi__get16be(s); y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); @@ -6127,7 +6380,7 @@ typedef struct int w,h; stbi_uc *out; // output buffer (always 4 components) stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; + stbi_uc *history; int flags, bgindex, ratio, transparent, eflags; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; @@ -6188,6 +6441,9 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in g->ratio = stbi__get8(s); g->transparent = -1; + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments if (is_info) return 1; @@ -6215,7 +6471,7 @@ static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { stbi_uc *p, *c; - int idx; + int idx; // recurse to decode the prefixes, since the linked-list is backwards, // and working backwards through an interleaved image would be nasty @@ -6224,12 +6480,12 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) if (g->cur_y >= g->max_y) return; - idx = g->cur_x + g->cur_y; + idx = g->cur_x + g->cur_y; p = &g->out[idx]; - g->history[idx / 4] = 1; + g->history[idx / 4] = 1; c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; + if (c[3] > 128) { // don't render transparent pixels; p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; @@ -6338,31 +6594,36 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) // two back is the image from two frames ago, used for a very specific disposal format static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) { - int dispose; - int first_frame; - int pi; - int pcount; + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; + first_frame = 0; if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->history = (stbi_uc *) stbi__malloc(g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); - // image is treated as "tranparent" at the start - ie, nothing overwrites the current background; + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to teh color that was there the previous frame. - memset( g->out, 0x00, 4 * g->w * g->h ); - memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent) - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - first_frame = 1; + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; } else { - // second frame - how do we dispoase of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; if ((dispose == 3) && (two_back == 0)) { dispose = 2; // if I don't have an image to revert back to, default to the old background @@ -6371,32 +6632,32 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i if (dispose == 3) { // use previous graphic for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); } } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); } } } else { - // This is a non-disposal case eithe way, so just + // This is a non-disposal case eithe way, so just // leave the pixels as is, and they will become the new background // 1: do not dispose // 0: not specified. } - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); } - // clear my history; + // clear my history; memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame for (;;) { - int tag = stbi__get8(s); + int tag = stbi__get8(s); switch (tag) { case 0x2C: /* Image Descriptor */ { @@ -6418,6 +6679,13 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i g->cur_x = g->start_x; g->cur_y = g->start_y; + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + g->lflags = stbi__get8(s); if (g->lflags & 0x40) { @@ -6434,19 +6702,19 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i } else if (g->flags & 0x80) { g->color_table = (stbi_uc *) g->pal; } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (o == NULL) return NULL; + return stbi__errpuc("missing color table", "Corrupt GIF"); - // if this was the first frame, - pcount = g->w * g->h; + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; if (first_frame && (g->bgindex > 0)) { // if first frame, any pixel not drawn to gets the background color for (pi = 0; pi < pcount; ++pi) { if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); } } } @@ -6457,7 +6725,7 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i case 0x21: // Comment Extension. { int len; - int ext = stbi__get8(s); + int ext = stbi__get8(s); if (ext == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { @@ -6466,23 +6734,23 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i // unset old transparent if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } + g->pal[g->transparent][3] = 255; + } if (g->eflags & 0x01) { g->transparent = stbi__get8(s); if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; + g->pal[g->transparent][3] = 0; } } else { // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; + stbi__skip(s, 1); + g->transparent = -1; } } else { stbi__skip(s, len); break; } - } + } while ((len = stbi__get8(s)) != 0) { stbi__skip(s, len); } @@ -6501,15 +6769,17 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { if (stbi__gif_test(s)) { - int layers = 0; + int layers = 0; stbi_uc *u = 0; stbi_uc *out = 0; - stbi_uc *two_back = 0; + stbi_uc *two_back = 0; stbi__gif g; - int stride; + int stride; + int out_size = 0; + int delays_size = 0; memset(&g, 0, sizeof(g)); if (delays) { - *delays = 0; + *delays = 0; } do { @@ -6519,44 +6789,58 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, if (u) { *x = g.w; *y = g.h; - ++layers; - stride = g.w * g.h * 4; - + ++layers; + stride = g.w * g.h * 4; + if (out) { - out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (NULL == tmp) { + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + return stbi__errpuc("outofmem", "Out of memory"); + } + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + if (delays) { - *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); + *delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + delays_size = layers * sizeof(int); } } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); + out = (stbi_uc*)stbi__malloc( layers * stride ); + out_size = layers * stride; if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + delays_size = layers * sizeof(int); } } - memcpy( out + ((layers - 1) * stride), u, stride ); + memcpy( out + ((layers - 1) * stride), u, stride ); if (layers >= 2) { - two_back = out - 2 * stride; + two_back = out - 2 * stride; } if (delays) { - (*delays)[layers - 1U] = g.delay; + (*delays)[layers - 1U] = g.delay; } } - } while (u != 0); + } while (u != 0); - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); - // do the final conversion after loading everything; + // do the final conversion after loading everything; if (req_comp && req_comp != 4) out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - *z = layers; + *z = layers; return out; } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); + return stbi__errpuc("not GIF", "Image was not as a gif type."); } } @@ -6565,6 +6849,7 @@ static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req stbi_uc *u = 0; stbi__gif g; memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); u = stbi__gif_load_next(s, &g, comp, req_comp, 0); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker @@ -6573,14 +6858,17 @@ static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req *y = g.h; // moved conversion to after successful load so that the same - // can be done for multiple frames. + // can be done for multiple frames. if (req_comp && req_comp != 4) u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); } - // free buffers needed for multiple frame loading; + // free buffers needed for multiple frame loading; STBI_FREE(g.history); - STBI_FREE(g.background); + STBI_FREE(g.background); return u; } @@ -6705,6 +6993,9 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re token += 3; width = (int) strtol(token, NULL, 10); + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + *x = width; *y = height; @@ -6852,7 +7143,12 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) return 0; if (x) *x = s->img_x; if (y) *y = s->img_y; - if (comp) *comp = info.ma ? 4 : 3; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } return 1; } #endif @@ -7014,6 +7310,9 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) return 0; + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + *x = s->img_x; *y = s->img_y; if (comp) *comp = s->img_n; @@ -7238,6 +7537,7 @@ STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user /* revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug From 816da9b6776fdca4dd5af418c552c955be586f9e Mon Sep 17 00:00:00 2001 From: Krishty Date: Wed, 5 May 2021 23:05:44 +0200 Subject: [PATCH 009/232] merge and update all copies of stb_image.h --- code/AssetLib/M3D/M3DWrapper.h | 4 + code/AssetLib/M3D/m3d.h | 1224 ----- code/Common/Assimp.cpp | 33 + code/Pbrt/PbrtExporter.cpp | 3 +- code/Pbrt/stb_image.h | 7756 -------------------------------- contrib/stb_image/stb_image.h | 652 ++- 6 files changed, 514 insertions(+), 9158 deletions(-) delete mode 100644 code/Pbrt/stb_image.h diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index 5c370e607..34bec49b2 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -56,6 +56,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //#define ASSIMP_USE_M3D_READFILECB //#define M3D_ASCII +// Share stb_image's PNG loader with other importers/exporters instead of bringing our own copy. +#define STBI_ONLY_PNG +#include + #include "m3d.h" namespace Assimp { diff --git a/code/AssetLib/M3D/m3d.h b/code/AssetLib/M3D/m3d.h index dfc30aec3..379d243a0 100644 --- a/code/AssetLib/M3D/m3d.h +++ b/code/AssetLib/M3D/m3d.h @@ -634,1230 +634,6 @@ static m3dcd_t m3d_commandtypes[] = { #include #include -#if !defined(M3D_NOIMPORTER) && !defined(STBI_INCLUDE_STB_IMAGE_H) -/* PNG decompressor from - - stb_image - v2.23 - public domain image loader - http://nothings.org/stb_image.h -*/ -static const char *_m3dstbi__g_failure_reason; - -enum { - STBI_default = 0, - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -enum { - STBI__SCAN_load = 0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -typedef unsigned short _m3dstbi_us; - -typedef uint16_t _m3dstbi__uint16; -typedef int16_t _m3dstbi__int16; -typedef uint32_t _m3dstbi__uint32; -typedef int32_t _m3dstbi__int32; - -typedef struct -{ - _m3dstbi__uint32 img_x, img_y; - int img_n, img_out_n; - - void *io_user_data; - - int read_from_callbacks; - int buflen; - unsigned char buffer_start[128]; - - unsigned char *img_buffer, *img_buffer_end; - unsigned char *img_buffer_original, *img_buffer_original_end; -} _m3dstbi__context; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} _m3dstbi__result_info; - -#define STBI_ASSERT(v) -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif -#define STBI__BYTECAST(x) ((unsigned char)((x)&255)) -#define STBI_MALLOC(sz) M3D_MALLOC(sz) -#define STBI_REALLOC(p, newsz) M3D_REALLOC(p, newsz) -#define STBI_FREE(p) M3D_FREE(p) -#define STBI_REALLOC_SIZED(p, oldsz, newsz) STBI_REALLOC(p, newsz) - -_inline static unsigned char _m3dstbi__get8(_m3dstbi__context *s) { - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - return 0; -} - -static void _m3dstbi__skip(_m3dstbi__context *s, int n) { - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - s->img_buffer += n; -} - -static int _m3dstbi__getn(_m3dstbi__context *s, unsigned char *buffer, int n) { - if (s->img_buffer + n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} - -static int _m3dstbi__get16be(_m3dstbi__context *s) { - int z = _m3dstbi__get8(s); - return (z << 8) + _m3dstbi__get8(s); -} - -static _m3dstbi__uint32 _m3dstbi__get32be(_m3dstbi__context *s) { - _m3dstbi__uint32 z = _m3dstbi__get16be(s); - return (z << 16) + _m3dstbi__get16be(s); -} - -#define _m3dstbi__err(x, y) _m3dstbi__errstr(y) -static int _m3dstbi__errstr(const char *str) { - _m3dstbi__g_failure_reason = str; - return 0; -} - -_inline static void *_m3dstbi__malloc(size_t size) { - return STBI_MALLOC(size); -} - -static int _m3dstbi__addsizes_valid(int a, int b) { - if (b < 0) return 0; - return a <= 2147483647 - b; -} - -static int _m3dstbi__mul2sizes_valid(int a, int b) { - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; - return a <= 2147483647 / b; -} - -static int _m3dstbi__mad2sizes_valid(int a, int b, int add) { - return _m3dstbi__mul2sizes_valid(a, b) && _m3dstbi__addsizes_valid(a * b, add); -} - -static int _m3dstbi__mad3sizes_valid(int a, int b, int c, int add) { - return _m3dstbi__mul2sizes_valid(a, b) && _m3dstbi__mul2sizes_valid(a * b, c) && - _m3dstbi__addsizes_valid(a * b * c, add); -} - -static void *_m3dstbi__malloc_mad2(int a, int b, int add) { - if (!_m3dstbi__mad2sizes_valid(a, b, add)) return NULL; - return _m3dstbi__malloc(a * b + add); -} - -static void *_m3dstbi__malloc_mad3(int a, int b, int c, int add) { - if (!_m3dstbi__mad3sizes_valid(a, b, c, add)) return NULL; - return _m3dstbi__malloc(a * b * c + add); -} - -static unsigned char _m3dstbi__compute_y(int r, int g, int b) { - return (unsigned char)(((r * 77) + (g * 150) + (29 * b)) >> 8); -} - -static unsigned char *_m3dstbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { - int i, j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *)_m3dstbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - _m3dstbi__err("outofmem", "Out of memory"); - return NULL; - } - - for (j = 0; j < (int)y; ++j) { - unsigned char *src = data + j * x * img_n; - unsigned char *dest = good + j * x * req_comp; - -#define STBI__COMBO(a, b) ((a)*8 + (b)) -#define STBI__CASE(a, b) \ - case STBI__COMBO(a, b): \ - for (i = x - 1; i >= 0; --i, src += a, dest += b) - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1, 2) { dest[0] = src[0], dest[1] = 255; } - break; - STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(1, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = 255; } - break; - STBI__CASE(2, 1) { dest[0] = src[0]; } - break; - STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(2, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = src[1]; } - break; - STBI__CASE(3, 4) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2], dest[3] = 255; } - break; - STBI__CASE(3, 1) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]); } - break; - STBI__CASE(3, 2) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]), dest[1] = 255; } - break; - STBI__CASE(4, 1) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]); } - break; - STBI__CASE(4, 2) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]), dest[1] = src[3]; } - break; - STBI__CASE(4, 3) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2]; } - break; - default: STBI_ASSERT(0); - } -#undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -static _m3dstbi__uint16 _m3dstbi__compute_y_16(int r, int g, int b) { - return (_m3dstbi__uint16)(((r * 77) + (g * 150) + (29 * b)) >> 8); -} - -static _m3dstbi__uint16 *_m3dstbi__convert_format16(_m3dstbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { - int i, j; - _m3dstbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (_m3dstbi__uint16 *)_m3dstbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - _m3dstbi__err("outofmem", "Out of memory"); - return NULL; - } - - for (j = 0; j < (int)y; ++j) { - _m3dstbi__uint16 *src = data + j * x * img_n; - _m3dstbi__uint16 *dest = good + j * x * req_comp; - -#define STBI__COMBO(a, b) ((a)*8 + (b)) -#define STBI__CASE(a, b) \ - case STBI__COMBO(a, b): \ - for (i = x - 1; i >= 0; --i, src += a, dest += b) - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1, 2) { dest[0] = src[0], dest[1] = 0xffff; } - break; - STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(1, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = 0xffff; } - break; - STBI__CASE(2, 1) { dest[0] = src[0]; } - break; - STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(2, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = src[1]; } - break; - STBI__CASE(3, 4) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2], dest[3] = 0xffff; } - break; - STBI__CASE(3, 1) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]); } - break; - STBI__CASE(3, 2) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]), dest[1] = 0xffff; } - break; - STBI__CASE(4, 1) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]); } - break; - STBI__CASE(4, 2) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]), dest[1] = src[3]; } - break; - STBI__CASE(4, 3) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2]; } - break; - default: STBI_ASSERT(0); - } -#undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -#define STBI__ZFAST_BITS 9 -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -typedef struct -{ - _m3dstbi__uint16 fast[1 << STBI__ZFAST_BITS]; - _m3dstbi__uint16 firstcode[16]; - int maxcode[17]; - _m3dstbi__uint16 firstsymbol[16]; - unsigned char size[288]; - _m3dstbi__uint16 value[288]; -} _m3dstbi__zhuffman; - -_inline static int _m3dstbi__bitreverse16(int n) { - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -_inline static int _m3dstbi__bit_reverse(int v, int bits) { - STBI_ASSERT(bits <= 16); - return _m3dstbi__bitreverse16(v) >> (16 - bits); -} - -static int _m3dstbi__zbuild_huffman(_m3dstbi__zhuffman *z, unsigned char *sizelist, int num) { - int i, k = 0; - int code, next_code[16], sizes[17]; - - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i = 0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i = 1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return _m3dstbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i = 1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (_m3dstbi__uint16)code; - z->firstsymbol[i] = (_m3dstbi__uint16)k; - code = (code + sizes[i]); - if (sizes[i]) - if (code - 1 >= (1 << i)) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - z->maxcode[i] = code << (16 - i); - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; - for (i = 0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - _m3dstbi__uint16 fastv = (_m3dstbi__uint16)((s << 9) | i); - z->size[c] = (unsigned char)s; - z->value[c] = (_m3dstbi__uint16)i; - if (s <= STBI__ZFAST_BITS) { - int j = _m3dstbi__bit_reverse(next_code[s], s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -typedef struct -{ - unsigned char *zbuffer, *zbuffer_end; - int num_bits; - _m3dstbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - _m3dstbi__zhuffman z_length, z_distance; -} _m3dstbi__zbuf; - -_inline static unsigned char _m3dstbi__zget8(_m3dstbi__zbuf *z) { - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; -} - -static void _m3dstbi__fill_bits(_m3dstbi__zbuf *z) { - do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= (unsigned int)_m3dstbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -_inline static unsigned int _m3dstbi__zreceive(_m3dstbi__zbuf *z, int n) { - unsigned int k; - if (z->num_bits < n) _m3dstbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int _m3dstbi__zhuffman_decode_slowpath(_m3dstbi__zbuf *a, _m3dstbi__zhuffman *z) { - int b, s, k; - k = _m3dstbi__bit_reverse(a->code_buffer, 16); - for (s = STBI__ZFAST_BITS + 1;; ++s) - if (k < z->maxcode[s]) - break; - if (s == 16) return -1; - b = (k >> (16 - s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -_inline static int _m3dstbi__zhuffman_decode(_m3dstbi__zbuf *a, _m3dstbi__zhuffman *z) { - int b, s; - if (a->num_bits < 16) _m3dstbi__fill_bits(a); - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return _m3dstbi__zhuffman_decode_slowpath(a, z); -} - -static int _m3dstbi__zexpand(_m3dstbi__zbuf *z, char *zout, int n) { - char *q; - int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return _m3dstbi__err("output buffer limit", "Corrupt PNG"); - cur = (int)(z->zout - z->zout_start); - limit = old_limit = (int)(z->zout_end - z->zout_start); - while (cur + n > limit) - limit *= 2; - q = (char *)STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return _m3dstbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static int _m3dstbi__zlength_base[31] = { - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, - 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, - 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 -}; - -static int _m3dstbi__zlength_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; - -static int _m3dstbi__zdist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; - -static int _m3dstbi__zdist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; - -static int _m3dstbi__parse_huffman_block(_m3dstbi__zbuf *a) { - char *zout = a->zout; - for (;;) { - int z = _m3dstbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return _m3dstbi__err("bad huffman code", "Corrupt PNG"); - if (zout >= a->zout_end) { - if (!_m3dstbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char)z; - } else { - unsigned char *p; - int len, dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = _m3dstbi__zlength_base[z]; - if (_m3dstbi__zlength_extra[z]) len += _m3dstbi__zreceive(a, _m3dstbi__zlength_extra[z]); - z = _m3dstbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return _m3dstbi__err("bad huffman code", "Corrupt PNG"); - dist = _m3dstbi__zdist_base[z]; - if (_m3dstbi__zdist_extra[z]) dist += _m3dstbi__zreceive(a, _m3dstbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return _m3dstbi__err("bad dist", "Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!_m3dstbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (unsigned char *)(zout - dist); - if (dist == 1) { - unsigned char v = *p; - if (len) { - do - *zout++ = v; - while (--len); - } - } else { - if (len) { - do - *zout++ = *p++; - while (--len); - } - } - } - } -} - -static int _m3dstbi__compute_huffman_codes(_m3dstbi__zbuf *a) { - static unsigned char length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - _m3dstbi__zhuffman z_codelength; - unsigned char lencodes[286 + 32 + 137]; - unsigned char codelength_sizes[19]; - int i, n; - - int hlit = _m3dstbi__zreceive(a, 5) + 257; - int hdist = _m3dstbi__zreceive(a, 5) + 1; - int hclen = _m3dstbi__zreceive(a, 4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i = 0; i < hclen; ++i) { - int s = _m3dstbi__zreceive(a, 3); - codelength_sizes[length_dezigzag[i]] = (unsigned char)s; - } - if (!_m3dstbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = _m3dstbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (unsigned char)c; - else { - unsigned char fill = 0; - if (c == 16) { - c = _m3dstbi__zreceive(a, 2) + 3; - if (n == 0) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n - 1]; - } else if (c == 17) - c = _m3dstbi__zreceive(a, 3) + 3; - else { - STBI_ASSERT(c == 18); - c = _m3dstbi__zreceive(a, 7) + 11; - } - if (ntot - n < c) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes + n, fill, c); - n += c; - } - } - if (n != ntot) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - if (!_m3dstbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!_m3dstbi__zbuild_huffman(&a->z_distance, lencodes + hlit, hdist)) return 0; - return 1; -} - -_inline static int _m3dstbi__parse_uncompressed_block(_m3dstbi__zbuf *a) { - unsigned char header[4]; - int len, nlen, k; - if (a->num_bits & 7) - _m3dstbi__zreceive(a, a->num_bits & 7); - k = 0; - while (a->num_bits > 0) { - header[k++] = (unsigned char)(a->code_buffer & 255); - a->code_buffer >>= 8; - a->num_bits -= 8; - } - STBI_ASSERT(a->num_bits == 0); - while (k < 4) - header[k++] = _m3dstbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return _m3dstbi__err("zlib corrupt", "Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return _m3dstbi__err("read past buffer", "Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!_m3dstbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int _m3dstbi__parse_zlib_header(_m3dstbi__zbuf *a) { - int cmf = _m3dstbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = _m3dstbi__zget8(a); - if ((cmf * 256 + flg) % 31 != 0) return _m3dstbi__err("bad zlib header", "Corrupt PNG"); - if (flg & 32) return _m3dstbi__err("no preset dict", "Corrupt PNG"); - if (cm != 8) return _m3dstbi__err("bad compression", "Corrupt PNG"); - return 1; -} - -static unsigned char _m3dstbi__zdefault_length[288], _m3dstbi__zdefault_distance[32]; -static void _m3dstbi__init_zdefaults(void) { - int i; - for (i = 0; i <= 143; ++i) - _m3dstbi__zdefault_length[i] = 8; - for (; i <= 255; ++i) - _m3dstbi__zdefault_length[i] = 9; - for (; i <= 279; ++i) - _m3dstbi__zdefault_length[i] = 7; - for (; i <= 287; ++i) - _m3dstbi__zdefault_length[i] = 8; - - for (i = 0; i <= 31; ++i) - _m3dstbi__zdefault_distance[i] = 5; -} - -static int _m3dstbi__parse_zlib(_m3dstbi__zbuf *a, int parse_header) { - int final, type; - if (parse_header) - if (!_m3dstbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = _m3dstbi__zreceive(a, 1); - type = _m3dstbi__zreceive(a, 2); - if (type == 0) { - if (!_m3dstbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - if (!_m3dstbi__zbuild_huffman(&a->z_length, _m3dstbi__zdefault_length, 288)) return 0; - if (!_m3dstbi__zbuild_huffman(&a->z_distance, _m3dstbi__zdefault_distance, 32)) return 0; - } else { - if (!_m3dstbi__compute_huffman_codes(a)) return 0; - } - if (!_m3dstbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int _m3dstbi__do_zlib(_m3dstbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) { - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - _m3dstbi__init_zdefaults(); - return _m3dstbi__parse_zlib(a, parse_header); -} - -char *_m3dstbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) { - _m3dstbi__zbuf a; - char *p = (char *)_m3dstbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (unsigned char *)buffer; - a.zbuffer_end = (unsigned char *)buffer + len; - if (_m3dstbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int)(a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -typedef struct -{ - _m3dstbi__uint32 length; - _m3dstbi__uint32 type; -} _m3dstbi__pngchunk; - -static _m3dstbi__pngchunk _m3dstbi__get_chunk_header(_m3dstbi__context *s) { - _m3dstbi__pngchunk c; - c.length = _m3dstbi__get32be(s); - c.type = _m3dstbi__get32be(s); - return c; -} - -_inline static int _m3dstbi__check_png_header(_m3dstbi__context *s) { - static unsigned char png_sig[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; - int i; - for (i = 0; i < 8; ++i) - if (_m3dstbi__get8(s) != png_sig[i]) return _m3dstbi__err("bad png sig", "Not a PNG"); - return 1; -} - -typedef struct -{ - _m3dstbi__context *s; - unsigned char *idata, *expanded, *out; - int depth; -} _m3dstbi__png; - -enum { - STBI__F_none = 0, - STBI__F_sub = 1, - STBI__F_up = 2, - STBI__F_avg = 3, - STBI__F_paeth = 4, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static unsigned char first_row_filter[5] = { - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int _m3dstbi__paeth(int a, int b, int c) { - int p = a + b - c; - int pa = abs(p - a); - int pb = abs(p - b); - int pc = abs(p - c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static unsigned char _m3dstbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01 }; - -static int _m3dstbi__create_png_image_raw(_m3dstbi__png *a, unsigned char *raw, _m3dstbi__uint32 raw_len, int out_n, _m3dstbi__uint32 x, _m3dstbi__uint32 y, int depth, int color) { - int bytes = (depth == 16 ? 2 : 1); - _m3dstbi__context *s = a->s; - _m3dstbi__uint32 i, j, stride = x * out_n * bytes; - _m3dstbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; - - int output_bytes = out_n * bytes; - int filter_bytes = img_n * bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n + 1); - a->out = (unsigned char *)_m3dstbi__malloc_mad3(x, y, output_bytes, 0); - if (!a->out) return _m3dstbi__err("outofmem", "Out of memory"); - - if (!_m3dstbi__mad3sizes_valid(img_n, x, depth, 7)) return _m3dstbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - if (s->img_x == x && s->img_y == y) { - if (raw_len != img_len) return _m3dstbi__err("not enough pixels", "Corrupt PNG"); - } else { - if (raw_len < img_len) return _m3dstbi__err("not enough pixels", "Corrupt PNG"); - } - - for (j = 0; j < y; ++j) { - unsigned char *cur = a->out + stride * j; - unsigned char *prior = cur - stride; - int filter = *raw++; - - if (filter > 4) - return _m3dstbi__err("invalid filter", "Corrupt PNG"); - - if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); - cur += x * out_n - img_width_bytes; - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; - - if (j == 0) filter = first_row_filter[filter]; - - for (k = 0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none: cur[k] = raw[k]; break; - case STBI__F_sub: cur[k] = raw[k]; break; - case STBI__F_up: cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg: cur[k] = STBI__BYTECAST(raw[k] + (prior[k] >> 1)); break; - case STBI__F_paeth: cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(0, prior[k], 0)); break; - case STBI__F_avg_first: cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; - cur[filter_bytes + 1] = 255; - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - if (depth < 8 || img_n == out_n) { - int nk = (width - 1) * filter_bytes; -#define STBI__CASE(f) \ - case f: \ - for (k = 0; k < nk; ++k) - switch (filter) { - case STBI__F_none: - memcpy(cur, raw, nk); - break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k - filter_bytes]); } - break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } - break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k - filter_bytes]) >> 1)); } - break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - filter_bytes], prior[k], prior[k - filter_bytes])); } - break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k - filter_bytes] >> 1)); } - break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - filter_bytes], 0, 0)); } - break; - } -#undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n + 1 == out_n); -#define STBI__CASE(f) \ - case f: \ - for (i = x - 1; i >= 1; --i, cur[filter_bytes] = 255, raw += filter_bytes, cur += output_bytes, prior += output_bytes) \ - for (k = 0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } - break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k - output_bytes]); } - break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } - break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k - output_bytes]) >> 1)); } - break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - output_bytes], prior[k], prior[k - output_bytes])); } - break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k - output_bytes] >> 1)); } - break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - output_bytes], 0, 0)); } - break; - } -#undef STBI__CASE - - if (depth == 16) { - cur = a->out + stride * j; - for (i = 0; i < x; ++i, cur += output_bytes) { - cur[filter_bytes + 1] = 255; - } - } - } - } - - if (depth < 8) { - for (j = 0; j < y; ++j) { - unsigned char *cur = a->out + stride * j; - unsigned char *in = a->out + stride * j + x * out_n - img_width_bytes; - unsigned char scale = (color == 0) ? _m3dstbi__depth_scale_table[depth] : 1; - - if (depth == 4) { - for (k = x * img_n; k >= 2; k -= 2, ++in) { - *cur++ = scale * ((*in >> 4)); - *cur++ = scale * ((*in) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4)); - } else if (depth == 2) { - for (k = x * img_n; k >= 4; k -= 4, ++in) { - *cur++ = scale * ((*in >> 6)); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6)); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k = x * img_n; k >= 8; k -= 8, ++in) { - *cur++ = scale * ((*in >> 7)); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7)); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - cur = a->out + stride * j; - if (img_n == 1) { - for (q = x - 1; q >= 0; --q) { - cur[q * 2 + 1] = 255; - cur[q * 2 + 0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q = x - 1; q >= 0; --q) { - cur[q * 4 + 3] = 255; - cur[q * 4 + 2] = cur[q * 3 + 2]; - cur[q * 4 + 1] = cur[q * 3 + 1]; - cur[q * 4 + 0] = cur[q * 3 + 0]; - } - } - } - } - } else if (depth == 16) { - unsigned char *cur = a->out; - _m3dstbi__uint16 *cur16 = (_m3dstbi__uint16 *)cur; - - for (i = 0; i < x * y * out_n; ++i, cur16++, cur += 2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } - - return 1; -} - -static int _m3dstbi__create_png_image(_m3dstbi__png *a, unsigned char *image_data, _m3dstbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) { - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - unsigned char *final; - int p; - if (!interlaced) - return _m3dstbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - final = (unsigned char *)_m3dstbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - for (p = 0; p < 7; ++p) { - int xorig[] = { 0, 4, 0, 2, 0, 1, 0 }; - int yorig[] = { 0, 0, 4, 0, 2, 0, 1 }; - int xspc[] = { 8, 8, 4, 4, 2, 2, 1 }; - int yspc[] = { 8, 8, 8, 4, 4, 2, 2 }; - int i, j, x, y; - x = (a->s->img_x - xorig[p] + xspc[p] - 1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p] - 1) / yspc[p]; - if (x && y) { - _m3dstbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!_m3dstbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j = 0; j < y; ++j) { - for (i = 0; i < x; ++i) { - int out_y = j * yspc[p] + yorig[p]; - int out_x = i * xspc[p] + xorig[p]; - memcpy(final + out_y * a->s->img_x * out_bytes + out_x * out_bytes, - a->out + (j * x + i) * out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int _m3dstbi__compute_transparency(_m3dstbi__png *z, unsigned char* tc, int out_n) { - _m3dstbi__context *s = z->s; - _m3dstbi__uint32 i, pixel_count = s->img_x * s->img_y; - unsigned char *p = z->out; - - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int _m3dstbi__compute_transparency16(_m3dstbi__png *z, _m3dstbi__uint16 tc[3], int out_n) { - _m3dstbi__context *s = z->s; - _m3dstbi__uint32 i, pixel_count = s->img_x * s->img_y; - _m3dstbi__uint16 *p = (_m3dstbi__uint16 *)z->out; - - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int _m3dstbi__expand_png_palette(_m3dstbi__png *a, unsigned char *palette, int len, int pal_img_n) { - _m3dstbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - unsigned char *p, *temp_out, *orig = a->out; - - p = (unsigned char *)_m3dstbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return _m3dstbi__err("outofmem", "Out of memory"); - - temp_out = p; - - if (pal_img_n == 3) { - for (i = 0; i < pixel_count; ++i) { - int n = orig[i] * 4; - p[0] = palette[n]; - p[1] = palette[n + 1]; - p[2] = palette[n + 2]; - p += 3; - } - } else { - for (i = 0; i < pixel_count; ++i) { - int n = orig[i] * 4; - p[0] = palette[n]; - p[1] = palette[n + 1]; - p[2] = palette[n + 2]; - p[3] = palette[n + 3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -#define STBI__PNG_TYPE(a, b, c, d) (((unsigned)(a) << 24) + ((unsigned)(b) << 16) + ((unsigned)(c) << 8) + (unsigned)(d)) - -static int _m3dstbi__parse_png_file(_m3dstbi__png *z, int scan, int req_comp) { - unsigned char palette[1024], pal_img_n = 0; - unsigned char has_trans = 0, tc[3] = {}; - _m3dstbi__uint16 tc16[3] = {}; - _m3dstbi__uint32 ioff = 0, idata_limit = 0, i, pal_len = 0; - int first = 1, k, interlace = 0, color = 0; - _m3dstbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!_m3dstbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - _m3dstbi__pngchunk c = _m3dstbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C', 'g', 'B', 'I'): - _m3dstbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I', 'H', 'D', 'R'): { - int comp, filter; - if (!first) return _m3dstbi__err("multiple IHDR", "Corrupt PNG"); - first = 0; - if (c.length != 13) return _m3dstbi__err("bad IHDR len", "Corrupt PNG"); - s->img_x = _m3dstbi__get32be(s); - if (s->img_x > (1 << 24)) return _m3dstbi__err("too large", "Very large image (corrupt?)"); - s->img_y = _m3dstbi__get32be(s); - if (s->img_y > (1 << 24)) return _m3dstbi__err("too large", "Very large image (corrupt?)"); - z->depth = _m3dstbi__get8(s); - if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return _m3dstbi__err("1/2/4/8/16-bit only", "PNG not supported: 1/2/4/8/16-bit only"); - color = _m3dstbi__get8(s); - if (color > 6) return _m3dstbi__err("bad ctype", "Corrupt PNG"); - if (color == 3 && z->depth == 16) return _m3dstbi__err("bad ctype", "Corrupt PNG"); - if (color == 3) - pal_img_n = 3; - else if (color & 1) - return _m3dstbi__err("bad ctype", "Corrupt PNG"); - comp = _m3dstbi__get8(s); - if (comp) return _m3dstbi__err("bad comp method", "Corrupt PNG"); - filter = _m3dstbi__get8(s); - if (filter) return _m3dstbi__err("bad filter method", "Corrupt PNG"); - interlace = _m3dstbi__get8(s); - if (interlace > 1) return _m3dstbi__err("bad interlace method", "Corrupt PNG"); - if (!s->img_x || !s->img_y) return _m3dstbi__err("0-pixel image", "Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return _m3dstbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return _m3dstbi__err("too large", "Corrupt PNG"); - } - break; - } - - case STBI__PNG_TYPE('P', 'L', 'T', 'E'): { - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256 * 3) return _m3dstbi__err("invalid PLTE", "Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return _m3dstbi__err("invalid PLTE", "Corrupt PNG"); - for (i = 0; i < pal_len; ++i) { - palette[i * 4 + 0] = _m3dstbi__get8(s); - palette[i * 4 + 1] = _m3dstbi__get8(s); - palette[i * 4 + 2] = _m3dstbi__get8(s); - palette[i * 4 + 3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t', 'R', 'N', 'S'): { - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return _m3dstbi__err("tRNS after IDAT", "Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { - s->img_n = 4; - return 1; - } - if (pal_len == 0) return _m3dstbi__err("tRNS before PLTE", "Corrupt PNG"); - if (c.length > pal_len) return _m3dstbi__err("bad tRNS len", "Corrupt PNG"); - pal_img_n = 4; - for (i = 0; i < c.length; ++i) - palette[i * 4 + 3] = _m3dstbi__get8(s); - } else { - if (!(s->img_n & 1)) return _m3dstbi__err("tRNS with alpha", "Corrupt PNG"); - if (c.length != (_m3dstbi__uint32)s->img_n * 2) return _m3dstbi__err("bad tRNS len", "Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) - tc16[k] = (_m3dstbi__uint16)_m3dstbi__get16be(s); - } else { - for (k = 0; k < s->img_n; ++k) - tc[k] = (unsigned char)(_m3dstbi__get16be(s) & 255) * _m3dstbi__depth_scale_table[z->depth]; - } - } - break; - } - - case STBI__PNG_TYPE('I', 'D', 'A', 'T'): { - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return _m3dstbi__err("no PLTE", "Corrupt PNG"); - if (scan == STBI__SCAN_header) { - s->img_n = pal_img_n; - return 1; - } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - _m3dstbi__uint32 idata_limit_old = idata_limit; - unsigned char *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (unsigned char *)STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); - if (p == NULL) return _m3dstbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!_m3dstbi__getn(s, z->idata + ioff, c.length)) return _m3dstbi__err("outofdata", "Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I', 'E', 'N', 'D'): { - _m3dstbi__uint32 raw_len, bpl; - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return _m3dstbi__err("no IDAT", "Corrupt PNG"); - bpl = (s->img_x * z->depth + 7) / 8; - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (unsigned char *)_m3dstbi_zlib_decode_malloc_guesssize_headerflag((char *)z->idata, ioff, raw_len, (int *)&raw_len, 1); - if (z->expanded == NULL) return 0; - STBI_FREE(z->idata); - z->idata = NULL; - if ((req_comp == s->img_n + 1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n + 1; - else - s->img_out_n = s->img_n; - if (!_m3dstbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!_m3dstbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!_m3dstbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (pal_img_n) { - s->img_n = pal_img_n; - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!_m3dstbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - ++s->img_n; - } - STBI_FREE(z->expanded); - z->expanded = NULL; - return 1; - } - - default: - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - return _m3dstbi__err("invalid_chunk", "PNG not supported: unknown PNG chunk type"); - } - _m3dstbi__skip(s, c.length); - break; - } - _m3dstbi__get32be(s); - } -} - -static void *_m3dstbi__do_png(_m3dstbi__png *p, int *x, int *y, int *n, int req_comp, _m3dstbi__result_info *ri) { - void *result = NULL; - if (req_comp < 0 || req_comp > 4) { - _m3dstbi__err("bad req_comp", "Internal error"); - return NULL; - } - if (_m3dstbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth < 8) - ri->bits_per_channel = 8; - else - ri->bits_per_channel = p->depth; - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = _m3dstbi__convert_format((unsigned char *)result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = _m3dstbi__convert_format16((_m3dstbi__uint16 *)result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); - p->out = NULL; - STBI_FREE(p->expanded); - p->expanded = NULL; - STBI_FREE(p->idata); - p->idata = NULL; - - return result; -} - -static void *_m3dstbi__png_load(_m3dstbi__context *s, int *x, int *y, int *comp, int req_comp, _m3dstbi__result_info *ri) { - _m3dstbi__png p; - p.s = s; - return _m3dstbi__do_png(&p, x, y, comp, req_comp, ri); -} -#define stbi__context _m3dstbi__context -#define stbi__result_info _m3dstbi__result_info -#define stbi__png_load _m3dstbi__png_load -#define stbi_zlib_decode_malloc_guesssize_headerflag _m3dstbi_zlib_decode_malloc_guesssize_headerflag -#endif - #if defined(M3D_EXPORTER) && !defined(INCLUDE_STB_IMAGE_WRITE_H) /* zlib_compressor from diff --git a/code/Common/Assimp.cpp b/code/Common/Assimp.cpp index 4e2c7117c..6074b84bc 100644 --- a/code/Common/Assimp.cpp +++ b/code/Common/Assimp.cpp @@ -1251,3 +1251,36 @@ ASSIMP_API void aiQuaternionInterpolate( ai_assert(nullptr != end); aiQuaternion::Interpolate(*dst, *start, *end, factor); } + + +// stb_image is a lightweight image loader. It is shared by: +// - M3D import +// - PBRT export +// Since it's a header-only library, its implementation must be instantiated in some cpp file. +// Don't scatter this task over multiple importers/exporters. Maintain it in a central place (here!). + +#define ASSIMP_HAS_PBRT_EXPORT (!ASSIMP_BUILD_NO_EXPORT && !ASSIMP_BUILD_NO_PBRT_EXPORTER) +#define ASSIMP_HAS_M3D ((!ASSIMP_BUILD_NO_EXPORT && !ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER) + +#if ASSIMP_HAS_PBRT_EXPORT +# define ASSIMP_NEEDS_STB_IMAGE 1 +#elif ASSIMP_HAS_M3D +# define ASSIMP_NEEDS_STB_IMAGE 1 +# define STBI_ONLY_PNG +#endif + +#if ASSIMP_NEEDS_STB_IMAGE + +# if _MSC_VER // "unreferenced function has been removed" (SSE2 detection routine in x64 builds) +# pragma warning(push) +# pragma warning(disable: 4505) +# endif + +# define STB_IMAGE_IMPLEMENTATION +# include "../contrib/stb_image/stb_image.h" + +# if _MSC_VER +# pragma warning(pop) +# endif + +#endif diff --git a/code/Pbrt/PbrtExporter.cpp b/code/Pbrt/PbrtExporter.cpp index b793c37f9..35b108702 100644 --- a/code/Pbrt/PbrtExporter.cpp +++ b/code/Pbrt/PbrtExporter.cpp @@ -83,8 +83,7 @@ Other: #include #include -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" +#include "../contrib/stb_image/stb_image.h" using namespace Assimp; diff --git a/code/Pbrt/stb_image.h b/code/Pbrt/stb_image.h deleted file mode 100644 index 65a205f6e..000000000 --- a/code/Pbrt/stb_image.h +++ /dev/null @@ -1,7756 +0,0 @@ -/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define STB_IMAGE_IMPLEMENTATION - #include "stb_image.h" - - You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. - And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free - - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8/16-bit-per-channel - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels, 8/16 bit-per-channel) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - PNM (PPM and PGM binary only) - - Animated GIF still needs a proper API, but here's one way to do it: - http://gist.github.com/urraka/685d9a6340b26b830d49 - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - - Full documentation under "DOCUMENTATION" below. - - -LICENSE - - See end of file for license information. - -RECENT REVISION HISTORY: - - 2.26 (2020-07-13) many minor fixes - 2.25 (2020-02-02) fix warnings - 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically - 2.23 (2019-08-11) fix clang static analysis warning - 2.22 (2019-03-04) gif fixes, fix warnings - 2.21 (2019-02-25) fix typo in comment - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings - 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes - 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 - RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; - correct channel count for PNG & BMP - 2.10 (2016-01-22) avoid warning introduced in 2.09 - 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED - - See end of file for full revision history. - - - ============================ Contributors ========================= - - Image formats Extensions, features - Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - github:urraka (animated gif) Junggon Kim (PNM comments) - Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) - socks-the-fox (16-bit PNG) - Jeremy Sawicki (handle all ImageNet JPGs) - Optimizations & bugfixes Mikhail Morozov (1-bit BMP) - Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine - John-Mark Allen - Carmelo J Fdez-Aguera - - Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski - Phil Jordan Dave Moore Roy Eltham - Hayaki Saito Nathan Reed Won Chun - Luke Graham Johan Duparc Nick Verigakis the Horde3D community - Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton github:snagar - Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex - Cass Everitt Ryamond Barbiero github:grim210 - Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw - Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus - Josh Tobin Matthew Gregan github:poppolopoppo - Julian Raschke Gregory Mullen Christian Floisand github:darealshinji - Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 - Brad Weinberger Matvey Cherevko [reserved] - Luca Sas Alexander Veselov Zack Middleton [reserved] - Ryan C. Gordon [reserved] [reserved] - DO NOT ADD YOUR NAME HERE - - To add your name to the credits, pick a random blank space in the middle and fill it. - 80% of merge conflicts on stb PRs are due to people adding their name at the end - of the credits. -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below for HDR usage): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *channels_in_file -- outputs # of image components in image file -// int desired_channels -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data, or NULL on an allocation failure or if the image is -// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'desired_channels' if desired_channels is non-zero, or -// *channels_in_file otherwise. If desired_channels is non-zero, -// *channels_in_file has the number of components that _would_ have been -// output otherwise. E.g. if you set desired_channels to 4, you will always -// get RGBA output, but you can check *channels_in_file to see if it's trivially -// opaque because e.g. there were only 3 channels in the source image. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *channels_in_file will be unchanged. The function -// stbi_failure_reason() can be queried for an extremely brief, end-user -// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS -// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// =========================================================================== -// -// UNICODE: -// -// If compiling for Windows and you wish to use Unicode filenames, compile -// with -// #define STBI_WINDOWS_UTF8 -// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert -// Windows wchar_t filenames to utf8. -// -// =========================================================================== -// -// Philosophy -// -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Sometimes I let "good performance" creep up in priority over "easy to maintain", -// and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy-to-use ones. Nevertheless, it's important -// to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. -// -// Some secondary priorities arise directly from the first two, some of which -// provide more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small source code footprint ("easy to maintain") -// - No dependencies ("ease of use") -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). -// -// =========================================================================== -// -// SIMD support -// -// The JPEG decoder will try to automatically use SIMD kernels on x86 when -// supported by the compiler. For ARM Neon support, you must explicitly -// request it. -// -// (The old do-it-yourself SIMD API is no longer supported in the current -// code.) -// -// On x86, SSE2 will automatically be used when available based on a run-time -// test; if not, the generic C versions are used as a fall-back. On ARM targets, -// the typical path is to have separate builds for NEON and non-NEON devices -// (at least this is true for iOS and Android). Therefore, the NEON support is -// toggled by a build flag: define STBI_NEON to get NEON loops. -// -// If for some reason you do not want to use any of SIMD code, or if -// you have issues compiling it, you can disable it entirely by -// defining STBI_NO_SIMD. -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image supports loading HDR images in general, and currently the Radiance -// .HDR file format specifically. You can still load any file through the existing -// interface; if you attempt to load an HDR file, it will be automatically remapped -// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// iPhone PNG support: -// -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// -// =========================================================================== -// -// ADDITIONAL CONFIGURATION -// -// - You can suppress implementation of any of the decoders to reduce -// your code footprint by #defining one or more of the following -// symbols before creating the implementation. -// -// STBI_NO_JPEG -// STBI_NO_PNG -// STBI_NO_BMP -// STBI_NO_PSD -// STBI_NO_TGA -// STBI_NO_GIF -// STBI_NO_HDR -// STBI_NO_PIC -// STBI_NO_PNM (.ppm and .pgm) -// -// - You can request *only* certain decoders and suppress all other ones -// (this will be more forward-compatible, as addition of new decoders -// doesn't require you to disable them explicitly): -// -// STBI_ONLY_JPEG -// STBI_ONLY_PNG -// STBI_ONLY_BMP -// STBI_ONLY_PSD -// STBI_ONLY_TGA -// STBI_ONLY_GIF -// STBI_ONLY_HDR -// STBI_ONLY_PIC -// STBI_ONLY_PNM (.ppm and .pgm) -// -// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still -// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB -// -// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater -// than that size (in either width or height) without further processing. -// This is to let programs in the wild set an upper bound to prevent -// denial-of-service attacks on untrusted data, as one could generate a -// valid image of gigantic dimensions and force stb_image to allocate a -// huge block of memory and spend disproportionate time decoding it. By -// default this is set to (1 << 24), which is 16777216, but that's still -// very big. - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for desired_channels - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -#include -typedef unsigned char stbi_uc; -typedef unsigned short stbi_us; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef STBIDEF -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -//////////////////////////////////// -// -// 8-bits-per-channel interface -// - -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - -#ifdef STBI_WINDOWS_UTF8 -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); -#endif - -//////////////////////////////////// -// -// 16-bits-per-channel interface -// - -STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -#endif - -//////////////////////////////////// -// -// float-per-channel interface -// -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); - #endif -#endif - -#ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR - -#ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR - -// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// on most compilers (and ALL modern mainstream compilers) this is threadsafe -STBIDEF const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit (char const *filename); -STBIDEF int stbi_is_16_bit_from_file(FILE *f); -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - -// flip the image vertically, so the first pixel in the output array is the bottom left -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); - -// as above, but only applies to images loaded on the thread that calls the function -// this function is only available if your compiler supports thread-local variables; -// calling it will fail to link if your compiler doesn't -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); - -// ZLIB client - used by PNG, available for other purposes - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ - || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ - || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ - || defined(STBI_ONLY_ZLIB) - #ifndef STBI_ONLY_JPEG - #define STBI_NO_JPEG - #endif - #ifndef STBI_ONLY_PNG - #define STBI_NO_PNG - #endif - #ifndef STBI_ONLY_BMP - #define STBI_NO_BMP - #endif - #ifndef STBI_ONLY_PSD - #define STBI_NO_PSD - #endif - #ifndef STBI_ONLY_TGA - #define STBI_NO_TGA - #endif - #ifndef STBI_ONLY_GIF - #define STBI_NO_GIF - #endif - #ifndef STBI_ONLY_HDR - #define STBI_NO_HDR - #endif - #ifndef STBI_ONLY_PIC - #define STBI_NO_PIC - #endif - #ifndef STBI_ONLY_PNM - #define STBI_NO_PNM - #endif -#endif - -#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) -#define STBI_NO_ZLIB -#endif - - -#include -#include // ptrdiff_t on osx -#include -#include -#include - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp, pow -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - -#ifdef __cplusplus -#define STBI_EXTERN extern "C" -#else -#define STBI_EXTERN extern -#endif - - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - -#ifndef STBI_NO_THREAD_LOCALS - #if defined(__cplusplus) && __cplusplus >= 201103L - #define STBI_THREAD_LOCAL thread_local - #elif defined(__GNUC__) && __GNUC__ < 5 - #define STBI_THREAD_LOCAL __thread - #elif defined(_MSC_VER) - #define STBI_THREAD_LOCAL __declspec(thread) - #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) - #define STBI_THREAD_LOCAL _Thread_local - #endif - - #ifndef STBI_THREAD_LOCAL - #if defined(__GNUC__) - #define STBI_THREAD_LOCAL __thread - #endif - #endif -#endif - -#ifdef _MSC_VER -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) -#endif - -#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) -// ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." -#endif - -#ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) -#endif - -#ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) -#endif - -// x86/x64 detection -#if defined(__x86_64__) || defined(_M_X64) -#define STBI__X64_TARGET -#elif defined(__i386) || defined(_M_IX86) -#define STBI__X86_TARGET -#endif - -#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// which in turn means it gets to use SSE2 everywhere. This is unfortunate, -// but previous attempts to provide the SSE2 functions with runtime -// detection caused numerous issues. The way architecture extensions are -// exposed in GCC/Clang is, sadly, not really suited for one-file libs. -// New behavior: if compiled with -msse2, we use SSE2 without any -// detection; if not, we don't use it at all. -#define STBI_NO_SIMD -#endif - -#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) -// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET -// -// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the -// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. -// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not -// simultaneously enabling "-mstackrealign". -// -// See https://github.com/nothings/stb/issues/81 for more information. -// -// So default to no SSE2 on 32-bit MinGW. If you've read this far and added -// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. -#define STBI_NO_SIMD -#endif - -#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) -#define STBI_SSE2 -#include - -#ifdef _MSC_VER - -#if _MSC_VER >= 1400 // not VC6 -#include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; -} -#else -static int stbi__cpuid3(void) -{ - int res; - __asm { - mov eax,1 - cpuid - mov res,edx - } - return res; -} -#endif - -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#endif - -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - // If we're even attempting to compile this on GCC/Clang, that means - // -msse2 is on, which means the compiler is allowed to use SSE2 - // instructions at will, and so are we. - return 1; -} -#endif - -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -// assume GCC or Clang on ARM targets -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#endif - -#ifndef STBI_MAX_DIMENSIONS -#define STBI_MAX_DIMENSIONS (1 << 24) -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - int callback_already_read; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - int ch; - fseek((FILE*) user, n, SEEK_CUR); - ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ - if (ch != EOF) { - ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ - } -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user) || ferror((FILE *) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; -} - -enum -{ - STBI_ORDER_RGB, - STBI_ORDER_BGR -}; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} stbi__result_info; - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__png_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__psd_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -static -#ifdef STBI_THREAD_LOCAL -STBI_THREAD_LOCAL -#endif -const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -#ifndef STBI_NO_FAILURE_STRINGS -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} -#endif - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// stb_image uses ints pervasively, including for offset calculations. -// therefore the largest decoded image size we can support with the -// current code, even on 64-bit targets, is INT_MAX. this is not a -// significant limitation for the intended use case. -// -// we do, however, need to make sure our size calculations don't -// overflow. hence a few helper functions for size calculations that -// multiply integers together, making sure that they're non-negative -// and no overflow occurs. - -// return 1 if the sum is valid, 0 on overflow. -// negative terms are considered invalid. -static int stbi__addsizes_valid(int a, int b) -{ - if (b < 0) return 0; - // now 0 <= b <= INT_MAX, hence also - // 0 <= INT_MAX - b <= INTMAX. - // And "a + b <= INT_MAX" (which might overflow) is the - // same as a <= INT_MAX - b (no overflow) - return a <= INT_MAX - b; -} - -// returns 1 if the product is valid, 0 on overflow. -// negative factors are considered invalid. -static int stbi__mul2sizes_valid(int a, int b) -{ - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; // mul-by-0 is always safe - // portable way to check for no overflows in a*b - return a <= INT_MAX/b; -} - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow -static int stbi__mad2sizes_valid(int a, int b, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); -} -#endif - -// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow -static int stbi__mad3sizes_valid(int a, int b, int c, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__addsizes_valid(a*b*c, add); -} - -// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); -} -#endif - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// mallocs with size overflow checking -static void *stbi__malloc_mad2(int a, int b, int add) -{ - if (!stbi__mad2sizes_valid(a, b, add)) return NULL; - return stbi__malloc(a*b + add); -} -#endif - -static void *stbi__malloc_mad3(int a, int b, int c, int add) -{ - if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; - return stbi__malloc(a*b*c + add); -} - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) -{ - if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; - return stbi__malloc(a*b*c*d + add); -} -#endif - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) -#else - #define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static int stbi__vertically_flip_on_load_global = 0; - -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_global = flag_true_if_should_flip; -} - -#ifndef STBI_THREAD_LOCAL -#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global -#else -static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; - -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_local = flag_true_if_should_flip; - stbi__vertically_flip_on_load_set = 1; -} - -#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ - ? stbi__vertically_flip_on_load_local \ - : stbi__vertically_flip_on_load_global) -#endif // STBI_THREAD_LOCAL - -static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields - ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed - ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order - ri->num_channels = 0; - - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); - #else - STBI_NOTUSED(bpc); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp, ri); - #endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi_uc *reduced; - - reduced = (stbi_uc *) stbi__malloc(img_len); - if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling - - STBI_FREE(orig); - return reduced; -} - -static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi__uint16 *enlarged; - - enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); - if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff - - STBI_FREE(orig); - return enlarged; -} - -static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) -{ - int row; - size_t bytes_per_row = (size_t)w * bytes_per_pixel; - stbi_uc temp[2048]; - stbi_uc *bytes = (stbi_uc *)image; - - for (row = 0; row < (h>>1); row++) { - stbi_uc *row0 = bytes + row*bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; - } - } -} - -#ifndef STBI_NO_GIF -static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) -{ - int slice; - int slice_size = w * h * bytes_per_pixel; - - stbi_uc *bytes = (stbi_uc *)image; - for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; - } -} -#endif - -static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 8) { - result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; - } - - // @TODO: move stbi__convert_format to here - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); - } - - return (unsigned char *) result; -} - -static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 16) { - result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; - } - - // @TODO: move stbi__convert_format16 to here - // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); - } - - return (stbi__uint16 *) result; -} - -#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); - } -} -#endif - -#ifndef STBI_NO_STDIO - -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) -STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); -STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); -#endif - -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) -{ - return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); -} -#endif - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) - wchar_t wMode[64]; - wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) - return 0; - - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) - return 0; - -#if _MSC_VER >= 1400 - if (0 != _wfopen_s(&f, wFilename, wMode)) - f = 0; -#else - f = _wfopen(wFilename, wMode); -#endif - -#elif defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint16 *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - stbi__uint16 *result; - if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file_16(f,x,y,comp,req_comp); - fclose(f); - return result; -} - - -#endif //!STBI_NO_STDIO - -STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - - result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); - if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); - } - - return result; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } - #endif - data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_LINEAR - -// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is -// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always -// reports false! - -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - long pos = ftell(f); - int res; - stbi__context s; - stbi__start_file(&s,f); - res = stbi__hdr_test(&s); - fseek(f, pos, SEEK_SET); - return res; - #else - STBI_NOTUSED(f); - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; - #endif -} - -#ifndef STBI_NO_LINEAR -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; - -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) -// nothing -#else -static void stbi__skip(stbi__context *s, int n) -{ - if (n == 0) return; // already there! - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) -// nothing -#else -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} -#endif - -#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) -// nothing -#else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} -#endif - -#ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); -} -#endif - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 stbi__compute_y_16(int r, int g, int b) -{ - return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - stbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - stbi__uint16 *src = data + j * x * img_n ; - stbi__uint16 *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output; - if (!data) return NULL; - output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - } - if (n < comp) { - for (i=0; i < x*y; ++i) { - output[i*comp + n] = data[i*comp + n]/255.0f; - } - } - STBI_FREE(data); - return output; -} -#endif - -#ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output; - if (!data) return NULL; - output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder -// -// simple implementation -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - some SIMD kernels for common paths on targets with SSE2/NEON -// - uses a lot of intermediate memory, could cache poorly - -#ifndef STBI_NO_JPEG - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi__uint16 dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - int jfif; - int app14_color_transform; // Adobe APP14 tag - int rgb; - - int scan_n, order[4]; - int restart_interval, todo; - -// kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0; - unsigned int code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -// build a table that decodes both magnitude and value of small ACs in -// one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; - - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (~0U << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB - k = stbi_lrot(j->code_buffer, n); - if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); -} - -// get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; -} - -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static const stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) -{ - int diff,dc,k; - int t; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * dequant[0]); - - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; -} - -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - diff = t ? stbi__extend_receive(j, t) : 0; - - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; -} - -// @OPTIMIZE: store non-zigzagged during the decode passes, -// and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->succ_high == 0) { - int shift = j->succ_low; - - if (j->eob_run) { - --j->eob_run; - return 1; - } - - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients - - short bit = (short) (1 << j->succ_low); - - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } - - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) * 4096) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0]*4; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SSE2 -// sse2 integer IDCT. not the fastest possible implementation but it -// produces bit-identical results to the generic C version so it's -// fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; - - // dot product constant: even elems=x, odd elems=y - #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) - - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y - #define dct_rot(out0,out1, x,y,c0,c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - - // out = in << 12 (in 16-bit, out 32-bit) - #define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - - // wide add - #define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) - - // wide sub - #define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - - // butterfly a/b, add bias, then shift by "s" and pack - #define dct_bfly32o(out0, out1, a,b,bias,s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } - - // 8-bit interleave step (for transposes) - #define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) - - // 16-bit interleave step (for transposes) - #define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) - - #define dct_pass(bias,shift) \ - { \ - /* even part */ \ - dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ - dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0,row7, x0,x7,bias,shift); \ - dct_bfly32o(row1,row6, x1,x6,bias,shift); \ - dct_bfly32o(row2,row5, x2,x5,bias,shift); \ - dct_bfly32o(row3,row4, x3,x4,bias,shift); \ - } - - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); - - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); - - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); - - // column pass - dct_pass(bias_0, 10); - - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); - - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); - - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } - - // row pass - dct_pass(bias_1, 17); - - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); - - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... - - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... - - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... - - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } - -#undef dct_const -#undef dct_rot -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_interleave8 -#undef dct_interleave16 -#undef dct_pass -} - -#endif // STBI_SSE2 - -#ifdef STBI_NEON - -// NEON integer IDCT. should produce bit-identical -// results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); - -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) - -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) - -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) - -// wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) - -// wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) - -// butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } - -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ - dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ - dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ - dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ - } - - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); - - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - - // column pass - dct_pass(vrshrn_n_s32, 10); - - // 16bit 8x8 transpose - { -// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. -// whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); - - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); - - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); - -#undef dct_trn16 -#undef dct_trn32 -#undef dct_trn64 - } - - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); - - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! - - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); - - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); - - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); - - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); - -#undef dct_trn8_8 -#undef dct_trn8_16 -#undef dct_trn8_32 - } - -#undef dct_long_mul -#undef dct_long_mac -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_pass -} - -#endif // STBI_NEON - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); - int t = q & 15,i; - if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } - - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - L = stbi__get16be(z->s); - if (L < 2) { - if (m == 0xFE) - return stbi__err("bad COM len","Corrupt JPEG"); - else - return stbi__err("bad APP len","Corrupt JPEG"); - } - L -= 2; - - if (m == 0xE0 && L >= 5) { // JFIF APP0 segment - static const unsigned char tag[5] = {'J','F','I','F','\0'}; - int ok = 1; - int i; - for (i=0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment - static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; - int ok = 1; - int i; - for (i=0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform - L -= 6; - } - } - - stbi__skip(z->s, L); - return 1; - } - - return stbi__err("unknown marker","Corrupt JPEG"); -} - -// after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } - - return 1; -} - -static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) -{ - int i; - for (i=0; i < ncomp; ++i) { - if (z->img_comp[i].raw_data) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } - } - return why; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - - z->rgb = 0; - for (i=0; i < s->img_n; ++i) { - static const unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != STBI__SCAN_load) return 1; - - if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - // these sizes can't be more than 17 bits - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - if (z->progressive) { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) - -#define stbi__SOF_progressive(x) ((x) == 0xc2) - -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->jfif = 0; - z->app14_color_transform = -1; // valid values are 0,1,2 - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -// decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else if (stbi__DNL(m)) { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; - - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { -#if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); - - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); - - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); -#elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); - - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); -#endif - - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } - - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); - - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} -#endif - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -// this is a reduced-precision calculation of YCbCr-to-RGB introduced -// to make sure the code produces the same results in both SIMD and scalar -#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; - -#ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel - - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); - - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); - - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); - - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); - - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } -#endif - -#ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); - - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); - - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); - - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } -#endif - - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -// set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; - -#ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; -#endif -} - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - stbi__free_jpeg_components(j, j->s->img_n, 0); -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -// fast 0..255 * 0..255 => 0..255 rounded multiplication -static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) -{ - unsigned int t = x*y + 128; - return (stbi_uc) ((t + (t >>8)) >> 8); -} - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n, is_rgb; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; - - is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - - if (z->s->img_n == 3 && n < 3 && !is_rgb) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - if (is_rgb) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else if (z->s->img_n == 4) { - if (z->app14_color_transform == 0) { // CMYK - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } else if (z->app14_color_transform == 2) { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } else { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - if (is_rgb) { - if (n == 1) - for (i=0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else { - for (i=0; i < z->s->img_x; ++i, out += 2) { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } - } - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output - return output; - } -} - -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - STBI_NOTUSED(ri); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return result; -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); - stbi__rewind(s); - STBI_FREE(j); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int result; - stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; -} -#endif - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -#ifndef STBI_NO_ZLIB - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int j = stbi__bit_reverse(next_code[s],s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static int stbi__zeof(stbi__zbuf *z) -{ - return (z->zbuffer >= z->zbuffer_end); -} - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - return stbi__zeof(z) ? 0 : *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - if (z->code_buffer >= (1U << z->num_bits)) { - z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ - return; - } - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s >= 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - if ((unsigned int)b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! - if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) { - if (stbi__zeof(a)) { - return -1; /* report error for unexpected end of data. */ - } - stbi__fill_bits(a); - } - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); -} - -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes -{ - char *q; - unsigned int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (unsigned int) (z->zout - z->zout_start); - limit = old_limit = (unsigned) (z->zout_end - z->zout_start); - if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); - while (cur + n > limit) { - if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); - limit *= 2; - } - q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static const int stbi__zlength_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static const int stbi__zlength_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, -257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static const int stbi__zdist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - if (len) { do *zout++ = v; while (--len); } - } else { - if (len) { do *zout++ = *p++; while (--len); } - } - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else { - stbi_uc fill = 0; - if (c == 16) { - c = stbi__zreceive(a,2)+3; - if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n-1]; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - } else if (c == 18) { - c = stbi__zreceive(a,7)+11; - } else { - return stbi__err("bad codelengths", "Corrupt PNG"); - } - if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes+n, fill, c); - n += c; - } - } - if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncompressed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -static const stbi_uc stbi__zdefault_length[288] = -{ - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 -}; -static const stbi_uc stbi__zdefault_distance[32] = -{ - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 -}; -/* -Init algorithm: -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} -*/ - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} -#endif - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - -#ifndef STBI_NO_PNG -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; - int depth; -} stbi__png; - - -enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int stbi__paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - int bytes = (depth == 16? 2 : 1); - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n*bytes; - stbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; // copy it into a local for later - - int output_bytes = out_n*bytes; - int filter_bytes = img_n*bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - - // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, - // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), - // so just check for raw_len < img_len always. - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; - int filter = *raw++; - - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } - - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - - if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4) ); - } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; - if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; - } - } - } - } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } - - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, - a->out + (j*x+i)*out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16*) z->out; - - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag = flag_true_if_should_convert; -} - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = ( t * 255 + half) / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]={0}; - stbi__uint16 tc16[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); - s->img_y = stbi__get32be(s); - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } - - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; - } - STBI_FREE(z->expanded); z->expanded = NULL; - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) -{ - void *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth <= 8) - ri->bits_per_channel = 8; - else if (p->depth == 16) - ri->bits_per_channel = 16; - else - return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; - - return result; -} - -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp, ri); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} - -static int stbi__png_is16(stbi__context *s) -{ - stbi__png p; - p.s = s; - if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) - return 0; - if (p.depth != 16) { - stbi__rewind(p.s); - return 0; - } - return 1; -} -#endif - -// Microsoft/Windows BMP image - -#ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) { n += 16; z >>= 16; } - if (z >= 0x00100) { n += 8; z >>= 8; } - if (z >= 0x00010) { n += 4; z >>= 4; } - if (z >= 0x00004) { n += 2; z >>= 2; } - if (z >= 0x00002) { n += 1;/* >>= 1;*/ } - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -// extract an arbitrarily-aligned N-bit value (N=bits) -// from v, and then make it 8-bits long and fractionally -// extend it to full full range. -static int stbi__shiftsigned(unsigned int v, int shift, int bits) -{ - static unsigned int mul_table[9] = { - 0, - 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, - 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, - }; - static unsigned int shift_table[9] = { - 0, 0,0,1,0,2,4,6,0, - }; - if (shift < 0) - v <<= -shift; - else - v >>= shift; - STBI_ASSERT(v < 256); - v >>= (8-bits); - STBI_ASSERT(bits >= 0 && bits <= 8); - return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; -} - -typedef struct -{ - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; - int extra_read; -} stbi__bmp_data; - -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - info->extra_read = 14; - - if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); - - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->extra_read += 12; - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *) 1; -} - - -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, all_a; - stbi_uc pal[256][4]; - int psize=0,i,j,width; - int flip_vertically, pad, target; - stbi__bmp_data info; - STBI_NOTUSED(ri); - - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set - - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; - - if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - info.extra_read - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - info.extra_read - info.hsz) >> 2; - } - if (psize == 0) { - STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); - if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) { - return stbi__errpuc("bad offset", "Corrupt BMP"); - } - } - - if (info.bpp == 24 && ma == 0xff000000) - s->img_n = 3; - else - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - - // sanity-check size - if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); - - out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - if (info.bpp == 1) { - for (j=0; j < (int) s->img_y; ++j) { - int bit_offset = 7, v = stbi__get8(s); - for (i=0; i < (int) s->img_x; ++i) { - int color = (v>>bit_offset)&0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - if((--bit_offset) < 0) { - bit_offset = 7; - v = stbi__get8(s); - } - } - stbi__skip(s, pad); - } - } else { - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - info.extra_read - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } - - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; - - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i]; p1[i] = p2[i]; p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} -#endif - -// Targa Truevision - TGA -// by Jonathan Dummer -#ifndef STBI_NO_TGA -// returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if (is_rgb16) *is_rgb16 = 0; - switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // fallthrough - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough - case 32: return bits_per_pixel/8; - default: return 0; - } -} - -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( tga_color_type == 1 ) { // colormapped (paletted) image - if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - stbi__skip(s,4); // skip image x and y origin - } else { // "normal" image w/o colormap - if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s,9); // skip colormap specification and image x/y origin - } - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 - -errorEnd: - stbi__rewind(s); - return res; -} - -// read 16bit value and convert to 24bit RGB -static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = (stbi__uint16)stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (stbi_uc)((r * 255)/31); - out[1] = (stbi_uc)((g * 255)/31); - out[2] = (stbi_uc)((b * 255)/31); - - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. -} - -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16=0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4] = {0}; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - STBI_NOTUSED(ri); - STBI_NOTUSED(tga_x_origin); // @TODO - STBI_NOTUSED(tga_y_origin); // @TODO - - if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); - - tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { - for (i=0; i < tga_height; ++i) { - int row = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - if (tga_palette_len == 0) { /* you have to have at least one entry! */ - STBI_FREE(tga_data); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } - - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - STBI_NOTUSED(tga_palette_start); - // OK, done - return tga_data; -} -#endif - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) -{ - int count, nleft, len; - - count = 0; - while ((nleft = pixelCount - count) > 0) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) return 0; // corrupt data - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - - return 1; -} - -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - int pixelCount; - int channelCount, compression; - int channel, i; - int bitdepth; - int w,h; - stbi_uc *out; - STBI_NOTUSED(ri); - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Check size - if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); - - // Create the destination image. - - if (!compression && bitdepth == 16 && bpc == 16) { - out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; - } else - out = (stbi_uc *) stbi__malloc(4 * w*h); - - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - if (channel >= channelCount) { - // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) { - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; - } else { - stbi_uc *p = out+channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } - } else { - if (ri->bits_per_channel == 16) { // output bpc - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16) stbi__get16be(s); - } else { - stbi_uc *p = out+channel; - if (bitdepth == 16) { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - } - - // remove weird white matte from PSD - if (channelCount >= 4) { - if (ri->bits_per_channel == 16) { - for (i=0; i < w*h; ++i) { - stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; - if (pixel[3] != 0 && pixel[3] != 65535) { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); - pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); - pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); - } - } - } else { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); - } - } - } - } - - // convert to desired output format - if (req_comp && req_comp != 4) { - if (ri->bits_per_channel == 16) - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = 4; - *y = h; - *x = w; - - return out; -} -#endif - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -#ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) -{ - stbi_uc *result; - int i, x,y, internal_comp; - STBI_NOTUSED(ri); - - if (!comp) comp = &internal_comp; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - - if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} -#endif - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb - -#ifndef STBI_NO_GIF -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[8192]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; - int delay; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); - stbi__rewind( s ); - return 0; - } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - int idx; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - idx = g->cur_x + g->cur_y; - p = &g->out[idx]; - g->history[idx / 4] = 1; - - c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc) init_code; - g->codes[init_code].suffix = (stbi_uc) init_code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) { - return stbi__errpuc("no clear code", "Corrupt GIF"); - } - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 8192) { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } - - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -// two back is the image from two frames ago, used for a very specific disposal format -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) -{ - int dispose; - int first_frame; - int pi; - int pcount; - STBI_NOTUSED(req_comp); - - // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) - return stbi__errpuc("too large", "GIF image is too large"); - pcount = g->w * g->h; - g->out = (stbi_uc *) stbi__malloc(4 * pcount); - g->background = (stbi_uc *) stbi__malloc(4 * pcount); - g->history = (stbi_uc *) stbi__malloc(pcount); - if (!g->out || !g->background || !g->history) - return stbi__errpuc("outofmem", "Out of memory"); - - // image is treated as "transparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to the color that was there the previous frame. - memset(g->out, 0x00, 4 * pcount); - memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) - memset(g->history, 0x00, pcount); // pixels that were affected previous frame - first_frame = 1; - } else { - // second frame - how do we dispose of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; - - if ((dispose == 3) && (two_back == 0)) { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } - - if (dispose == 3) { // use previous graphic - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); - } - } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); - } - } - } else { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } - - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); - } - - // clear my history; - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - - for (;;) { - int tag = stbi__get8(s); - switch (tag) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - // if the width of the specified rectangle is 0, that means - // we may not see *any* pixels or the image is malformed; - // to make sure this is caught, move the current y down to - // max_y (which is what out_gif_code checks). - if (w == 0) - g->cur_y = g->max_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (!o) return NULL; - - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); - } - } - } - - return o; - } - - case 0x21: // Comment Extension. - { - int len; - int ext = stbi__get8(s); - if (ext == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. - - // unset old transparent - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; - } - } else { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) { - stbi__skip(s, len); - } - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } -} - -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - if (stbi__gif_test(s)) { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - memset(&g, 0, sizeof(g)); - if (delays) { - *delays = 0; - } - - do { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - - if (u) { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; - - if (out) { - void *tmp = (stbi_uc*) STBI_REALLOC( out, layers * stride ); - if (NULL == tmp) { - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - return stbi__errpuc("outofmem", "Out of memory"); - } - else { - out = (stbi_uc*) tmp; - } - - if (delays) { - *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); - } - } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - } - } - memcpy( out + ((layers - 1) * stride), u, stride ); - if (layers >= 2) { - two_back = out - 2 * stride; - } - - if (delays) { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); - - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - - *z = layers; - return out; - } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); - } -} - -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - STBI_NOTUSED(ri); - - u = stbi__gif_load_next(s, &g, comp, req_comp, 0); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - - // moved conversion to after successful load so that the same - // can be done for multiple frames. - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g.w, g.h); - } else if (g.out) { - // if there was an error and we allocated an image buffer, free it! - STBI_FREE(g.out); - } - - // free buffers needed for multiple frame loading; - STBI_FREE(g.history); - STBI_FREE(g.background); - - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s, const char *signature) -{ - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - stbi__rewind(s); - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); - stbi__rewind(s); - if(!r) { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); - } - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - const char *headerToken; - STBI_NOTUSED(ri); - - // Check identifier - headerToken = stbi__hdr_gettoken(s,buffer); - if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); - - // Read data - hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); - if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); - if (!scanline) { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); - } - } - - for (k = 0; k < 4; ++k) { - int nleft; - i = 0; - while ((nleft = width - i) > 0) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - if (scanline) - STBI_FREE(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int dummy; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -#ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; - - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) - return 0; - if (x) *x = s->img_x; - if (y) *y = s->img_y; - if (comp) { - if (info.bpp == 24 && info.ma == 0xff000000) - *comp = 3; - else - *comp = info.ma ? 4 : 3; - } - return 1; -} -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount, dummy, depth; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 8 && depth != 16) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} - -static int stbi__psd_is16(stbi__context *s) -{ - int channelCount, depth; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - (void) stbi__get32be(s); - (void) stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 16) { - stbi__rewind( s ); - return 0; - } - return 1; -} -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained,dummy; - stbi__pic_packet packets[10]; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; - } - - stbi__skip(s, 88); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; - } - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} -#endif - -// ************************************************************************************************* -// Portable Gray Map and Portable Pixel Map loader -// by Ken Miller -// -// PGM: http://netpbm.sourceforge.net/doc/pgm.html -// PPM: http://netpbm.sourceforge.net/doc/ppm.html -// -// Known limitations: -// Does not support comments in the header section -// Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel - -#ifndef STBI_NO_PNM - -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; -} - -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - STBI_NOTUSED(ri); - - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) - return 0; - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "PNM too large"); - - out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); - - if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; -} - -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; -} - -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); - - if (stbi__at_eof(s) || *c != '#') - break; - - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); - } -} - -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; - - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - } - - return value; -} - -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv, dummy; - char c, p, t; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - stbi__rewind(s); - - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind(s); - return 0; - } - - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); - - *x = stbi__pnm_getinteger(s, &c); // read width - stbi__pnm_skip_whitespace(s, &c); - - *y = stbi__pnm_getinteger(s, &c); // read height - stbi__pnm_skip_whitespace(s, &c); - - maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); - else - return 1; -} -#endif - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; - #endif - - // test tga last because it's a crappy test! - #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; - #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -static int stbi__is_16_main(stbi__context *s) -{ - #ifndef STBI_NO_PNG - if (stbi__png_is16(s)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_is16(s)) return 1; - #endif - - return 0; -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} - -STBIDEF int stbi_is_16_bit(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; -} - -STBIDEF int stbi_is_16_bit_from_file(FILE *f) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__is_16_main(&s); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__is_16_main(&s); -} - -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__is_16_main(&s); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug - 1-bit BMP - *_is_16_bit api - avoid warnings - 2.16 (2017-07-23) all functions have 16-bit variants; - STBI_NO_STDIO works again; - compilation fixes; - fix rounding in unpremultiply; - optimize vertical flip; - disable raw_len validation; - documentation fixes - 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; - warning fixes; disable run-time SSE detection on gcc; - uniform handling of optional "return" values; - thread-safe initialization of zlib tables - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) allocate large structures on the stack - remove white matting for transparent PSD - fix reported channel count for PNG & BMP - re-enable SSE2 in non-gcc 64-bit - support RGB-formatted JPEG - read 16-bit PNGs (only as 8-bit) - 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED - 2.09 (2016-01-16) allow comments in PNM files - 16-bit-per-pixel TGA (not bit-per-component) - info() for TGA could break due to .hdr handling - info() for BMP to shares code instead of sloppy parse - can use STBI_REALLOC_SIZED if allocator doesn't support realloc - code cleanup - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) fix compiler warnings - partial animated GIF support - limited 16-bpc PSD support - #ifdef unused functions - bug with < 92 byte PIC,PNM,HDR,TGA - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) extra corruption checking (mmozeiko) - stbi_set_flip_vertically_on_load (nguillemot) - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 (2008-08-02) - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 (2006-11-19) - first released version -*/ - - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/contrib/stb_image/stb_image.h b/contrib/stb_image/stb_image.h index d9c21bc81..accef4839 100644 --- a/contrib/stb_image/stb_image.h +++ b/contrib/stb_image/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.19 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: @@ -48,6 +48,13 @@ LICENSE RECENT REVISION HISTORY: + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings @@ -84,23 +91,33 @@ RECENT REVISION HISTORY: Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) Arseny Kapoulkine John-Mark Allen + Carmelo J Fdez-Aguera Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan - Dave Moore Roy Eltham Hayaki Saito Nathan Reed - Won Chun Luke Graham Johan Duparc Nick Verigakis - the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar - Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex - Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 - Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw - Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus - Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo - Christian Floisand Kevin Schmidt github:darealshinji - Blazej Dariusz Roszkowski github:Michaelangel007 + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko [reserved] + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. */ #ifndef STBI_INCLUDE_STB_IMAGE_H @@ -161,6 +178,16 @@ RECENT REVISION HISTORY: // // =========================================================================== // +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// // Philosophy // // stb libraries are designed with the following priorities: @@ -171,12 +198,12 @@ RECENT REVISION HISTORY: // // Sometimes I let "good performance" creep up in priority over "easy to maintain", // and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy to use ones. Nevertheless, it's important +// performance, in addition to the easy-to-use ones. Nevertheless, it's important // to keep in mind that from the standpoint of you, a client of this library, // all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. // // Some secondary priorities arise directly from the first two, some of which -// make more explicit reasons why performance can't be emphasized. +// provide more explicit reasons why performance can't be emphasized. // // - Portable ("ease of use") // - Small source code footprint ("easy to maintain") @@ -219,11 +246,10 @@ RECENT REVISION HISTORY: // // HDR image support (disable by defining STBI_NO_HDR) // -// stb_image now supports loading HDR images in general, and currently -// the Radiance .HDR file format, although the support is provided -// generically. You can still load any file through the existing interface; -// if you attempt to load an HDR file, it will be automatically remapped to -// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; // both of these constants can be reconfigured through this interface: // // stbi_hdr_to_ldr_gamma(2.2f); @@ -257,7 +283,7 @@ RECENT REVISION HISTORY: // // By default we convert iphone-formatted PNGs back to RGB, even though // they are internally encoded differently. You can disable this conversion -// by by calling stbi_convert_iphone_png_to_rgb(0), in which case +// by calling stbi_convert_iphone_png_to_rgb(0), in which case // you will always just get the native iphone "format" through (which // is BGR stored in RGB). // @@ -301,7 +327,14 @@ RECENT REVISION HISTORY: // - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still // want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB // - +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. #ifndef STBI_NO_STDIO #include @@ -319,6 +352,7 @@ enum STBI_rgb_alpha = 4 }; +#include typedef unsigned char stbi_uc; typedef unsigned short stbi_us; @@ -326,11 +360,13 @@ typedef unsigned short stbi_us; extern "C" { #endif +#ifndef STBIDEF #ifdef STB_IMAGE_STATIC #define STBIDEF static #else #define STBIDEF extern #endif +#endif ////////////////////////////////////////////////////////////////////////////// // @@ -355,10 +391,6 @@ typedef struct STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - #ifndef STBI_NO_STDIO STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); @@ -366,6 +398,14 @@ STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in // for stbi_load_from_file, file pointer is left pointing immediately after image #endif +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + //////////////////////////////////// // // 16-bits-per-channel interface @@ -413,7 +453,7 @@ STBIDEF int stbi_is_hdr_from_file(FILE *f); // get a VERY brief reason for failure -// NOT THREADSAFE +// on most compilers (and ALL modern mainstream compilers) this is threadsafe STBIDEF const char *stbi_failure_reason (void); // free the loaded image -- this is just free() @@ -446,6 +486,11 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); // flip the image vertically, so the first pixel in the output array is the bottom left STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + // ZLIB client - used by PNG, available for other purposes STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); @@ -525,6 +570,12 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #define STBI_ASSERT(x) assert(x) #endif +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + #ifndef _MSC_VER #ifdef __cplusplus @@ -536,6 +587,23 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #define stbi_inline __forceinline #endif +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif #ifdef _MSC_VER typedef unsigned short stbi__uint16; @@ -649,14 +717,18 @@ static int stbi__cpuid3(void) #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { int info3 = stbi__cpuid3(); return ((info3 >> 26) & 1) != 0; } +#endif + #else // assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { // If we're even attempting to compile this on GCC/Clang, that means @@ -664,6 +736,8 @@ static int stbi__sse2_available(void) // instructions at will, and so are we. return 1; } +#endif + #endif #endif @@ -682,6 +756,10 @@ static int stbi__sse2_available(void) #define STBI_SIMD_ALIGN(type, name) type name #endif +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + /////////////////////////////////////////////// // // stbi__context struct and start_xxx functions @@ -699,6 +777,7 @@ typedef struct int read_from_callbacks; int buflen; stbi_uc buffer_start[128]; + int callback_already_read; stbi_uc *img_buffer, *img_buffer_end; stbi_uc *img_buffer_original, *img_buffer_original_end; @@ -712,6 +791,7 @@ static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) { s->io.read = NULL; s->read_from_callbacks = 0; + s->callback_already_read = 0; s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; } @@ -723,7 +803,8 @@ static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void * s->io_user_data = user; s->buflen = sizeof(s->buffer_start); s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; stbi__refill_buffer(s); s->img_buffer_original_end = s->img_buffer_end; } @@ -737,12 +818,17 @@ static int stbi__stdio_read(void *user, char *data, int size) static void stbi__stdio_skip(void *user, int n) { + int ch; fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } } static int stbi__stdio_eof(void *user) { - return feof((FILE*) user); + return feof((FILE*) user) || ferror((FILE *) user); } static stbi_io_callbacks stbi__stdio_callbacks = @@ -840,19 +926,24 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); #endif -// this is not threadsafe -static const char *stbi__g_failure_reason; +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; STBIDEF const char *stbi_failure_reason(void) { return stbi__g_failure_reason; } +#ifndef STBI_NO_FAILURE_STRINGS static int stbi__err(const char *str) { stbi__g_failure_reason = str; return 0; } +#endif static void *stbi__malloc(size_t size) { @@ -891,11 +982,13 @@ static int stbi__mul2sizes_valid(int a, int b) return a <= INT_MAX/b; } +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow static int stbi__mad2sizes_valid(int a, int b, int add) { return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); } +#endif // returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow static int stbi__mad3sizes_valid(int a, int b, int c, int add) @@ -913,12 +1006,14 @@ static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) } #endif +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // mallocs with size overflow checking static void *stbi__malloc_mad2(int a, int b, int add) { if (!stbi__mad2sizes_valid(a, b, add)) return NULL; return stbi__malloc(a*b + add); } +#endif static void *stbi__malloc_mad3(int a, int b, int c, int add) { @@ -962,13 +1057,29 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); #endif -static int stbi__vertically_flip_on_load = 0; +static int stbi__vertically_flip_on_load_global = 0; STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { - stbi__vertically_flip_on_load = flag_true_if_should_flip; + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; } +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields @@ -990,6 +1101,8 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re #endif #ifndef STBI_NO_PSD if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); #endif #ifndef STBI_NO_PIC if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); @@ -1070,6 +1183,7 @@ static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) } } +#ifndef STBI_NO_GIF static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) { int slice; @@ -1077,10 +1191,11 @@ static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int byt stbi_uc *bytes = (stbi_uc *)image; for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; } } +#endif static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { @@ -1090,8 +1205,10 @@ static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, if (result == NULL) return NULL; + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + if (ri.bits_per_channel != 8) { - STBI_ASSERT(ri.bits_per_channel == 16); result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 8; } @@ -1114,8 +1231,10 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, if (result == NULL) return NULL; + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + if (ri.bits_per_channel != 16) { - STBI_ASSERT(ri.bits_per_channel == 8); result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 16; } @@ -1131,7 +1250,7 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, return (stbi__uint16 *) result; } -#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR) +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { @@ -1143,10 +1262,38 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in #ifndef STBI_NO_STDIO +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + static FILE *stbi__fopen(char const *filename, char const *mode) { FILE *f; -#if defined(_MSC_VER) && _MSC_VER >= 1400 +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != fopen_s(&f, filename, mode)) f=0; #else @@ -1237,15 +1384,15 @@ STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *u STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - + stbi__context s; + stbi__start_mem(&s,buffer,len); + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); } - return result; + return result; } #endif @@ -1390,6 +1537,7 @@ enum static void stbi__refill_buffer(stbi__context *s) { int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); if (n == 0) { // at end of file, treat same as if from memory, but need to handle case // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file @@ -1414,6 +1562,9 @@ stbi_inline static stbi_uc stbi__get8(stbi__context *s) return 0; } +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else stbi_inline static int stbi__at_eof(stbi__context *s) { if (s->io.read) { @@ -1425,9 +1576,14 @@ stbi_inline static int stbi__at_eof(stbi__context *s) return s->img_buffer >= s->img_buffer_end; } +#endif +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else static void stbi__skip(stbi__context *s, int n) { + if (n == 0) return; // already there! if (n < 0) { s->img_buffer = s->img_buffer_end; return; @@ -1442,7 +1598,11 @@ static void stbi__skip(stbi__context *s, int n) } s->img_buffer += n; } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) { if (s->io.read) { @@ -1466,18 +1626,27 @@ static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) } else return 0; } +#endif +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else static int stbi__get16be(stbi__context *s) { int z = stbi__get8(s); return (z << 8) + stbi__get8(s); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else static stbi__uint32 stbi__get32be(stbi__context *s) { stbi__uint32 z = stbi__get16be(s); return (z << 16) + stbi__get16be(s); } +#endif #if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) // nothing @@ -1499,7 +1668,9 @@ static stbi__uint32 stbi__get32le(stbi__context *s) #define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else ////////////////////////////////////////////////////////////////////////////// // // generic converter from built-in img_n to req_comp @@ -1515,7 +1686,11 @@ static stbi_uc stbi__compute_y(int r, int g, int b) { return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; @@ -1539,19 +1714,19 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } @@ -1559,12 +1734,20 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r STBI_FREE(data); return good; } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else static stbi__uint16 stbi__compute_y_16(int r, int g, int b) { return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; @@ -1588,19 +1771,19 @@ static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int r // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } @@ -1608,6 +1791,7 @@ static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int r STBI_FREE(data); return good; } +#endif #ifndef STBI_NO_LINEAR static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) @@ -1623,7 +1807,11 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) for (k=0; k < n; ++k) { output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } } STBI_FREE(data); return output; @@ -1904,7 +2092,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB k = stbi_lrot(j->code_buffer, n); - STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; @@ -2015,6 +2203,7 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__ // first scan for DC coefficient, must be first memset(data,0,64*sizeof(data[0])); // 0 all the ac values now t = stbi__jpeg_huff_decode(j, hdc); + if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); diff = t ? stbi__extend_receive(j, t) : 0; dc = j->img_comp[b].dc_pred + diff; @@ -3005,6 +3194,8 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); c = stbi__get8(s); if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); s->img_n = c; @@ -3596,7 +3787,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp int k; unsigned int i,j; stbi_uc *output; - stbi_uc *coutput[4]; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; stbi__resample res_comp[4]; @@ -3717,7 +3908,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp if (n == 1) for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } } } } @@ -3885,16 +4076,23 @@ typedef struct stbi__zhuffman z_length, z_distance; } stbi__zbuf; +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) { - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; + return stbi__zeof(z) ? 0 : *z->zbuffer++; } static void stbi__fill_bits(stbi__zbuf *z) { do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; z->num_bits += 8; } while (z->num_bits <= 24); @@ -3919,10 +4117,11 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) for (s=STBI__ZFAST_BITS+1; ; ++s) if (k < z->maxcode[s]) break; - if (s == 16) return -1; // invalid code! + if (s >= 16) return -1; // invalid code! // code size is s, so: b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); + if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. a->code_buffer >>= s; a->num_bits -= s; return z->value[b]; @@ -3931,7 +4130,12 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { int b,s; - if (a->num_bits < 16) stbi__fill_bits(a); + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; if (b) { s = b >> 9; @@ -3945,13 +4149,16 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes { char *q; - int cur, limit, old_limit; + unsigned int cur, limit, old_limit; z->zout = zout; if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = old_limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); limit *= 2; + } q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); STBI_NOTUSED(old_limit); if (q == NULL) return stbi__err("outofmem", "Out of memory"); @@ -4049,11 +4256,12 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) c = stbi__zreceive(a,2)+3; if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); fill = lencodes[n-1]; - } else if (c == 17) + } else if (c == 17) { c = stbi__zreceive(a,3)+3; - else { - STBI_ASSERT(c == 18); + } else if (c == 18) { c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); } if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); memset(lencodes+n, fill, c); @@ -4079,7 +4287,7 @@ static int stbi__parse_uncompressed_block(stbi__zbuf *a) a->code_buffer >>= 8; a->num_bits -= 8; } - STBI_ASSERT(a->num_bits == 0); + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); // now fill header the normal way while (k < 4) header[k++] = stbi__zget8(a); @@ -4101,6 +4309,7 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) int cm = cmf & 15; /* int cinfo = cmf >> 4; */ int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png @@ -4362,7 +4571,7 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r return stbi__err("invalid filter","Corrupt PNG"); if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place filter_bytes = 1; width = img_width_bytes; @@ -4731,7 +4940,7 @@ static void stbi__de_iphone(stbi__png *z) static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]; + stbi_uc has_trans=0, tc[3]={0}; stbi__uint16 tc16[3]; stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; int first=1,k,interlace=0, color=0, is_iphone=0; @@ -4757,8 +4966,10 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); first = 0; if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); @@ -4875,6 +5086,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) ++s->img_n; } STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); return 1; } @@ -4905,10 +5118,12 @@ static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, st void *result=NULL; if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth < 8) + if (p->depth <= 8) ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; else - ri->bits_per_channel = p->depth; + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { @@ -5009,11 +5224,11 @@ static int stbi__high_bit(unsigned int z) { int n=0; if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } return n; } @@ -5030,7 +5245,7 @@ static int stbi__bitcount(unsigned int a) // extract an arbitrarily-aligned N-bit value (N=bits) // from v, and then make it 8-bits long and fractionally // extend it to full full range. -static int stbi__shiftsigned(int v, int shift, int bits) +static int stbi__shiftsigned(unsigned int v, int shift, int bits) { static unsigned int mul_table[9] = { 0, @@ -5044,7 +5259,7 @@ static int stbi__shiftsigned(int v, int shift, int bits) v <<= -shift; else v >>= shift; - STBI_ASSERT(v >= 0 && v < 256); + STBI_ASSERT(v < 256); v >>= (8-bits); STBI_ASSERT(bits >= 0 && bits <= 8); return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; @@ -5054,6 +5269,7 @@ typedef struct { int bpp, offset, hsz; unsigned int mr,mg,mb,ma, all_a; + int extra_read; } stbi__bmp_data; static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) @@ -5066,6 +5282,9 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->offset = stbi__get32le(s); info->hsz = hsz = stbi__get32le(s); info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); if (hsz == 12) { @@ -5109,6 +5328,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); + info->extra_read += 12; // not documented, but generated by photoshop and handled by mspaint if (info->mr == info->mg && info->mg == info->mb) { // ?!?!? @@ -5157,6 +5377,9 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req flip_vertically = ((int) s->img_y) > 0; s->img_y = abs((int) s->img_y); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + mr = info.mr; mg = info.mg; mb = info.mb; @@ -5165,13 +5388,22 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req if (info.hsz == 12) { if (info.bpp < 24) - psize = (info.offset - 14 - 24) / 3; + psize = (info.offset - info.extra_read - 24) / 3; } else { if (info.bpp < 16) - psize = (info.offset - 14 - info.hsz) >> 2; + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); + if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } } - s->img_n = ma ? 4 : 3; + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else @@ -5193,7 +5425,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req if (info.hsz != 12) stbi__get8(s); pal[i][3] = 255; } - stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); if (info.bpp == 1) width = (s->img_x + 7) >> 3; else if (info.bpp == 4) width = (s->img_x + 1) >> 1; else if (info.bpp == 8) width = s->img_x; @@ -5207,6 +5439,8 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req out[z++] = pal[color][0]; out[z++] = pal[color][1]; out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; if((--bit_offset) < 0) { bit_offset = 7; v = stbi__get8(s); @@ -5240,7 +5474,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; int z = 0; int easy=0; - stbi__skip(s, info.offset - 14 - info.hsz); + stbi__skip(s, info.offset - info.extra_read - info.hsz); if (info.bpp == 24) width = 3 * s->img_x; else if (info.bpp == 16) width = 2*s->img_x; else /* bpp = 32 and pad = 0 */ width=0; @@ -5258,6 +5492,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } } for (j=0; j < (int) s->img_y; ++j) { if (easy) { @@ -5299,7 +5534,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req stbi_uc *p1 = out + j *s->img_x*target; stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; + t = p1[i]; p1[i] = p2[i]; p2[i] = t; } } } @@ -5479,6 +5714,11 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req int RLE_repeating = 0; int read_next_pixel = 1; STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); // do a tiny bit of precessing if ( tga_image_type >= 8 ) @@ -5519,6 +5759,11 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // do I need to load a palette? if ( tga_indexed) { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + // any data to skip? (offset usually = 0) stbi__skip(s, tga_palette_start ); // load the palette @@ -5642,6 +5887,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // Microsoft's C compilers happy... [8^( tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); // OK, done return tga_data; } @@ -5726,6 +5972,9 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req h = stbi__get32be(s); w = stbi__get32be(s); + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + // Make sure the depth is 8 bits. bitdepth = stbi__get16be(s); if (bitdepth != 8 && bitdepth != 16) @@ -5789,7 +6038,7 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req // Else if n is 128, noop. // Endloop - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, // which we're going to just skip. stbi__skip(s, h * channelCount * 2 ); @@ -6080,6 +6329,10 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c x = stbi__get16be(s); y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); @@ -6127,7 +6380,7 @@ typedef struct int w,h; stbi_uc *out; // output buffer (always 4 components) stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; + stbi_uc *history; int flags, bgindex, ratio, transparent, eflags; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; @@ -6188,6 +6441,9 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in g->ratio = stbi__get8(s); g->transparent = -1; + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments if (is_info) return 1; @@ -6215,7 +6471,7 @@ static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { stbi_uc *p, *c; - int idx; + int idx; // recurse to decode the prefixes, since the linked-list is backwards, // and working backwards through an interleaved image would be nasty @@ -6224,12 +6480,12 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) if (g->cur_y >= g->max_y) return; - idx = g->cur_x + g->cur_y; + idx = g->cur_x + g->cur_y; p = &g->out[idx]; - g->history[idx / 4] = 1; + g->history[idx / 4] = 1; c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; + if (c[3] > 128) { // don't render transparent pixels; p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; @@ -6338,31 +6594,36 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) // two back is the image from two frames ago, used for a very specific disposal format static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) { - int dispose; - int first_frame; - int pi; - int pcount; + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; + first_frame = 0; if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->history = (stbi_uc *) stbi__malloc(g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); - // image is treated as "tranparent" at the start - ie, nothing overwrites the current background; + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to teh color that was there the previous frame. - memset( g->out, 0x00, 4 * g->w * g->h ); - memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent) - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - first_frame = 1; + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; } else { - // second frame - how do we dispoase of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; if ((dispose == 3) && (two_back == 0)) { dispose = 2; // if I don't have an image to revert back to, default to the old background @@ -6371,32 +6632,32 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i if (dispose == 3) { // use previous graphic for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); } } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); } } } else { - // This is a non-disposal case eithe way, so just + // This is a non-disposal case eithe way, so just // leave the pixels as is, and they will become the new background // 1: do not dispose // 0: not specified. } - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); } - // clear my history; + // clear my history; memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame for (;;) { - int tag = stbi__get8(s); + int tag = stbi__get8(s); switch (tag) { case 0x2C: /* Image Descriptor */ { @@ -6418,6 +6679,13 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i g->cur_x = g->start_x; g->cur_y = g->start_y; + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + g->lflags = stbi__get8(s); if (g->lflags & 0x40) { @@ -6434,19 +6702,19 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i } else if (g->flags & 0x80) { g->color_table = (stbi_uc *) g->pal; } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (o == NULL) return NULL; + return stbi__errpuc("missing color table", "Corrupt GIF"); - // if this was the first frame, - pcount = g->w * g->h; + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; if (first_frame && (g->bgindex > 0)) { // if first frame, any pixel not drawn to gets the background color for (pi = 0; pi < pcount; ++pi) { if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); } } } @@ -6457,7 +6725,7 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i case 0x21: // Comment Extension. { int len; - int ext = stbi__get8(s); + int ext = stbi__get8(s); if (ext == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { @@ -6466,23 +6734,23 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i // unset old transparent if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } + g->pal[g->transparent][3] = 255; + } if (g->eflags & 0x01) { g->transparent = stbi__get8(s); if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; + g->pal[g->transparent][3] = 0; } } else { // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; + stbi__skip(s, 1); + g->transparent = -1; } } else { stbi__skip(s, len); break; } - } + } while ((len = stbi__get8(s)) != 0) { stbi__skip(s, len); } @@ -6501,15 +6769,17 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { if (stbi__gif_test(s)) { - int layers = 0; + int layers = 0; stbi_uc *u = 0; stbi_uc *out = 0; - stbi_uc *two_back = 0; + stbi_uc *two_back = 0; stbi__gif g; - int stride; + int stride; + int out_size = 0; + int delays_size = 0; memset(&g, 0, sizeof(g)); if (delays) { - *delays = 0; + *delays = 0; } do { @@ -6519,44 +6789,58 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, if (u) { *x = g.w; *y = g.h; - ++layers; - stride = g.w * g.h * 4; - + ++layers; + stride = g.w * g.h * 4; + if (out) { - out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (NULL == tmp) { + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + return stbi__errpuc("outofmem", "Out of memory"); + } + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + if (delays) { - *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); + *delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + delays_size = layers * sizeof(int); } } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); + out = (stbi_uc*)stbi__malloc( layers * stride ); + out_size = layers * stride; if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + delays_size = layers * sizeof(int); } } - memcpy( out + ((layers - 1) * stride), u, stride ); + memcpy( out + ((layers - 1) * stride), u, stride ); if (layers >= 2) { - two_back = out - 2 * stride; + two_back = out - 2 * stride; } if (delays) { - (*delays)[layers - 1U] = g.delay; + (*delays)[layers - 1U] = g.delay; } } - } while (u != 0); + } while (u != 0); - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); - // do the final conversion after loading everything; + // do the final conversion after loading everything; if (req_comp && req_comp != 4) out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - *z = layers; + *z = layers; return out; } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); + return stbi__errpuc("not GIF", "Image was not as a gif type."); } } @@ -6565,6 +6849,7 @@ static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req stbi_uc *u = 0; stbi__gif g; memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); u = stbi__gif_load_next(s, &g, comp, req_comp, 0); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker @@ -6573,14 +6858,17 @@ static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req *y = g.h; // moved conversion to after successful load so that the same - // can be done for multiple frames. + // can be done for multiple frames. if (req_comp && req_comp != 4) u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); } - // free buffers needed for multiple frame loading; + // free buffers needed for multiple frame loading; STBI_FREE(g.history); - STBI_FREE(g.background); + STBI_FREE(g.background); return u; } @@ -6705,6 +6993,9 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re token += 3; width = (int) strtol(token, NULL, 10); + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + *x = width; *y = height; @@ -6852,7 +7143,12 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) return 0; if (x) *x = s->img_x; if (y) *y = s->img_y; - if (comp) *comp = info.ma ? 4 : 3; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } return 1; } #endif @@ -7014,6 +7310,9 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) return 0; + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + *x = s->img_x; *y = s->img_y; if (comp) *comp = s->img_n; @@ -7238,6 +7537,7 @@ STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user /* revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug From 52228a93f8215ec6bb4d89f313d66049546ed884 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 6 May 2021 21:07:38 +0200 Subject: [PATCH 010/232] Fix X3DGeohelper. --- code/AssetLib/X3D/X3DGeoHelper.cpp | 530 ++++++++++++++++++++++++++ code/AssetLib/X3D/X3DGeoHelper.h | 38 ++ code/AssetLib/X3D/X3DImporter.cpp | 179 ++++++++- code/AssetLib/X3D/X3DImporter.hpp | 101 ++++- code/AssetLib/glTF/glTFExporter.cpp | 9 +- code/AssetLib/glTF2/glTF2Exporter.cpp | 50 +-- code/CMakeLists.txt | 2 + code/Common/scene.cpp | 65 ++-- code/PostProcessing/ProcessHelper.h | 4 +- include/assimp/scene.h | 2 - 10 files changed, 895 insertions(+), 85 deletions(-) create mode 100644 code/AssetLib/X3D/X3DGeoHelper.cpp create mode 100644 code/AssetLib/X3D/X3DGeoHelper.h diff --git a/code/AssetLib/X3D/X3DGeoHelper.cpp b/code/AssetLib/X3D/X3DGeoHelper.cpp new file mode 100644 index 000000000..8a078d0f5 --- /dev/null +++ b/code/AssetLib/X3D/X3DGeoHelper.cpp @@ -0,0 +1,530 @@ +#include "X3DGeoHelper.h" +#include "X3DImporter.hpp" + +#include +#include + +#include + +namespace Assimp { + +aiVector3D X3DGeoHelper::make_point2D(float angle, float radius) { + return aiVector3D(radius * std::cos(angle), radius * std::sin(angle), 0); +} + +void X3DGeoHelper::make_arc2D(float pStartAngle, float pEndAngle, float pRadius, size_t numSegments, std::list &pVertices) { + // check argument values ranges. + if ((pStartAngle < -AI_MATH_TWO_PI_F) || (pStartAngle > AI_MATH_TWO_PI_F)) { + throw DeadlyImportError("GeometryHelper_Make_Arc2D.pStartAngle"); + } + if ((pEndAngle < -AI_MATH_TWO_PI_F) || (pEndAngle > AI_MATH_TWO_PI_F)) { + throw DeadlyImportError("GeometryHelper_Make_Arc2D.pEndAngle"); + } + if (pRadius <= 0) { + throw DeadlyImportError("GeometryHelper_Make_Arc2D.pRadius"); + } + + // calculate arc angle and check type of arc + float angle_full = std::fabs(pEndAngle - pStartAngle); + if ((angle_full > AI_MATH_TWO_PI_F) || (angle_full == 0.0f)) { + angle_full = AI_MATH_TWO_PI_F; + } + + // calculate angle for one step - angle to next point of line. + float angle_step = angle_full / (float)numSegments; + // make points + for (size_t pi = 0; pi <= numSegments; pi++) { + float tangle = pStartAngle + pi * angle_step; + pVertices.emplace_back(make_point2D(tangle, pRadius)); + } // for(size_t pi = 0; pi <= pNumSegments; pi++) + + // if we making full circle then add last vertex equal to first vertex + if (angle_full == AI_MATH_TWO_PI_F) pVertices.push_back(*pVertices.begin()); +} + +void X3DGeoHelper::extend_point_to_line(const std::list &pPoint, std::list &pLine) { + std::list::const_iterator pit = pPoint.begin(); + std::list::const_iterator pit_last = pPoint.end(); + + --pit_last; + + if (pPoint.size() < 2) { + throw DeadlyImportError("GeometryHelper_Extend_PointToLine.pPoint.size() can not be less than 2."); + } + + // add first point of first line. + pLine.push_back(*pit++); + // add internal points + while (pit != pit_last) { + pLine.push_back(*pit); // second point of previous line + pLine.push_back(*pit); // first point of next line + ++pit; + } + // add last point of last line + pLine.push_back(*pit); +} + +void X3DGeoHelper::polylineIdx_to_lineIdx(const std::list &pPolylineCoordIdx, std::list &pLineCoordIdx) { + std::list::const_iterator plit = pPolylineCoordIdx.begin(); + + while (plit != pPolylineCoordIdx.end()) { + // add first point of polyline + pLineCoordIdx.push_back(*plit++); + while ((*plit != (-1)) && (plit != pPolylineCoordIdx.end())) { + std::list::const_iterator plit_next; + + plit_next = plit, ++plit_next; + pLineCoordIdx.push_back(*plit); // second point of previous line. + pLineCoordIdx.push_back(-1); // delimiter + if ((*plit_next == (-1)) || (plit_next == pPolylineCoordIdx.end())) break; // current polyline is finished + + pLineCoordIdx.push_back(*plit); // first point of next line. + plit = plit_next; + } // while((*plit != (-1)) && (plit != pPolylineCoordIdx.end())) + } // while(plit != pPolylineCoordIdx.end()) +} + +#define MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4) \ + do { \ + if (pCCW) { \ + pOut.push_back(pIn[pP1]); \ + pOut.push_back(pIn[pP2]); \ + pOut.push_back(pIn[pP3]); \ + pOut.push_back(pIn[pP4]); \ + } else { \ + pOut.push_back(pIn[pP4]); \ + pOut.push_back(pIn[pP3]); \ + pOut.push_back(pIn[pP2]); \ + pOut.push_back(pIn[pP1]); \ + } \ + } while (false) + +#define MESH_RectParallelepiped_CREATE_VERT \ + aiVector3D vert_set[8]; \ + float x1, x2, y1, y2, z1, z2, hs; \ + \ + hs = pSize.x / 2, x1 = -hs, x2 = hs; \ + hs = pSize.y / 2, y1 = -hs, y2 = hs; \ + hs = pSize.z / 2, z1 = -hs, z2 = hs; \ + vert_set[0].Set(x2, y1, z2); \ + vert_set[1].Set(x2, y2, z2); \ + vert_set[2].Set(x2, y2, z1); \ + vert_set[3].Set(x2, y1, z1); \ + vert_set[4].Set(x1, y1, z2); \ + vert_set[5].Set(x1, y2, z2); \ + vert_set[6].Set(x1, y2, z1); \ + vert_set[7].Set(x1, y1, z1) + +void X3DGeoHelper::rect_parallele_piped(const aiVector3D &pSize, std::list &pVertices) { + MESH_RectParallelepiped_CREATE_VERT; + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 3, 2, 1, 0); // front + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 6, 7, 4, 5); // back + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 7, 3, 0, 4); // left + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 2, 6, 5, 1); // right + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 0, 1, 5, 4); // top + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 7, 6, 2, 3); // bottom +} + +#undef MESH_RectParallelepiped_CREATE_VERT + +void X3DGeoHelper::coordIdx_str2faces_arr(const std::vector &pCoordIdx, std::vector &pFaces, unsigned int &pPrimitiveTypes) { + std::vector f_data(pCoordIdx); + std::vector inds; + unsigned int prim_type = 0; + + if (f_data.back() != (-1)) { + f_data.push_back(-1); + } + + // reserve average size. + pFaces.reserve(f_data.size() / 3); + inds.reserve(4); + //PrintVectorSet("build. ci", pCoordIdx); + for (std::vector::iterator it = f_data.begin(); it != f_data.end(); ++it) { + // when face is got count how many indices in it. + if (*it == (-1)) { + aiFace tface; + size_t ts; + + ts = inds.size(); + switch (ts) { + case 0: + goto mg_m_err; + case 1: + prim_type |= aiPrimitiveType_POINT; + break; + case 2: + prim_type |= aiPrimitiveType_LINE; + break; + case 3: + prim_type |= aiPrimitiveType_TRIANGLE; + break; + default: + prim_type |= aiPrimitiveType_POLYGON; + break; + } + + tface.mNumIndices = static_cast(ts); + tface.mIndices = new unsigned int[ts]; + memcpy(tface.mIndices, inds.data(), ts * sizeof(unsigned int)); + pFaces.push_back(tface); + inds.clear(); + } // if(*it == (-1)) + else { + inds.push_back(*it); + } // if(*it == (-1)) else + } // for(std::list::iterator it = f_data.begin(); it != f_data.end(); it++) + //PrintVectorSet("build. faces", pCoordIdx); + + pPrimitiveTypes = prim_type; + + return; + +mg_m_err: + for (size_t i = 0, i_e = pFaces.size(); i < i_e; i++) + delete[] pFaces.at(i).mIndices; + + pFaces.clear(); +} + +void X3DGeoHelper::add_color(aiMesh &pMesh, const std::list &pColors, const bool pColorPerVertex) { + std::list tcol; + + // create RGBA array from RGB. + for (std::list::const_iterator it = pColors.begin(); it != pColors.end(); ++it) + tcol.push_back(aiColor4D((*it).r, (*it).g, (*it).b, 1)); + + // call existing function for adding RGBA colors + add_color(pMesh, tcol, pColorPerVertex); +} + +void X3DGeoHelper::add_color(aiMesh &pMesh, const std::list &pColors, const bool pColorPerVertex) { + std::list::const_iterator col_it = pColors.begin(); + + if (pColorPerVertex) { + if (pColors.size() < pMesh.mNumVertices) { + throw DeadlyImportError("MeshGeometry_AddColor1. Colors count(" + to_string(pColors.size()) + ") can not be less than Vertices count(" + + to_string(pMesh.mNumVertices) + ")."); + } + + // copy colors to mesh + pMesh.mColors[0] = new aiColor4D[pMesh.mNumVertices]; + for (size_t i = 0; i < pMesh.mNumVertices; i++) + pMesh.mColors[0][i] = *col_it++; + } // if(pColorPerVertex) + else { + if (pColors.size() < pMesh.mNumFaces) { + throw DeadlyImportError("MeshGeometry_AddColor1. Colors count(" + to_string(pColors.size()) + ") can not be less than Faces count(" + + to_string(pMesh.mNumFaces) + ")."); + } + + // copy colors to mesh + pMesh.mColors[0] = new aiColor4D[pMesh.mNumVertices]; + for (size_t fi = 0; fi < pMesh.mNumFaces; fi++) { + // apply color to all vertices of face + for (size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) { + pMesh.mColors[0][pMesh.mFaces[fi].mIndices[vi]] = *col_it; + } + + ++col_it; + } + } // if(pColorPerVertex) else +} + +void X3DGeoHelper::add_color(aiMesh &pMesh, const std::vector &pCoordIdx, const std::vector &pColorIdx, + const std::list &pColors, const bool pColorPerVertex) { + std::list tcol; + + // create RGBA array from RGB. + for (std::list::const_iterator it = pColors.begin(); it != pColors.end(); ++it) { + tcol.push_back(aiColor4D((*it).r, (*it).g, (*it).b, 1)); + } + + // call existing function for adding RGBA colors + add_color(pMesh, pCoordIdx, pColorIdx, tcol, pColorPerVertex); +} + +void X3DGeoHelper::add_color(aiMesh &pMesh, const std::vector &coordIdx, const std::vector &colorIdx, + const std::list &colors, bool pColorPerVertex) { + std::vector col_tgt_arr; + std::list col_tgt_list; + std::vector col_arr_copy; + + if (coordIdx.size() == 0) { + throw DeadlyImportError("MeshGeometry_AddColor2. pCoordIdx can not be empty."); + } + + // copy list to array because we are need indexed access to colors. + col_arr_copy.reserve(colors.size()); + for (std::list::const_iterator it = colors.begin(); it != colors.end(); ++it) { + col_arr_copy.push_back(*it); + } + + if (pColorPerVertex) { + if (colorIdx.size() > 0) { + // check indices array count. + if (colorIdx.size() < coordIdx.size()) { + throw DeadlyImportError("MeshGeometry_AddColor2. Colors indices count(" + to_string(colorIdx.size()) + + ") can not be less than Coords inidces count(" + to_string(coordIdx.size()) + ")."); + } + // create list with colors for every vertex. + col_tgt_arr.resize(pMesh.mNumVertices); + for (std::vector::const_iterator colidx_it = colorIdx.begin(), coordidx_it = coordIdx.begin(); colidx_it != colorIdx.end(); ++colidx_it, ++coordidx_it) { + if (*colidx_it == (-1)) { + continue; // skip faces delimiter + } + if ((unsigned int)(*coordidx_it) > pMesh.mNumVertices) { + throw DeadlyImportError("MeshGeometry_AddColor2. Coordinate idx is out of range."); + } + if ((unsigned int)*colidx_it > pMesh.mNumVertices) { + throw DeadlyImportError("MeshGeometry_AddColor2. Color idx is out of range."); + } + + col_tgt_arr[*coordidx_it] = col_arr_copy[*colidx_it]; + } + } // if(pColorIdx.size() > 0) + else { + // when color indices list is absent use CoordIdx. + // check indices array count. + if (colors.size() < pMesh.mNumVertices) { + throw DeadlyImportError("MeshGeometry_AddColor2. Colors count(" + to_string(colors.size()) + ") can not be less than Vertices count(" + + to_string(pMesh.mNumVertices) + ")."); + } + // create list with colors for every vertex. + col_tgt_arr.resize(pMesh.mNumVertices); + for (size_t i = 0; i < pMesh.mNumVertices; i++) { + col_tgt_arr[i] = col_arr_copy[i]; + } + } // if(pColorIdx.size() > 0) else + } // if(pColorPerVertex) + else { + if (colorIdx.size() > 0) { + // check indices array count. + if (colorIdx.size() < pMesh.mNumFaces) { + throw DeadlyImportError("MeshGeometry_AddColor2. Colors indices count(" + to_string(colorIdx.size()) + + ") can not be less than Faces count(" + to_string(pMesh.mNumFaces) + ")."); + } + // create list with colors for every vertex using faces indices. + col_tgt_arr.resize(pMesh.mNumFaces); + + std::vector::const_iterator colidx_it = colorIdx.begin(); + for (size_t fi = 0; fi < pMesh.mNumFaces; fi++) { + if ((unsigned int)*colidx_it > pMesh.mNumFaces) throw DeadlyImportError("MeshGeometry_AddColor2. Face idx is out of range."); + + col_tgt_arr[fi] = col_arr_copy[*colidx_it++]; + } + } // if(pColorIdx.size() > 0) + else { + // when color indices list is absent use CoordIdx. + // check indices array count. + if (colors.size() < pMesh.mNumFaces) { + throw DeadlyImportError("MeshGeometry_AddColor2. Colors count(" + to_string(colors.size()) + ") can not be less than Faces count(" + + to_string(pMesh.mNumFaces) + ")."); + } + // create list with colors for every vertex using faces indices. + col_tgt_arr.resize(pMesh.mNumFaces); + for (size_t fi = 0; fi < pMesh.mNumFaces; fi++) + col_tgt_arr[fi] = col_arr_copy[fi]; + + } // if(pColorIdx.size() > 0) else + } // if(pColorPerVertex) else + + // copy array to list for calling function that add colors. + for (std::vector::const_iterator it = col_tgt_arr.begin(); it != col_tgt_arr.end(); ++it) + col_tgt_list.push_back(*it); + // add prepared colors list to mesh. + add_color(pMesh, col_tgt_list, pColorPerVertex); +} + +void X3DGeoHelper::add_normal(aiMesh &pMesh, const std::vector &pCoordIdx, const std::vector &pNormalIdx, + const std::list &pNormals, const bool pNormalPerVertex) { + std::vector tind; + std::vector norm_arr_copy; + + // copy list to array because we are need indexed access to normals. + norm_arr_copy.reserve(pNormals.size()); + for (std::list::const_iterator it = pNormals.begin(); it != pNormals.end(); ++it) { + norm_arr_copy.push_back(*it); + } + + if (pNormalPerVertex) { + if (pNormalIdx.size() > 0) { + // check indices array count. + if (pNormalIdx.size() != pCoordIdx.size()) throw DeadlyImportError("Normals and Coords inidces count must be equal."); + + tind.reserve(pNormalIdx.size()); + for (std::vector::const_iterator it = pNormalIdx.begin(); it != pNormalIdx.end(); ++it) { + if (*it != (-1)) tind.push_back(*it); + } + + // copy normals to mesh + pMesh.mNormals = new aiVector3D[pMesh.mNumVertices]; + for (size_t i = 0; (i < pMesh.mNumVertices) && (i < tind.size()); i++) { + if (tind[i] >= norm_arr_copy.size()) + throw DeadlyImportError("MeshGeometry_AddNormal. Normal index(" + to_string(tind[i]) + + ") is out of range. Normals count: " + to_string(norm_arr_copy.size()) + "."); + + pMesh.mNormals[i] = norm_arr_copy[tind[i]]; + } + } else { + if (pNormals.size() != pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and vertices count must be equal."); + + // copy normals to mesh + pMesh.mNormals = new aiVector3D[pMesh.mNumVertices]; + std::list::const_iterator norm_it = pNormals.begin(); + for (size_t i = 0; i < pMesh.mNumVertices; i++) + pMesh.mNormals[i] = *norm_it++; + } + } // if(pNormalPerVertex) + else { + if (pNormalIdx.size() > 0) { + if (pMesh.mNumFaces != pNormalIdx.size()) throw DeadlyImportError("Normals faces count must be equal to mesh faces count."); + + std::vector::const_iterator normidx_it = pNormalIdx.begin(); + + tind.reserve(pNormalIdx.size()); + for (size_t i = 0, i_e = pNormalIdx.size(); i < i_e; i++) + tind.push_back(*normidx_it++); + + } else { + tind.reserve(pMesh.mNumFaces); + for (size_t i = 0; i < pMesh.mNumFaces; i++) + tind.push_back(i); + } + + // copy normals to mesh + pMesh.mNormals = new aiVector3D[pMesh.mNumVertices]; + for (size_t fi = 0; fi < pMesh.mNumFaces; fi++) { + aiVector3D tnorm; + + tnorm = norm_arr_copy[tind[fi]]; + for (size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) + pMesh.mNormals[pMesh.mFaces[fi].mIndices[vi]] = tnorm; + } + } // if(pNormalPerVertex) else +} + +void X3DGeoHelper::add_normal(aiMesh &pMesh, const std::list &pNormals, const bool pNormalPerVertex) { + std::list::const_iterator norm_it = pNormals.begin(); + + if (pNormalPerVertex) { + if (pNormals.size() != pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and vertices count must be equal."); + + // copy normals to mesh + pMesh.mNormals = new aiVector3D[pMesh.mNumVertices]; + for (size_t i = 0; i < pMesh.mNumVertices; i++) + pMesh.mNormals[i] = *norm_it++; + } // if(pNormalPerVertex) + else { + if (pNormals.size() != pMesh.mNumFaces) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and faces count must be equal."); + + // copy normals to mesh + pMesh.mNormals = new aiVector3D[pMesh.mNumVertices]; + for (size_t fi = 0; fi < pMesh.mNumFaces; fi++) { + // apply color to all vertices of face + for (size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) + pMesh.mNormals[pMesh.mFaces[fi].mIndices[vi]] = *norm_it; + + ++norm_it; + } + } // if(pNormalPerVertex) else +} + +void X3DGeoHelper::add_tex_coord(aiMesh &pMesh, const std::vector &pCoordIdx, const std::vector &pTexCoordIdx, + const std::list &pTexCoords) { + std::vector texcoord_arr_copy; + std::vector faces; + unsigned int prim_type; + + // copy list to array because we are need indexed access to normals. + texcoord_arr_copy.reserve(pTexCoords.size()); + for (std::list::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); ++it) { + texcoord_arr_copy.push_back(aiVector3D((*it).x, (*it).y, 0)); + } + + if (pTexCoordIdx.size() > 0) { + coordIdx_str2faces_arr(pTexCoordIdx, faces, prim_type); + if (faces.empty()) { + throw DeadlyImportError("Failed to add texture coordinates to mesh, faces list is empty."); + } + if (faces.size() != pMesh.mNumFaces) { + throw DeadlyImportError("Texture coordinates faces count must be equal to mesh faces count."); + } + } else { + coordIdx_str2faces_arr(pCoordIdx, faces, prim_type); + } + + pMesh.mTextureCoords[0] = new aiVector3D[pMesh.mNumVertices]; + pMesh.mNumUVComponents[0] = 2; + for (size_t fi = 0, fi_e = faces.size(); fi < fi_e; fi++) { + if (pMesh.mFaces[fi].mNumIndices != faces.at(fi).mNumIndices) + throw DeadlyImportError("Number of indices in texture face and mesh face must be equal. Invalid face index: " + to_string(fi) + "."); + + for (size_t ii = 0; ii < pMesh.mFaces[fi].mNumIndices; ii++) { + size_t vert_idx = pMesh.mFaces[fi].mIndices[ii]; + size_t tc_idx = faces.at(fi).mIndices[ii]; + + pMesh.mTextureCoords[0][vert_idx] = texcoord_arr_copy.at(tc_idx); + } + } // for(size_t fi = 0, fi_e = faces.size(); fi < fi_e; fi++) +} + +void X3DGeoHelper::add_tex_coord(aiMesh &pMesh, const std::list &pTexCoords) { + std::vector tc_arr_copy; + + if (pTexCoords.size() != pMesh.mNumVertices) { + throw DeadlyImportError("MeshGeometry_AddTexCoord. Texture coordinates and vertices count must be equal."); + } + + // copy list to array because we are need convert aiVector2D to aiVector3D and also get indexed access as a bonus. + tc_arr_copy.reserve(pTexCoords.size()); + for (std::list::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); ++it) { + tc_arr_copy.push_back(aiVector3D((*it).x, (*it).y, 0)); + } + + // copy texture coordinates to mesh + pMesh.mTextureCoords[0] = new aiVector3D[pMesh.mNumVertices]; + pMesh.mNumUVComponents[0] = 2; + for (size_t i = 0; i < pMesh.mNumVertices; i++) { + pMesh.mTextureCoords[0][i] = tc_arr_copy[i]; + } +} + +aiMesh *X3DGeoHelper::make_mesh(const std::vector &pCoordIdx, const std::list &pVertices) { + std::vector faces; + unsigned int prim_type = 0; + + // create faces array from input string with vertices indices. + X3DGeoHelper::coordIdx_str2faces_arr(pCoordIdx, faces, prim_type); + if (!faces.size()) { + throw DeadlyImportError("Failed to create mesh, faces list is empty."); + } + + // + // Create new mesh and copy geometry data. + // + aiMesh *tmesh = new aiMesh; + size_t ts = faces.size(); + // faces + tmesh->mFaces = new aiFace[ts]; + tmesh->mNumFaces = static_cast(ts); + for (size_t i = 0; i < ts; i++) + tmesh->mFaces[i] = faces.at(i); + + // vertices + std::list::const_iterator vit = pVertices.begin(); + + ts = pVertices.size(); + tmesh->mVertices = new aiVector3D[ts]; + tmesh->mNumVertices = static_cast(ts); + for (size_t i = 0; i < ts; i++) { + tmesh->mVertices[i] = *vit++; + } + + // set primitives type and return result. + tmesh->mPrimitiveTypes = prim_type; + + return tmesh; +} + +} // namespace Assimp diff --git a/code/AssetLib/X3D/X3DGeoHelper.h b/code/AssetLib/X3D/X3DGeoHelper.h new file mode 100644 index 000000000..38b6de4dc --- /dev/null +++ b/code/AssetLib/X3D/X3DGeoHelper.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +#include +#include + +struct aiFace; +struct aiMesh; + +namespace Assimp { + +class X3DGeoHelper { +public: + static aiVector3D make_point2D(float angle, float radius); + static void make_arc2D(float pStartAngle, float pEndAngle, float pRadius, size_t numSegments, std::list &pVertices); + static void extend_point_to_line(const std::list &pPoint, std::list &pLine); + static void polylineIdx_to_lineIdx(const std::list &pPolylineCoordIdx, std::list &pLineCoordIdx); + static void rect_parallele_piped(const aiVector3D &pSize, std::list &pVertices); + static void coordIdx_str2faces_arr(const std::vector &pCoordIdx, std::vector &pFaces, unsigned int &pPrimitiveTypes); + static void add_color(aiMesh &pMesh, const std::list &pColors, const bool pColorPerVertex); + static void add_color(aiMesh &pMesh, const std::list &pColors, const bool pColorPerVertex); + static void add_color(aiMesh &pMesh, const std::vector &pCoordIdx, const std::vector &pColorIdx, + const std::list &pColors, const bool pColorPerVertex); + static void add_color(aiMesh &pMesh, const std::vector &pCoordIdx, const std::vector &pColorIdx, + const std::list &pColors, const bool pColorPerVertex); + static void add_normal(aiMesh &pMesh, const std::vector &pCoordIdx, const std::vector &pNormalIdx, + const std::list &pNormals, const bool pNormalPerVertex); + static void add_normal(aiMesh &pMesh, const std::list &pNormals, const bool pNormalPerVertex); + static void add_tex_coord(aiMesh &pMesh, const std::vector &pCoordIdx, const std::vector &pTexCoordIdx, + const std::list &pTexCoords); + static void add_tex_coord(aiMesh &pMesh, const std::list &pTexCoords); + static aiMesh *make_mesh(const std::vector &pCoordIdx, const std::list &pVertices); +}; + +} // namespace Assimp diff --git a/code/AssetLib/X3D/X3DImporter.cpp b/code/AssetLib/X3D/X3DImporter.cpp index ff6e4f40e..341cc9d99 100644 --- a/code/AssetLib/X3D/X3DImporter.cpp +++ b/code/AssetLib/X3D/X3DImporter.cpp @@ -46,16 +46,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER #include "X3DImporter.hpp" -#include -// Header files, Assimp. +#include +#include #include #include // Header files, stdlib. #include #include -#include namespace Assimp { @@ -126,7 +125,8 @@ struct WordIterator { const char *WordIterator::whitespace = ", \t\r\n"; X3DImporter::X3DImporter() : - mNodeElementCur(nullptr) { + mNodeElementCur(nullptr), + mScene(nullptr) { // empty } @@ -153,10 +153,29 @@ void X3DImporter::ParseFile(const std::string &file, IOSystem *pIOHandler) { std::unique_ptr fileStream(pIOHandler->Open(file, mode)); if (!fileStream.get()) { throw DeadlyImportError("Failed to open file " + file + "."); - } + } + + XmlParser theParser; + if (!theParser.parse(fileStream.get())) { + return; + } + + XmlNode *node = theParser.findNode("X3D"); + if (nullptr == node) { + return; + } + + for (auto ¤tNode : node->children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == "head") { + readMetadata(currentNode); + } else if (currentName == "Scene") { + readScene(currentNode); + } + } } -bool X3DImporter::CanRead( const std::string &pFile, IOSystem * /*pIOHandler*/, bool checkSig ) const { +bool X3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool checkSig) const { if (checkSig) { std::string::size_type pos = pFile.find_last_of(".x3d"); if (pos != std::string::npos) { @@ -167,16 +186,17 @@ bool X3DImporter::CanRead( const std::string &pFile, IOSystem * /*pIOHandler*/, return false; } -void X3DImporter::GetExtensionList( std::set &extensionList ) { +void X3DImporter::GetExtensionList(std::set &extensionList) { extensionList.insert("x3d"); } -void X3DImporter::InternReadFile( const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler ) { +void X3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { std::shared_ptr stream(pIOHandler->Open(pFile, "rb")); if (!stream) { throw DeadlyImportError("Could not open file for reading"); } + mScene = pScene; pScene->mRootNode = new aiNode(pFile); } @@ -184,6 +204,147 @@ const aiImporterDesc *X3DImporter::GetInfo() const { return &Description; } -} +struct meta_entry { + std::string name; + std::string value; +}; + +void X3DImporter::readMetadata(XmlNode &node) { + std::vector metaArray; + for (auto currentNode : node.children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == "meta") { + meta_entry entry; + if (XmlParser::getStdStrAttribute(currentNode, "name", entry.name)) { + XmlParser::getStdStrAttribute(currentNode, "content", entry.value); + metaArray.emplace_back(entry); + } + } + } + mScene->mMetaData = aiMetadata::Alloc(static_cast(metaArray.size())); + unsigned int i = 0; + for (auto currentMeta : metaArray) { + mScene->mMetaData->Set(i, currentMeta.name, currentMeta.value); + ++i; + } +} + +void X3DImporter::readScene(XmlNode &node) { + for (auto currentNode : node.children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == "Viewpoint") { + readViewpoint(currentNode); + } + } +} + +void X3DImporter::readViewpoint(XmlNode &node) { + for (auto currentNode : node.children()) { + //const std::string ¤tName = currentNode.name(); + } +} + +void readMetadataBoolean(XmlNode &node, X3DNodeElementBase *parent) { + std::string val; + X3DNodeElementMetaBoolean *boolean = nullptr; + if (XmlParser::getStdStrAttribute(node, "value", val)) { + std::vector values; + tokenize(val, values, " "); + boolean = new X3DNodeElementMetaBoolean(parent); + for (size_t i = 0; i < values.size(); ++i) { + bool current_boolean = false; + if (values[i] == "true") { + current_boolean = true; + } + boolean->Value.emplace_back(current_boolean); + } + } +} + +void readMetadataDouble(XmlNode &node, X3DNodeElementBase *parent) { + std::string val; + X3DNodeElementMetaDouble *doubleNode = nullptr; + if (XmlParser::getStdStrAttribute(node, "value", val)) { + std::vector values; + tokenize(val, values, " "); + doubleNode = new X3DNodeElementMetaDouble(parent); + for (size_t i = 0; i < values.size(); ++i) { + double current_double = static_cast(fast_atof(values[i].c_str())); + doubleNode->Value.emplace_back(current_double); + } + } +} + +void readMetadataFloat(XmlNode &node, X3DNodeElementBase *parent) { + std::string val; + X3DNodeElementMetaFloat *floatNode = nullptr; + if (XmlParser::getStdStrAttribute(node, "value", val)) { + std::vector values; + tokenize(val, values, " "); + floatNode = new X3DNodeElementMetaFloat(parent); + for (size_t i = 0; i < values.size(); ++i) { + float current_float = static_cast(fast_atof(values[i].c_str())); + floatNode->Value.emplace_back(current_float); + } + } +} + +void readMetadataInteger(XmlNode &node, X3DNodeElementBase *parent) { + std::string val; + X3DNodeElementMetaInt *intNode = nullptr; + if (XmlParser::getStdStrAttribute(node, "value", val)) { + std::vector values; + tokenize(val, values, " "); + intNode = new X3DNodeElementMetaInt(parent); + for (size_t i = 0; i < values.size(); ++i) { + int current_int = static_cast(std::atoi(values[i].c_str())); + intNode->Value.emplace_back(current_int); + } + } +} + +void readMetadataSet(XmlNode &node, X3DNodeElementBase *parent) { + std::string val; + X3DNodeElementMetaSet *setNode = new X3DNodeElementMetaSet(parent); + if (XmlParser::getStdStrAttribute(node, "name", val)) { + setNode->Name = val; + } + + if (XmlParser::getStdStrAttribute(node, "reference", val)) { + setNode->Reference = val; + } +} + +void readMetadataString(XmlNode &node, X3DNodeElementBase *parent) { + std::string val; + X3DNodeElementMetaString *strNode = nullptr; + if (XmlParser::getStdStrAttribute(node, "value", val)) { + std::vector values; + tokenize(val, values, " "); + strNode = new X3DNodeElementMetaString(parent); + for (size_t i = 0; i < values.size(); ++i) { + strNode->Value.emplace_back(values[i]); + } + } +} + +void X3DImporter::readMetadataObject(XmlNode &node) { + const std::string &name = node.name(); + if (name == "MetadataBoolean") { + readMetadataBoolean(node, mNodeElementCur); + } else if (name == "MetadataDouble") { + readMetadataDouble(node, mNodeElementCur); + } else if (name == "MetadataFloat") { + readMetadataFloat(node, mNodeElementCur); + } else if (name == "MetadataInteger") { + readMetadataInteger(node, mNodeElementCur); + } else if (name == "MetadataSet") { + readMetadataSet(node, mNodeElementCur); + } else if (name == "MetadataString") { + readMetadataString(node, mNodeElementCur); + } +} + +} // namespace Assimp #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter.hpp b/code/AssetLib/X3D/X3DImporter.hpp index 4444bacd0..1b6410c36 100644 --- a/code/AssetLib/X3D/X3DImporter.hpp +++ b/code/AssetLib/X3D/X3DImporter.hpp @@ -38,16 +38,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/// \file X3DImporter.hpp -/// \brief X3D-format files importer for Assimp. -/// \date 2015-2016 -/// \author smal.root@gmail.com -// Thanks to acorn89 for support. - #ifndef INCLUDED_AI_X3D_IMPORTER_H #define INCLUDED_AI_X3D_IMPORTER_H -// Header files, Assimp. + #include #include #include @@ -56,7 +50,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include +#include namespace Assimp { @@ -282,8 +278,90 @@ enum class X3DElemType { struct X3DNodeElementBase { X3DNodeElementBase *Parent; std::string ID; - std::list Child; + std::list Children; X3DElemType Type; + +protected: + X3DNodeElementBase(X3DElemType type, X3DNodeElementBase *pParent) : + Type(type), Parent(pParent) { + // empty + } +}; + +struct CX3DNodeElementGroup : X3DNodeElementBase { + aiMatrix4x4 Transformation; ///< Transformation matrix. + bool Static; + bool UseChoice; ///< Flag: if true then use number from \ref Choice to choose what the child will be kept. + int32_t Choice; ///< Number of the child which will be kept. +}; + +struct X3DNodeElementMeta : X3DNodeElementBase { + std::string Name; ///< Name of metadata object. + std::string Reference; + + virtual ~X3DNodeElementMeta() { + // empty + } + +protected: + X3DNodeElementMeta(X3DElemType type, X3DNodeElementBase *parent) : + X3DNodeElementBase(type, parent) { + // empty + } +}; + +struct X3DNodeElementMetaBoolean : X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + X3DNodeElementMetaBoolean(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaBoolean, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaDouble : X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + X3DNodeElementMetaDouble(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaDouble, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaFloat : public X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + X3DNodeElementMetaFloat(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaFloat, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaInt : public X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + X3DNodeElementMetaInt(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaInteger, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaSet : public X3DNodeElementMeta { + std::list Value; ///< Stored value. + + X3DNodeElementMetaSet(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaSet, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaString : public X3DNodeElementMeta { + std::list Value; ///< Stored value. + + X3DNodeElementMetaString(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaString, pParent) { + // empty + } }; class X3DImporter : public BaseImporter { @@ -311,10 +389,15 @@ public: void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); const aiImporterDesc *GetInfo() const; void Clear(); + void readMetadata(XmlNode &node); + void readScene(XmlNode &node); + void readViewpoint(XmlNode &node); + void readMetadataObject(XmlNode &node); private: static const aiImporterDesc Description; - X3DNodeElementBase *mNodeElementCur; ///< Current element. + X3DNodeElementBase *mNodeElementCur; + aiScene *mScene; }; // class X3DImporter } // namespace Assimp diff --git a/code/AssetLib/glTF/glTFExporter.cpp b/code/AssetLib/glTF/glTFExporter.cpp index 1951167c6..528604cd8 100644 --- a/code/AssetLib/glTF/glTFExporter.cpp +++ b/code/AssetLib/glTF/glTFExporter.cpp @@ -322,8 +322,8 @@ void glTFExporter::GetTexSampler(const aiMaterial* mat, glTF::TexProperty& prop) prop.texture->sampler->minFilter = SamplerMinFilter_Linear; } -void glTFExporter::GetMatColorOrTex(const aiMaterial* mat, glTF::TexProperty& prop, const char* propName, int type, int idx, aiTextureType tt) -{ +void glTFExporter::GetMatColorOrTex(const aiMaterial* mat, glTF::TexProperty& prop, + const char* propName, int type, int idx, aiTextureType tt) { aiString tex; aiColor4D col; if (mat->GetTextureCount(tt) > 0) { @@ -370,7 +370,10 @@ void glTFExporter::GetMatColorOrTex(const aiMaterial* mat, glTF::TexProperty& pr } if (mat->Get(propName, type, idx, col) == AI_SUCCESS) { - prop.color[0] = col.r; prop.color[1] = col.g; prop.color[2] = col.b; prop.color[3] = col.a; + prop.color[0] = col.r; + prop.color[1] = col.g; + prop.color[2] = col.b; + prop.color[3] = col.a; } } diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 565117ddb..8eaf3c169 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -1297,24 +1297,24 @@ void glTF2Exporter::ExportMetadata() } } -inline Ref GetSamplerInputRef(Asset& asset, std::string& animId, Ref& buffer, std::vector& times) -{ +inline Ref GetSamplerInputRef(Asset& asset, std::string& animId, + Ref& buffer, std::vector& times) { return ExportData(asset, animId, buffer, (unsigned int)times.size(), ×[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT); } -inline void ExtractTranslationSampler(Asset& asset, std::string& animId, Ref& buffer, const aiNodeAnim* nodeChannel, float ticksPerSecond, Animation::Sampler& sampler) -{ +inline void ExtractTranslationSampler(Asset& asset, std::string& animId, Ref& buffer, + const aiNodeAnim* nodeChannel, float ticksPerSecond, Animation::Sampler& sampler) { const unsigned int numKeyframes = nodeChannel->mNumPositionKeys; - std::vector times(numKeyframes); - std::vector values(numKeyframes * 3); + std::vector times(numKeyframes); + std::vector values(numKeyframes * 3); for (unsigned int i = 0; i < numKeyframes; ++i) { const aiVectorKey& key = nodeChannel->mPositionKeys[i]; // mTime is measured in ticks, but GLTF time is measured in seconds, so convert. times[i] = static_cast(key.mTime / ticksPerSecond); - values[(i * 3) + 0] = key.mValue.x; - values[(i * 3) + 1] = key.mValue.y; - values[(i * 3) + 2] = key.mValue.z; + values[(i * 3) + 0] = (ai_real) key.mValue.x; + values[(i * 3) + 1] = (ai_real) key.mValue.y; + values[(i * 3) + 2] = (ai_real) key.mValue.z; } sampler.input = GetSamplerInputRef(asset, animId, buffer, times); @@ -1322,19 +1322,19 @@ inline void ExtractTranslationSampler(Asset& asset, std::string& animId, Ref& buffer, const aiNodeAnim* nodeChannel, float ticksPerSecond, Animation::Sampler& sampler) -{ +inline void ExtractScaleSampler(Asset& asset, std::string& animId, Ref& buffer, + const aiNodeAnim* nodeChannel, float ticksPerSecond, Animation::Sampler& sampler) { const unsigned int numKeyframes = nodeChannel->mNumScalingKeys; - std::vector times(numKeyframes); - std::vector values(numKeyframes * 3); + std::vector times(numKeyframes); + std::vector values(numKeyframes * 3); for (unsigned int i = 0; i < numKeyframes; ++i) { const aiVectorKey& key = nodeChannel->mScalingKeys[i]; // mTime is measured in ticks, but GLTF time is measured in seconds, so convert. times[i] = static_cast(key.mTime / ticksPerSecond); - values[(i * 3) + 0] = key.mValue.x; - values[(i * 3) + 1] = key.mValue.y; - values[(i * 3) + 2] = key.mValue.z; + values[(i * 3) + 0] = (ai_real) key.mValue.x; + values[(i * 3) + 1] = (ai_real) key.mValue.y; + values[(i * 3) + 2] = (ai_real) key.mValue.z; } sampler.input = GetSamplerInputRef(asset, animId, buffer, times); @@ -1342,20 +1342,20 @@ inline void ExtractScaleSampler(Asset& asset, std::string& animId, Ref& sampler.interpolation = Interpolation_LINEAR; } -inline void ExtractRotationSampler(Asset& asset, std::string& animId, Ref& buffer, const aiNodeAnim* nodeChannel, float ticksPerSecond, Animation::Sampler& sampler) -{ +inline void ExtractRotationSampler(Asset& asset, std::string& animId, Ref& buffer, + const aiNodeAnim* nodeChannel, float ticksPerSecond, Animation::Sampler& sampler) { const unsigned int numKeyframes = nodeChannel->mNumRotationKeys; - std::vector times(numKeyframes); - std::vector values(numKeyframes * 4); + std::vector times(numKeyframes); + std::vector values(numKeyframes * 4); for (unsigned int i = 0; i < numKeyframes; ++i) { const aiQuatKey& key = nodeChannel->mRotationKeys[i]; // mTime is measured in ticks, but GLTF time is measured in seconds, so convert. times[i] = static_cast(key.mTime / ticksPerSecond); - values[(i * 4) + 0] = key.mValue.x; - values[(i * 4) + 1] = key.mValue.y; - values[(i * 4) + 2] = key.mValue.z; - values[(i * 4) + 3] = key.mValue.w; + values[(i * 4) + 0] = (ai_real) key.mValue.x; + values[(i * 4) + 1] = (ai_real) key.mValue.y; + values[(i * 4) + 2] = (ai_real) key.mValue.z; + values[(i * 4) + 3] = (ai_real) key.mValue.w; } sampler.input = GetSamplerInputRef(asset, animId, buffer, times); @@ -1417,7 +1417,7 @@ void glTF2Exporter::ExportAnimations() } } - // Assimp documentation staes this is not used (not implemented) + // Assimp documentation states this is not used (not implemented) // for (unsigned int channelIndex = 0; channelIndex < anim->mNumMeshChannels; ++channelIndex) { // const aiMeshAnim* meshChannel = anim->mMeshChannels[channelIndex]; // } diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index bbcad86e5..0dbbd85c4 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -798,6 +798,8 @@ ADD_ASSIMP_IMPORTER( X ADD_ASSIMP_IMPORTER( X3D AssetLib/X3D/X3DImporter.cpp AssetLib/X3D/X3DImporter.hpp + AssetLib/X3D/X3DGeoHelper.cpp + AssetLib/X3D/X3DGeoHelper.h ) ADD_ASSIMP_IMPORTER( GLTF diff --git a/code/Common/scene.cpp b/code/Common/scene.cpp index f56562b1c..12667f530 100644 --- a/code/Common/scene.cpp +++ b/code/Common/scene.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -42,25 +40,25 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include -aiNode::aiNode() -: mName("") -, mParent(nullptr) -, mNumChildren(0) -, mChildren(nullptr) -, mNumMeshes(0) -, mMeshes(nullptr) -, mMetaData(nullptr) { +aiNode::aiNode() : + mName(""), + mParent(nullptr), + mNumChildren(0), + mChildren(nullptr), + mNumMeshes(0), + mMeshes(nullptr), + mMetaData(nullptr) { // empty } -aiNode::aiNode(const std::string& name) -: mName(name) -, mParent(nullptr) -, mNumChildren(0) -, mChildren(nullptr) -, mNumMeshes(0) -, mMeshes(nullptr) -, mMetaData(nullptr) { +aiNode::aiNode(const std::string &name) : + mName(name), + mParent(nullptr), + mNumChildren(0), + mChildren(nullptr), + mNumMeshes(0), + mMeshes(nullptr), + mMetaData(nullptr) { // empty } @@ -68,8 +66,7 @@ aiNode::aiNode(const std::string& name) aiNode::~aiNode() { // delete all children recursively // to make sure we won't crash if the data is invalid ... - if (mNumChildren && mChildren) - { + if (mNumChildren && mChildren) { for (unsigned int a = 0; a < mNumChildren; a++) delete mChildren[a]; } @@ -78,7 +75,7 @@ aiNode::~aiNode() { delete mMetaData; } -const aiNode *aiNode::FindNode(const char* name) const { +const aiNode *aiNode::FindNode(const char *name) const { if (nullptr == name) { return nullptr; } @@ -86,7 +83,7 @@ const aiNode *aiNode::FindNode(const char* name) const { return this; } for (unsigned int i = 0; i < mNumChildren; ++i) { - const aiNode* const p = mChildren[i]->FindNode(name); + const aiNode *const p = mChildren[i]->FindNode(name); if (p) { return p; } @@ -95,11 +92,10 @@ const aiNode *aiNode::FindNode(const char* name) const { return nullptr; } -aiNode *aiNode::FindNode(const char* name) { - if (!::strcmp(mName.data, name))return this; - for (unsigned int i = 0; i < mNumChildren; ++i) - { - aiNode* const p = mChildren[i]->FindNode(name); +aiNode *aiNode::FindNode(const char *name) { + if (!::strcmp(mName.data, name)) return this; + for (unsigned int i = 0; i < mNumChildren; ++i) { + aiNode *const p = mChildren[i]->FindNode(name); if (p) { return p; } @@ -121,17 +117,16 @@ void aiNode::addChildren(unsigned int numChildren, aiNode **children) { } if (mNumChildren > 0) { - aiNode **tmp = new aiNode*[mNumChildren]; - ::memcpy(tmp, mChildren, sizeof(aiNode*) * mNumChildren); + aiNode **tmp = new aiNode *[mNumChildren]; + ::memcpy(tmp, mChildren, sizeof(aiNode *) * mNumChildren); delete[] mChildren; - mChildren = new aiNode*[mNumChildren + numChildren]; - ::memcpy(mChildren, tmp, sizeof(aiNode*) * mNumChildren); - ::memcpy(&mChildren[mNumChildren], children, sizeof(aiNode*)* numChildren); + mChildren = new aiNode *[mNumChildren + numChildren]; + ::memcpy(mChildren, tmp, sizeof(aiNode *) * mNumChildren); + ::memcpy(&mChildren[mNumChildren], children, sizeof(aiNode *) * numChildren); mNumChildren += numChildren; delete[] tmp; - } - else { - mChildren = new aiNode*[numChildren]; + } else { + mChildren = new aiNode *[numChildren]; for (unsigned int i = 0; i < numChildren; i++) { mChildren[i] = children[i]; } diff --git a/code/PostProcessing/ProcessHelper.h b/code/PostProcessing/ProcessHelper.h index 8520b21ec..e851c97e9 100644 --- a/code/PostProcessing/ProcessHelper.h +++ b/code/PostProcessing/ProcessHelper.h @@ -133,12 +133,12 @@ inline ::aiQuatKey max(const ::aiQuatKey &a, const ::aiQuatKey &b) { // std::min for aiVertexWeight inline ::aiVertexWeight min(const ::aiVertexWeight &a, const ::aiVertexWeight &b) { - return ::aiVertexWeight(min(a.mVertexId, b.mVertexId), min(a.mWeight, b.mWeight)); + return ::aiVertexWeight(min(a.mVertexId, b.mVertexId),static_cast(min(a.mWeight, b.mWeight))); } // std::max for aiVertexWeight inline ::aiVertexWeight max(const ::aiVertexWeight &a, const ::aiVertexWeight &b) { - return ::aiVertexWeight(max(a.mVertexId, b.mVertexId), max(a.mWeight, b.mWeight)); + return ::aiVertexWeight(static_cast(max(a.mVertexId, b.mVertexId)), static_cast(max(a.mWeight, b.mWeight))); } } // end namespace std diff --git a/include/assimp/scene.h b/include/assimp/scene.h index 2a9a77b02..b8f034ebf 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, From 2b9d88c9434b13a3c0824bb5b402eb2e83857b0b Mon Sep 17 00:00:00 2001 From: ywang Date: Thu, 6 May 2021 15:10:06 -0700 Subject: [PATCH 011/232] support basis universal --- code/AssetLib/glTF2/glTF2Asset.h | 2 ++ code/AssetLib/glTF2/glTF2Asset.inl | 2 ++ code/AssetLib/glTF2/glTF2AssetWriter.inl | 13 +++++++++ code/AssetLib/glTF2/glTF2Exporter.cpp | 36 ++++++++++++++++++++---- code/AssetLib/glTF2/glTF2Importer.cpp | 6 ++++ 5 files changed, 53 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index fad5cba83..f2c0369d6 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -1118,11 +1118,13 @@ public: bool KHR_materials_transmission; bool KHR_draco_mesh_compression; bool FB_ngon_encoding; + bool KHR_texture_basisu; } extensionsUsed; //! Keeps info about the required extensions struct RequiredExtensions { bool KHR_draco_mesh_compression; + bool KHR_texture_basisu; } extensionsRequired; AssetMetadata asset; diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 77537028f..42d3f060e 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1121,6 +1121,7 @@ inline Image::Image() : } inline void Image::Read(Value &obj, Asset &r) { + //basisu: no need to handle .ktx2, .basis, load as is if (!mDataLength) { Value *curUri = FindString(obj, "uri"); if (nullptr != curUri) { @@ -2101,6 +2102,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) { CHECK_EXT(KHR_materials_clearcoat); CHECK_EXT(KHR_materials_transmission); CHECK_EXT(KHR_draco_mesh_compression); + CHECK_EXT(KHR_texture_basisu); #undef CHECK_EXT } diff --git a/code/AssetLib/glTF2/glTF2AssetWriter.inl b/code/AssetLib/glTF2/glTF2AssetWriter.inl index 01a28d4b7..bf7dbbb2e 100644 --- a/code/AssetLib/glTF2/glTF2AssetWriter.inl +++ b/code/AssetLib/glTF2/glTF2AssetWriter.inl @@ -250,6 +250,7 @@ namespace glTF2 { inline void Write(Value& obj, Image& img, AssetWriter& w) { + //basisu: no need to handle .ktx2, .basis, write as is if (img.bufferView) { obj.AddMember("bufferView", img.bufferView->index, w.mAl); obj.AddMember("mimeType", Value(img.mimeType, w.mAl).Move(), w.mAl); @@ -892,10 +893,22 @@ namespace glTF2 { if (this->mAsset.extensionsUsed.FB_ngon_encoding) { exts.PushBack(StringRef("FB_ngon_encoding"), mAl); } + + if (this->mAsset.extensionsUsed.KHR_texture_basisu) { + exts.PushBack(StringRef("KHR_texture_basisu"), mAl); + } } if (!exts.Empty()) mDoc.AddMember("extensionsUsed", exts, mAl); + + //basisu extensionRequired + Value extsReq; + extsReq.SetArray(); + if (this->mAsset.extensionsUsed.KHR_texture_basisu) { + extsReq.PushBack(StringRef("KHR_texture_basisu"), mAl); + mDoc.AddMember("extensionsRequired", extsReq, mAl); + } } template diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 51aef013d..e039bf88a 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -494,7 +494,6 @@ void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, float& prop, const char void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) { - if (mat->GetTextureCount(tt) > 0) { aiString tex; @@ -507,6 +506,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe texture = mAsset->textures.Get(it->second); } + bool useBasisUniversal = false; if (!texture) { std::string texId = mAsset->FindUniqueID("", "texture"); texture = mAsset->textures.Create(texId); @@ -519,18 +519,42 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe aiTexture* curTex = mScene->mTextures[atoi(&path[1])]; texture->source->name = curTex->mFilename.C_Str(); - - // The asset has its own buffer, see Image::SetData - texture->source->SetData(reinterpret_cast(curTex->pcData), curTex->mWidth, *mAsset); - + + //basisu: embedded ktx2, bu if (curTex->achFormatHint[0]) { std::string mimeType = "image/"; - mimeType += (memcmp(curTex->achFormatHint, "jpg", 3) == 0) ? "jpeg" : curTex->achFormatHint; + if(memcmp(curTex->achFormatHint, "jpg", 3) == 0) + mimeType += "jpeg"; + else if(memcmp(curTex->achFormatHint, "ktx", 3) == 0) { + useBasisUniversal = true; + mimeType += "ktx2"; + } + else if(memcmp(curTex->achFormatHint, "bu", 3) == 0) { + useBasisUniversal = true; + mimeType += "basis"; + } + else + mimeType += curTex->achFormatHint; texture->source->mimeType = mimeType; } + + // The asset has its own buffer, see Image::SetData + //basisu: "image/ktx2", "image/basis" as is + texture->source->SetData(reinterpret_cast(curTex->pcData), curTex->mWidth, *mAsset); } else { texture->source->uri = path; + if(texture->source->uri.find(".ktx2")!=std::string::npos || + texture->source->uri.find(".basis")!=std::string::npos) + { + useBasisUniversal = true; + } + } + + //basisu + if(useBasisUniversal) { + mAsset->extensionsUsed.KHR_texture_basisu = true; + mAsset->extensionsRequired.KHR_texture_basisu = true; } GetTexSampler(mat, texture, tt, slot); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index ab1f01bf8..b109891cb 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1476,6 +1476,12 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { if (strcmp(ext, "jpeg") == 0) { ext = "jpg"; } + else if(strcmp(ext, "ktx2") == 0) { //basisu + ext = "ktx"; + } + else if(strcmp(ext, "basis") == 0) { //basisu + ext = "bu"; + } size_t len = strlen(ext); if (len <= 3) { From 56bfa1ce5c0dc472b2faaa531d0587b0c9fa3363 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 7 May 2021 11:36:21 +0200 Subject: [PATCH 012/232] Make constructros with one arg explicit --- code/AssetLib/X3D/X3DImporter.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/X3D/X3DImporter.hpp b/code/AssetLib/X3D/X3DImporter.hpp index b3a7c4d06..360dfe6c5 100644 --- a/code/AssetLib/X3D/X3DImporter.hpp +++ b/code/AssetLib/X3D/X3DImporter.hpp @@ -313,7 +313,7 @@ protected: struct X3DNodeElementMetaBoolean : X3DNodeElementMeta { std::vector Value; ///< Stored value. - X3DNodeElementMetaBoolean(X3DNodeElementBase *pParent) : + explicit X3DNodeElementMetaBoolean(X3DNodeElementBase *pParent) : X3DNodeElementMeta(X3DElemType::ENET_MetaBoolean, pParent) { // empty } @@ -322,7 +322,7 @@ struct X3DNodeElementMetaBoolean : X3DNodeElementMeta { struct X3DNodeElementMetaDouble : X3DNodeElementMeta { std::vector Value; ///< Stored value. - X3DNodeElementMetaDouble(X3DNodeElementBase *pParent) : + explicit X3DNodeElementMetaDouble(X3DNodeElementBase *pParent) : X3DNodeElementMeta(X3DElemType::ENET_MetaDouble, pParent) { // empty } @@ -331,7 +331,7 @@ struct X3DNodeElementMetaDouble : X3DNodeElementMeta { struct X3DNodeElementMetaFloat : public X3DNodeElementMeta { std::vector Value; ///< Stored value. - X3DNodeElementMetaFloat(X3DNodeElementBase *pParent) : + explicit X3DNodeElementMetaFloat(X3DNodeElementBase *pParent) : X3DNodeElementMeta(X3DElemType::ENET_MetaFloat, pParent) { // empty } @@ -340,7 +340,7 @@ struct X3DNodeElementMetaFloat : public X3DNodeElementMeta { struct X3DNodeElementMetaInt : public X3DNodeElementMeta { std::vector Value; ///< Stored value. - X3DNodeElementMetaInt(X3DNodeElementBase *pParent) : + explicit X3DNodeElementMetaInt(X3DNodeElementBase *pParent) : X3DNodeElementMeta(X3DElemType::ENET_MetaInteger, pParent) { // empty } @@ -349,7 +349,7 @@ struct X3DNodeElementMetaInt : public X3DNodeElementMeta { struct X3DNodeElementMetaSet : public X3DNodeElementMeta { std::list Value; ///< Stored value. - X3DNodeElementMetaSet(X3DNodeElementBase *pParent) : + explicit X3DNodeElementMetaSet(X3DNodeElementBase *pParent) : X3DNodeElementMeta(X3DElemType::ENET_MetaSet, pParent) { // empty } @@ -358,7 +358,7 @@ struct X3DNodeElementMetaSet : public X3DNodeElementMeta { struct X3DNodeElementMetaString : public X3DNodeElementMeta { std::list Value; ///< Stored value. - X3DNodeElementMetaString(X3DNodeElementBase *pParent) : + explicit X3DNodeElementMetaString(X3DNodeElementBase *pParent) : X3DNodeElementMeta(X3DElemType::ENET_MetaString, pParent) { // empty } From 8cae8c5461e61f02de404a415f43c17a730c5cb3 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 7 May 2021 11:52:16 +0200 Subject: [PATCH 013/232] Fix static code analysis findings --- code/AssetLib/X3D/X3DImporter.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/AssetLib/X3D/X3DImporter.hpp b/code/AssetLib/X3D/X3DImporter.hpp index 360dfe6c5..f65dda559 100644 --- a/code/AssetLib/X3D/X3DImporter.hpp +++ b/code/AssetLib/X3D/X3DImporter.hpp @@ -290,9 +290,6 @@ protected: struct CX3DNodeElementGroup : X3DNodeElementBase { aiMatrix4x4 Transformation; ///< Transformation matrix. - bool Static; - bool UseChoice; ///< Flag: if true then use number from \ref Choice to choose what the child will be kept. - int32_t Choice; ///< Number of the child which will be kept. }; struct X3DNodeElementMeta : X3DNodeElementBase { From 964778cac1a98fd333d00ab18f43577ff69ce39b Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Fri, 7 May 2021 17:30:26 +0200 Subject: [PATCH 014/232] Add AI_CONFIG_EXPORT_BLOB_NAME export property. --- include/assimp/config.h.in | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/assimp/config.h.in b/include/assimp/config.h.in index d78568da7..8ea82482f 100644 --- a/include/assimp/config.h.in +++ b/include/assimp/config.h.in @@ -1066,6 +1066,23 @@ enum aiComponent */ #define AI_CONFIG_EXPORT_POINT_CLOUDS "EXPORT_POINT_CLOUDS" +/** + * @brief Specifies the blob name, assimp uses for exporting. + * + * Some formats require auxiliary files to be written, that need to be linked back into + * the original file. For example, OBJ files export materials to a separate MTL file and + * use the `mtllib` keyword to reference this file. + * + * When exporting blobs using #ExportToBlob, assimp does not know the name of the blob + * file and thus outputs `mtllib $blobfile.mtl`, which might not be desired, since the + * MTL file might be called differently. + * + * This property can be used to give the exporter a hint on how to use the magic + * `$blobfile` keyword. If the exporter detects the keyword and is provided with a name + * for the blob, it instead uses this name. + */ +#define AI_CONFIG_EXPORT_BLOB_NAME "EXPORT_BLOB_NAME" + /** * @brief Specifies a gobal key factor for scale, float value */ From be85f238f4dd96f1182678d23e997b2e675484a7 Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Fri, 7 May 2021 17:30:58 +0200 Subject: [PATCH 015/232] Add optional blob base name to blob IO system. --- include/assimp/BlobIOSystem.h | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/include/assimp/BlobIOSystem.h b/include/assimp/BlobIOSystem.h index 4e3d5c2a3..081ccf32a 100644 --- a/include/assimp/BlobIOSystem.h +++ b/include/assimp/BlobIOSystem.h @@ -194,8 +194,14 @@ class BlobIOSystem : public IOSystem { friend class BlobIOStream; typedef std::pair BlobEntry; + public: - BlobIOSystem() { + BlobIOSystem() : + baseName{} { + } + + BlobIOSystem(const std::string &baseName) : + baseName(baseName) { } virtual ~BlobIOSystem() { @@ -207,27 +213,32 @@ public: public: // ------------------------------------------------------------------- const char *GetMagicFileName() const { - return AI_BLOBIO_MAGIC; + return baseName.empty() ? AI_BLOBIO_MAGIC : baseName.c_str(); } // ------------------------------------------------------------------- aiExportDataBlob *GetBlobChain() { + const auto magicName = std::string(this->GetMagicFileName()); + const bool hasBaseName = baseName.empty(); + // one must be the master aiExportDataBlob *master = nullptr, *cur; + for (const BlobEntry &blobby : blobs) { - if (blobby.first == AI_BLOBIO_MAGIC) { + if (blobby.first == magicName) { master = blobby.second; + master->name.Set(hasBaseName ? blobby.first : ""); break; } } + if (!master) { ASSIMP_LOG_ERROR("BlobIOSystem: no data written or master file was not closed properly."); return nullptr; } - master->name.Set(""); - cur = master; + for (const BlobEntry &blobby : blobs) { if (blobby.second == master) { continue; @@ -236,9 +247,14 @@ public: cur->next = blobby.second; cur = cur->next; - // extract the file extension from the file written - const std::string::size_type s = blobby.first.find_first_of('.'); - cur->name.Set(s == std::string::npos ? blobby.first : blobby.first.substr(s + 1)); + if (hasBaseName) { + cur->name.Set(blobby.first); + } + else { + // extract the file extension from the file written + const std::string::size_type s = blobby.first.find_first_of('.'); + cur->name.Set(s == std::string::npos ? blobby.first : blobby.first.substr(s + 1)); + } } // give up blob ownership @@ -283,6 +299,7 @@ private: } private: + std::string baseName; std::set created; std::vector blobs; }; From 8ff52c0f89fe52d52b6fc0ac6f111c9355616139 Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Fri, 7 May 2021 17:31:30 +0200 Subject: [PATCH 016/232] Pass base name from export properties to the IO system. --- code/Common/Exporter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/Common/Exporter.cpp b/code/Common/Exporter.cpp index 20cbb05d6..ebcc955df 100644 --- a/code/Common/Exporter.cpp +++ b/code/Common/Exporter.cpp @@ -343,9 +343,11 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha delete pimpl->blob; pimpl->blob = nullptr; } + + auto baseName = pProperties ? pProperties->GetPropertyString(AI_CONFIG_EXPORT_BLOB_NAME, AI_BLOBIO_MAGIC) : AI_BLOBIO_MAGIC; std::shared_ptr old = pimpl->mIOSystem; - BlobIOSystem* blobio = new BlobIOSystem(); + BlobIOSystem *blobio = new BlobIOSystem(baseName); pimpl->mIOSystem = std::shared_ptr( blobio ); if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName(), pPreprocessing, pProperties)) { From 1b33dd1965dd0a58b20fa4e270d1809abb920c58 Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Fri, 7 May 2021 17:31:38 +0200 Subject: [PATCH 017/232] Document AI_CONFIG_EXPORT_BLOB_NAME. --- include/assimp/cexport.h | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/include/assimp/cexport.h b/include/assimp/cexport.h index 2e84b1f30..44843af0e 100644 --- a/include/assimp/cexport.h +++ b/include/assimp/cexport.h @@ -205,16 +205,22 @@ struct aiExportDataBlob { 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. + * 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. + * + * The blob names generated can be influenced by + * setting the #AI_CONFIG_EXPORT_BLOB_NAME export + * property to the name that is used for the master + * blob. All other names are typically derived from + * the base name, by the file format exporter. */ C_STRUCT aiString name; From a19b708144b91d6936339b40f93ab7540ee25689 Mon Sep 17 00:00:00 2001 From: ywang Date: Fri, 7 May 2021 16:27:23 -0700 Subject: [PATCH 018/232] support both ktx and ktx2 --- code/AssetLib/glTF2/glTF2Exporter.cpp | 8 ++++++-- code/AssetLib/glTF2/glTF2Importer.cpp | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index e039bf88a..751508225 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -526,10 +526,14 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe if(memcmp(curTex->achFormatHint, "jpg", 3) == 0) mimeType += "jpeg"; else if(memcmp(curTex->achFormatHint, "ktx", 3) == 0) { + useBasisUniversal = true; + mimeType += "ktx"; + } + else if(memcmp(curTex->achFormatHint, "kx2", 3) == 0) { useBasisUniversal = true; mimeType += "ktx2"; } - else if(memcmp(curTex->achFormatHint, "bu", 3) == 0) { + else if(memcmp(curTex->achFormatHint, "bu", 2) == 0) { useBasisUniversal = true; mimeType += "basis"; } @@ -544,7 +548,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe } else { texture->source->uri = path; - if(texture->source->uri.find(".ktx2")!=std::string::npos || + if(texture->source->uri.find(".ktx")!=std::string::npos || texture->source->uri.find(".basis")!=std::string::npos) { useBasisUniversal = true; diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index b109891cb..db5da8813 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1476,8 +1476,8 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { if (strcmp(ext, "jpeg") == 0) { ext = "jpg"; } - else if(strcmp(ext, "ktx2") == 0) { //basisu - ext = "ktx"; + else if(strcmp(ext, "ktx2") == 0) { //basisu: ktx remains + ext = "kx2"; } else if(strcmp(ext, "basis") == 0) { //basisu ext = "bu"; From 859b32c04581644a9f54c6f18860dd7927a1d127 Mon Sep 17 00:00:00 2001 From: Jason C Date: Fri, 7 May 2021 22:32:32 -0400 Subject: [PATCH 019/232] [Logger] Log a notification instead of silently dropping long log messages. Logs a notification instead of silently dropping long log messages, which can complicate debugging. This way, if you don't see a message you expect to see, you'll immediately know why. The *correct* approach would be to eliminate length filtering here entirely and use `snprintf` appropriately (also there's a tiny -- probably negligible -- performance hit here in calling `strlen` regardless of whether or not the verbosity level matches). Failing that, the second best option is to copy and truncate messages here. However, for now, this should be OK. --- code/Common/DefaultLogger.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/code/Common/DefaultLogger.cpp b/code/Common/DefaultLogger.cpp index aa13ca5ce..3f6d2d7ed 100644 --- a/code/Common/DefaultLogger.cpp +++ b/code/Common/DefaultLogger.cpp @@ -169,7 +169,7 @@ void Logger::debug(const char *message) { // sometimes importers will include data from the input file // (i.e. node names) in their messages. if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { - return; + return OnDebug(""); } return OnDebug(message); } @@ -181,7 +181,7 @@ void Logger::verboseDebug(const char *message) { // sometimes importers will include data from the input file // (i.e. node names) in their messages. if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { - return; + return OnVerboseDebug(""); } return OnVerboseDebug(message); } @@ -191,7 +191,7 @@ void Logger::info(const char *message) { // SECURITY FIX: see above if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { - return; + return OnInfo(""); } return OnInfo(message); } @@ -201,7 +201,7 @@ void Logger::warn(const char *message) { // SECURITY FIX: see above if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { - return; + return OnWarn(""); } return OnWarn(message); } @@ -210,7 +210,7 @@ void Logger::warn(const char *message) { void Logger::error(const char *message) { // SECURITY FIX: see above if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { - return; + return OnError(""); } return OnError(message); } From d75f8bd5b0e74d0c93245b2e96092a9675c2111a Mon Sep 17 00:00:00 2001 From: Krishty Date: Mon, 10 May 2021 20:44:43 +0200 Subject: [PATCH 020/232] silence warnings in stb_image.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-applies 09c5564d5b1123125ba0a7f07f7f8c1dfcdf7a86 to stb_image.h’s new path. --- contrib/stb_image/stb_image.h | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/contrib/stb_image/stb_image.h b/contrib/stb_image/stb_image.h index accef4839..65a205f6e 100644 --- a/contrib/stb_image/stb_image.h +++ b/contrib/stb_image/stb_image.h @@ -4120,7 +4120,7 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) if (s >= 16) return -1; // invalid code! // code size is s, so: b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! + if ((unsigned int)b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. a->code_buffer >>= s; a->num_bits -= s; @@ -6775,8 +6775,6 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, stbi_uc *two_back = 0; stbi__gif g; int stride; - int out_size = 0; - int delays_size = 0; memset(&g, 0, sizeof(g)); if (delays) { *delays = 0; @@ -6793,7 +6791,7 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, stride = g.w * g.h * 4; if (out) { - void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + void *tmp = (stbi_uc*) STBI_REALLOC( out, layers * stride ); if (NULL == tmp) { STBI_FREE(g.out); STBI_FREE(g.history); @@ -6802,19 +6800,15 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, } else { out = (stbi_uc*) tmp; - out_size = layers * stride; } if (delays) { - *delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); - delays_size = layers * sizeof(int); + *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); } } else { out = (stbi_uc*)stbi__malloc( layers * stride ); - out_size = layers * stride; if (delays) { *delays = (int*) stbi__malloc( layers * sizeof(int) ); - delays_size = layers * sizeof(int); } } memcpy( out + ((layers - 1) * stride), u, stride ); From 27135bd3e7ec444bb7000a2d8be179a52f77a5cf Mon Sep 17 00:00:00 2001 From: ogjamesfranco Date: Sat, 15 May 2021 15:27:24 -0400 Subject: [PATCH 021/232] changed the assimp output directory vars to cached vars --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7027e3300..7b9d6fc55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -334,9 +334,9 @@ INCLUDE (FindPkgMacros) INCLUDE (PrecompiledHeader) # Set Assimp project output directory variables. -SET(ASSIMP_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") -SET(ASSIMP_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") -SET(ASSIMP_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib") +SET(ASSIMP_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin" CACHE STRING "Path for runtime output files") +SET(ASSIMP_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin" CACHE STRING "Path for library output files") +SET(ASSIMP_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" CACHE STRING "Path for archive output files") # Macro used to set the output directories of a target to the # respective Assimp output directories. From 53790e82736c022b2dd935db957036e1342ea624 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 21 May 2021 12:25:36 +0100 Subject: [PATCH 022/232] Update Draco to upstream e4103dc Fixes some MSVC and mingw compiler issues Sets VERSION and SO_VERSION https://github.com/google/draco/commit/e4103dc39fe1c70c6ad40d26a01248f4b5d3887b --- contrib/draco/.ruby-version | 1 - contrib/draco/.travis.yml | 31 --------- contrib/draco/CMakeLists.txt | 8 +-- contrib/draco/README.md | 6 +- .../draco/cmake/draco_build_definitions.cmake | 9 ++- contrib/draco/cmake/draco_features.cmake | 63 ------------------- contrib/draco/cmake/draco_flags.cmake | 9 +++ contrib/draco/cmake/draco_install.cmake | 2 +- contrib/draco/cmake/draco_sanitizer.cmake | 20 +++--- contrib/draco/cmake/draco_targets.cmake | 24 ++++--- contrib/draco/src/draco/core/cycle_timer.cc | 14 ++--- contrib/draco/src/draco/core/cycle_timer.h | 7 ++- contrib/draco/src/draco/io/parser_utils.cc | 3 +- contrib/draco/src/draco/io/ply_reader.cc | 4 +- .../draco/src/draco/io/stdio_file_reader.cc | 7 +++ 15 files changed, 69 insertions(+), 139 deletions(-) delete mode 100644 contrib/draco/.ruby-version delete mode 100644 contrib/draco/.travis.yml delete mode 100644 contrib/draco/cmake/draco_features.cmake diff --git a/contrib/draco/.ruby-version b/contrib/draco/.ruby-version deleted file mode 100644 index 276cbf9e2..000000000 --- a/contrib/draco/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.3.0 diff --git a/contrib/draco/.travis.yml b/contrib/draco/.travis.yml deleted file mode 100644 index e9ef7123f..000000000 --- a/contrib/draco/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -cache: ccache -language: cpp -matrix: - include: - - os: linux - dist: xenial - compiler: clang - - os: linux - dist: xenial - compiler: gcc - - os: osx - compiler: clang - -addons: - apt: - packages: - - cmake - -script: - # Output version info for compilers, cmake, and make - - ${CC} -v - - ${CXX} -v - - cmake --version - - make --version - # Clone googletest - - pushd .. && git clone https://github.com/google/googletest.git && popd - # Configure and build - - mkdir _travis_build && cd _travis_build - - cmake -G "Unix Makefiles" -DENABLE_TESTS=ON .. - - make -j10 - - ./draco_tests diff --git a/contrib/draco/CMakeLists.txt b/contrib/draco/CMakeLists.txt index 3da2c664a..5526e7f60 100644 --- a/contrib/draco/CMakeLists.txt +++ b/contrib/draco/CMakeLists.txt @@ -804,7 +804,7 @@ else() draco_points_enc) # Library targets that consume the object collections. - if(MSVC OR WIN32) + if(MSVC) # In order to produce a DLL and import library the Windows tools require # that the exported symbols are part of the DLL target. The unfortunate side # effect of this is that a single configuration cannot output both the @@ -889,9 +889,6 @@ else() # For Mac, we need to build a .bundle for the unity plugin. if(APPLE) set_target_properties(dracodec_unity PROPERTIES BUNDLE true) - elseif(NOT unity_decoder_lib_type STREQUAL STATIC) - set_target_properties(dracodec_unity - PROPERTIES SOVERSION ${DRACO_SOVERSION}) endif() endif() @@ -916,9 +913,6 @@ else() # For Mac, we need to build a .bundle for the plugin. if(APPLE) set_target_properties(draco_maya_wrapper PROPERTIES BUNDLE true) - else() - set_target_properties(draco_maya_wrapper - PROPERTIES SOVERSION ${DRACO_SOVERSION}) endif() endif() diff --git a/contrib/draco/README.md b/contrib/draco/README.md index add66edcb..0d980b387 100644 --- a/contrib/draco/README.md +++ b/contrib/draco/README.md @@ -2,16 +2,16 @@

-![Build Status: master](https://travis-ci.org/google/draco.svg?branch=master) +[![Build Status](https://github.com/google/draco/workflows/Build/badge.svg)](https://github.com/google/draco/actions?query=workflow%3ABuild) News ======= ### Version 1.4.1 release -* Using the versioned gstatic.com WASM and Javascript decoders is now +* Using the versioned www.gstatic.com WASM and Javascript decoders is now recommended. To use v1.4.1, use this URL: * https://www.gstatic.com/draco/versioned/decoders/1.4.1/* * Replace the * with the files to load. E.g. - * https://gstatic.com/draco/versioned/decoders/1.4.1/draco_decoder.js + * https://www.gstatic.com/draco/versioned/decoders/1.4.1/draco_decoder.js * This works with the v1.3.6 and v1.4.0 releases, and will work with future Draco releases. * Bug fixes diff --git a/contrib/draco/cmake/draco_build_definitions.cmake b/contrib/draco/cmake/draco_build_definitions.cmake index c1ada6206..f7354c15f 100644 --- a/contrib/draco/cmake/draco_build_definitions.cmake +++ b/contrib/draco/cmake/draco_build_definitions.cmake @@ -6,7 +6,7 @@ set(DRACO_CMAKE_DRACO_BUILD_DEFINITIONS_CMAKE_ 1) # Utility for controlling the main draco library dependency. This changes in # shared builds, and when an optional target requires a shared library build. macro(set_draco_target) - if(MSVC OR WIN32) + if(MSVC) set(draco_dependency draco) set(draco_plugin_dependency ${draco_dependency}) else() @@ -63,6 +63,11 @@ macro(draco_set_build_definitions) if(BUILD_SHARED_LIBS) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) endif() + else() + if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) + # Ensure 64-bit platforms can support large files. + list(APPEND draco_defines "_LARGEFILE_SOURCE" "_FILE_OFFSET_BITS=64") + endif() endif() if(ANDROID) @@ -114,4 +119,6 @@ macro(draco_set_build_definitions) draco_check_emscripten_environment() draco_get_required_emscripten_flags(FLAG_LIST_VAR draco_base_cxx_flags) endif() + + draco_configure_sanitizer() endmacro() diff --git a/contrib/draco/cmake/draco_features.cmake b/contrib/draco/cmake/draco_features.cmake deleted file mode 100644 index be444bf24..000000000 --- a/contrib/draco/cmake/draco_features.cmake +++ /dev/null @@ -1,63 +0,0 @@ -if(DRACO_CMAKE_DRACO_FEATURES_CMAKE_) - return() -endif() -set(DRACO_CMAKE_DRACO_FEATURES_CMAKE_ 1) - -set(draco_features_file_name "${draco_build_dir}/draco/draco_features.h") -set(draco_features_list) - -# Macro that handles tracking of Draco preprocessor symbols for the purpose of -# producing draco_features.h. -# -# draco_enable_feature(FEATURE [TARGETS ]) FEATURE -# is required. It should be a Draco preprocessor symbol. TARGETS is optional. It -# can be one or more draco targets. -# -# When the TARGETS argument is not present the preproc symbol is added to -# draco_features.h. When it is draco_features.h is unchanged, and -# target_compile_options() is called for each target specified. -macro(draco_enable_feature) - set(def_flags) - set(def_single_arg_opts FEATURE) - set(def_multi_arg_opts TARGETS) - cmake_parse_arguments(DEF "${def_flags}" "${def_single_arg_opts}" - "${def_multi_arg_opts}" ${ARGN}) - if("${DEF_FEATURE}" STREQUAL "") - message(FATAL_ERROR "Empty FEATURE passed to draco_enable_feature().") - endif() - - # Do nothing/return early if $DEF_FEATURE is already in the list. - list(FIND draco_features_list ${DEF_FEATURE} df_index) - if(NOT df_index EQUAL -1) - return() - endif() - - list(LENGTH DEF_TARGETS df_targets_list_length) - if(${df_targets_list_length} EQUAL 0) - list(APPEND draco_features_list ${DEF_FEATURE}) - else() - foreach(target ${DEF_TARGETS}) - target_compile_definitions(${target} PRIVATE ${DEF_FEATURE}) - endforeach() - endif() -endmacro() - -# Function for generating draco_features.h. -function(draco_generate_features_h) - file(WRITE "${draco_features_file_name}.new" - "// GENERATED FILE -- DO NOT EDIT\n\n" "#ifndef DRACO_FEATURES_H_\n" - "#define DRACO_FEATURES_H_\n\n") - - foreach(feature ${draco_features_list}) - file(APPEND "${draco_features_file_name}.new" "#define ${feature}\n") - endforeach() - - file(APPEND "${draco_features_file_name}.new" - "\n#endif // DRACO_FEATURES_H_") - - # Will replace ${draco_features_file_name} only if the file content has - # changed. This prevents forced Draco rebuilds after CMake runs. - configure_file("${draco_features_file_name}.new" - "${draco_features_file_name}") - file(REMOVE "${draco_features_file_name}.new") -endfunction() diff --git a/contrib/draco/cmake/draco_flags.cmake b/contrib/draco/cmake/draco_flags.cmake index cb9d489e6..0397859a4 100644 --- a/contrib/draco/cmake/draco_flags.cmake +++ b/contrib/draco/cmake/draco_flags.cmake @@ -80,6 +80,12 @@ macro(draco_test_cxx_flag) # Run the actual compile test. unset(draco_all_cxx_flags_pass CACHE) message("--- Running combined CXX flags test, flags: ${all_cxx_flags}") + + # check_cxx_compiler_flag() requires that the flags are a string. When flags + # are passed as a list it will remove the list separators, and attempt to run + # a compile command using list entries concatenated together as a single + # argument. Avoid the problem by forcing the argument to be a string. + draco_set_and_stringify(SOURCE_VARS all_cxx_flags DEST all_cxx_flags) check_cxx_compiler_flag("${all_cxx_flags}" draco_all_cxx_flags_pass) if(cxx_test_FLAG_REQUIRED AND NOT draco_all_cxx_flags_pass) @@ -194,6 +200,9 @@ macro(draco_test_exe_linker_flag) else() unset(CMAKE_EXE_LINKER_FLAGS) endif() + + list(APPEND DRACO_EXE_LINKER_FLAGS ${${link_FLAG_LIST_VAR_NAME}}) + list(REMOVE_DUPLICATES DRACO_EXE_LINKER_FLAGS) endmacro() # Runs the draco compiler tests. This macro builds up the list of list var(s) diff --git a/contrib/draco/cmake/draco_install.cmake b/contrib/draco/cmake/draco_install.cmake index 5c63ecb4a..09bfb591d 100644 --- a/contrib/draco/cmake/draco_install.cmake +++ b/contrib/draco/cmake/draco_install.cmake @@ -55,7 +55,7 @@ macro(draco_setup_install_target) install(TARGETS draco_encoder DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") - if(WIN32) + if(MSVC) install(TARGETS draco DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") else() diff --git a/contrib/draco/cmake/draco_sanitizer.cmake b/contrib/draco/cmake/draco_sanitizer.cmake index ca8e23176..d2e41a6cb 100644 --- a/contrib/draco/cmake/draco_sanitizer.cmake +++ b/contrib/draco/cmake/draco_sanitizer.cmake @@ -5,28 +5,28 @@ set(DRACO_CMAKE_DRACO_SANITIZER_CMAKE_ 1) # Handles the details of enabling sanitizers. macro(draco_configure_sanitizer) - if(DRACO_SANITIZE AND NOT MSVC) + if(DRACO_SANITIZE AND NOT EMSCRIPTEN AND NOT MSVC) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(DRACO_SANITIZE MATCHES "cfi") - list(APPEND DRACO_CXX_FLAGS "-flto" "-fno-sanitize-trap=cfi") - list(APPEND DRACO_EXE_LINKER_FLAGS "-flto" "-fno-sanitize-trap=cfi" + list(APPEND SAN_CXX_FLAGS "-flto" "-fno-sanitize-trap=cfi") + list(APPEND SAN_LINKER_FLAGS "-flto" "-fno-sanitize-trap=cfi" "-fuse-ld=gold") endif() if(${CMAKE_SIZEOF_VOID_P} EQUAL 4 AND DRACO_SANITIZE MATCHES "integer|undefined") - list(APPEND DRACO_EXE_LINKER_FLAGS "--rtlib=compiler-rt" "-lgcc_s") + list(APPEND SAN_LINKER_FLAGS "--rtlib=compiler-rt" "-lgcc_s") endif() endif() - list(APPEND DRACO_CXX_FLAGS "-fsanitize=${DRACO_SANITIZE}") - list(APPEND DRACO_EXE_LINKER_FLAGS "-fsanitize=${DRACO_SANITIZE}") + list(APPEND SAN_CXX_FLAGS "-fsanitize=${DRACO_SANITIZE}") + list(APPEND SAN_LINKER_FLAGS "-fsanitize=${DRACO_SANITIZE}") # Make sanitizer callstacks accurate. - list(APPEND DRACO_CXX_FLAGS "-fno-omit-frame-pointer" - "-fno-optimize-sibling-calls") + list(APPEND SAN_CXX_FLAGS "-fno-omit-frame-pointer") + list(APPEND SAN_CXX_FLAGS "-fno-optimize-sibling-calls") - draco_test_cxx_flag(FLAG_LIST_VAR_NAMES DRACO_CXX_FLAGS FLAG_REQUIRED) - draco_test_exe_linker_flag(FLAG_LIST_VAR_NAME DRACO_EXE_LINKER_FLAGS) + draco_test_cxx_flag(FLAG_LIST_VAR_NAMES SAN_CXX_FLAGS FLAG_REQUIRED) + draco_test_exe_linker_flag(FLAG_LIST_VAR_NAME SAN_LINKER_FLAGS) endif() endmacro() diff --git a/contrib/draco/cmake/draco_targets.cmake b/contrib/draco/cmake/draco_targets.cmake index 6dfa6a0c4..0456c4d7b 100644 --- a/contrib/draco/cmake/draco_targets.cmake +++ b/contrib/draco/cmake/draco_targets.cmake @@ -87,6 +87,7 @@ macro(draco_add_executable) endif() add_executable(${exe_NAME} ${exe_SOURCES}) + set_target_properties(${exe_NAME} PROPERTIES VERSION ${DRACO_VERSION}) if(exe_OUTPUT_NAME) set_target_properties(${exe_NAME} PROPERTIES OUTPUT_NAME ${exe_OUTPUT_NAME}) @@ -109,10 +110,11 @@ macro(draco_add_executable) if(exe_LINK_FLAGS OR DRACO_EXE_LINKER_FLAGS) if(${CMAKE_VERSION} VERSION_LESS "3.13") - set(link_flags ${exe_LINK_FLAGS} ${DRACO_EXE_LINKER_FLAGS}) + list(APPEND exe_LINK_FLAGS "${DRACO_EXE_LINKER_FLAGS}") + # LINK_FLAGS is managed as a string. + draco_set_and_stringify(SOURCE "${exe_LINK_FLAGS}" DEST exe_LINK_FLAGS) set_target_properties(${exe_NAME} - PROPERTIES LINK_FLAGS ${exe_LINK_FLAGS} - ${DRACO_EXE_LINKER_FLAGS}) + PROPERTIES LINK_FLAGS "${exe_LINK_FLAGS}") else() target_link_options(${exe_NAME} PRIVATE ${exe_LINK_FLAGS} ${DRACO_EXE_LINKER_FLAGS}) @@ -130,7 +132,7 @@ macro(draco_add_executable) endif() if(BUILD_SHARED_LIBS AND (MSVC OR WIN32)) - target_compile_definitions(${lib_NAME} PRIVATE "DRACO_BUILDING_DLL=0") + target_compile_definitions(${exe_NAME} PRIVATE "DRACO_BUILDING_DLL=0") endif() if(exe_LIB_DEPS) @@ -163,8 +165,8 @@ endmacro() # cmake-format: off # - OUTPUT_NAME: Override output file basename. Target basename defaults to # NAME. OUTPUT_NAME is ignored when BUILD_SHARED_LIBS is enabled and CMake -# is generating a build for which MSVC or WIN32 are true. This is to avoid -# output basename collisions with DLL import libraries. +# is generating a build for which MSVC is true. This is to avoid output +# basename collisions with DLL import libraries. # - TEST: Flag. Presence means treat library as a test. # - DEFINES: List of preprocessor macro definitions. # - INCLUDES: list of include directories for the target. @@ -259,7 +261,7 @@ macro(draco_add_library) endif() if(lib_OUTPUT_NAME) - if(NOT (BUILD_SHARED_LIBS AND (MSVC OR WIN32))) + if(NOT (BUILD_SHARED_LIBS AND MSVC)) set_target_properties(${lib_NAME} PROPERTIES OUTPUT_NAME ${lib_OUTPUT_NAME}) endif() @@ -318,8 +320,12 @@ macro(draco_add_library) set_target_properties(${lib_NAME} PROPERTIES PREFIX "") endif() - if(lib_TYPE STREQUAL SHARED AND NOT MSVC) - set_target_properties(${lib_NAME} PROPERTIES SOVERSION ${DRACO_SOVERSION}) + # VERSION and SOVERSION as necessary + if(NOT lib_TYPE STREQUAL STATIC AND NOT lib_TYPE STREQUAL MODULE) + set_target_properties(${lib_NAME} PROPERTIES VERSION ${DRACO_VERSION}) + if(NOT MSVC) + set_target_properties(${lib_NAME} PROPERTIES SOVERSION ${DRACO_SOVERSION}) + endif() endif() if(BUILD_SHARED_LIBS AND (MSVC OR WIN32)) diff --git a/contrib/draco/src/draco/core/cycle_timer.cc b/contrib/draco/src/draco/core/cycle_timer.cc index 94b4b28b2..58df4df77 100644 --- a/contrib/draco/src/draco/core/cycle_timer.cc +++ b/contrib/draco/src/draco/core/cycle_timer.cc @@ -17,31 +17,31 @@ namespace draco { void DracoTimer::Start() { #ifdef _WIN32 - QueryPerformanceCounter(&tv_start); + QueryPerformanceCounter(&tv_start_); #else - gettimeofday(&tv_start, nullptr); + gettimeofday(&tv_start_, nullptr); #endif } void DracoTimer::Stop() { #ifdef _WIN32 - QueryPerformanceCounter(&tv_end); + QueryPerformanceCounter(&tv_end_); #else - gettimeofday(&tv_end, nullptr); + gettimeofday(&tv_end_, nullptr); #endif } int64_t DracoTimer::GetInMs() { #ifdef _WIN32 LARGE_INTEGER elapsed = {0}; - elapsed.QuadPart = tv_end.QuadPart - tv_start.QuadPart; + elapsed.QuadPart = tv_end_.QuadPart - tv_start_.QuadPart; LARGE_INTEGER frequency = {0}; QueryPerformanceFrequency(&frequency); return elapsed.QuadPart * 1000 / frequency.QuadPart; #else - const int64_t seconds = (tv_end.tv_sec - tv_start.tv_sec) * 1000; - const int64_t milliseconds = (tv_end.tv_usec - tv_start.tv_usec) / 1000; + const int64_t seconds = (tv_end_.tv_sec - tv_start_.tv_sec) * 1000; + const int64_t milliseconds = (tv_end_.tv_usec - tv_start_.tv_usec) / 1000; return seconds + milliseconds; #endif } diff --git a/contrib/draco/src/draco/core/cycle_timer.h b/contrib/draco/src/draco/core/cycle_timer.h index 172f1c2e9..f480cc9d3 100644 --- a/contrib/draco/src/draco/core/cycle_timer.h +++ b/contrib/draco/src/draco/core/cycle_timer.h @@ -20,9 +20,10 @@ #define WIN32_LEAN_AND_MEAN #endif #include -typedef LARGE_INTEGER timeval; +typedef LARGE_INTEGER DracoTimeVal; #else #include +typedef timeval DracoTimeVal; #endif #include @@ -39,8 +40,8 @@ class DracoTimer { int64_t GetInMs(); private: - timeval tv_start; - timeval tv_end; + DracoTimeVal tv_start_; + DracoTimeVal tv_end_; }; typedef DracoTimer CycleTimer; diff --git a/contrib/draco/src/draco/io/parser_utils.cc b/contrib/draco/src/draco/io/parser_utils.cc index 4f95f6f84..12afacff6 100644 --- a/contrib/draco/src/draco/io/parser_utils.cc +++ b/contrib/draco/src/draco/io/parser_utils.cc @@ -18,6 +18,7 @@ #include #include #include +#include namespace draco { namespace parser { @@ -252,7 +253,7 @@ DecoderBuffer ParseLineIntoDecoderBuffer(DecoderBuffer *buffer) { std::string ToLower(const std::string &str) { std::string out; - std::transform(str.begin(), str.end(), std::back_inserter(out), [](unsigned char c){return tolower(c);}); + std::transform(str.begin(), str.end(), std::back_inserter(out), tolower); return out; } diff --git a/contrib/draco/src/draco/io/ply_reader.cc b/contrib/draco/src/draco/io/ply_reader.cc index cb32df225..ea7f2689a 100644 --- a/contrib/draco/src/draco/io/ply_reader.cc +++ b/contrib/draco/src/draco/io/ply_reader.cc @@ -268,14 +268,14 @@ std::vector PlyReader::SplitWords(const std::string &line) { while ((end = line.find_first_of(" \t\n\v\f\r", start)) != std::string::npos) { const std::string word(line.substr(start, end - start)); - if (!std::all_of(word.begin(), word.end(), [](unsigned char c){return isspace(c);})) { + if (!std::all_of(word.begin(), word.end(), isspace)) { output.push_back(word); } start = end + 1; } const std::string last_word(line.substr(start)); - if (!std::all_of(last_word.begin(), last_word.end(), [](unsigned char c){return isspace(c);})) { + if (!std::all_of(last_word.begin(), last_word.end(), isspace)) { output.push_back(last_word); } return output; diff --git a/contrib/draco/src/draco/io/stdio_file_reader.cc b/contrib/draco/src/draco/io/stdio_file_reader.cc index 560c3e9e8..a99c96f8f 100644 --- a/contrib/draco/src/draco/io/stdio_file_reader.cc +++ b/contrib/draco/src/draco/io/stdio_file_reader.cc @@ -87,7 +87,14 @@ size_t StdioFileReader::GetFileSize() { return false; } +#if _FILE_OFFSET_BITS == 64 + const size_t file_size = static_cast(ftello(file_)); +#elif defined _WIN64 + const size_t file_size = static_cast(_ftelli64(file_)); +#else const size_t file_size = static_cast(ftell(file_)); +#endif + rewind(file_); return file_size; From 1f32743f8b455082432adb249426f17c39dfa756 Mon Sep 17 00:00:00 2001 From: dlyr Date: Sat, 22 May 2021 00:56:01 +0200 Subject: [PATCH 023/232] Fix camera fov comment since full fov is stored --- include/assimp/camera.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/assimp/camera.h b/include/assimp/camera.h index d7324d10d..6a7acadbb 100644 --- a/include/assimp/camera.h +++ b/include/assimp/camera.h @@ -137,7 +137,7 @@ struct aiCamera */ C_STRUCT aiVector3D mLookAt; - /** Half horizontal field of view angle, in radians. + /** 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. From 28e34878cb9d85627cce81ed20dcb6f8b493d83b Mon Sep 17 00:00:00 2001 From: Jagoon <5785026+jagoon@users.noreply.github.com> Date: Sat, 22 May 2021 23:20:34 +0900 Subject: [PATCH 024/232] Fix fbx exporter bug if root node contains meshes. --- code/AssetLib/FBX/FBXExporter.cpp | 32 +++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index e519f7e77..b24360cd5 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -541,10 +541,17 @@ void FBXExporter::WriteReferences () // (before any actual data is written) // --------------------------------------------------------------- -size_t count_nodes(const aiNode* n) { - size_t count = 1; +size_t count_nodes(const aiNode* n, const aiNode* root) { + size_t count; + if (n == root) { + count = n->mNumMeshes; // (not counting root node) + } else if (n->mNumMeshes > 1) { + count = n->mNumMeshes + 1; + } else { + count = 1; + } for (size_t i = 0; i < n->mNumChildren; ++i) { - count += count_nodes(n->mChildren[i]); + count += count_nodes(n->mChildren[i], root); } return count; } @@ -714,7 +721,7 @@ void FBXExporter::WriteDefinitions () // Model / FbxNode // <~~ node hierarchy - count = int32_t(count_nodes(mScene->mRootNode)) - 1; // (not counting root node) + count = int32_t(count_nodes(mScene->mRootNode, mScene->mRootNode)); if (count) { n = FBX::Node("ObjectType", "Model"); n.AddChild("Count", count); @@ -2625,17 +2632,14 @@ void FBXExporter::WriteModelNodes( ], new_node_uid ); - // write model node - FBX::Node m("Model"); + + aiNode new_node; // take name from mesh name, if it exists - std::string name = mScene->mMeshes[node->mMeshes[i]]->mName.C_Str(); - name += FBX::SEPARATOR + "Model"; - m.AddProperties(new_node_uid, name, "Mesh"); - m.AddChild("Version", int32_t(232)); - FBX::Node p("Properties70"); - p.AddP70enum("InheritType", 1); - m.AddChild(p); - m.Dump(outstream, binary, 1); + new_node.mName = mScene->mMeshes[node->mMeshes[i]]->mName; + // write model node + WriteModelNode( + outstream, binary, &new_node, new_node_uid, "Mesh", transform_chain + ); } } From b7b3c6db7e3ba84814dcec1394787e40d3ff7e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Verdon?= Date: Sat, 22 May 2021 16:57:07 +0200 Subject: [PATCH 025/232] Fixing GCC 4.9 compilation issues --- code/AssetLib/glTF2/glTF2Asset.inl | 2 ++ include/assimp/TinyFormatter.h | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 8a793c144..256fc8931 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -58,7 +58,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma clang diagnostic ignored "-Wsign-compare" #elif defined(__GNUC__) #pragma GCC diagnostic push +#if (__GNUC__ > 4) #pragma GCC diagnostic ignored "-Wbool-compare" +#endif #pragma GCC diagnostic ignored "-Wsign-compare" #endif diff --git a/include/assimp/TinyFormatter.h b/include/assimp/TinyFormatter.h index ace20be5c..112f19013 100644 --- a/include/assimp/TinyFormatter.h +++ b/include/assimp/TinyFormatter.h @@ -88,9 +88,17 @@ public: underlying << sin; } + // Same problem as the copy constructor below, but with root cause is that stream move + // is not permitted on older GCC versions. Small performance impact on those platforms. +#if defined(__GNUC__) && (__GNUC__ == 4 && __GNUC_MINOR__ <= 9) + basic_formatter(basic_formatter&& other) { + underlying << (string)other; + } +#else basic_formatter(basic_formatter&& other) : underlying(std::move(other.underlying)) { } +#endif // The problem described here: // https://sourceforge.net/tracker/?func=detail&atid=1067632&aid=3358562&group_id=226462 From f96e3cde2d299aa1bc1fd5126d55fbc4aec5cc73 Mon Sep 17 00:00:00 2001 From: Jagoon <5785026+jagoon@users.noreply.github.com> Date: Sun, 23 May 2021 00:06:05 +0900 Subject: [PATCH 026/232] Fix transform chain is applied twice --- code/AssetLib/FBX/FBXExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index b24360cd5..f7beee2e0 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -2638,7 +2638,7 @@ void FBXExporter::WriteModelNodes( new_node.mName = mScene->mMeshes[node->mMeshes[i]]->mName; // write model node WriteModelNode( - outstream, binary, &new_node, new_node_uid, "Mesh", transform_chain + outstream, binary, &new_node, new_node_uid, "Mesh", std::vector>() ); } } From 799384f2b85b8d3ee9c5b9e18bbe532b4dc7c63c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Verdon?= Date: Sat, 22 May 2021 17:36:39 +0200 Subject: [PATCH 027/232] Adding the required c flag to compile zip files using gcc 4.9 --- code/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index ebc3e0116..d3cb6e923 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -1137,6 +1137,9 @@ ELSE() TARGET_COMPILE_OPTIONS(assimp PRIVATE -Werror) ENDIF() +# adds C_FLAGS required to compile zip.c on old GCC 4.x compiler +TARGET_COMPILE_FEATURES(assimp PUBLIC c_std_99) + TARGET_INCLUDE_DIRECTORIES ( assimp PUBLIC $ $ From f13515a39109f717c13ee1201e9000da6eab786a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Verdon?= Date: Sun, 23 May 2021 19:06:16 +0200 Subject: [PATCH 028/232] Adding basic support for lights in FBX exporter --- code/AssetLib/FBX/FBXExporter.cpp | 77 +++++++++++++++++++++++++++++-- code/AssetLib/FBX/FBXExporter.h | 2 + 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index e519f7e77..f2cae00b2 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -2196,7 +2196,65 @@ void FBXExporter::WriteObjects () bpnode.Dump(outstream, binary, indent); }*/ - // TODO: cameras, lights + // lights + indent = 1; + lights_uids.clear(); + for (size_t li = 0; li < mScene->mNumLights; ++li) { + aiLight* l = mScene->mLights[li]; + + int64_t uid = generate_uid(); + const std::string lightNodeAttributeName = l->mName.C_Str() + FBX::SEPARATOR + "NodeAttribute"; + + FBX::Node lna("NodeAttribute"); + lna.AddProperties(uid, lightNodeAttributeName, "Light"); + FBX::Node lnap("Properties70"); + + // Light color. + lnap.AddP70colorA("Color", l->mColorDiffuse.r, l->mColorDiffuse.g, l->mColorDiffuse.b); + + // TODO Assimp light description is quite concise and do not handle light intensity. + // Default value to 1000W. + lnap.AddP70numberA("Intensity", 1000); + + // FBXLight::EType conversion + switch (l->mType) { + case aiLightSource_POINT: + lnap.AddP70enum("LightType", 0); + break; + case aiLightSource_DIRECTIONAL: + lnap.AddP70enum("LightType", 1); + break; + case aiLightSource_SPOT: + lnap.AddP70enum("LightType", 2); + lnap.AddP70numberA("InnerAngle", AI_RAD_TO_DEG(l->mAngleInnerCone)); + lnap.AddP70numberA("OuterAngle", AI_RAD_TO_DEG(l->mAngleOuterCone)); + break; + // TODO Assimp do not handle 'area' nor 'volume' lights, but FBX does. + /*case aiLightSource_AREA: + lnap.AddP70enum("LightType", 3); + lnap.AddP70enum("AreaLightShape", 0); // 0=Rectangle, 1=Sphere + break; + case aiLightSource_VOLUME: + lnap.AddP70enum("LightType", 4); + break;*/ + default: + break; + } + + // Did not understood how to configure the decay so disabling attenuation. + lnap.AddP70enum("DecayType", 0); + + // Dump to FBX stream + lna.AddChild(lnap); + lna.AddChild("TypeFlags", FBX::FBXExportProperty("Light")); + lna.AddChild("GeometryVersion", FBX::FBXExportProperty(int32_t(124))); + lna.Dump(outstream, binary, indent); + + // Store name and uid (will be used later when parsing scene nodes) + lights_uids[l->mName.C_Str()] = uid; + } + + // TODO: cameras // write nodes (i.e. model hierarchy) // start at root node @@ -2600,10 +2658,19 @@ void FBXExporter::WriteModelNodes( // and connect them connections.emplace_back("C", "OO", node_attribute_uid, node_uid); } else { - // generate a null node so we can add children to it - WriteModelNode( - outstream, binary, node, node_uid, "Null", transform_chain - ); + const auto& lightIt = lights_uids.find(node->mName.C_Str()); + if(lightIt != lights_uids.end()) { + // Node has a light connected to it. + WriteModelNode( + outstream, binary, node, node_uid, "Light", transform_chain + ); + connections.emplace_back("C", "OO", lightIt->second, node_uid); + } else { + // generate a null node so we can add children to it + WriteModelNode( + outstream, binary, node, node_uid, "Null", transform_chain + ); + } } // if more than one child mesh, make nodes for each mesh diff --git a/code/AssetLib/FBX/FBXExporter.h b/code/AssetLib/FBX/FBXExporter.h index dcd1d2727..563183268 100644 --- a/code/AssetLib/FBX/FBXExporter.h +++ b/code/AssetLib/FBX/FBXExporter.h @@ -63,6 +63,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. struct aiScene; struct aiNode; +struct aiLight; //struct aiMaterial; namespace Assimp @@ -95,6 +96,7 @@ namespace Assimp std::vector mesh_uids; std::vector material_uids; std::map node_uids; + std::map lights_uids; // this crude unique-ID system is actually fine int64_t last_uid = 999999; From 5468dd667e41069da6f6ca97141c953838107eef Mon Sep 17 00:00:00 2001 From: Evangel Date: Wed, 26 May 2021 18:36:56 +1000 Subject: [PATCH 029/232] Fix bug in aiMetadata constructor that overwrites an array of one of aiString, aiVector3D, or aiMetadata with the first entry aiMetadata copy constructor calls aiMetadata::Get on the copied from aiMetadata using the const aiString &key version. When this is called on the metadata of an array type, this overwrites all entries with the first entry. This is due to the key of all entries in an array being the name of the array. ie, in a glTF2 file with an extension: "Extension" : [ "Value1", "Value2", "Value3" ] the aiMetadata struct for the "Extension" entry will have 3 entries with key/value pairs as: "Extension"/"Value1" "Extension"/"Value2" "Extension"/"Value3" So when the copy constructor calls the key based aiMetadata::Get, it will find "Value1" for all three entries. This change simply replaces the key based aiMetadata::Get with the index based aiMetadata::Get --- include/assimp/metadata.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/assimp/metadata.h b/include/assimp/metadata.h index fdc88be56..39ac386e4 100644 --- a/include/assimp/metadata.h +++ b/include/assimp/metadata.h @@ -202,17 +202,17 @@ struct aiMetadata { } break; case AI_AISTRING: { aiString v; - rhs.Get(mKeys[i], v); + rhs.Get(i, v); mValues[i].mData = new aiString(v); } break; case AI_AIVECTOR3D: { aiVector3D v; - rhs.Get(mKeys[i], v); + rhs.Get(i, v); mValues[i].mData = new aiVector3D(v); } break; case AI_AIMETADATA: { aiMetadata v; - rhs.Get(mKeys[i], v); + rhs.Get(i, v); mValues[i].mData = new aiMetadata(v); } break; #ifndef SWIG From 2f4fba070364ec3ceb34104e884ee9bddcbd8acf Mon Sep 17 00:00:00 2001 From: Evangel Date: Wed, 26 May 2021 19:11:19 +1000 Subject: [PATCH 030/232] Static cast i back to unsigned int because MSVC complains otherwise. i will never be bigger than an unsigned int since that's what mNumProperties is to begin with. --- include/assimp/metadata.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/assimp/metadata.h b/include/assimp/metadata.h index 39ac386e4..551a9aba4 100644 --- a/include/assimp/metadata.h +++ b/include/assimp/metadata.h @@ -156,7 +156,7 @@ struct aiMetadata { #ifdef __cplusplus - /** + /** * @brief The default constructor, set all members to zero by default. */ aiMetadata() AI_NO_EXCEPT @@ -202,17 +202,17 @@ struct aiMetadata { } break; case AI_AISTRING: { aiString v; - rhs.Get(i, v); + rhs.Get(static_cast(i), v); mValues[i].mData = new aiString(v); } break; case AI_AIVECTOR3D: { aiVector3D v; - rhs.Get(i, v); + rhs.Get(static_cast(i), v); mValues[i].mData = new aiVector3D(v); } break; case AI_AIMETADATA: { aiMetadata v; - rhs.Get(i, v); + rhs.Get(static_cast(i), v); mValues[i].mData = new aiMetadata(v); } break; #ifndef SWIG From c33a4b26346fd1cdaf7133dad06fcc29f47990aa Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Thu, 27 May 2021 10:10:55 +0200 Subject: [PATCH 031/232] Fixed base name check. --- include/assimp/BlobIOSystem.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/include/assimp/BlobIOSystem.h b/include/assimp/BlobIOSystem.h index 081ccf32a..0abf166bc 100644 --- a/include/assimp/BlobIOSystem.h +++ b/include/assimp/BlobIOSystem.h @@ -197,7 +197,7 @@ class BlobIOSystem : public IOSystem { public: BlobIOSystem() : - baseName{} { + baseName{AI_BLOBIO_MAGIC} { } BlobIOSystem(const std::string &baseName) : @@ -213,13 +213,13 @@ public: public: // ------------------------------------------------------------------- const char *GetMagicFileName() const { - return baseName.empty() ? AI_BLOBIO_MAGIC : baseName.c_str(); + return baseName.c_str(); } // ------------------------------------------------------------------- aiExportDataBlob *GetBlobChain() { const auto magicName = std::string(this->GetMagicFileName()); - const bool hasBaseName = baseName.empty(); + const bool hasBaseName = baseName != AI_BLOBIO_MAGIC; // one must be the master aiExportDataBlob *master = nullptr, *cur; @@ -249,8 +249,7 @@ public: if (hasBaseName) { cur->name.Set(blobby.first); - } - else { + } else { // extract the file extension from the file written const std::string::size_type s = blobby.first.find_first_of('.'); cur->name.Set(s == std::string::npos ? blobby.first : blobby.first.substr(s + 1)); From 59467b204a6f099ed13ab7234bee08c1d89d24e6 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 27 May 2021 15:50:28 +0200 Subject: [PATCH 032/232] Create tech_debt.md --- .github/ISSUE_TEMPLATE/tech_debt.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/tech_debt.md diff --git a/.github/ISSUE_TEMPLATE/tech_debt.md b/.github/ISSUE_TEMPLATE/tech_debt.md new file mode 100644 index 000000000..2cd84d975 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tech_debt.md @@ -0,0 +1,25 @@ +--- +name: Technical debt +about: Create a report to help us to fix and detect tech debts +title: '' +labels: '' +assignees: '' + +--- + +**Describe the technical debt** +A clear and concise description of what the tech debt is about. + +**Better solution** +A clear and concise description of what you would expect. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. From 919ae69fe8208524301689371e43a5581b8889c6 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 28 May 2021 08:44:40 +0200 Subject: [PATCH 033/232] Update tech_debt.md --- .github/ISSUE_TEMPLATE/tech_debt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/tech_debt.md b/.github/ISSUE_TEMPLATE/tech_debt.md index 2cd84d975..a1172d932 100644 --- a/.github/ISSUE_TEMPLATE/tech_debt.md +++ b/.github/ISSUE_TEMPLATE/tech_debt.md @@ -2,7 +2,7 @@ name: Technical debt about: Create a report to help us to fix and detect tech debts title: '' -labels: '' +labels: 'Techdebt' assignees: '' --- From 3c51ff771cfa001e96b78382e299788250647ef8 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 28 May 2021 09:34:03 +0200 Subject: [PATCH 034/232] Update bug_report.md - Add the bug label - Make platform config more easy --- .github/ISSUE_TEMPLATE/bug_report.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea782..0f9a5f105 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,8 +1,8 @@ --- name: Bug report about: Create a report to help us improve -title: '' -labels: '' +title: 'Bug:' +labels: 'Bug' assignees: '' --- @@ -23,16 +23,10 @@ A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. -**Desktop (please complete the following information):** +**Platform (please complete the following information):** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] - **Additional context** Add any other context about the problem here. From 2559befaca621a94f6e911493519730d42f17775 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 28 May 2021 10:26:03 +0200 Subject: [PATCH 035/232] Update feature_request.md - Add label Feature-Request --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7d6..87302a0f0 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,7 +2,7 @@ name: Feature request about: Suggest an idea for this project title: '' -labels: '' +labels: 'Feature-Request' assignees: '' --- From 7534b149cff5ef287fce50da4f17cebf4c5a3608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20M=C3=B6ller?= Date: Fri, 28 May 2021 11:55:46 +0200 Subject: [PATCH 036/232] fix non skipped CR in header parsing --- code/AssetLib/Ply/PlyParser.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/Ply/PlyParser.cpp b/code/AssetLib/Ply/PlyParser.cpp index 4b416d1a1..59cb6b976 100644 --- a/code/AssetLib/Ply/PlyParser.cpp +++ b/code/AssetLib/Ply/PlyParser.cpp @@ -419,7 +419,8 @@ bool PLY::DOM::ParseHeader(IOStreamBuffer &streamBuffer, std::vector if (PLY::Element::ParseElement(streamBuffer, buffer, &out)) { // add the element to the list of elements alElements.push_back(out); - } else if (TokenMatch(buffer, "end_header", 10)) { + } else if ( TokenMatch(buffer, "end_header\r", 11) || //checks for header end with /r/n ending + TokenMatch(buffer, "end_header", 10)) { //checks for /n ending, if it doesn't end with /r/n // we have reached the end of the header break; } else { From c776924adf904cc1cb6cd4121394cb5d538a2c13 Mon Sep 17 00:00:00 2001 From: Garux Date: Sun, 30 May 2021 11:45:50 +0300 Subject: [PATCH 037/232] fix -Waddress-of-packed-member --- code/AssetLib/MDL/MDLFileData.h | 4 ++-- code/AssetLib/MDL/MDLLoader.cpp | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/code/AssetLib/MDL/MDLFileData.h b/code/AssetLib/MDL/MDLFileData.h index 872cee7f8..473a06989 100644 --- a/code/AssetLib/MDL/MDLFileData.h +++ b/code/AssetLib/MDL/MDLFileData.h @@ -704,8 +704,8 @@ struct GroupFrame //! Maximum vertex for all single frames Vertex max; - //! Time for all single frames - float time; // float[numframes] + //! List of times for all single frames + float *times; //! List of single frames SimpleFrame *frames; diff --git a/code/AssetLib/MDL/MDLLoader.cpp b/code/AssetLib/MDL/MDLLoader.cpp index b5010a37f..0f84111af 100644 --- a/code/AssetLib/MDL/MDLLoader.cpp +++ b/code/AssetLib/MDL/MDLLoader.cpp @@ -427,8 +427,9 @@ void MDLImporter::InternReadFile_Quake1() { pcFirstFrame = (MDL::SimpleFrame *)&pcFrames->frame; } else { // get the first frame in the group - BE_NCONST MDL::GroupFrame *pcFrames2 = (BE_NCONST MDL::GroupFrame *)pcFrames; - pcFirstFrame = (MDL::SimpleFrame *)( &pcFrames2->time + pcFrames2->numframes ); + BE_NCONST MDL::GroupFrame *pcFrames2 = (BE_NCONST MDL::GroupFrame *)szCurrent; + pcFirstFrame = (MDL::SimpleFrame *)( szCurrent + sizeof(MDL::GroupFrame::type) + sizeof(MDL::GroupFrame::numframes) + + sizeof(MDL::GroupFrame::min) + sizeof(MDL::GroupFrame::max) + sizeof(*MDL::GroupFrame::times) * pcFrames2->numframes ); } BE_NCONST MDL::Vertex *pcVertices = (BE_NCONST MDL::Vertex *)((pcFirstFrame->name) + sizeof(pcFirstFrame->name)); VALIDATE_FILE_SIZE((const unsigned char *)(pcVertices + pcHeader->num_verts)); From 77ce4080b689d4fcee3435813c3811dfa5781682 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 30 May 2021 21:54:04 +0200 Subject: [PATCH 038/232] fix viewer in case of unknown primitives. --- code/AssetLib/DXF/DXFLoader.cpp | 2 -- tools/assimp_view/assimp_view.cpp | 7 +++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/DXF/DXFLoader.cpp b/code/AssetLib/DXF/DXFLoader.cpp index 5d32ed121..49d572b0b 100644 --- a/code/AssetLib/DXF/DXFLoader.cpp +++ b/code/AssetLib/DXF/DXFLoader.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, diff --git a/tools/assimp_view/assimp_view.cpp b/tools/assimp_view/assimp_view.cpp index 260b22941..5ab7c53ad 100644 --- a/tools/assimp_view/assimp_view.cpp +++ b/tools/assimp_view/assimp_view.cpp @@ -489,7 +489,7 @@ int CreateAssetData() { nidx = 3; break; default: - ai_assert(false); + CLogWindow::Instance().WriteLine("Unknown primitiv type"); break; }; @@ -500,8 +500,7 @@ int CreateAssetData() { // check whether we can use 16 bit indices if (numIndices >= 65536) { // create 32 bit index buffer - if (FAILED(g_piDevice->CreateIndexBuffer(4 * - numIndices, + if (FAILED(g_piDevice->CreateIndexBuffer(4 * numIndices, D3DUSAGE_WRITEONLY | dwUsage, D3DFMT_INDEX32, D3DPOOL_DEFAULT, @@ -523,7 +522,7 @@ int CreateAssetData() { } else { // create 16 bit index buffer if (FAILED(g_piDevice->CreateIndexBuffer(2 * - numIndices, +numIndices, D3DUSAGE_WRITEONLY | dwUsage, D3DFMT_INDEX16, D3DPOOL_DEFAULT, From 84a2e1fc922542c3c60dff94c5db682f7ab95711 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 20:43:37 +0200 Subject: [PATCH 039/232] Update unity plugin to trilib2 - closes https://github.com/assimp/assimp/issues/3872 --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index f2ea4b094..949d60966 100644 --- a/Readme.md +++ b/Readme.md @@ -42,7 +42,7 @@ Take a look into the https://github.com/assimp/assimp/blob/master/Build.md file. * [.NET](https://bitbucket.org/Starnick/assimpnet/src/master/) * [Pascal](port/AssimpPascal/Readme.md) * [Javascript (Alpha)](https://github.com/makc/assimp2json) -* [Unity 3d Plugin](https://www.assetstore.unity3d.com/en/#!/content/91777) +* [Unity 3d Plugin](https://ricardoreis.net/trilib-2/) * [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (current [status](https://github.com/kotlin-graphics/assimp/wiki/Status)) * [HAXE-Port](https://github.com/longde123/assimp-haxe) The Assimp-HAXE-port. * [Rust](https://github.com/jkvargas/russimp) From 3a32612b71d4b00ca100b332268b0b272ffc14ba Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 20:55:31 +0200 Subject: [PATCH 040/232] Add skipping of unused nodes. --- code/AssetLib/X3D/X3DImporter.cpp | 97 +++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/code/AssetLib/X3D/X3DImporter.cpp b/code/AssetLib/X3D/X3DImporter.cpp index 24814876b..0fcfec726 100644 --- a/code/AssetLib/X3D/X3DImporter.cpp +++ b/code/AssetLib/X3D/X3DImporter.cpp @@ -124,6 +124,103 @@ struct WordIterator { const char *WordIterator::whitespace = ", \t\r\n"; +void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) { + static const size_t Uns_Skip_Len = 192; + static const char *Uns_Skip[Uns_Skip_Len] = { + // CAD geometry component + "CADAssembly", "CADFace", "CADLayer", "CADPart", "IndexedQuadSet", "QuadSet", + // Core + "ROUTE", "ExternProtoDeclare", "ProtoDeclare", "ProtoInstance", "ProtoInterface", "WorldInfo", + // Distributed interactive simulation (DIS) component + "DISEntityManager", "DISEntityTypeMapping", "EspduTransform", "ReceiverPdu", "SignalPdu", "TransmitterPdu", + // Cube map environmental texturing component + "ComposedCubeMapTexture", "GeneratedCubeMapTexture", "ImageCubeMapTexture", + // Environmental effects component + "Background", "Fog", "FogCoordinate", "LocalFog", "TextureBackground", + // Environmental sensor component + "ProximitySensor", "TransformSensor", "VisibilitySensor", + // Followers component + "ColorChaser", "ColorDamper", "CoordinateChaser", "CoordinateDamper", "OrientationChaser", "OrientationDamper", "PositionChaser", "PositionChaser2D", + "PositionDamper", "PositionDamper2D", "ScalarChaser", "ScalarDamper", "TexCoordChaser2D", "TexCoordDamper2D", + // Geospatial component + "GeoCoordinate", "GeoElevationGrid", "GeoLocation", "GeoLOD", "GeoMetadata", "GeoOrigin", "GeoPositionInterpolator", "GeoProximitySensor", + "GeoTouchSensor", "GeoTransform", "GeoViewpoint", + // Humanoid Animation (H-Anim) component + "HAnimDisplacer", "HAnimHumanoid", "HAnimJoint", "HAnimSegment", "HAnimSite", + // Interpolation component + "ColorInterpolator", "CoordinateInterpolator", "CoordinateInterpolator2D", "EaseInEaseOut", "NormalInterpolator", "OrientationInterpolator", + "PositionInterpolator", "PositionInterpolator2D", "ScalarInterpolator", "SplinePositionInterpolator", "SplinePositionInterpolator2D", + "SplineScalarInterpolator", "SquadOrientationInterpolator", + // Key device sensor component + "KeySensor", "StringSensor", + // Layering component + "Layer", "LayerSet", "Viewport", + // Layout component + "Layout", "LayoutGroup", "LayoutLayer", "ScreenFontStyle", "ScreenGroup", + // Navigation component + "Billboard", "Collision", "LOD", "NavigationInfo", "OrthoViewpoint", "Viewpoint", "ViewpointGroup", + // Networking component + "EXPORT", "IMPORT", "Anchor", "LoadSensor", + // NURBS component + "Contour2D", "ContourPolyline2D", "CoordinateDouble", "NurbsCurve", "NurbsCurve2D", "NurbsOrientationInterpolator", "NurbsPatchSurface", + "NurbsPositionInterpolator", "NurbsSet", "NurbsSurfaceInterpolator", "NurbsSweptSurface", "NurbsSwungSurface", "NurbsTextureCoordinate", + "NurbsTrimmedSurface", + // Particle systems component + "BoundedPhysicsModel", "ConeEmitter", "ExplosionEmitter", "ForcePhysicsModel", "ParticleSystem", "PointEmitter", "PolylineEmitter", "SurfaceEmitter", + "VolumeEmitter", "WindPhysicsModel", + // Picking component + "LinePickSensor", "PickableGroup", "PointPickSensor", "PrimitivePickSensor", "VolumePickSensor", + // Pointing device sensor component + "CylinderSensor", "PlaneSensor", "SphereSensor", "TouchSensor", + // Rendering component + "ClipPlane", + // Rigid body physics + "BallJoint", "CollidableOffset", "CollidableShape", "CollisionCollection", "CollisionSensor", "CollisionSpace", "Contact", "DoubleAxisHingeJoint", + "MotorJoint", "RigidBody", "RigidBodyCollection", "SingleAxisHingeJoint", "SliderJoint", "UniversalJoint", + // Scripting component + "Script", + // Programmable shaders component + "ComposedShader", "FloatVertexAttribute", "Matrix3VertexAttribute", "Matrix4VertexAttribute", "PackagedShader", "ProgramShader", "ShaderPart", + "ShaderProgram", + // Shape component + "FillProperties", "LineProperties", "TwoSidedMaterial", + // Sound component + "AudioClip", "Sound", + // Text component + "FontStyle", "Text", + // Texturing3D Component + "ComposedTexture3D", "ImageTexture3D", "PixelTexture3D", "TextureCoordinate3D", "TextureCoordinate4D", "TextureTransformMatrix3D", "TextureTransform3D", + // Texturing component + "MovieTexture", "MultiTexture", "MultiTextureCoordinate", "MultiTextureTransform", "PixelTexture", "TextureCoordinateGenerator", "TextureProperties", + // Time component + "TimeSensor", + // Event Utilities component + "BooleanFilter", "BooleanSequencer", "BooleanToggle", "BooleanTrigger", "IntegerSequencer", "IntegerTrigger", "TimeTrigger", + // Volume rendering component + "BlendedVolumeStyle", "BoundaryEnhancementVolumeStyle", "CartoonVolumeStyle", "ComposedVolumeStyle", "EdgeEnhancementVolumeStyle", "IsoSurfaceVolumeData", + "OpacityMapVolumeStyle", "ProjectionVolumeStyle", "SegmentedVolumeData", "ShadedVolumeStyle", "SilhouetteEnhancementVolumeStyle", "ToneMappedVolumeStyle", + "VolumeData" + }; + + const std::string nn = node.name(); + bool found = false; + bool close_found = false; + + for (size_t i = 0; i < Uns_Skip_Len; i++) { + if (nn == Uns_Skip[i]) { + found = true; + if (node.empty()) { + close_found = true; + break; + } + } + } + + if (!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + "."); + + LogInfo("Skipping node \"" + nn + "\" in " + pParentNodeName + "."); +} + X3DImporter::X3DImporter() : mNodeElementCur(nullptr), mScene(nullptr) { From b4fc41bc094574400b0a688ec1381888e40ee99c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 22:52:10 +0200 Subject: [PATCH 041/232] Use corret attribute name - closes https://github.com/assimp/assimp/issues/3887 --- port/PyAssimp/pyassimp/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/PyAssimp/pyassimp/core.py b/port/PyAssimp/pyassimp/core.py index 37beac886..35ad882b3 100644 --- a/port/PyAssimp/pyassimp/core.py +++ b/port/PyAssimp/pyassimp/core.py @@ -211,7 +211,7 @@ def _init(self, target = None, parent = None): else: # starts with 'm' but not iterable - setattr(target, name, obj) + setattr(target, m, obj) logger.debug("Added " + name + " as self." + name + " (type: " + str(type(obj)) + ")") if _is_init_type(obj): From cc912f09f7cf1d0d7cbdff5d6d6617d5af904c3a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 23:16:20 +0200 Subject: [PATCH 042/232] update pugi_xml to 1.11 --- contrib/pugixml/readme.txt | 10 +- contrib/pugixml/src/pugiconfig.hpp | 21 +- contrib/pugixml/src/pugixml.cpp | 638 +++++++++++++++++++---------- contrib/pugixml/src/pugixml.hpp | 69 +++- 4 files changed, 496 insertions(+), 242 deletions(-) diff --git a/contrib/pugixml/readme.txt b/contrib/pugixml/readme.txt index 5beb08a90..bfb1875cb 100644 --- a/contrib/pugixml/readme.txt +++ b/contrib/pugixml/readme.txt @@ -1,7 +1,7 @@ -pugixml 1.9 - an XML processing library +pugixml 1.11 - an XML processing library -Copyright (C) 2006-2018, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) -Report bugs and download new versions at http://pugixml.org/ +Copyright (C) 2006-2020, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) +Report bugs and download new versions at https://pugixml.org/ This is the distribution of pugixml, which is a C++ XML processing library, which consists of a DOM-like interface with rich traversal/modification @@ -13,8 +13,6 @@ automatically during parsing/saving). The distribution contains the following folders: - contrib/ - various contributions to pugixml - docs/ - documentation docs/samples - pugixml usage examples docs/quickstart.html - quick start guide @@ -28,7 +26,7 @@ The distribution contains the following folders: This library is distributed under the MIT License: -Copyright (c) 2006-2018 Arseny Kapoulkine +Copyright (c) 2006-2019 Arseny Kapoulkine Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/contrib/pugixml/src/pugiconfig.hpp b/contrib/pugixml/src/pugiconfig.hpp index 065b3c8bb..405a66b65 100644 --- a/contrib/pugixml/src/pugiconfig.hpp +++ b/contrib/pugixml/src/pugiconfig.hpp @@ -1,8 +1,8 @@ /** - * pugixml parser - version 1.9 + * pugixml parser - version 1.11 * -------------------------------------------------------- - * Copyright (C) 2006-2018, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ + * Copyright (C) 2006-2020, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at https://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. @@ -30,10 +30,8 @@ // #define PUGIXML_NO_EXCEPTIONS // Set this to control attributes for public classes/functions, i.e.: -//#ifdef _WIN32 -//#define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL -//#define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL -//#endif +// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL +// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL // #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall // In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead @@ -42,16 +40,19 @@ // #define PUGIXML_MEMORY_OUTPUT_STACK 10240 // #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 +// Tune this constant to adjust max nesting for XPath queries +// #define PUGIXML_XPATH_DEPTH_LIMIT 1024 + // Uncomment this to switch to header-only version -#define PUGIXML_HEADER_ONLY +// #define PUGIXML_HEADER_ONLY // Uncomment this to enable long long support -//#define PUGIXML_HAS_LONG_LONG +// #define PUGIXML_HAS_LONG_LONG #endif /** - * Copyright (c) 2006-2018 Arseny Kapoulkine + * Copyright (c) 2006-2020 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/contrib/pugixml/src/pugixml.cpp b/contrib/pugixml/src/pugixml.cpp index 2afff09dd..efdcdf699 100644 --- a/contrib/pugixml/src/pugixml.cpp +++ b/contrib/pugixml/src/pugixml.cpp @@ -1,8 +1,8 @@ /** - * pugixml parser - version 1.9 + * pugixml parser - version 1.11 * -------------------------------------------------------- - * Copyright (C) 2006-2018, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ + * Copyright (C) 2006-2020, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at https://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. @@ -378,7 +378,7 @@ PUGI__NS_BEGIN static PUGI__UNSIGNED_OVERFLOW unsigned int hash(const void* key) { - unsigned int h = static_cast(reinterpret_cast(key)); + unsigned int h = static_cast(reinterpret_cast(key) & 0xffffffff); // MurmurHash3 32-bit finalizer h ^= h >> 16; @@ -1861,7 +1861,7 @@ PUGI__NS_BEGIN enum chartypex_t { ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, > - ctx_special_attr = 2, // Any symbol >= 0 and < 32 (except \t), &, <, >, " + ctx_special_attr = 2, // Any symbol >= 0 and < 32, &, <, ", ' ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _ ctx_digit = 8, // 0-9 ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, . @@ -1869,10 +1869,10 @@ PUGI__NS_BEGIN static const unsigned char chartypex_table[256] = { - 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 2, 3, 3, // 0-15 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 3, 3, 2, 3, 3, // 0-15 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31 - 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63 + 0, 0, 2, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 1, 0, // 48-63 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95 @@ -2709,7 +2709,7 @@ PUGI__NS_BEGIN { PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800); - switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (eol escapes trim) + switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (trim eol escapes); this simultaneously checks 3 options from assertion above { case 0: return strconv_pcdata_impl::parse; case 1: return strconv_pcdata_impl::parse; @@ -2878,7 +2878,7 @@ PUGI__NS_BEGIN { PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); - switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes) + switch ((optmask >> 4) & 15) // get bitmask for flags (wnorm wconv eol escapes); this simultaneously checks 4 options from assertion above { case 0: return strconv_attribute_impl::parse_simple; case 1: return strconv_attribute_impl::parse_simple; @@ -3903,7 +3903,7 @@ PUGI__NS_BEGIN xml_encoding encoding; }; - PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type) + PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) { while (*s) { @@ -3930,7 +3930,17 @@ PUGI__NS_BEGIN ++s; break; case '"': - writer.write('&', 'q', 'u', 'o', 't', ';'); + if (flags & format_attribute_single_quote) + writer.write('"'); + else + writer.write('&', 'q', 'u', 'o', 't', ';'); + ++s; + break; + case '\'': + if (flags & format_attribute_single_quote) + writer.write('&', 'a', 'p', 'o', 's', ';'); + else + writer.write('\''); ++s; break; default: // s is not a usual symbol @@ -3938,7 +3948,8 @@ PUGI__NS_BEGIN unsigned int ch = static_cast(*s++); assert(ch < 32); - writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); + if (!(flags & format_skip_control_chars)) + writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); } } } @@ -3949,7 +3960,7 @@ PUGI__NS_BEGIN if (flags & format_no_escapes) writer.write_string(s); else - text_output_escaped(writer, s, type); + text_output_escaped(writer, s, type, flags); } PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s) @@ -4063,6 +4074,7 @@ PUGI__NS_BEGIN PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); + const char_t enquotation_char = (flags & format_attribute_single_quote) ? '\'' : '"'; for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) { @@ -4078,12 +4090,12 @@ PUGI__NS_BEGIN } writer.write_string(a->name ? a->name + 0 : default_name); - writer.write('=', '"'); + writer.write('=', enquotation_char); if (a->value) text_output(writer, a->value, ctx_special_attr, flags); - writer.write('"'); + writer.write(enquotation_char); } } @@ -4423,6 +4435,9 @@ PUGI__NS_BEGIN while (sit && sit != sn) { + // loop invariant: dit is inside the subtree rooted at dn + assert(dit); + // when a tree is copied into one of the descendants, we need to skip that subtree to avoid an infinite loop if (sit != dn) { @@ -4452,9 +4467,14 @@ PUGI__NS_BEGIN sit = sit->parent; dit = dit->parent; + + // loop invariant: dit is inside the subtree rooted at dn while sit is inside sn + assert(sit == sn || dit); } while (sit != sn); } + + assert(!sit || dit == dn->parent); } PUGI__FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa) @@ -4653,19 +4673,19 @@ PUGI__NS_BEGIN } template - PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value) + PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value, int precision) { char buf[128]; - PUGI__SNPRINTF(buf, "%.9g", value); + PUGI__SNPRINTF(buf, "%.*g", precision, double(value)); return set_value_ascii(dest, header, header_mask, buf); } template - PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value) + PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value, int precision) { char buf[128]; - PUGI__SNPRINTF(buf, "%.17g", value); + PUGI__SNPRINTF(buf, "%.*g", precision, value); return set_value_ascii(dest, header, header_mask, buf); } @@ -4688,6 +4708,7 @@ PUGI__NS_BEGIN char_t* buffer = 0; size_t length = 0; + // coverity[var_deref_model] if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); // delete original buffer if we performed a conversion @@ -4960,7 +4981,12 @@ PUGI__NS_BEGIN #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))) PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) { +#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 + FILE* file = 0; + return _wfopen_s(&file, path, mode) == 0 ? file : 0; +#else return _wfopen(path, mode); +#endif } #else PUGI__FN char* convert_path_heap(const wchar_t* str) @@ -5004,6 +5030,16 @@ PUGI__NS_BEGIN } #endif + PUGI__FN FILE* open_file(const char* path, const char* mode) + { +#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 + FILE* file = 0; + return fopen_s(&file, path, mode) == 0 ? file : 0; +#else + return fopen(path, mode); +#endif + } + PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding) { if (!file) return false; @@ -5329,14 +5365,28 @@ namespace pugi { if (!_attr) return false; - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, default_double_precision); + } + + PUGI__FN bool xml_attribute::set_value(double rhs, int precision) + { + if (!_attr) return false; + + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, precision); } PUGI__FN bool xml_attribute::set_value(float rhs) { if (!_attr) return false; - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, default_float_precision); + } + + PUGI__FN bool xml_attribute::set_value(float rhs, int precision) + { + if (!_attr) return false; + + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, precision); } PUGI__FN bool xml_attribute::set_value(bool rhs) @@ -6046,6 +6096,27 @@ namespace pugi return true; } + PUGI__FN bool xml_node::remove_attributes() + { + if (!_root) return false; + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return false; + + for (xml_attribute_struct* attr = _root->first_attribute; attr; ) + { + xml_attribute_struct* next = attr->next_attribute; + + impl::destroy_attribute(attr, alloc); + + attr = next; + } + + _root->first_attribute = 0; + + return true; + } + PUGI__FN bool xml_node::remove_child(const char_t* name_) { return remove_child(child(name_)); @@ -6064,6 +6135,27 @@ namespace pugi return true; } + PUGI__FN bool xml_node::remove_children() + { + if (!_root) return false; + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return false; + + for (xml_node_struct* cur = _root->first_child; cur; ) + { + xml_node_struct* next = cur->next_sibling; + + impl::destroy_node(cur, alloc); + + cur = next; + } + + _root->first_child = 0; + + return true; + } + PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) { // append_buffer is only valid for elements/documents @@ -6164,16 +6256,9 @@ namespace pugi PUGI__FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const { - xml_node found = *this; // Current search context. + xml_node context = path_[0] == delimiter ? root() : *this; - if (!_root || !path_[0]) return found; - - if (path_[0] == delimiter) - { - // Absolute path; e.g. '/foo/bar' - found = found.root(); - ++path_; - } + if (!context._root) return xml_node(); const char_t* path_segment = path_; @@ -6183,19 +6268,19 @@ namespace pugi while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end; - if (path_segment == path_segment_end) return found; + if (path_segment == path_segment_end) return context; const char_t* next_segment = path_segment_end; while (*next_segment == delimiter) ++next_segment; if (*path_segment == '.' && path_segment + 1 == path_segment_end) - return found.first_element_by_path(next_segment, delimiter); + return context.first_element_by_path(next_segment, delimiter); else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end) - return found.parent().first_element_by_path(next_segment, delimiter); + return context.parent().first_element_by_path(next_segment, delimiter); else { - for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling) + for (xml_node_struct* j = context._root->first_child; j; j = j->next_sibling) { if (j->name && impl::strequalrange(j->name, path_segment, static_cast(path_segment_end - path_segment))) { @@ -6490,14 +6575,28 @@ namespace pugi { xml_node_struct* dn = _data_new(); - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, default_float_precision) : false; + } + + PUGI__FN bool xml_text::set(float rhs, int precision) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, precision) : false; } PUGI__FN bool xml_text::set(double rhs) { xml_node_struct* dn = _data_new(); - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, default_double_precision) : false; + } + + PUGI__FN bool xml_text::set(double rhs, int precision) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, precision) : false; } PUGI__FN bool xml_text::set(bool rhs) @@ -6873,8 +6972,7 @@ namespace pugi { reset(); - for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling()) - append_copy(cur); + impl::node_copy_tree(_root, proto._root); } PUGI__FN void xml_document::_create() @@ -7104,7 +7202,7 @@ namespace pugi reset(); using impl::auto_deleter; // MSVC7 workaround - auto_deleter file(fopen(path_, "rb"), impl::close_file); + auto_deleter file(impl::open_file(path_, "rb"), impl::close_file); return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); } @@ -7187,7 +7285,7 @@ namespace pugi PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { using impl::auto_deleter; // MSVC7 workaround - auto_deleter file(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file); + auto_deleter file(impl::open_file(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file); return impl::save_file_impl(*this, file.data, indent, flags, encoding); } @@ -7331,14 +7429,14 @@ PUGI__NS_BEGIN } }; - template void swap(T& lhs, T& rhs) + template inline void swap(T& lhs, T& rhs) { T temp = lhs; lhs = rhs; rhs = temp; } - template I min_element(I begin, I end, const Pred& pred) + template PUGI__FN I min_element(I begin, I end, const Pred& pred) { I result = begin; @@ -7349,17 +7447,20 @@ PUGI__NS_BEGIN return result; } - template void reverse(I begin, I end) + template PUGI__FN void reverse(I begin, I end) { - while (end - begin > 1) swap(*begin++, *--end); + while (end - begin > 1) + swap(*begin++, *--end); } - template I unique(I begin, I end) + template PUGI__FN I unique(I begin, I end) { // fast skip head - while (end - begin > 1 && *begin != *(begin + 1)) begin++; + while (end - begin > 1 && *begin != *(begin + 1)) + begin++; - if (begin == end) return begin; + if (begin == end) + return begin; // last written element I write = begin++; @@ -7377,7 +7478,7 @@ PUGI__NS_BEGIN return write + 1; } - template void insertion_sort(T* begin, T* end, const Pred& pred) + template PUGI__FN void insertion_sort(T* begin, T* end, const Pred& pred) { if (begin == end) return; @@ -7399,16 +7500,19 @@ PUGI__NS_BEGIN } } - template I median3(I first, I middle, I last, const Pred& pred) + template inline I median3(I first, I middle, I last, const Pred& pred) { - if (pred(*middle, *first)) swap(middle, first); - if (pred(*last, *middle)) swap(last, middle); - if (pred(*middle, *first)) swap(middle, first); + if (pred(*middle, *first)) + swap(middle, first); + if (pred(*last, *middle)) + swap(last, middle); + if (pred(*middle, *first)) + swap(middle, first); return middle; } - template void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend) + template PUGI__FN void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend) { // invariant: array is split into 4 groups: = < ? > (each variable denotes the boundary between the groups) T* eq = begin; @@ -7435,7 +7539,7 @@ PUGI__NS_BEGIN *out_eqend = gt; } - template void sort(I begin, I end, const Pred& pred) + template PUGI__FN void sort(I begin, I end, const Pred& pred) { // sort large chunks while (end - begin > 16) @@ -7464,6 +7568,41 @@ PUGI__NS_BEGIN // insertion sort small chunk insertion_sort(begin, end, pred); } + + PUGI__FN bool hash_insert(const void** table, size_t size, const void* key) + { + assert(key); + + unsigned int h = static_cast(reinterpret_cast(key)); + + // MurmurHash3 32-bit finalizer + h ^= h >> 16; + h *= 0x85ebca6bu; + h ^= h >> 13; + h *= 0xc2b2ae35u; + h ^= h >> 16; + + size_t hashmod = size - 1; + size_t bucket = h & hashmod; + + for (size_t probe = 0; probe <= hashmod; ++probe) + { + if (table[bucket] == 0) + { + table[bucket] = key; + return true; + } + + if (table[bucket] == key) + return false; + + // hash collision, quadratic probing + bucket = (bucket + probe + 1) & hashmod; + } + + assert(false && "Hash table is full"); // unreachable + return false; + } PUGI__NS_END // Allocator used for AST and evaluation stacks @@ -8053,15 +8192,6 @@ PUGI__NS_BEGIN } }; - struct duplicate_comparator - { - bool operator()(const xpath_node& lhs, const xpath_node& rhs) const - { - if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true; - else return rhs.attribute() ? false : lhs.node() < rhs.node(); - } - }; - PUGI__FN double gen_nan() { #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) @@ -8069,7 +8199,7 @@ PUGI__NS_BEGIN typedef uint32_t UI; // BCC5 workaround union { float f; UI i; } u; u.i = 0x7fc00000; - return u.f; + return double(u.f); #else // fallback const volatile double zero = 0.0; @@ -8849,12 +8979,42 @@ PUGI__NS_BEGIN _end = pos; } - void remove_duplicates() + void remove_duplicates(xpath_allocator* alloc) { - if (_type == xpath_node_set::type_unsorted) - sort(_begin, _end, duplicate_comparator()); + if (_type == xpath_node_set::type_unsorted && _end - _begin > 2) + { + xpath_allocator_capture cr(alloc); - _end = unique(_begin, _end); + size_t size_ = static_cast(_end - _begin); + + size_t hash_size = 1; + while (hash_size < size_ + size_ / 2) hash_size *= 2; + + const void** hash_data = static_cast(alloc->allocate(hash_size * sizeof(void**))); + if (!hash_data) return; + + memset(hash_data, 0, hash_size * sizeof(const void**)); + + xpath_node* write = _begin; + + for (xpath_node* it = _begin; it != _end; ++it) + { + const void* attr = it->attribute().internal_object(); + const void* node = it->node().internal_object(); + const void* key = attr ? attr : node; + + if (key && hash_insert(hash_data, hash_size, key)) + { + *write++ = *it; + } + } + + _end = write; + } + else + { + _end = unique(_begin, _end); + } } xpath_node_set::type_t type() const @@ -9611,7 +9771,7 @@ PUGI__NS_BEGIN { xpath_context c(*it, i, size); - if (expr->eval_number(c, stack) == i) + if (expr->eval_number(c, stack) == static_cast(i)) { *last++ = *it; @@ -9635,11 +9795,11 @@ PUGI__NS_BEGIN double er = expr->eval_number(c, stack); - if (er >= 1.0 && er <= size) + if (er >= 1.0 && er <= static_cast(size)) { size_t eri = static_cast(er); - if (er == eri) + if (er == static_cast(eri)) { xpath_node r = last[eri - 1]; @@ -10083,6 +10243,7 @@ PUGI__NS_BEGIN bool once = (axis == axis_attribute && _test == nodetest_name) || (!_right && eval_once(axis_type, eval)) || + // coverity[mixed_enums] (_right && !_right->_next && _right->_test == predicate_constant_one); xpath_node_set_raw ns; @@ -10115,7 +10276,7 @@ PUGI__NS_BEGIN // child, attribute and self axes always generate unique set of nodes // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted) - ns.remove_duplicates(); + ns.remove_duplicates(stack.temp); return ns; } @@ -10275,35 +10436,38 @@ PUGI__NS_BEGIN if (_rettype == xpath_type_boolean) return _data.variable->get_boolean(); + + // variable needs to be converted to the correct type, this is handled by the fallthrough block below + break; } - // fallthrough default: - { - switch (_rettype) - { - case xpath_type_number: - return convert_number_to_boolean(eval_number(c, stack)); - - case xpath_type_string: - { - xpath_allocator_capture cr(stack.result); - - return !eval_string(c, stack).empty(); - } - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.result); - - return !eval_node_set(c, stack, nodeset_eval_any).empty(); - } - - default: - assert(false && "Wrong expression for return type boolean"); // unreachable - return false; - } + ; } + + // none of the ast types that return the value directly matched, we need to perform type conversion + switch (_rettype) + { + case xpath_type_number: + return convert_number_to_boolean(eval_number(c, stack)); + + case xpath_type_string: + { + xpath_allocator_capture cr(stack.result); + + return !eval_string(c, stack).empty(); + } + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.result); + + return !eval_node_set(c, stack, nodeset_eval_any).empty(); + } + + default: + assert(false && "Wrong expression for return type boolean"); // unreachable + return false; } } @@ -10410,36 +10574,38 @@ PUGI__NS_BEGIN if (_rettype == xpath_type_number) return _data.variable->get_number(); + + // variable needs to be converted to the correct type, this is handled by the fallthrough block below + break; } - // fallthrough default: - { - switch (_rettype) - { - case xpath_type_boolean: - return eval_boolean(c, stack) ? 1 : 0; - - case xpath_type_string: - { - xpath_allocator_capture cr(stack.result); - - return convert_string_to_number(eval_string(c, stack).c_str()); - } - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.result); - - return convert_string_to_number(eval_string(c, stack).c_str()); - } - - default: - assert(false && "Wrong expression for return type number"); // unreachable - return 0; - } - + ; } + + // none of the ast types that return the value directly matched, we need to perform type conversion + switch (_rettype) + { + case xpath_type_boolean: + return eval_boolean(c, stack) ? 1 : 0; + + case xpath_type_string: + { + xpath_allocator_capture cr(stack.result); + + return convert_string_to_number(eval_string(c, stack).c_str()); + } + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.result); + + return convert_string_to_number(eval_string(c, stack).c_str()); + } + + default: + assert(false && "Wrong expression for return type number"); // unreachable + return 0; } } @@ -10596,7 +10762,7 @@ PUGI__NS_BEGIN double first = round_nearest(_right->eval_number(c, stack)); if (is_nan(first)) return xpath_string(); // NaN - else if (first >= s_length + 1) return xpath_string(); + else if (first >= static_cast(s_length + 1)) return xpath_string(); size_t pos = first < 1 ? 1 : static_cast(first); assert(1 <= pos && pos <= s_length + 1); @@ -10620,12 +10786,12 @@ PUGI__NS_BEGIN double last = first + round_nearest(_right->_next->eval_number(c, stack)); if (is_nan(first) || is_nan(last)) return xpath_string(); - else if (first >= s_length + 1) return xpath_string(); + else if (first >= static_cast(s_length + 1)) return xpath_string(); else if (first >= last) return xpath_string(); else if (last < 1) return xpath_string(); size_t pos = first < 1 ? 1 : static_cast(first); - size_t end = last >= s_length + 1 ? s_length + 1 : static_cast(last); + size_t end = last >= static_cast(s_length + 1) ? s_length + 1 : static_cast(last); assert(1 <= pos && pos <= end && end <= s_length + 1); const char_t* rbegin = s.c_str() + (pos - 1); @@ -10694,34 +10860,37 @@ PUGI__NS_BEGIN if (_rettype == xpath_type_string) return xpath_string::from_const(_data.variable->get_string()); + + // variable needs to be converted to the correct type, this is handled by the fallthrough block below + break; } - // fallthrough default: - { - switch (_rettype) - { - case xpath_type_boolean: - return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); - - case xpath_type_number: - return convert_number_to_string(eval_number(c, stack), stack.result); - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first); - return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); - } - - default: - assert(false && "Wrong expression for return type string"); // unreachable - return xpath_string(); - } + ; } + + // none of the ast types that return the value directly matched, we need to perform type conversion + switch (_rettype) + { + case xpath_type_boolean: + return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); + + case xpath_type_number: + return convert_number_to_string(eval_number(c, stack), stack.result); + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first); + return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); + } + + default: + assert(false && "Wrong expression for return type string"); // unreachable + return xpath_string(); } } @@ -10735,16 +10904,16 @@ PUGI__NS_BEGIN xpath_stack swapped_stack = {stack.temp, stack.result}; - xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval); - xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval); + xpath_node_set_raw ls = _left->eval_node_set(c, stack, eval); + xpath_node_set_raw rs = _right->eval_node_set(c, swapped_stack, eval); // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother - rs.set_type(xpath_node_set::type_unsorted); + ls.set_type(xpath_node_set::type_unsorted); - rs.append(ls.begin(), ls.end(), stack.result); - rs.remove_duplicates(); + ls.append(rs.begin(), rs.end(), stack.result); + ls.remove_duplicates(stack.temp); - return rs; + return ls; } case ast_filter: @@ -10843,13 +11012,18 @@ PUGI__NS_BEGIN return ns; } + + // variable needs to be converted to the correct type, this is handled by the fallthrough block below + break; } - // fallthrough default: - assert(false && "Wrong expression for return type node set"); // unreachable - return xpath_node_set_raw(); + ; } + + // none of the ast types that return the value directly matched, but conversions to node set are invalid + assert(false && "Wrong expression for return type node set"); // unreachable + return xpath_node_set_raw(); } void optimize(xpath_allocator* alloc) @@ -10863,6 +11037,7 @@ PUGI__NS_BEGIN if (_next) _next->optimize(alloc); + // coverity[var_deref_model] optimize_self(alloc); } @@ -10871,13 +11046,14 @@ PUGI__NS_BEGIN // Rewrite [position()=expr] with [expr] // Note that this step has to go before classification to recognize [position()=1] if ((_type == ast_filter || _type == ast_predicate) && + _right && // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate) _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number) { _right = _right->_right; } // Classify filter/predicate ops to perform various optimizations during evaluation - if (_type == ast_filter || _type == ast_predicate) + if ((_type == ast_filter || _type == ast_predicate) && _right) // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate) { assert(_test == predicate_default); @@ -10893,8 +11069,8 @@ PUGI__NS_BEGIN // The former is a full form of //foo, the latter is much faster since it executes the node test immediately // Do a similar kind of rewrite for self/descendant/descendant-or-self axes // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1]) - if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left && - _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right && + if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && + _left && _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right && is_posinv_step()) { if (_axis == axis_child || _axis == axis_descendant) @@ -10906,7 +11082,9 @@ PUGI__NS_BEGIN } // Use optimized lookup table implementation for translate() with constant arguments - if (_type == ast_func_translate && _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant) + if (_type == ast_func_translate && + _right && // workaround for clang static analyzer (_right is never null for ast_func_translate) + _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant) { unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string); @@ -10919,6 +11097,8 @@ PUGI__NS_BEGIN // Use optimized path for @attr = 'value' or @attr = $value if (_type == ast_op_equal && + _left && _right && // workaround for clang static analyzer and Coverity (_left and _right are never null for ast_op_equal) + // coverity[mixed_enums] _left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right && (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string))) { @@ -10978,6 +11158,14 @@ PUGI__NS_BEGIN } }; + static const size_t xpath_ast_depth_limit = + #ifdef PUGIXML_XPATH_DEPTH_LIMIT + PUGIXML_XPATH_DEPTH_LIMIT + #else + 1024 + #endif + ; + struct xpath_parser { xpath_allocator* _alloc; @@ -10990,6 +11178,8 @@ PUGI__NS_BEGIN char_t _scratch[32]; + size_t _depth; + xpath_ast_node* error(const char* message) { _result->error = message; @@ -11006,6 +11196,11 @@ PUGI__NS_BEGIN return 0; } + xpath_ast_node* error_rec() + { + return error("Exceeded maximum allowed query depth"); + } + void* alloc_node() { return _alloc->allocate(sizeof(xpath_ast_node)); @@ -11361,6 +11556,8 @@ PUGI__NS_BEGIN return error("Unrecognized function call"); _lexer.next(); + size_t old_depth = _depth; + while (_lexer.current() != lex_close_brace) { if (argc > 0) @@ -11370,6 +11567,9 @@ PUGI__NS_BEGIN _lexer.next(); } + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + xpath_ast_node* n = parse_expression(); if (!n) return 0; @@ -11382,6 +11582,8 @@ PUGI__NS_BEGIN _lexer.next(); + _depth = old_depth; + return parse_function(function, argc, args); } @@ -11398,10 +11600,15 @@ PUGI__NS_BEGIN xpath_ast_node* n = parse_primary_expression(); if (!n) return 0; + size_t old_depth = _depth; + while (_lexer.current() == lex_open_square_brace) { _lexer.next(); + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + if (n->rettype() != xpath_type_node_set) return error("Predicate has to be applied to node set"); @@ -11417,6 +11624,8 @@ PUGI__NS_BEGIN _lexer.next(); } + _depth = old_depth; + return n; } @@ -11568,12 +11777,17 @@ PUGI__NS_BEGIN xpath_ast_node* n = alloc_node(ast_step, set, axis, nt_type, nt_name_copy); if (!n) return 0; + size_t old_depth = _depth; + xpath_ast_node* last = 0; while (_lexer.current() == lex_open_square_brace) { _lexer.next(); + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + xpath_ast_node* expr = parse_expression(); if (!expr) return 0; @@ -11590,6 +11804,8 @@ PUGI__NS_BEGIN last = pred; } + _depth = old_depth; + return n; } @@ -11599,11 +11815,16 @@ PUGI__NS_BEGIN xpath_ast_node* n = parse_step(set); if (!n) return 0; + size_t old_depth = _depth; + while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) { lexeme_t l = _lexer.current(); _lexer.next(); + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + if (l == lex_double_slash) { n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); @@ -11614,6 +11835,8 @@ PUGI__NS_BEGIN if (!n) return 0; } + _depth = old_depth; + return n; } @@ -11799,6 +12022,9 @@ PUGI__NS_BEGIN { _lexer.next(); + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + xpath_ast_node* rhs = parse_path_or_unary_expression(); if (!rhs) return 0; @@ -11844,13 +12070,22 @@ PUGI__NS_BEGIN // | MultiplicativeExpr 'mod' UnaryExpr xpath_ast_node* parse_expression(int limit = 0) { + size_t old_depth = _depth; + + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + xpath_ast_node* n = parse_path_or_unary_expression(); if (!n) return 0; - return parse_expression_rec(n, limit); + n = parse_expression_rec(n, limit); + + _depth = old_depth; + + return n; } - xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result) + xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result), _depth(0) { } @@ -11859,6 +12094,8 @@ PUGI__NS_BEGIN xpath_ast_node* n = parse_expression(); if (!n) return 0; + assert(_depth == 0); + // check if there are unparsed tokens left if (_lexer.current() != lex_eof) return error("Incorrect query"); @@ -12013,74 +12250,61 @@ namespace pugi size_t size_ = static_cast(end_ - begin_); - if (size_ <= 1) + // use internal buffer for 0 or 1 elements, heap buffer otherwise + xpath_node* storage = (size_ <= 1) ? _storage : static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); + + if (!storage) { - // deallocate old buffer - if (_begin != &_storage) impl::xml_memory::deallocate(_begin); - - // use internal buffer - if (begin_ != end_) _storage = *begin_; - - _begin = &_storage; - _end = &_storage + size_; - _type = type_; + #ifdef PUGIXML_NO_EXCEPTIONS + return; + #else + throw std::bad_alloc(); + #endif } - else - { - // make heap copy - xpath_node* storage = static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); - if (!storage) - { - #ifdef PUGIXML_NO_EXCEPTIONS - return; - #else - throw std::bad_alloc(); - #endif - } + // deallocate old buffer + if (_begin != _storage) + impl::xml_memory::deallocate(_begin); + // size check is necessary because for begin_ = end_ = nullptr, memcpy is UB + if (size_) memcpy(storage, begin_, size_ * sizeof(xpath_node)); - // deallocate old buffer - if (_begin != &_storage) impl::xml_memory::deallocate(_begin); - - // finalize - _begin = storage; - _end = storage + size_; - _type = type_; - } + _begin = storage; + _end = storage + size_; + _type = type_; } #ifdef PUGIXML_HAS_MOVE PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs) PUGIXML_NOEXCEPT { _type = rhs._type; - _storage = rhs._storage; - _begin = (rhs._begin == &rhs._storage) ? &_storage : rhs._begin; + _storage[0] = rhs._storage[0]; + _begin = (rhs._begin == rhs._storage) ? _storage : rhs._begin; _end = _begin + (rhs._end - rhs._begin); rhs._type = type_unsorted; - rhs._begin = &rhs._storage; - rhs._end = rhs._begin; + rhs._begin = rhs._storage; + rhs._end = rhs._storage; } #endif - PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage) + PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(_storage), _end(_storage) { } - PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(&_storage), _end(&_storage) + PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(_storage), _end(_storage) { _assign(begin_, end_, type_); } PUGI__FN xpath_node_set::~xpath_node_set() { - if (_begin != &_storage) + if (_begin != _storage) impl::xml_memory::deallocate(_begin); } - PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(&_storage), _end(&_storage) + PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(_storage), _end(_storage) { _assign(ns._begin, ns._end, ns._type); } @@ -12095,7 +12319,7 @@ namespace pugi } #ifdef PUGIXML_HAS_MOVE - PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(&_storage), _end(&_storage) + PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(_storage), _end(_storage) { _move(rhs); } @@ -12104,7 +12328,7 @@ namespace pugi { if (this == &rhs) return *this; - if (_begin != &_storage) + if (_begin != _storage) impl::xml_memory::deallocate(_begin); _move(rhs); @@ -12771,7 +12995,7 @@ namespace pugi #endif /** - * Copyright (c) 2006-2018 Arseny Kapoulkine + * Copyright (c) 2006-2020 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/contrib/pugixml/src/pugixml.hpp b/contrib/pugixml/src/pugixml.hpp index 1775600f1..7e2ce7776 100644 --- a/contrib/pugixml/src/pugixml.hpp +++ b/contrib/pugixml/src/pugixml.hpp @@ -1,8 +1,8 @@ /** - * pugixml parser - version 1.9 + * pugixml parser - version 1.11 * -------------------------------------------------------- - * Copyright (C) 2006-2018, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ + * Copyright (C) 2006-2020, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at https://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. @@ -12,8 +12,9 @@ */ #ifndef PUGIXML_VERSION -// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons -# define PUGIXML_VERSION 190 +// Define version macro; evaluates to major * 1000 + minor * 10 + patch so that it's safe to use in less-than comparisons +// Note: pugixml used major * 100 + minor * 10 + patch format up until 1.9 (which had version identifier 190); starting from pugixml 1.10, the minor version number is two digits +# define PUGIXML_VERSION 1110 #endif // Include user configuration file (this can define various configuration macros) @@ -110,6 +111,15 @@ # endif #endif +// If C++ is 2011 or higher, use 'nullptr' +#ifndef PUGIXML_NULL +# if __cplusplus >= 201103 +# define PUGIXML_NULL nullptr +# else +# define PUGIXML_NULL 0 +# endif +#endif + // Character interface macros #ifdef PUGIXML_WCHAR_MODE # define PUGIXML_TEXT(t) L ## t @@ -252,10 +262,19 @@ namespace pugi // Don't output empty element tags, instead writing an explicit start and end tag even if there are no children. This flag is off by default. const unsigned int format_no_empty_element_tags = 0x80; + // Skip characters belonging to range [0; 32) instead of "&#xNN;" encoding. This flag is off by default. + const unsigned int format_skip_control_chars = 0x100; + + // Use single quotes ' instead of double quotes " for enclosing attribute values. This flag is off by default. + const unsigned int format_attribute_single_quote = 0x200; + // The default set of formatting flags. // Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none. const unsigned int format_default = format_indent; + const int default_double_precision = 17; + const int default_float_precision = 9; + // Forward declarations struct xml_attribute_struct; struct xml_node_struct; @@ -403,7 +422,9 @@ namespace pugi bool set_value(long rhs); bool set_value(unsigned long rhs); bool set_value(double rhs); + bool set_value(double rhs, int precision); bool set_value(float rhs); + bool set_value(float rhs, int precision); bool set_value(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG @@ -569,10 +590,16 @@ namespace pugi bool remove_attribute(const xml_attribute& a); bool remove_attribute(const char_t* name); + // Remove all attributes + bool remove_attributes(); + // Remove specified child bool remove_child(const xml_node& n); bool remove_child(const char_t* name); + // Remove all children + bool remove_children(); + // Parses buffer as an XML document fragment and appends all nodes as children of the current node. // Copies/converts the buffer, so it may be deleted or changed after the function returns. // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. @@ -643,15 +670,15 @@ namespace pugi #ifndef PUGIXML_NO_XPATH // Select single node by evaluating XPath query. Returns first node from the resulting node set. - xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const; + xpath_node select_node(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const; xpath_node select_node(const xpath_query& query) const; // Select node set by evaluating XPath query - xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; + xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const; xpath_node_set select_nodes(const xpath_query& query) const; // (deprecated: use select_node instead) Select single node by evaluating XPath query. - PUGIXML_DEPRECATED xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const; + PUGIXML_DEPRECATED xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const; PUGIXML_DEPRECATED xpath_node select_single_node(const xpath_query& query) const; #endif @@ -754,7 +781,9 @@ namespace pugi bool set(long rhs); bool set(unsigned long rhs); bool set(double rhs); + bool set(double rhs, int precision); bool set(float rhs); + bool set(float rhs, int precision); bool set(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG @@ -1192,7 +1221,7 @@ namespace pugi public: // Construct a compiled object from XPath expression. // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. - explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); + explicit xpath_query(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL); // Constructor xpath_query(); @@ -1251,11 +1280,12 @@ namespace pugi }; #ifndef PUGIXML_NO_EXCEPTIONS - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning( disable: 4275 ) -#endif + #if defined(_MSC_VER) + // C4275 can be ignored in Visual C++ if you are deriving + // from a type in the Standard C++ Library + #pragma warning(push) + #pragma warning(disable: 4275) + #endif // XPath exception class class PUGIXML_CLASS xpath_exception: public std::exception { @@ -1272,10 +1302,11 @@ namespace pugi // Get parse result const xpath_parse_result& result() const; }; + #if defined(_MSC_VER) + #pragma warning(pop) + #endif #endif -#ifdef _MSC_VER -# pragma warning(pop) -#endif + // XPath node class (either xml_node or xml_attribute) class PUGIXML_CLASS xpath_node { @@ -1379,7 +1410,7 @@ namespace pugi private: type_t _type; - xpath_node _storage; + xpath_node _storage[1]; xpath_node* _begin; xpath_node* _end; @@ -1443,7 +1474,7 @@ namespace std #endif /** - * Copyright (c) 2006-2018 Arseny Kapoulkine + * Copyright (c) 2006-2020 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation From bf0f8d4c1b2f53a457b4e1834abf7f841ef30300 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 23:29:53 +0200 Subject: [PATCH 043/232] Update pugiconfig.hpp --- contrib/pugixml/src/pugiconfig.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/pugixml/src/pugiconfig.hpp b/contrib/pugixml/src/pugiconfig.hpp index 405a66b65..03f854bf1 100644 --- a/contrib/pugixml/src/pugiconfig.hpp +++ b/contrib/pugixml/src/pugiconfig.hpp @@ -44,7 +44,7 @@ // #define PUGIXML_XPATH_DEPTH_LIMIT 1024 // Uncomment this to switch to header-only version -// #define PUGIXML_HEADER_ONLY +#define PUGIXML_HEADER_ONLY // Uncomment this to enable long long support // #define PUGIXML_HAS_LONG_LONG From a716f741d83b818e63ba38e76368957c6a0e2185 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 23:35:12 +0200 Subject: [PATCH 044/232] fix the include --- code/Common/Assimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/Common/Assimp.cpp b/code/Common/Assimp.cpp index 6074b84bc..a6c539bca 100644 --- a/code/Common/Assimp.cpp +++ b/code/Common/Assimp.cpp @@ -1277,7 +1277,7 @@ ASSIMP_API void aiQuaternionInterpolate( # endif # define STB_IMAGE_IMPLEMENTATION -# include "../contrib/stb_image/stb_image.h" +# include "stb_image/stb_image.h" # if _MSC_VER # pragma warning(pop) From 742250c5fb595364ec6d532a61c43030417c9907 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 23:42:07 +0200 Subject: [PATCH 045/232] Update CMakeLists.txt --- code/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index d3cb6e923..e17657b53 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -1029,14 +1029,14 @@ ELSE () ENDIF () # RapidJSON +INCLUDE_DIRECTORIES( "../contrib" ) IF(ASSIMP_HUNTER_ENABLED) hunter_add_package(RapidJSON) find_package(RapidJSON CONFIG REQUIRED) ELSE() - INCLUDE_DIRECTORIES( "../contrib/rapidjson/include" ) - INCLUDE_DIRECTORIES( "../contrib" ) - INCLUDE_DIRECTORIES( "../contrib/pugixml/src" ) - ADD_DEFINITIONS( -DRAPIDJSON_HAS_STDSTRING=1 ) + INCLUDE_DIRECTORIES("../contrib/rapidjson/include") + INCLUDE_DIRECTORIES("../contrib/pugixml/src") + ADD_DEFINITIONS( -DRAPIDJSON_HAS_STDSTRING=1) option( ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR "Suppress rapidjson warning on MSVC (NOTE: breaks android build)" ON ) if(ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR) ADD_DEFINITIONS( -DRAPIDJSON_NOMEMBERITERATORCLASS ) From e6b83feb9fb43a3bc0779ad64bc54f13cc127873 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 23:45:48 +0200 Subject: [PATCH 046/232] Update CMakeLists.txt --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b9d6fc55..15ceb2177 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -227,6 +227,7 @@ INCLUDE_DIRECTORIES( BEFORE include ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/include + contrib/ ) LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules" ) From 84db4d3a08510e8709e3c23ff02ee0d48aef3668 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 23:56:52 +0200 Subject: [PATCH 047/232] Update CMakeLists.txt --- code/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index e17657b53..fe4abc06f 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -1029,12 +1029,12 @@ ELSE () ENDIF () # RapidJSON -INCLUDE_DIRECTORIES( "../contrib" ) IF(ASSIMP_HUNTER_ENABLED) hunter_add_package(RapidJSON) find_package(RapidJSON CONFIG REQUIRED) ELSE() INCLUDE_DIRECTORIES("../contrib/rapidjson/include") + INCLUDE_DIRECTORIES( "../contrib" ) INCLUDE_DIRECTORIES("../contrib/pugixml/src") ADD_DEFINITIONS( -DRAPIDJSON_HAS_STDSTRING=1) option( ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR "Suppress rapidjson warning on MSVC (NOTE: breaks android build)" ON ) From 444fc9c373ae3fb407bc4bfe3a6c5558175e3fba Mon Sep 17 00:00:00 2001 From: Scott Baldric Date: Tue, 1 Jun 2021 17:08:26 -0500 Subject: [PATCH 048/232] Increasing length of mDataLength if rewriting the texture index increases magnitutde of index. --- code/Common/SceneCombiner.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/code/Common/SceneCombiner.cpp b/code/Common/SceneCombiner.cpp index 555d46b6a..8f10d6308 100644 --- a/code/Common/SceneCombiner.cpp +++ b/code/Common/SceneCombiner.cpp @@ -406,11 +406,25 @@ void SceneCombiner::MergeScenes(aiScene **_dest, aiScene *master, std::vector, // where n is the index of the texture. - aiString &s = *((aiString *)prop->mData); + // Copy here because we overwrite the string data in-place and the buffer inside of aiString + // will be a lie if we just reinterpret from prop->mData. The size of mData is not guaranteed to be + // MAXLEN in size. + aiString s(*(aiString *)prop->mData); if ('*' == s.data[0]) { // Offset the index and write it back .. const unsigned int idx = strtoul10(&s.data[1]) + offset[n]; - ASSIMP_itoa10(&s.data[1], sizeof(s.data) - 1, idx); + const unsigned int oldLen = s.length; + + s.length = 1 + ASSIMP_itoa10(&s.data[1], sizeof(s.data) - 1, idx); + + // The string changed in size so we need to reallocate the buffer for the property. + if (oldLen < s.length) { + prop->mDataLength += s.length - oldLen; + delete[] prop->mData; + prop->mData = new char[prop->mDataLength]; + } + + memcpy(prop->mData, static_cast(&s), prop->mDataLength); } } From 121c0e7d0c9db2db6c80897b46dfc4743f64d2b7 Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Mon, 7 Jun 2021 21:53:28 -0700 Subject: [PATCH 049/232] Add GetEmbeddedTextureAndIndex() to aiScene. It allows the caller to get the index of the embedded texture that is always computed anyway. --- include/assimp/scene.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/include/assimp/scene.h b/include/assimp/scene.h index 769499a27..fb3b2ef6e 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -397,22 +397,27 @@ struct aiScene //! Returns an embedded texture const aiTexture* GetEmbeddedTexture(const char* filename) const { + return GetEmbeddedTextureAndIndex(filename).first; + } + + //! Returns an embedded texture and its index + std::pair GetEmbeddedTextureAndIndex(const char* filename) const { // lookup using texture ID (if referenced like: "*1", "*2", etc.) if ('*' == *filename) { int index = std::atoi(filename + 1); if (0 > index || mNumTextures <= static_cast(index)) - return nullptr; - return mTextures[index]; + return std::make_pair(nullptr, -1); + return std::make_pair(mTextures[index], index); } // lookup using filename const char* shortFilename = GetShortFilename(filename); for (unsigned int i = 0; i < mNumTextures; i++) { const char* shortTextureFilename = GetShortFilename(mTextures[i]->mFilename.C_Str()); if (strcmp(shortTextureFilename, shortFilename) == 0) { - return mTextures[i]; + return std::make_pair(mTextures[i], i); } } - return nullptr; + return std::make_pair(nullptr, -1); } #endif // __cplusplus From ef739c170312a1667e1b8fd2e54a2d1614631a56 Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Tue, 8 Jun 2021 12:53:18 -0700 Subject: [PATCH 050/232] glTF2: Make handling of embedded textures safer. Previous code does not check whether the embedded texture exists. --- code/AssetLib/glTF2/glTF2Exporter.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 751508225..83356f7c2 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -118,14 +118,14 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai ExportScene(); ExportAnimations(); - + // export extras if(mProperties->HasPropertyCallback("extras")) { std::function ExportExtras = mProperties->GetPropertyCallback("extras"); mAsset->extras = (rapidjson::Value*)ExportExtras(0); } - + AssetWriter writer(*mAsset); if (isBinary) { @@ -515,11 +515,10 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe std::string imgId = mAsset->FindUniqueID("", "image"); texture->source = mAsset->images.Create(imgId); - if (path[0] == '*') { // embedded - aiTexture* curTex = mScene->mTextures[atoi(&path[1])]; - + const aiTexture* curTex = mScene->GetEmbeddedTexture(path.c_str()); + if (curTex != nullptr) { // embedded texture->source->name = curTex->mFilename.C_Str(); - + //basisu: embedded ktx2, bu if (curTex->achFormatHint[0]) { std::string mimeType = "image/"; @@ -541,7 +540,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe mimeType += curTex->achFormatHint; texture->source->mimeType = mimeType; } - + // The asset has its own buffer, see Image::SetData //basisu: "image/ktx2", "image/basis" as is texture->source->SetData(reinterpret_cast(curTex->pcData), curTex->mWidth, *mAsset); @@ -554,7 +553,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe useBasisUniversal = true; } } - + //basisu if(useBasisUniversal) { mAsset->extensionsUsed.KHR_texture_basisu = true; From 9f9d77f882efc445f46139ed2188ce21fc857032 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Thu, 10 Jun 2021 18:13:46 +0100 Subject: [PATCH 051/232] First pass at simplifying glTFv2 PBR Removed 'core' set of GLTF-specific properties --- code/AssetLib/glTF2/glTF2Exporter.cpp | 60 ++++++++++++++------------- code/AssetLib/glTF2/glTF2Importer.cpp | 21 ++++++---- include/assimp/material.h | 40 +++++++++++++++++- include/assimp/pbrmaterial.h | 14 +++---- test/unit/utglTF2ImportExport.cpp | 55 ++++++++++++++++++++---- 5 files changed, 138 insertions(+), 52 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 751508225..dfa62372a 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -645,7 +645,7 @@ void glTF2Exporter::ExportMaterials() m->name = name; - GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_BASE_COLOR); if (!m->pbrMetallicRoughness.baseColorTexture.texture) { //if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture @@ -654,19 +654,19 @@ void glTF2Exporter::ExportMaterials() GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR) != AI_SUCCESS) { + if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_BASE_COLOR) != AI_SUCCESS) { // if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material. //a fallback to any diffuse color should be used instead GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); } - if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { + if (mat->Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0 m->pbrMetallicRoughness.metallicFactor = 0; } // get roughness if source is gltf2 file - if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { + if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { // otherwise, try to derive and convert from specular + shininess values aiColor4D specularColor; ai_real shininess; @@ -712,36 +712,38 @@ void glTF2Exporter::ExportMaterials() } } - bool hasPbrSpecularGlossiness = false; - mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS, hasPbrSpecularGlossiness); - - if (hasPbrSpecularGlossiness) { - - if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { - mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; - } - + { + // If has a Specular color, use the KHR_materials_pbrSpecularGlossiness extension PbrSpecularGlossiness pbrSG; - - GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); - GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR); - - if (mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { - float shininess; - - if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = shininess / 1000; + if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { + if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { + mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; } + + GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); + + // If don't have explicit glossiness then convert from roughness or shininess + if (mat->Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { + float shininess; + if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way + } else if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = shininess / 1000; + } + } + + // Add any appropriate textures + GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); + GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); + + m->pbrSpecularGlossiness = Nullable(pbrSG); } - - GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); - GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - - m->pbrSpecularGlossiness = Nullable(pbrSG); } - bool unlit; - if (mat->Get(AI_MATKEY_GLTF_UNLIT, unlit) == AI_SUCCESS && unlit) { + // glTFv2 is either PBR or Unlit + aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; + mat->Get(AI_MATKEY_SHADING_MODEL, shadingMode); + if (shadingMode == aiShadingMode_Unlit) { mAsset->extensionsUsed.KHR_materials_unlit = true; m->unlit = true; } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index c62989c3b..b5ba65857 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -238,16 +238,18 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M aimat->AddProperty(&str, AI_MATKEY_NAME); } + // Set Assimp DIFFUSE and BASE COLOR to the pbrMetallicRoughness base color and texture for backwards compatibility + // Technically should not load any pbrMetallicRoughness if extensionsRequired contains KHR_materials_pbrSpecularGlossiness SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); - SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR); + SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_BASE_COLOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_BASE_COLOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR); - aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR); + aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_METALLIC_FACTOR); + aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_ROUGHNESS_FACTOR); float roughnessAsShininess = 1 - mat.pbrMetallicRoughness.roughnessFactor; roughnessAsShininess *= roughnessAsShininess * 1000; @@ -268,22 +270,27 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M if (mat.pbrSpecularGlossiness.isPresent) { PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value; - aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS); SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_COLOR_SPECULAR); float glossinessAsShininess = pbrSG.glossinessFactor * 1000.0f; aimat->AddProperty(&glossinessAsShininess, 1, AI_MATKEY_SHININESS); - aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR); + aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLOSSINESS_FACTOR); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, aiTextureType_DIFFUSE); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, aiTextureType_SPECULAR); } + + // glTFv2 is either PBR or Unlit + aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; if (mat.unlit) { - aimat->AddProperty(&mat.unlit, 1, AI_MATKEY_GLTF_UNLIT); + shadingMode = aiShadingMode_Unlit; } + aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL); + + //KHR_materials_sheen if (mat.materialSheen.isPresent) { MaterialSheen &sheen = mat.materialSheen.value; diff --git a/include/assimp/material.h b/include/assimp/material.h index 08c0491c0..11cdef1f4 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -202,11 +202,15 @@ enum aiTextureType { /** The texture is combined with the result of the diffuse * lighting equation. + * OR + * PBR Specular/Glossiness */ aiTextureType_DIFFUSE = 1, /** The texture is combined with the result of the specular * lighting equation. + * OR + * PBR Specular/Glossiness */ aiTextureType_SPECULAR = 2, @@ -309,7 +313,9 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); // --------------------------------------------------------------------------- /** @brief Defines all shading models supported by the library - * + * + * #AI_MATKEY_SHADING_MODEL + * * 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 @@ -364,13 +370,27 @@ enum aiShadingMode { aiShadingMode_CookTorrance = 0x8, /** No shading at all. Constant light influence of 1.0. + * Also known as "Unlit" */ aiShadingMode_NoShading = 0x9, + aiShadingMode_Unlit = aiShadingMode_NoShading, // Alias /** Fresnel shading */ aiShadingMode_Fresnel = 0xa, + /** Physically-Based Rendering (PBR) shading using + * Bidirectional scattering/reflectance distribution function (BSDF/BRDF) + * There are multiple methods under this banner, and model files may provide + * data for more than one PBR-BRDF method. + * Applications should use the set of provided properties to determine which + * of their preferred PBDR methods are available + * eg: + * - If AI_MATKEY_METALLIC_FACTOR is set, then a Metallic/Roughness is available + * - If AI_MATKEY_COLOR_SPECULAR is set, then a Specular/Glossiness is available + */ + aiShadingMode_PBR_BRDF = 0xb, + #ifndef SWIG _aiShadingMode_Force32Bit = INT_MAX #endif @@ -923,11 +943,29 @@ extern "C" { // --------------------------------------------------------------------------- // PBR material support #define AI_MATKEY_USE_COLOR_MAP "$mat.useColorMap", 0, 0 + +// Metallic/Roughness Workflow +// --------------------------- +// Base color factor. Will be multiplied by final base color texture values if extant #define AI_MATKEY_BASE_COLOR "$clr.base", 0, 0 #define AI_MATKEY_USE_METALLIC_MAP "$mat.useMetallicMap", 0, 0 +// Metallic factor. 0.0 = Full Dielectric, 1.0 = Full Metal #define AI_MATKEY_METALLIC_FACTOR "$mat.metallicFactor", 0, 0 #define AI_MATKEY_USE_ROUGHNESS_MAP "$mat.useRoughnessMap", 0, 0 +// Roughness factor. 0.0 = Perfectly Smooth, 1.0 = Completely Rough #define AI_MATKEY_ROUGHNESS_FACTOR "$mat.roughnessFactor", 0, 0 + +// Specular/Glossiness Workflow +// --------------------------- +// Diffuse/Albedo Color. Note: Pure Metals have a diffuse of {0,0,0} +// AI_MATKEY_COLOR_DIFFUSE +// Specular Color +// AI_MATKEY_COLOR_SPECULAR +// Glossiness factor. 0.0 = Completely Rough, 1.0 = Perfectly Smooth +#define AI_MATKEY_GLOSSINESS_FACTOR "$mat.glossinessFactor", 0, 0 + +// Emissive +// -------- #define AI_MATKEY_USE_EMISSIVE_MAP "$mat.useEmissiveMap", 0, 0 #define AI_MATKEY_EMISSIVE_INTENSITY "$mat.emissiveIntensity", 0, 0 #define AI_MATKEY_USE_AO_MAP "$mat.useAOMap", 0, 0 diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h index 2e41b8b6d..fd904e4fc 100644 --- a/include/assimp/pbrmaterial.h +++ b/include/assimp/pbrmaterial.h @@ -50,16 +50,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # pragma GCC system_header #endif -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0 #define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 #define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0 -#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 -#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 -#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0 +//#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 +//#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 +//#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0 #define AI_MATKEY_GLTF_MATERIAL_SHEEN "$mat.gltf.materialSheen", 0, 0 #define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR "$mat.gltf.materialSheen.sheenColorFactor", 0, 0 #define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR "$mat.gltf.materialSheen.sheenRoughnessFactor", 0, 0 diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 4110edcfc..e0ac10ad5 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -57,10 +57,9 @@ using namespace Assimp; class utglTF2ImportExport : public AbstractImportExportBase { public: - virtual bool importerTest() { + virtual bool importerMatTest(const char *file, bool spec_gloss, std::array exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", - aiProcess_ValidateDataStructure); + const aiScene *scene = importer.ReadFile(file, aiProcess_ValidateDataStructure); EXPECT_NE(scene, nullptr); if (!scene) { return false; @@ -72,13 +71,49 @@ public: } const aiMaterial *material = scene->mMaterials[0]; + // This Material should be a PBR + aiShadingMode shadingMode; + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_SHADING_MODEL, shadingMode)); + EXPECT_EQ(aiShadingMode_PBR_BRDF, shadingMode); + + // Should import the texture as diffuse and as base color aiString path; - aiTextureMapMode modes[2]; + std::array modes; EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, - nullptr, nullptr, modes)); + nullptr, nullptr, modes.data())); EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png"); - EXPECT_EQ(modes[0], aiTextureMapMode_Mirror); - EXPECT_EQ(modes[1], aiTextureMapMode_Clamp); + EXPECT_EQ(exp_modes, modes); + + // Also as Base Color + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_BASE_COLOR, 0, &path, nullptr, nullptr, + nullptr, nullptr, modes.data())); + EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png"); + EXPECT_EQ(exp_modes, modes); + + // Should have a MetallicFactor (default is 1.0) + ai_real metal_factor = ai_real(0.5); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_METALLIC_FACTOR, metal_factor)); + EXPECT_EQ(ai_real(0.0), metal_factor); + + // And a roughness factor (default is 1.0) + ai_real roughness_factor = ai_real(0.5); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_ROUGHNESS_FACTOR, roughness_factor)); + EXPECT_EQ(ai_real(1.0), roughness_factor); + + aiColor3D spec_color = { 0, 0, 0 }; + ai_real glossiness = ai_real(0.5); + if (spec_gloss) { + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color)); + constexpr ai_real spec_val(0.20000000298023225); // From the file + EXPECT_EQ(spec_val, spec_color.r); + EXPECT_EQ(spec_val, spec_color.g); + EXPECT_EQ(spec_val, spec_color.b); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness)); + EXPECT_EQ(ai_real(1.0), glossiness); + } else { + EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color)); + EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness)); + } return true; } @@ -105,13 +140,17 @@ public: }; TEST_F(utglTF2ImportExport, importglTF2FromFileTest) { - EXPECT_TRUE(importerTest()); + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", false, {aiTextureMapMode_Mirror, aiTextureMapMode_Clamp})); } TEST_F(utglTF2ImportExport, importBinaryglTF2FromFileTest) { EXPECT_TRUE(binaryImporterTest()); } +TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) { + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", true)); +} + #ifndef ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) { Assimp::Importer importer; From ebb9b1b2af421698ed92eba7436e21b933736fba Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 10 Jun 2021 23:36:07 +0200 Subject: [PATCH 052/232] Next iteration --- code/AssetLib/X3D/X3DImporter.cpp | 2455 ++++++++++++++++++++++++++++- code/AssetLib/X3D/X3DImporter.hpp | 278 +++- 2 files changed, 2729 insertions(+), 4 deletions(-) diff --git a/code/AssetLib/X3D/X3DImporter.cpp b/code/AssetLib/X3D/X3DImporter.cpp index 0fcfec726..4c6f24795 100644 --- a/code/AssetLib/X3D/X3DImporter.cpp +++ b/code/AssetLib/X3D/X3DImporter.cpp @@ -288,9 +288,61 @@ void X3DImporter::InternReadFile( const std::string &pFile, aiScene *pScene, IOS if (!stream) { throw DeadlyImportError("Could not open file for reading"); } + std::string::size_type slashPos = pFile.find_last_of("\\/"); + pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1)); + ParseFile(pFile, pIOHandler); + pIOHandler->PopDirectory(); + + // mScene = pScene; pScene->mRootNode = new aiNode(pFile); + pScene->mRootNode->mParent = nullptr; + pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED; + + //search for root node element + + mNodeElementCur = NodeElement_List.front(); + while (mNodeElementCur->Parent != nullptr) { + mNodeElementCur = mNodeElementCur->Parent; + } + + { // fill aiScene with objects. + std::list mesh_list; + std::list mat_list; + std::list light_list; + + // create nodes tree + Postprocess_BuildNode(*mNodeElementCur, *pScene->mRootNode, mesh_list, mat_list, light_list); + // copy needed data to scene + if (!mesh_list.empty()) { + std::list::const_iterator it = mesh_list.begin(); + + pScene->mNumMeshes = static_cast(mesh_list.size()); + pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; + for (size_t i = 0; i < pScene->mNumMeshes; i++) + pScene->mMeshes[i] = *it++; + } + + if (!mat_list.empty()) { + std::list::const_iterator it = mat_list.begin(); + + pScene->mNumMaterials = static_cast(mat_list.size()); + pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials]; + for (size_t i = 0; i < pScene->mNumMaterials; i++) + pScene->mMaterials[i] = *it++; + } + + if (!light_list.empty()) { + std::list::const_iterator it = light_list.begin(); + + pScene->mNumLights = static_cast(light_list.size()); + pScene->mLights = new aiLight *[pScene->mNumLights]; + for (size_t i = 0; i < pScene->mNumLights; i++) + pScene->mLights[i] = *it++; + } + } + } const aiImporterDesc *X3DImporter::GetInfo() const { @@ -421,6 +473,1741 @@ void readMetadataString(XmlNode &node, X3DNodeElementBase *parent) { } } +void X3DImporter::ParseDirectionalLight(XmlNode &node) { + std::string def, use; + float ambientIntensity = 0; + aiColor3D color(1, 1, 1); + aiVector3D direction(0, 0, -1); + bool global = false; + float intensity = 1; + bool on = true; + X3DNodeElementBase *ne = nullptr; + + //MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + //MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f); + MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_DirectionalLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeNodeElementLight(CX3DImporter_NodeElement::ENET_DirectionalLight, NodeElement_Cur); + if (!def.empty()) + ne->ID = def; + else + ne->ID = "DirectionalLight_" + to_string((size_t)ne); // make random name + + ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeNodeElementLight *)ne)->Color = color; + ((X3DNodeNodeElementLight *)ne)->Direction = direction; + ((X3DNodeNodeElementLight *)ne)->Global = global; + ((X3DNodeNodeElementLight *)ne)->Intensity = intensity; + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "DirectionalLight"); + else + mNodeElementCur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Lighting_PointLight() { + std::string def, use; + float ambientIntensity = 0; + aiVector3D attenuation(1, 0, 0); + aiColor3D color(1, 1, 1); + bool global = true; + float intensity = 1; + aiVector3D location(0, 0, 0); + bool on = true; + float radius = 100; + X3DNodeElementBase *ne = nullptr; + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f); + MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_PointLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeNodeElementLight(X3DElemType::ENET_PointLight, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeNodeElementLight *)ne)->Attenuation = attenuation; + ((X3DNodeNodeElementLight *)ne)->Color = color; + ((X3DNodeNodeElementLight *)ne)->Global = global; + ((X3DNodeNodeElementLight *)ne)->Intensity = intensity; + ((X3DNodeNodeElementLight *)ne)->Location = location; + ((X3DNodeNodeElementLight *)ne)->Radius = radius; + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + // make random name + if (ne->ID.empty()) ne->ID = "PointLight_" + to_string((size_t)ne); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "PointLight"); + else + mNodeElementCur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Lighting_SpotLight() { + std::string def, use; + float ambientIntensity = 0; + aiVector3D attenuation(1, 0, 0); + float beamWidth = 0.7854f; + aiColor3D color(1, 1, 1); + float cutOffAngle = 1.570796f; + aiVector3D direction(0, 0, -1); + bool global = true; + float intensity = 1; + aiVector3D location(0, 0, 0); + bool on = true; + float radius = 100; + X3DNodeElementBase *ne = nullptr; + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_RET("beamWidth", beamWidth, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f); + MACRO_ATTRREAD_CHECK_RET("cutOffAngle", cutOffAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_SpotLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeNodeElementLight(X3DElemType::ENET_SpotLight, mNodeElementCur); + if (!def.empty()) + ne->ID = def; + + if (beamWidth > cutOffAngle) + beamWidth = cutOffAngle; + + ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeNodeElementLight *)ne)->Attenuation = attenuation; + ((X3DNodeNodeElementLight *)ne)->BeamWidth = beamWidth; + ((X3DNodeNodeElementLight *)ne)->Color = color; + ((X3DNodeNodeElementLight *)ne)->CutOffAngle = cutOffAngle; + ((X3DNodeNodeElementLight *)ne)->Direction = direction; + ((X3DNodeNodeElementLight *)ne)->Global = global; + ((X3DNodeNodeElementLight *)ne)->Intensity = intensity; + ((X3DNodeNodeElementLight *)ne)->Location = location; + ((X3DNodeNodeElementLight *)ne)->Radius = radius; + + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + // make random name + if (ne->ID.empty()) ne->ID = "SpotLight_" + to_string((size_t)ne); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "SpotLight"); + else + mNodeElementCur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +void X3DImporter::ParseNode_Grouping_Group() { + std::string def, use; + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne = nullptr; + + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (mReader->isEmptyElement()) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::ParseNode_Grouping_GroupEnd() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +// +// +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The StaticGroup node contains children nodes which cannot be modified. StaticGroup children are guaranteed to not change, send events, receive events or +// contain any USE references outside the StaticGroup. +void X3DImporter::ParseNode_Grouping_StaticGroup() { + std::string def, use; + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne = nullptr; + + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (mReader->isEmptyElement()) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::ParseNode_Grouping_StaticGroupEnd() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +// +// +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The Switch grouping node traverses zero or one of the nodes specified in the children field. The whichChoice field specifies the index of the child +// to traverse, with the first child having index 0. If whichChoice is less than zero or greater than the number of nodes in the children field, nothing +// is chosen. +void X3DImporter::ParseNode_Grouping_Switch() { + std::string def, use; + int32_t whichChoice = -1; + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("whichChoice", whichChoice, XML_ReadNode_GetAttrVal_AsI32); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + CX3DImporter_NodeElement *ne; + + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) NodeElement_Cur->ID = def; + + // also set values specific to this type of group + ((CX3DNodeElementGroup *)NodeElement_Cur)->UseChoice = true; + ((CX3DNodeElementGroup *)NodeElement_Cur)->Choice = whichChoice; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (mReader->isEmptyElement()) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::ParseNode_Grouping_SwitchEnd() { + // just exit from node. Defined choice will be accepted at postprocessing stage. + ParseHelper_Node_Exit(); // go up in scene graph +} + +// +// +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The Transform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors. +// Given a 3-dimensional point P and Transform node, P is transformed into point P' in its parent's coordinate system by a series of intermediate +// transformations. In matrix transformation notation, where C (center), SR (scaleOrientation), T (translation), R (rotation), and S (scale) are the +// equivalent transformation matrices, +// P' = T * C * R * SR * S * -SR * -C * P +void X3DImporter::ParseNode_Grouping_Transform() { + aiVector3D center(0, 0, 0); + float rotation[4] = { 0, 0, 1, 0 }; + aiVector3D scale(1, 1, 1); // A value of zero indicates that any child geometry shall not be displayed + float scale_orientation[4] = { 0, 0, 1, 0 }; + aiVector3D translation(0, 0, 0); + aiMatrix4x4 matr, tmatr; + std::string use, def; + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_REF("center", center, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_REF("translation", translation, XML_ReadNode_GetAttrVal_AsVec3f); + if (an == "rotation") { + std::vector tvec; + + XML_ReadNode_GetAttrVal_AsArrF(idx, tvec); + if (tvec.size() != 4) throw DeadlyImportError(": rotation vector must have 4 elements."); + + memcpy(rotation, tvec.data(), sizeof(rotation)); + + continue; + } + + if (an == "scaleOrientation") { + std::vector tvec; + XML_ReadNode_GetAttrVal_AsArrF(idx, tvec); + if (tvec.size() != 4) { + throw DeadlyImportError(": scaleOrientation vector must have 4 elements."); + } + + ::memcpy(scale_orientation, tvec.data(), sizeof(scale_orientation)); + + continue; + } + + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) { + NodeElement_Cur->ID = def; + } + + // + // also set values specific to this type of group + // + // calculate transformation matrix + aiMatrix4x4::Translation(translation, matr); // T + aiMatrix4x4::Translation(center, tmatr); // C + matr *= tmatr; + aiMatrix4x4::Rotation(rotation[3], aiVector3D(rotation[0], rotation[1], rotation[2]), tmatr); // R + matr *= tmatr; + aiMatrix4x4::Rotation(scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // SR + matr *= tmatr; + aiMatrix4x4::Scaling(scale, tmatr); // S + matr *= tmatr; + aiMatrix4x4::Rotation(-scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // -SR + matr *= tmatr; + aiMatrix4x4::Translation(-center, tmatr); // -C + matr *= tmatr; + // and assign it + ((CX3DNodeElementGroup *)mNodeElementCur)->Transformation = matr; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (mReader->isEmptyElement()) { + ParseHelper_Node_Exit(); + } + } // if(!use.empty()) else +} + +void X3DImporter::ParseNode_Grouping_TransformEnd() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +void X3DImporter::ParseNode_Geometry2D_Arc2D() { + std::string def, use; + float endAngle = AI_MATH_HALF_PI_F; + float radius = 1; + float startAngle = 0; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Arc2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Arc2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object and convert it to line set. + std::list tlist; + + GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg + GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Arc2D"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// The ArcClose node specifies a portion of a circle whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping +// towards the positive y-axis. The end points of the arc specified are connected as defined by the closureType field. The radius field specifies the radius +// of the circle of which the arc is a portion. The arc extends from the startAngle counterclockwise to the endAngle. The value of radius shall be greater +// than zero. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different default angle base unit has +// been specified). If startAngle and endAngle have the same value, a circle is specified and closureType is ignored. If the absolute difference between +// startAngle and endAngle is greater than or equal to 2pi, a complete circle is produced with no chord or radial line(s) drawn from the center. +// A closureType of "PIE" connects the end point to the start point by defining two straight line segments first from the end point to the center and then +// the center to the start point. A closureType of "CHORD" connects the end point to the start point by defining a straight line segment from the end point +// to the start point. Textures are applied individually to each face of the ArcClose2D. On the front (+Z) and back (-Z) faces of the ArcClose2D, when +// viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. +void X3DImporter::ParseNode_Geometry2D_ArcClose2D() { + std::string def, use; + std::string closureType("PIE"); + float endAngle = AI_MATH_HALF_PI_F; + float radius = 1; + bool solid = false; + float startAngle = 0; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("closureType", closureType, mReader->getAttributeValue); + MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_ArcClose2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_ArcClose2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; + // create point list of geometry object. + GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); ///TODO: IME - AI_CONFIG for NumSeg + // add chord or two radiuses only if not a circle was defined + if (!((std::fabs(endAngle - startAngle) >= AI_MATH_TWO_PI_F) || (endAngle == startAngle))) { + std::list &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias. + + if ((closureType == "PIE") || (closureType == "\"PIE\"")) + vlist.push_back(aiVector3D(0, 0, 0)); // center point - first radial line + else if ((closureType != "CHORD") && (closureType != "\"CHORD\"")) + Throw_IncorrectAttrValue("closureType"); + + vlist.push_back(*vlist.begin()); // arc first point - chord from first to last point of arc(if CHORD) or second radial line(if PIE). + } + + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.size(); + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "ArcClose2D"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry2D_Circle2D() { + std::string def, use; + float radius = 1; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Circle2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Circle2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object and convert it to line set. + std::list tlist; + + GeometryHelper_Make_Arc2D(0, 0, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg + GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Circle2D"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// The Disk2D node specifies a circular disk which is centred at (0, 0) in the local coordinate system. The outerRadius field specifies the radius of the +// outer dimension of the Disk2D. The innerRadius field specifies the inner dimension of the Disk2D. The value of outerRadius shall be greater than zero. +// The value of innerRadius shall be greater than or equal to zero and less than or equal to outerRadius. If innerRadius is zero, the Disk2D is completely +// filled. Otherwise, the area within the innerRadius forms a hole in the Disk2D. If innerRadius is equal to outerRadius, a solid circular line shall +// be drawn using the current line properties. Textures are applied individually to each face of the Disk2D. On the front (+Z) and back (-Z) faces of +// the Disk2D, when viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. +void X3DImporter::ParseNode_Geometry2D_Disk2D() { + std::string def, use; + float innerRadius = 0; + float outerRadius = 1; + bool solid = false; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("innerRadius", innerRadius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("outerRadius", outerRadius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Disk2D, ne); + } else { + std::list tlist_o, tlist_i; + + if (innerRadius > outerRadius) Throw_IncorrectAttrValue("innerRadius"); + + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Disk2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object. + ///TODO: IME - AI_CONFIG for NumSeg + GeometryHelper_Make_Arc2D(0, 0, outerRadius, 10, tlist_o); // outer circle + if (innerRadius == 0.0f) { // make filled disk + // in tlist_o we already have points of circle. just copy it and sign as polygon. + ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices = tlist_o; + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = tlist_o.size(); + } else if (innerRadius == outerRadius) { // make circle + // in tlist_o we already have points of circle. convert it to line set. + GeometryHelper_Extend_PointToLine(tlist_o, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; + } else { // make disk + std::list &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias. + + GeometryHelper_Make_Arc2D(0, 0, innerRadius, 10, tlist_i); // inner circle + // + // create quad list from two point lists + // + if (tlist_i.size() < 2) throw DeadlyImportError("Disk2D. Not enough points for creating quad list."); // tlist_i and tlist_o has equal size. + + // add all quads except last + for (std::list::iterator it_i = tlist_i.begin(), it_o = tlist_o.begin(); it_i != tlist_i.end();) { + // do not forget - CCW direction + vlist.push_back(*it_i++); // 1st point + vlist.push_back(*it_o++); // 2nd point + vlist.push_back(*it_o); // 3rd point + vlist.push_back(*it_i); // 4th point + } + + // add last quad + vlist.push_back(*tlist_i.end()); // 1st point + vlist.push_back(*tlist_o.end()); // 2nd point + vlist.push_back(*tlist_o.begin()); // 3rd point + vlist.push_back(*tlist_o.begin()); // 4th point + + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 4; + } + + ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Disk2D"); + else + mNodeElementCur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry2D_Polyline2D() { + std::string def, use; + std::list lineSegments; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_REF("lineSegments", lineSegments, XML_ReadNode_GetAttrVal_AsListVec2f); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polyline2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polyline2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // + // convert read point list of geometry object to line set. + // + std::list tlist; + + // convert vec2 to vec3 + for (std::list::iterator it2 = lineSegments.begin(); it2 != lineSegments.end(); ++it2) + tlist.push_back(aiVector3D(it2->x, it2->y, 0)); + + // convert point set to line set + GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Polyline2D"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry2D_Polypoint2D() { + std::string def, use; + std::list point; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_REF("point", point, XML_ReadNode_GetAttrVal_AsListVec2f); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polypoint2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polypoint2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // convert vec2 to vec3 + for (std::list::iterator it2 = point.begin(); it2 != point.end(); ++it2) { + ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); + } + + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 1; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Polypoint2D"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry2D_Rectangle2D() { + std::string def, use; + aiVector2D size(2, 2); + bool solid = false; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec2f); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Rectangle2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Rectangle2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + float x1 = -size.x / 2.0f; + float x2 = size.x / 2.0f; + float y1 = -size.y / 2.0f; + float y2 = size.y / 2.0f; + std::list &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias. + + vlist.push_back(aiVector3D(x2, y1, 0)); // 1st point + vlist.push_back(aiVector3D(x2, y2, 0)); // 2nd point + vlist.push_back(aiVector3D(x1, y2, 0)); // 3rd point + vlist.push_back(aiVector3D(x1, y1, 0)); // 4th point + ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 4; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Rectangle2D"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry2D_TriangleSet2D() { + std::string def, use; + bool solid = false; + std::list vertices; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_REF("vertices", vertices, XML_ReadNode_GetAttrVal_AsListVec2f); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_TriangleSet2D, ne); + } else { + if (vertices.size() % 3) throw DeadlyImportError("TriangleSet2D. Not enough points for defining triangle."); + + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_TriangleSet2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // convert vec2 to vec3 + for (std::list::iterator it2 = vertices.begin(); it2 != vertices.end(); ++it2) { + ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); + } + + ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "TriangleSet2D"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// The Box node specifies a rectangular parallelepiped box centred at (0, 0, 0) in the local coordinate system and aligned with the local coordinate axes. +// By default, the box measures 2 units in each dimension, from -1 to +1. The size field specifies the extents of the box along the X-, Y-, and Z-axes +// respectively and each component value shall be greater than zero. +void X3DImporter::ParseNode_Geometry3D_Box() { + std::string def, use; + bool solid = true; + aiVector3D size(2, 2, 2); + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Box, ne); + } else { + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Box, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + GeometryHelper_MakeQL_RectParallelepiped(size, ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices); // get quad list + ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; + ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 4; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Box"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry3D_Cone() { + std::string use, def; + bool bottom = true; + float bottomRadius = 1; + float height = 2; + bool side = true; + bool solid = true; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("bottomRadius", bottomRadius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cone, ne); + } else { + const unsigned int tess = 30; ///TODO: IME tessellation factor through ai_property + + std::vector tvec; // temp array for vertices. + + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cone, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // make cone or parts according to flags. + if (side) { + StandardShapes::MakeCone(height, 0, bottomRadius, tess, tvec, !bottom); + } else if (bottom) { + StandardShapes::MakeCircle(bottomRadius, tess, tvec); + height = -(height / 2); + for (std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) + it->y = height; // y - because circle made in oXZ. + } + + // copy data from temp array + for (std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) + ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices.push_back(*it); + + ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; + ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Cone"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry3D_Cylinder() { + std::string use, def; + bool bottom = true; + float height = 2; + float radius = 1; + bool side = true; + bool solid = true; + bool top = true; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("top", top, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cylinder, ne); + } else { + const unsigned int tess = 30; ///TODO: IME tessellation factor through ai_property + + std::vector tside; // temp array for vertices of side. + std::vector tcir; // temp array for vertices of circle. + + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cylinder, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // make cilynder or parts according to flags. + if (side) StandardShapes::MakeCone(height, radius, radius, tess, tside, true); + + height /= 2; // height defined for whole cylinder, when creating top and bottom circle we are using just half of height. + if (top || bottom) StandardShapes::MakeCircle(radius, tess, tcir); + // copy data from temp arrays + std::list &vlist = ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices; // just short alias. + + for (std::vector::iterator it = tside.begin(); it != tside.end(); ++it) + vlist.push_back(*it); + + if (top) { + for (std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) { + (*it).y = height; // y - because circle made in oXZ. + vlist.push_back(*it); + } + } // if(top) + + if (bottom) { + for (std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) { + (*it).y = -height; // y - because circle made in oXZ. + vlist.push_back(*it); + } + } // if(top) + + ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; + ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Cylinder"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ColorNormalTexCoordContentModel can contain Color (or ColorRGBA), Normal and TextureCoordinate, in any order. No more than one instance of any single +// node type is allowed. A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The ElevationGrid node specifies a uniform rectangular grid of varying height in the Y=0 plane of the local coordinate system. The geometry is described +// by a scalar array of height values that specify the height of a surface above each point of the grid. The xDimension and zDimension fields indicate +// the number of elements of the grid height array in the X and Z directions. Both xDimension and zDimension shall be greater than or equal to zero. +// If either the xDimension or the zDimension is less than two, the ElevationGrid contains no quadrilaterals. +void X3DImporter::ParseNode_Geometry3D_ElevationGrid() { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + float creaseAngle = 0; + std::vector height; + bool normalPerVertex = true; + bool solid = true; + int32_t xDimension = 0; + float xSpacing = 1; + int32_t zDimension = 0; + float zSpacing = 1; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("height", height, XML_ReadNode_GetAttrVal_AsArrF); + MACRO_ATTRREAD_CHECK_RET("xDimension", xDimension, XML_ReadNode_GetAttrVal_AsI32); + MACRO_ATTRREAD_CHECK_RET("xSpacing", xSpacing, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("zDimension", zDimension, XML_ReadNode_GetAttrVal_AsI32); + MACRO_ATTRREAD_CHECK_RET("zSpacing", zSpacing, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_ElevationGrid, ne); + } else { + if ((xSpacing == 0.0f) || (zSpacing == 0.0f)) throw DeadlyImportError("Spacing in must be grater than zero."); + if ((xDimension <= 0) || (zDimension <= 0)) throw DeadlyImportError("Dimension in must be grater than zero."); + if ((size_t)(xDimension * zDimension) != height.size()) Throw_IncorrectAttrValue("Heights count must be equal to \"xDimension * zDimension\""); + + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_ElevationGrid(CX3DImporter_NodeElement::ENET_ElevationGrid, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + CX3DImporter_NodeElement_ElevationGrid &grid_alias = *((CX3DImporter_NodeElement_ElevationGrid *)ne); // create alias for conveience + + { // create grid vertices list + std::vector::const_iterator he_it = height.begin(); + + for (int32_t zi = 0; zi < zDimension; zi++) // rows + { + for (int32_t xi = 0; xi < xDimension; xi++) // columns + { + aiVector3D tvec(xSpacing * xi, *he_it, zSpacing * zi); + + grid_alias.Vertices.push_back(tvec); + ++he_it; + } + } + } // END: create grid vertices list + // + // create faces list. In "coordIdx" format + // + // check if we have quads + if ((xDimension < 2) || (zDimension < 2)) // only one element in dimension is set, create line set. + { + ((CX3DImporter_NodeElement_ElevationGrid *)ne)->NumIndices = 2; // will be holded as line set. + for (size_t i = 0, i_e = (grid_alias.Vertices.size() - 1); i < i_e; i++) { + grid_alias.CoordIdx.push_back(static_cast(i)); + grid_alias.CoordIdx.push_back(static_cast(i + 1)); + grid_alias.CoordIdx.push_back(-1); + } + } else // two or more elements in every dimension is set. create quad set. + { + ((CX3DImporter_NodeElement_ElevationGrid *)ne)->NumIndices = 4; + for (int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) // rows + { + for (int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) // columns + { + // points direction in face. + if (ccw) { + // CCW: + // 3 2 + // 0 1 + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); + } else { + // CW: + // 0 1 + // 3 2 + grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); + grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); + } // if(ccw) else + + grid_alias.CoordIdx.push_back(-1); + } // for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) + } // for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) + } // if((xDimension < 2) || (zDimension < 2)) else + + grid_alias.ColorPerVertex = colorPerVertex; + grid_alias.NormalPerVertex = normalPerVertex; + grid_alias.CreaseAngle = creaseAngle; + grid_alias.Solid = solid; + // check for child nodes + if (!mReader->isEmptyElement()) { + ParseHelper_Node_Enter(ne); + MACRO_NODECHECK_LOOPBEGIN("ElevationGrid"); + // check for X3DComposedGeometryNodes + if (XML_CheckNode_NameEqual("Color")) { + ParseNode_Rendering_Color(); + continue; + } + if (XML_CheckNode_NameEqual("ColorRGBA")) { + ParseNode_Rendering_ColorRGBA(); + continue; + } + if (XML_CheckNode_NameEqual("Normal")) { + ParseNode_Rendering_Normal(); + continue; + } + if (XML_CheckNode_NameEqual("TextureCoordinate")) { + ParseNode_Texturing_TextureCoordinate(); + continue; + } + // check for X3DMetadataObject + if (!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("ElevationGrid"); + + MACRO_NODECHECK_LOOPEND("ElevationGrid"); + ParseHelper_Node_Exit(); + } // if(!mReader->isEmptyElement()) + else { + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + } // if(!mReader->isEmptyElement()) else + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +template +static void GeometryHelper_Extrusion_CurveIsClosed(std::vector &pCurve, const bool pDropTail, const bool pRemoveLastPoint, bool &pCurveIsClosed) { + size_t cur_sz = pCurve.size(); + + pCurveIsClosed = false; + // for curve with less than four points checking is have no sense, + if (cur_sz < 4) return; + + for (size_t s = 3, s_e = cur_sz; s < s_e; s++) { + // search for first point of duplicated part. + if (pCurve[0] == pCurve[s]) { + bool found = true; + + // check if tail(indexed by b2) is duplicate of head(indexed by b1). + for (size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) { + if (pCurve[b1] != pCurve[b2]) { // points not match: clear flag and break loop. + found = false; + + break; + } + } // for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) + + // if duplicate tail is found then drop or not it depending on flags. + if (found) { + pCurveIsClosed = true; + if (pDropTail) { + if (!pRemoveLastPoint) s++; // prepare value for iterator's arithmetics. + + pCurve.erase(pCurve.begin() + s, pCurve.end()); // remove tail + } + + break; + } // if(found) + } // if(pCurve[0] == pCurve[s]) + } // for(size_t s = 3, s_e = (cur_sz - 1); s < s_e; s++) +} + +static aiVector3D GeometryHelper_Extrusion_GetNextY(const size_t pSpine_PointIdx, const std::vector &pSpine, const bool pSpine_Closed) { + const size_t spine_idx_last = pSpine.size() - 1; + aiVector3D tvec; + + if ((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) // at first special cases + { + if (pSpine_Closed) { // If the spine curve is closed: The SCP for the first and last points is the same and is found using (spine[1] - spine[n - 2]) to compute the Y-axis. + // As we even for closed spine curve last and first point in pSpine are not the same: duplicates(spine[n - 1] which are equivalent to spine[0]) + // in tail are removed. + // So, last point in pSpine is a spine[n - 2] + tvec = pSpine[1] - pSpine[spine_idx_last]; + } else if (pSpine_PointIdx == 0) { // The Y-axis used for the first point is the vector from spine[0] to spine[1] + tvec = pSpine[1] - pSpine[0]; + } else { // The Y-axis used for the last point it is the vector from spine[n-2] to spine[n-1]. In our case(see above about dropping tail) spine[n - 1] is + // the spine[0]. + tvec = pSpine[spine_idx_last] - pSpine[spine_idx_last - 1]; + } + } // if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) + else { // For all points other than the first or last: The Y-axis for spine[i] is found by normalizing the vector defined by (spine[i+1] - spine[i-1]). + tvec = pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx - 1]; + } // if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) else + + return tvec.Normalize(); +} + +static aiVector3D GeometryHelper_Extrusion_GetNextZ(const size_t pSpine_PointIdx, const std::vector &pSpine, const bool pSpine_Closed, + const aiVector3D pVecZ_Prev) { + const aiVector3D zero_vec(0); + const size_t spine_idx_last = pSpine.size() - 1; + + aiVector3D tvec; + + // at first special cases + if (pSpine.size() < 3) // spine have not enough points for vector calculations. + { + tvec.Set(0, 0, 1); + } else if (pSpine_PointIdx == 0) // special case: first point + { + if (pSpine_Closed) // for calculating use previous point in curve s[n - 2]. In list it's a last point, because point s[n - 1] was removed as duplicate. + { + tvec = (pSpine[1] - pSpine[0]) ^ (pSpine[spine_idx_last] - pSpine[0]); + } else // for not closed curve first and next point(s[0] and s[1]) has the same vector Z. + { + bool found = false; + + // As said: "If the Z-axis of the first point is undefined (because the spine is not closed and the first two spine segments are collinear) + // then the Z-axis for the first spine point with a defined Z-axis is used." + // Walk through spine and find Z. + for (size_t next_point = 2; (next_point <= spine_idx_last) && !found; next_point++) { + // (pSpine[2] - pSpine[1]) ^ (pSpine[0] - pSpine[1]) + tvec = (pSpine[next_point] - pSpine[next_point - 1]) ^ (pSpine[next_point - 2] - pSpine[next_point - 1]); + found = !tvec.Equal(zero_vec); + } + + // if entire spine are collinear then use OZ axis. + if (!found) tvec.Set(0, 0, 1); + } // if(pSpine_Closed) else + } // else if(pSpine_PointIdx == 0) + else if (pSpine_PointIdx == spine_idx_last) // special case: last point + { + if (pSpine_Closed) { // do not forget that real last point s[n - 1] is removed as duplicated. And in this case we are calculating vector Z for point s[n - 2]. + tvec = (pSpine[0] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); + // if taken spine vectors are collinear then use previous vector Z. + if (tvec.Equal(zero_vec)) tvec = pVecZ_Prev; + } else { // vector Z for last point of not closed curve is previous vector Z. + tvec = pVecZ_Prev; + } + } else // regular point + { + tvec = (pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); + // if taken spine vectors are collinear then use previous vector Z. + if (tvec.Equal(zero_vec)) tvec = pVecZ_Prev; + } + + // After determining the Z-axis, its dot product with the Z-axis of the previous spine point is computed. If this value is negative, the Z-axis + // is flipped (multiplied by -1). + if ((tvec * pVecZ_Prev) < 0) tvec = -tvec; + + return tvec.Normalize(); +} + +// +void X3DImporter::ParseNode_Geometry3D_Extrusion() { + std::string use, def; + bool beginCap = true; + bool ccw = true; + bool convex = true; + float creaseAngle = 0; + std::vector crossSection; + bool endCap = true; + std::vector orientation; + std::vector scale; + bool solid = true; + std::vector spine; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("beginCap", beginCap, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("crossSection", crossSection, XML_ReadNode_GetAttrVal_AsArrVec2f); + MACRO_ATTRREAD_CHECK_RET("endCap", endCap, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_REF("orientation", orientation, XML_ReadNode_GetAttrVal_AsArrF); + MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsArrVec2f); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_REF("spine", spine, XML_ReadNode_GetAttrVal_AsArrVec3f); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Extrusion, ne); + } else { + // + // check if default values must be assigned + // + if (spine.size() == 0) { + spine.resize(2); + spine[0].Set(0, 0, 0), spine[1].Set(0, 1, 0); + } else if (spine.size() == 1) { + throw DeadlyImportError("ParseNode_Geometry3D_Extrusion. Spine must have at least two points."); + } + + if (crossSection.size() == 0) { + crossSection.resize(5); + crossSection[0].Set(1, 1), crossSection[1].Set(1, -1), crossSection[2].Set(-1, -1), crossSection[3].Set(-1, 1), crossSection[4].Set(1, 1); + } + + { // orientation + size_t ori_size = orientation.size() / 4; + + if (ori_size < spine.size()) { + float add_ori[4]; // values that will be added + + if (ori_size == 1) // if "orientation" has one element(means one MFRotation with four components) then use it value for all spine points. + { + add_ori[0] = orientation[0], add_ori[1] = orientation[1], add_ori[2] = orientation[2], add_ori[3] = orientation[3]; + } else // else - use default values + { + add_ori[0] = 0, add_ori[1] = 0, add_ori[2] = 1, add_ori[3] = 0; + } + + orientation.reserve(spine.size() * 4); + for (size_t i = 0, i_e = (spine.size() - ori_size); i < i_e; i++) + orientation.push_back(add_ori[0]), orientation.push_back(add_ori[1]), orientation.push_back(add_ori[2]), orientation.push_back(add_ori[3]); + } + + if (orientation.size() % 4) throw DeadlyImportError("Attribute \"orientation\" in must has multiple four quantity of numbers."); + } // END: orientation + + { // scale + if (scale.size() < spine.size()) { + aiVector2D add_sc; + + if (scale.size() == 1) // if "scale" has one element then use it value for all spine points. + add_sc = scale[0]; + else // else - use default values + add_sc.Set(1, 1); + + scale.reserve(spine.size()); + for (size_t i = 0, i_e = (spine.size() - scale.size()); i < i_e; i++) + scale.push_back(add_sc); + } + } // END: scale + // + // create and if needed - define new geometry object. + // + ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_Extrusion, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + CX3DImporter_NodeElement_IndexedSet &ext_alias = *((CX3DImporter_NodeElement_IndexedSet *)ne); // create alias for conveience + // assign part of input data + ext_alias.CCW = ccw; + ext_alias.Convex = convex; + ext_alias.CreaseAngle = creaseAngle; + ext_alias.Solid = solid; + + // + // How we done it at all? + // 1. At first we will calculate array of basises for every point in spine(look SCP in ISO-dic). Also "orientation" vector + // are applied vor every basis. + // 2. After that we can create array of point sets: which are scaled, transferred to basis of relative basis and at final translated to real position + // using relative spine point. + // 3. Next step is creating CoordIdx array(do not forget "-1" delimiter). While creating CoordIdx also created faces for begin and end caps, if + // needed. While createing CootdIdx is taking in account CCW flag. + // 4. The last step: create Vertices list. + // + bool spine_closed; // flag: true if spine curve is closed. + bool cross_closed; // flag: true if cross curve is closed. + std::vector basis_arr; // array of basises. ROW_a - X, ROW_b - Y, ROW_c - Z. + std::vector> pointset_arr; // array of point sets: cross curves. + + // detect closed curves + GeometryHelper_Extrusion_CurveIsClosed(crossSection, true, true, cross_closed); // true - drop tail, true - remove duplicate end. + GeometryHelper_Extrusion_CurveIsClosed(spine, true, true, spine_closed); // true - drop tail, true - remove duplicate end. + // If both cap are requested and spine curve is closed then we can make only one cap. Because second cap will be the same surface. + if (spine_closed) { + beginCap |= endCap; + endCap = false; + } + + { // 1. Calculate array of basises. + aiMatrix4x4 rotmat; + aiVector3D vecX(0), vecY(0), vecZ(0); + + basis_arr.resize(spine.size()); + for (size_t i = 0, i_e = spine.size(); i < i_e; i++) { + aiVector3D tvec; + + // get axises of basis. + vecY = GeometryHelper_Extrusion_GetNextY(i, spine, spine_closed); + vecZ = GeometryHelper_Extrusion_GetNextZ(i, spine, spine_closed, vecZ); + vecX = (vecY ^ vecZ).Normalize(); + // get rotation matrix and apply "orientation" to basis + aiMatrix4x4::Rotation(orientation[i * 4 + 3], aiVector3D(orientation[i * 4], orientation[i * 4 + 1], orientation[i * 4 + 2]), rotmat); + tvec = vecX, tvec *= rotmat, basis_arr[i].a1 = tvec.x, basis_arr[i].a2 = tvec.y, basis_arr[i].a3 = tvec.z; + tvec = vecY, tvec *= rotmat, basis_arr[i].b1 = tvec.x, basis_arr[i].b2 = tvec.y, basis_arr[i].b3 = tvec.z; + tvec = vecZ, tvec *= rotmat, basis_arr[i].c1 = tvec.x, basis_arr[i].c2 = tvec.y, basis_arr[i].c3 = tvec.z; + } // for(size_t i = 0, i_e = spine.size(); i < i_e; i++) + } // END: 1. Calculate array of basises + + { // 2. Create array of point sets. + aiMatrix4x4 scmat; + std::vector tcross(crossSection.size()); + + pointset_arr.resize(spine.size()); + for (size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) { + aiVector3D tc23vec; + + tc23vec.Set(scale[spi].x, 0, scale[spi].y); + aiMatrix4x4::Scaling(tc23vec, scmat); + for (size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) { + aiVector3D tvecX, tvecY, tvecZ; + + tc23vec.Set(crossSection[cri].x, 0, crossSection[cri].y); + // apply scaling to point + tcross[cri] = scmat * tc23vec; + // + // transfer point to new basis + // calculate coordinate in new basis + tvecX.Set(basis_arr[spi].a1, basis_arr[spi].a2, basis_arr[spi].a3), tvecX *= tcross[cri].x; + tvecY.Set(basis_arr[spi].b1, basis_arr[spi].b2, basis_arr[spi].b3), tvecY *= tcross[cri].y; + tvecZ.Set(basis_arr[spi].c1, basis_arr[spi].c2, basis_arr[spi].c3), tvecZ *= tcross[cri].z; + // apply new coordinates and translate it to spine point. + tcross[cri] = tvecX + tvecY + tvecZ + spine[spi]; + } // for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; i++) + + pointset_arr[spi] = tcross; // store transferred point set + } // for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; i++) + } // END: 2. Create array of point sets. + + { // 3. Create CoordIdx. + // add caps if needed + if (beginCap) { + // add cap as polygon. vertices of cap are places at begin, so just add numbers from zero. + for (size_t i = 0, i_e = crossSection.size(); i < i_e; i++) + ext_alias.CoordIndex.push_back(static_cast(i)); + + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } // if(beginCap) + + if (endCap) { + // add cap as polygon. vertices of cap are places at end, as for beginCap use just sequence of numbers but with offset. + size_t beg = (pointset_arr.size() - 1) * crossSection.size(); + + for (size_t i = beg, i_e = (beg + crossSection.size()); i < i_e; i++) + ext_alias.CoordIndex.push_back(static_cast(i)); + + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } // if(beginCap) + + // add quads + for (size_t spi = 0, spi_e = (spine.size() - 1); spi <= spi_e; spi++) { + const size_t cr_sz = crossSection.size(); + const size_t cr_last = crossSection.size() - 1; + + size_t right_col; // hold index basis for points of quad placed in right column; + + if (spi != spi_e) + right_col = spi + 1; + else if (spine_closed) // if spine curve is closed then one more quad is needed: between first and last points of curve. + right_col = 0; + else + break; // if spine curve is not closed then break the loop, because spi is out of range for that type of spine. + + for (size_t cri = 0; cri < cr_sz; cri++) { + if (cri != cr_last) { + MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, + static_cast(spi * cr_sz + cri), + static_cast(right_col * cr_sz + cri), + static_cast(right_col * cr_sz + cri + 1), + static_cast(spi * cr_sz + cri + 1)); + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } else if (cross_closed) // if cross curve is closed then one more quad is needed: between first and last points of curve. + { + MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, + static_cast(spi * cr_sz + cri), + static_cast(right_col * cr_sz + cri), + static_cast(right_col * cr_sz + 0), + static_cast(spi * cr_sz + 0)); + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } + } // for(size_t cri = 0; cri < cr_sz; cri++) + } // for(size_t spi = 0, spi_e = (spine.size() - 2); spi < spi_e; spi++) + } // END: 3. Create CoordIdx. + + { // 4. Create vertices list. + // just copy all vertices + for (size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) { + for (size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) { + ext_alias.Vertices.push_back(pointset_arr[spi][cri]); + } + } + } // END: 4. Create vertices list. + //PrintVectorSet("Ext. CoordIdx", ext_alias.CoordIndex); + //PrintVectorSet("Ext. Vertices", ext_alias.Vertices); + // check for child nodes + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Extrusion"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::ParseNode_Geometry3D_IndexedFaceSet() { + std::string use, def; + bool ccw = true; + std::vector colorIndex; + bool colorPerVertex = true; + bool convex = true; + std::vector coordIndex; + float creaseAngle = 0; + std::vector normalIndex; + bool normalPerVertex = true; + bool solid = true; + std::vector texCoordIndex; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_REF("colorIndex", colorIndex, XML_ReadNode_GetAttrVal_AsArrI32); + MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_REF("coordIndex", coordIndex, XML_ReadNode_GetAttrVal_AsArrI32); + MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("normalIndex", normalIndex, XML_ReadNode_GetAttrVal_AsArrI32); + MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_REF("texCoordIndex", texCoordIndex, XML_ReadNode_GetAttrVal_AsArrI32); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedFaceSet, ne); + } else { + // check data + if (coordIndex.size() == 0) throw DeadlyImportError("IndexedFaceSet must contain not empty \"coordIndex\" attribute."); + + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedFaceSet, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + CX3DImporter_NodeElement_IndexedSet &ne_alias = *((CX3DImporter_NodeElement_IndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorIndex = colorIndex; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.Convex = convex; + ne_alias.CoordIndex = coordIndex; + ne_alias.CreaseAngle = creaseAngle; + ne_alias.NormalIndex = normalIndex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + ne_alias.TexCoordIndex = texCoordIndex; + // check for child nodes + if (!mReader->isEmptyElement()) { + ParseHelper_Node_Enter(ne); + MACRO_NODECHECK_LOOPBEGIN("IndexedFaceSet"); + // check for X3DComposedGeometryNodes + if (XML_CheckNode_NameEqual("Color")) { + ParseNode_Rendering_Color(); + continue; + } + if (XML_CheckNode_NameEqual("ColorRGBA")) { + ParseNode_Rendering_ColorRGBA(); + continue; + } + if (XML_CheckNode_NameEqual("Coordinate")) { + ParseNode_Rendering_Coordinate(); + continue; + } + if (XML_CheckNode_NameEqual("Normal")) { + ParseNode_Rendering_Normal(); + continue; + } + if (XML_CheckNode_NameEqual("TextureCoordinate")) { + ParseNode_Texturing_TextureCoordinate(); + continue; + } + // check for X3DMetadataObject + if (!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedFaceSet"); + + MACRO_NODECHECK_LOOPEND("IndexedFaceSet"); + ParseHelper_Node_Exit(); + } // if(!mReader->isEmptyElement()) + else { + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry3D_Sphere() { + std::string use, def; + ai_real radius = 1; + bool solid = true; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Sphere, ne); + } else { + const unsigned int tess = 3; ///TODO: IME tessellation factor through ai_property + + std::vector tlist; + + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Sphere, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + StandardShapes::MakeSphere(tess, tlist); + // copy data from temp array and apply scale + for (std::vector::iterator it = tlist.begin(); it != tlist.end(); ++it) { + ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices.push_back(*it * radius); + } + + ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; + ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Sphere"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + + + void X3DImporter::readMetadataObject(XmlNode &node) { const std::string &name = node.name(); if (name == "MetadataBoolean") { @@ -438,6 +2225,672 @@ void X3DImporter::readMetadataObject(XmlNode &node) { } } -} // namespace Assimp + +aiMatrix4x4 PostprocessHelper_Matrix_GlobalToCurrent() { + X3DNodeElementBase *cur_node = nullptr; + std::list matr; + aiMatrix4x4 out_matr; + + // starting walk from current element to root + cur_node = cur_node; + if (cur_node != nullptr) { + do { + // if cur_node is group then store group transformation matrix in list. + if (cur_node->Type == X3DNodeElementBase::ENET_Group) matr.push_back(((X3DNodeElementBase *)cur_node)->Transformation); + + cur_node = cur_node->Parent; + } while (cur_node != nullptr); + } + + // multiplicate all matrices in reverse order + for (std::list::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); ++rit) + out_matr = out_matr * (*rit); + + return out_matr; +} + +void X3DImporter::PostprocessHelper_CollectMetadata(const CX3DImporter_NodeElement &pNodeElement, std::list &pList) const { + // walk through childs and find for metadata. + for (std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it) { + if (((*el_it)->Type == X3DElemType::ENET_MetaBoolean) || ((*el_it)->Type == X3DElemType::ENET_MetaDouble) || + ((*el_it)->Type == X3DElemType::ENET_MetaFloat) || ((*el_it)->Type == X3DElemType::ENET_MetaInteger) || + ((*el_it)->Type == X3DElemType::ENET_MetaString)) { + pList.push_back(*el_it); + } else if ((*el_it)->Type == X3DElemType::ENET_MetaSet) { + PostprocessHelper_CollectMetadata(**el_it, pList); + } + } // for(std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++) +} + +bool X3DImporter::PostprocessHelper_ElementIsMetadata(const CX3DImporter_NodeElement::EType pType) const { + if ((pType == X3DNodeElementBase::ENET_MetaBoolean) || (pType == X3DElemType::ENET_MetaDouble) || + (pType == X3DElemType::ENET_MetaFloat) || (pType == X3DElemType::ENET_MetaInteger) || + (pType == X3DElemType::ENET_MetaString) || (pType == X3DElemType::ENET_MetaSet)) { + return true; + } + return false; +} + +bool X3DImporter::PostprocessHelper_ElementIsMesh(const CX3DImporter_NodeElement::EType pType) const { + if ((pType == X3DElemType::ENET_Arc2D) || (pType == X3DElemType::ENET_ArcClose2D) || + (pType == X3DElemType::ENET_Box) || (pType == X3DElemType::ENET_Circle2D) || + (pType == X3DElemType::ENET_Cone) || (pType == X3DElemType::ENET_Cylinder) || + (pType == X3DElemType::ENET_Disk2D) || (pType == X3DElemType::ENET_ElevationGrid) || + (pType == X3DElemType::ENET_Extrusion) || (pType == X3DElemType::ENET_IndexedFaceSet) || + (pType == X3DElemType::ENET_IndexedLineSet) || (pType == X3DElemType::ENET_IndexedTriangleFanSet) || + (pType == X3DElemType::ENET_IndexedTriangleSet) || (pType == X3DElemType::ENET_IndexedTriangleStripSet) || + (pType == X3DElemType::ENET_PointSet) || (pType == X3DElemType::ENET_LineSet) || + (pType == X3DElemType::ENET_Polyline2D) || (pType == X3DElemType::ENET_Polypoint2D) || + (pType == X3DElemType::ENET_Rectangle2D) || (pType == X3DElemType::ENET_Sphere) || + (pType == X3DElemType::ENET_TriangleFanSet) || (pType == X3DElemType::ENET_TriangleSet) || + (pType == X3DElemType::ENET_TriangleSet2D) || (pType == X3DElemType::ENET_TriangleStripSet)) { + return true; + } else { + return false; + } +} + +void X3DImporter::Postprocess_BuildLight(const CX3DImporter_NodeElement &pNodeElement, std::list &pSceneLightList) const { + const CX3DImporter_NodeElement_Light &ne = *((CX3DImporter_NodeElement_Light *)&pNodeElement); + aiMatrix4x4 transform_matr = PostprocessHelper_Matrix_GlobalToCurrent(); + aiLight *new_light = new aiLight; + + new_light->mName = ne.ID; + new_light->mColorAmbient = ne.Color * ne.AmbientIntensity; + new_light->mColorDiffuse = ne.Color * ne.Intensity; + new_light->mColorSpecular = ne.Color * ne.Intensity; + switch (pNodeElement.Type) { + case CX3DImporter_NodeElement::ENET_DirectionalLight: + new_light->mType = aiLightSource_DIRECTIONAL; + new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; + + break; + case CX3DImporter_NodeElement::ENET_PointLight: + new_light->mType = aiLightSource_POINT; + new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; + new_light->mAttenuationConstant = ne.Attenuation.x; + new_light->mAttenuationLinear = ne.Attenuation.y; + new_light->mAttenuationQuadratic = ne.Attenuation.z; + + break; + case CX3DImporter_NodeElement::ENET_SpotLight: + new_light->mType = aiLightSource_SPOT; + new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; + new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; + new_light->mAttenuationConstant = ne.Attenuation.x; + new_light->mAttenuationLinear = ne.Attenuation.y; + new_light->mAttenuationQuadratic = ne.Attenuation.z; + new_light->mAngleInnerCone = ne.BeamWidth; + new_light->mAngleOuterCone = ne.CutOffAngle; + + break; + default: + throw DeadlyImportError("Postprocess_BuildLight. Unknown type of light: " + to_string(pNodeElement.Type) + "."); + } + + pSceneLightList.push_back(new_light); +} + +void X3DImporter::Postprocess_BuildMaterial(const CX3DImporter_NodeElement &pNodeElement, aiMaterial **pMaterial) const { + // check argument + if (pMaterial == nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. pMaterial is nullptr."); + if (*pMaterial != nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. *pMaterial must be nullptr."); + + *pMaterial = new aiMaterial; + aiMaterial &taimat = **pMaterial; // creating alias for convenience. + + // at this point pNodeElement point to node. Walk through childs and add all stored data. + for (std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it) { + if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material) { + aiColor3D tcol3; + float tvalf; + CX3DImporter_NodeElement_Material &tnemat = *((CX3DImporter_NodeElement_Material *)*el_it); + + tcol3.r = tnemat.AmbientIntensity, tcol3.g = tnemat.AmbientIntensity, tcol3.b = tnemat.AmbientIntensity; + taimat.AddProperty(&tcol3, 1, AI_MATKEY_COLOR_AMBIENT); + taimat.AddProperty(&tnemat.DiffuseColor, 1, AI_MATKEY_COLOR_DIFFUSE); + taimat.AddProperty(&tnemat.EmissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE); + taimat.AddProperty(&tnemat.SpecularColor, 1, AI_MATKEY_COLOR_SPECULAR); + tvalf = 1; + taimat.AddProperty(&tvalf, 1, AI_MATKEY_SHININESS_STRENGTH); + taimat.AddProperty(&tnemat.Shininess, 1, AI_MATKEY_SHININESS); + tvalf = 1.0f - tnemat.Transparency; + taimat.AddProperty(&tvalf, 1, AI_MATKEY_OPACITY); + } // if((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material) + else if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture) { + CX3DImporter_NodeElement_ImageTexture &tnetex = *((CX3DImporter_NodeElement_ImageTexture *)*el_it); + aiString url_str(tnetex.URL.c_str()); + int mode = aiTextureOp_Multiply; + + taimat.AddProperty(&url_str, AI_MATKEY_TEXTURE_DIFFUSE(0)); + taimat.AddProperty(&tnetex.RepeatS, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0)); + taimat.AddProperty(&tnetex.RepeatT, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0)); + taimat.AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0)); + } // else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture) + else if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform) { + aiUVTransform trans; + CX3DImporter_NodeElement_TextureTransform &tnetextr = *((CX3DImporter_NodeElement_TextureTransform *)*el_it); + + trans.mTranslation = tnetextr.Translation - tnetextr.Center; + trans.mScaling = tnetextr.Scale; + trans.mRotation = tnetextr.Rotation; + taimat.AddProperty(&trans, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); + } // else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform) + } // for(std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++) +} + +void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement &pNodeElement, aiMesh **pMesh) const { + // check argument + if (pMesh == nullptr) throw DeadlyImportError("Postprocess_BuildMesh. pMesh is nullptr."); + if (*pMesh != nullptr) throw DeadlyImportError("Postprocess_BuildMesh. *pMesh must be nullptr."); + + /************************************************************************************************************************************/ + /************************************************************ Geometry2D ************************************************************/ + /************************************************************************************************************************************/ + if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Arc2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_ArcClose2D) || + (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Circle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Disk2D) || + (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polyline2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polypoint2D) || + (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Rectangle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet2D)) { + CX3DImporter_NodeElement_Geometry2D &tnemesh = *((CX3DImporter_NodeElement_Geometry2D *)&pNodeElement); // create alias for convenience + std::vector tarr; + + tarr.reserve(tnemesh.Vertices.size()); + for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) + tarr.push_back(*it); + *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. + + return; // mesh is build, nothing to do anymore. + } + /************************************************************************************************************************************/ + /************************************************************ Geometry3D ************************************************************/ + /************************************************************************************************************************************/ + // + // Predefined figures + // + if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Box) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cone) || + (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cylinder) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Sphere)) { + CX3DImporter_NodeElement_Geometry3D &tnemesh = *((CX3DImporter_NodeElement_Geometry3D *)&pNodeElement); // create alias for convenience + std::vector tarr; + + tarr.reserve(tnemesh.Vertices.size()); + for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) + tarr.push_back(*it); + + *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. + + return; // mesh is build, nothing to do anymore. + } + // + // Parametric figures + // + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid) { + CX3DImporter_NodeElement_ElevationGrid &tnemesh = *((CX3DImporter_NodeElement_ElevationGrid *)&pNodeElement); // create alias for convenience + + // at first create mesh from existing vertices. + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIdx, tnemesh.Vertices); + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) + MeshGeometry_AddNormal(**pMesh, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) + MeshGeometry_AddTexCoord(**pMesh, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid) + // + // Indexed primitives sets + // + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet) { + CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) + MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) + MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet) + + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet) { + CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet) + + if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleSet) || + (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) || + (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet)) { + CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) + MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) + MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedTriangleSet or IndexedTriangleFanSet, or \ + IndexedTriangleStripSet: " + + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet)) + + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion) { + CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience + + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, tnemesh.Vertices); + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion) + + // + // Primitives sets + // + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet) { + CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + std::vector vec_copy; + + vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.size()); + for (std::list::const_iterator it = ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.begin(); + it != ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.end(); ++it) { + vec_copy.push_back(*it); + } + + *pMesh = StandardShapes::MakeMesh(vec_copy, 1); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, true); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, true); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet) + + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet) { + CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, true); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, true); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet) + + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet) { + CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if (nullptr == *pMesh) { + break; + } + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) + MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) + MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet) + + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet) { + CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + std::vector vec_copy; + + vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.size()); + for (std::list::const_iterator it = ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.begin(); + it != ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.end(); ++it) { + vec_copy.push_back(*it); + } + + *pMesh = StandardShapes::MakeMesh(vec_copy, 3); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) + MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) + MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet) + + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet) { + CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) + MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) + MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet) + + throw DeadlyImportError("Postprocess_BuildMesh. Unknown mesh type: " + to_string(pNodeElement.Type) + "."); +} + +void X3DImporter::Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, + std::list &pSceneMaterialList, std::list &pSceneLightList) const { + X3DElementList::const_iterator chit_begin = pNodeElement.Children.begin(); + X3DElementList::const_iterator chit_end = pNodeElement.Children.end(); + std::list SceneNode_Child; + std::list SceneNode_Mesh; + + // At first read all metadata + Postprocess_CollectMetadata(pNodeElement, pSceneNode); + // check if we have deal with grouping node. Which can contain transformation or switch + if (pNodeElement.Type == X3DElemType::ENET_Group) { + const CX3DNodeElementGroup &tne_group = *((CX3DNodeElementGroup*)&pNodeElement); // create alias for convenience + + pSceneNode.mTransformation = tne_group.Transformation; + if (tne_group.UseChoice) { + // If Choice is less than zero or greater than the number of nodes in the children field, nothing is chosen. + if ((tne_group.Choice < 0) || ((size_t)tne_group.Choice >= pNodeElement.Children.size())) { + chit_begin = pNodeElement.Children.end(); + chit_end = pNodeElement.Children.end(); + } else { + for (size_t i = 0; i < (size_t)tne_group.Choice; i++) + ++chit_begin; // forward iterator to chosen node. + + chit_end = chit_begin; + ++chit_end; // point end iterator to next element after chosen node. + } + } // if(tne_group.UseChoice) + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Group) + + // Reserve memory for fast access and check children. + for (std::list::const_iterator it = chit_begin; it != chit_end; ++it) { // in this loop we do not read metadata because it's already read at begin. + if ((*it)->Type == X3DElemType::ENET_Group) { + // if child is group then create new node and do recursive call. + aiNode *new_node = new aiNode; + + new_node->mName = (*it)->ID; + new_node->mParent = &pSceneNode; + SceneNode_Child.push_back(new_node); + Postprocess_BuildNode(**it, *new_node, pSceneMeshList, pSceneMaterialList, pSceneLightList); + } else if ((*it)->Type == X3DElemType::ENET_Shape) { + // shape can contain only one geometry and one appearance nodes. + Postprocess_BuildShape(*((CX3DImporter_NodeElement_Shape *)*it), SceneNode_Mesh, pSceneMeshList, pSceneMaterialList); + } else if (((*it)->Type == X3DElemType::ENET_DirectionalLight) || ((*it)->Type == X3DElemType::ENET_PointLight) || + ((*it)->Type == X3DElemType::ENET_SpotLight)) { + Postprocess_BuildLight(*((X3DElemType *)*it), pSceneLightList); + } else if (!PostprocessHelper_ElementIsMetadata((*it)->Type)) // skip metadata + { + throw DeadlyImportError("Postprocess_BuildNode. Unknown type: " + to_string((*it)->Type) + "."); + } + } // for(std::list::const_iterator it = chit_begin; it != chit_end; it++) + + // copy data about children and meshes to aiNode. + if (!SceneNode_Child.empty()) { + std::list::const_iterator it = SceneNode_Child.begin(); + + pSceneNode.mNumChildren = static_cast(SceneNode_Child.size()); + pSceneNode.mChildren = new aiNode *[pSceneNode.mNumChildren]; + for (size_t i = 0; i < pSceneNode.mNumChildren; i++) + pSceneNode.mChildren[i] = *it++; + } + + if (!SceneNode_Mesh.empty()) { + std::list::const_iterator it = SceneNode_Mesh.begin(); + + pSceneNode.mNumMeshes = static_cast(SceneNode_Mesh.size()); + pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes]; + for (size_t i = 0; i < pSceneNode.mNumMeshes; i++) + pSceneNode.mMeshes[i] = *it++; + } + + // that's all. return to previous deals +} + +void X3DImporter::Postprocess_BuildShape(const CX3DImporter_NodeElement_Shape &pShapeNodeElement, std::list &pNodeMeshInd, + std::list &pSceneMeshList, std::list &pSceneMaterialList) const { + aiMaterial *tmat = nullptr; + aiMesh *tmesh = nullptr; + X3DElemType mesh_type = X3DElemType::ENET_Invalid; + unsigned int mat_ind = 0; + + for (X3DElementList::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); ++it) { + if (PostprocessHelper_ElementIsMesh((*it)->Type)) { + Postprocess_BuildMesh(**it, &tmesh); + if (tmesh != nullptr) { + // if mesh successfully built then add data about it to arrays + pNodeMeshInd.push_back(static_cast(pSceneMeshList.size())); + pSceneMeshList.push_back(tmesh); + // keep mesh type. Need above for texture coordinate generation. + mesh_type = (*it)->Type; + } + } else if ((*it)->Type == X3DElemType::ENET_Appearance) { + Postprocess_BuildMaterial(**it, &tmat); + if (tmat != nullptr) { + // if material successfully built then add data about it to array + mat_ind = static_cast(pSceneMaterialList.size()); + pSceneMaterialList.push_back(tmat); + } + } + } // for(std::list::const_iterator it = pShapeNodeElement.Child.begin(); it != pShapeNodeElement.Child.end(); it++) + + // associate read material with read mesh. + if ((tmesh != nullptr) && (tmat != nullptr)) { + tmesh->mMaterialIndex = mat_ind; + // Check texture mapping. If material has texture but mesh has no texture coordinate then try to ask Assimp to generate texture coordinates. + if ((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) { + int32_t tm; + aiVector3D tvec3; + + switch (mesh_type) { + case X3DElemType::ENET_Box: + tm = aiTextureMapping_BOX; + break; + case X3DElemType::ENET_Cone: + case X3DElemType::ENET_Cylinder: + tm = aiTextureMapping_CYLINDER; + break; + case X3DElemType::ENET_Sphere: + tm = aiTextureMapping_SPHERE; + break; + default: + tm = aiTextureMapping_PLANE; + break; + } // switch(mesh_type) + + tmat->AddProperty(&tm, 1, AI_MATKEY_MAPPING_DIFFUSE(0)); + } // if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) + } // if((tmesh != nullptr) && (tmat != nullptr)) +} + +void X3DImporter::Postprocess_CollectMetadata(const CX3DImporter_NodeElement &pNodeElement, aiNode &pSceneNode) const { + X3DElementList meta_list; + size_t meta_idx; + + PostprocessHelper_CollectMetadata(pNodeElement, meta_list); // find metadata in current node element. + if (!meta_list.empty()) { + if (pSceneNode.mMetaData != nullptr) { + throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong."); + } + + // copy collected metadata to output node. + pSceneNode.mMetaData = aiMetadata::Alloc(static_cast(meta_list.size())); + meta_idx = 0; + for (X3DElementList::const_iterator it = meta_list.begin(); it != meta_list.end(); ++it, ++meta_idx) { + CX3DImporter_NodeElement_Meta *cur_meta = (CX3DImporter_NodeElement_Meta *)*it; + + // due to limitations we can add only first element of value list. + // Add an element according to its type. + if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaBoolean) { + if (((CX3DImporter_NodeElement_MetaBoolean *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaBoolean *)cur_meta)->Value.begin())); + } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaDouble) { + if (((CX3DImporter_NodeElement_MetaDouble *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, (float)*(((CX3DImporter_NodeElement_MetaDouble *)cur_meta)->Value.begin())); + } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaFloat) { + if (((CX3DImporter_NodeElement_MetaFloat *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaFloat *)cur_meta)->Value.begin())); + } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaInteger) { + if (((CX3DImporter_NodeElement_MetaInteger *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaInteger *)cur_meta)->Value.begin())); + } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaString) { + if (((CX3DImporter_NodeElement_MetaString *)cur_meta)->Value.size() > 0) { + aiString tstr(((CX3DImporter_NodeElement_MetaString *)cur_meta)->Value.begin()->data()); + + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, tstr); + } + } else { + throw DeadlyImportError("Postprocess. Unknown metadata type."); + } // if((*it)->Type == CX3DImporter_NodeElement::ENET_Meta*) else + } // for(std::list::const_iterator it = meta_list.begin(); it != meta_list.end(); it++) + } // if( !meta_list.empty() ) +} + #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER + +} // namespace Assimp diff --git a/code/AssetLib/X3D/X3DImporter.hpp b/code/AssetLib/X3D/X3DImporter.hpp index f65dda559..559e4932f 100644 --- a/code/AssetLib/X3D/X3DImporter.hpp +++ b/code/AssetLib/X3D/X3DImporter.hpp @@ -41,7 +41,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef INCLUDED_AI_X3D_IMPORTER_H #define INCLUDED_AI_X3D_IMPORTER_H - #include #include #include @@ -50,8 +49,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include #include +#include #include namespace Assimp { @@ -288,8 +287,248 @@ protected: } }; +/// This struct hold value. +struct CX3DImporter_NodeElement_Color : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_Color(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Color, pParent) {} + +}; // struct CX3DImporter_NodeElement_Color + +/// This struct hold value. +struct CX3DImporter_NodeElement_ColorRGBA : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_ColorRGBA(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_ColorRGBA, pParent) {} + +}; // struct CX3DImporter_NodeElement_ColorRGBA + +/// This struct hold value. +struct CX3DImporter_NodeElement_Coordinate : public X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_Coordinate(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Coordinate, pParent) {} + +}; // struct CX3DImporter_NodeElement_Coordinate + +/// This struct hold value. +struct CX3DImporter_NodeElement_Normal : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_Normal(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Normal, pParent) {} + +}; // struct CX3DImporter_NodeElement_Normal + +/// This struct hold value. +struct CX3DImporter_NodeElement_TextureCoordinate : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_TextureCoordinate(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_TextureCoordinate, pParent) {} + +}; // struct CX3DImporter_NodeElement_TextureCoordinate + +/// Two-dimensional figure. +struct CX3DImporter_NodeElement_Geometry2D : X3DNodeElementBase { + std::list Vertices; ///< Vertices list. + size_t NumIndices; ///< Number of indices in one face. + bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + CX3DImporter_NodeElement_Geometry2D(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pType, pParent), Solid(true) {} + +}; // class CX3DImporter_NodeElement_Geometry2D + +/// Three-dimensional body. +struct CX3DImporter_NodeElement_Geometry3D : X3DNodeElementBase { + std::list Vertices; ///< Vertices list. + size_t NumIndices; ///< Number of indices in one face. + bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + CX3DImporter_NodeElement_Geometry3D(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pType, pParent), Vertices(), NumIndices(0), Solid(true) { + // empty + } +}; // class CX3DImporter_NodeElement_Geometry3D + +/// Uniform rectangular grid of varying height. +struct CX3DImporter_NodeElement_ElevationGrid : CX3DImporter_NodeElement_Geometry3D { + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are + /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. + float CreaseAngle; + std::vector CoordIdx; ///< Coordinates list by faces. In X3D format: "-1" - delimiter for faces. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + CX3DImporter_NodeElement_ElevationGrid(X3DElemType pType, X3DNodeElementBase *pParent) : + CX3DImporter_NodeElement_Geometry3D(pType, pParent) {} +}; // class CX3DImporter_NodeElement_IndexedSet + +/// Shape with indexed vertices. +struct CX3DImporter_NodeElement_IndexedSet : public CX3DImporter_NodeElement_Geometry3D { + /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors + /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to + /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the + /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite + /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the + /// ccw field, results are undefined. + bool CCW; + std::vector ColorIndex; ///< Field to specify the polygonal faces by indexing into the or . + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + /// The convex field indicates whether all polygons in the shape are convex (TRUE). A polygon is convex if it is planar, does not intersect itself, + /// and all of the interior angles at its vertices are less than 180 degrees. Non planar and self intersecting polygons may produce undefined results + /// even if the convex field is FALSE. + bool Convex; + std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . + /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are + /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. + float CreaseAngle; + std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + CX3DImporter_NodeElement_IndexedSet(X3DElemType pType, X3DNodeElementBase *pParent) : + CX3DImporter_NodeElement_Geometry3D(pType, pParent) {} +}; // class CX3DImporter_NodeElement_IndexedSet + +/// Shape with set of vertices. +struct CX3DImporter_NodeElement_Set : CX3DImporter_NodeElement_Geometry3D { + /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors + /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to + /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the + /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite + /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the + /// ccw field, results are undefined. + bool CCW; + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . + std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . + std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . + std::vector VertexCount; ///< Field describes how many vertices are to be used in each polyline(polygon) from the field. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + CX3DImporter_NodeElement_Set(X3DElemType type, X3DNodeElementBase *pParent) : + CX3DImporter_NodeElement_Geometry3D(type, pParent) {} + +}; // class CX3DImporter_NodeElement_Set + +/// This struct hold value. +struct CX3DImporter_NodeElement_Shape : X3DNodeElementBase { + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_Shape(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Shape, pParent) {} +}; // struct CX3DImporter_NodeElement_Shape + +/// This struct hold value. +struct CX3DImporter_NodeElement_Appearance : public X3DNodeElementBase { + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_Appearance(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Appearance, pParent) {} + +}; // struct CX3DImporter_NodeElement_Appearance + +struct CX3DImporter_NodeElement_Material : public X3DNodeElementBase { + float AmbientIntensity; ///< Specifies how much ambient light from light sources this surface shall reflect. + aiColor3D DiffuseColor; ///< Reflects all X3D light sources depending on the angle of the surface with respect to the light source. + aiColor3D EmissiveColor; ///< Models "glowing" objects. This can be useful for displaying pre-lit models. + float Shininess; ///< Lower shininess values produce soft glows, while higher values result in sharper, smaller highlights. + aiColor3D SpecularColor; ///< The specularColor and shininess fields determine the specular highlights. + float Transparency; ///< Specifies how "clear" an object is, with 1.0 being completely transparent, and 0.0 completely opaque. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + CX3DImporter_NodeElement_Material(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Material, pParent), + AmbientIntensity(0.0f), + DiffuseColor(), + EmissiveColor(), + Shininess(0.0f), + SpecularColor(), + Transparency(1.0f) { + // empty + } +}; // class CX3DImporter_NodeElement_Material + +/// This struct hold value. +struct CX3DImporter_NodeElement_ImageTexture : X3DNodeElementBase { + /// RepeatS and RepeatT, that specify how the texture wraps in the S and T directions. If repeatS is TRUE (the default), the texture map is repeated + /// outside the [0.0, 1.0] texture coordinate range in the S direction so that it fills the shape. If repeatS is FALSE, the texture coordinates are + /// clamped in the S direction to lie within the [0.0, 1.0] range. The repeatT field is analogous to the repeatS field. + bool RepeatS; + bool RepeatT; ///< See \ref RepeatS. + std::string URL; ///< URL of the texture. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_ImageTexture(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_ImageTexture, pParent) {} + +}; // struct CX3DImporter_NodeElement_ImageTexture + +/// This struct hold value. +struct CX3DImporter_NodeElement_TextureTransform : X3DNodeElementBase { + aiVector2D Center; ///< Specifies a translation offset in texture coordinate space about which the rotation and scale fields are applied. + float Rotation; ///< Specifies a rotation in angle base units of the texture coordinates about the center point after the scale has been applied. + aiVector2D Scale; ///< Specifies a scaling factor in S and T of the texture coordinates about the center point. + aiVector2D Translation; ///< Specifies a translation of the texture coordinates. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_TextureTransform(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_TextureTransform, pParent) {} + +}; // struct CX3DImporter_NodeElement_TextureTransform + struct CX3DNodeElementGroup : X3DNodeElementBase { aiMatrix4x4 Transformation; ///< Transformation matrix. + + /// As you know node elements can use already defined node elements when attribute "USE" is defined. + /// Standard search when looking for an element in the whole scene graph, existing at this moment. + /// If a node is marked as static, the children(or lower) can not search for elements in the nodes upper then static. + bool Static; + + bool UseChoice; ///< Flag: if true then use number from \ref Choice to choose what the child will be kept. + int32_t Choice; ///< Number of the child which will be kept. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pStatic - static node flag. + CX3DNodeElementGroup(X3DNodeElementBase *pParent, const bool pStatic = false) : + X3DNodeElementBase(X3DElemType::ENET_Group, pParent), Static(pStatic), UseChoice(false) {} }; struct X3DNodeElementMeta : X3DNodeElementBase { @@ -352,7 +591,7 @@ struct X3DNodeElementMetaSet : public X3DNodeElementMeta { } }; -struct X3DNodeElementMetaString : public X3DNodeElementMeta { +struct X3DNodeElementMetaString : X3DNodeElementMeta { std::list Value; ///< Stored value. explicit X3DNodeElementMetaString(X3DNodeElementBase *pParent) : @@ -361,6 +600,36 @@ struct X3DNodeElementMetaString : public X3DNodeElementMeta { } }; +/// \struct CX3DImporter_NodeElement_Light +/// This struct hold value. +struct X3DNodeNodeElementLight : X3DNodeElementBase { + float AmbientIntensity; ///< Specifies the intensity of the ambient emission from the light. + aiColor3D Color; ///< specifies the spectral colour properties of both the direct and ambient light emission as an RGB value. + aiVector3D Direction; ///< Specifies the direction vector of the illumination emanating from the light source in the local coordinate system. + /// \var Global + /// Field that determines whether the light is global or scoped. Global lights illuminate all objects that fall within their volume of lighting influence. + /// Scoped lights only illuminate objects that are in the same transformation hierarchy as the light. + bool Global; + float Intensity; ///< Specifies the brightness of the direct emission from the light. + /// \var Attenuation + /// PointLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor + /// is: "1 / max(attenuation[0] + attenuation[1] * r + attenuation[2] * r2, 1)", where r is the distance from the light to the surface being illuminated. + aiVector3D Attenuation; + aiVector3D Location; ///< Specifies a translation offset of the centre point of the light source from the light's local coordinate system origin. + float Radius; ///< Specifies the radial extent of the solid angle and the maximum distance from location that may be illuminated by the light source. + float BeamWidth; ///< Specifies an inner solid angle in which the light source emits light at uniform full intensity. + float CutOffAngle; ///< The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle). + + /// Constructor + /// \param [in] pParent - pointer to parent node. + /// \param [in] pLightType - type of the light source. + X3DNodeNodeElementLight(X3DElemType pLightType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pLightType, pParent) {} + +}; // struct CX3DImporter_NodeElement_Light + +using X3DElementList = std::list; + class X3DImporter : public BaseImporter { public: std::list NodeElement_List; ///< All elements of scene graph. @@ -389,6 +658,9 @@ public: void readScene(XmlNode &node); void readViewpoint(XmlNode &node); void readMetadataObject(XmlNode &node); + void ParseDirectionalLight(XmlNode &node); + void Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, + std::list &pSceneMaterialList, std::list &pSceneLightList) const; private: static const aiImporterDesc Description; From 6dd9ab062c0404383fb6ebc034687b71a482c7a8 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Fri, 11 Jun 2021 10:56:45 +0300 Subject: [PATCH 053/232] the expression does not throw an exception. maybe you just forgot this word. --- contrib/poly2tri/poly2tri/sweep/sweep.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/poly2tri/poly2tri/sweep/sweep.cc b/contrib/poly2tri/poly2tri/sweep/sweep.cc index 23aeb6b57..8e3d794c0 100644 --- a/contrib/poly2tri/poly2tri/sweep/sweep.cc +++ b/contrib/poly2tri/poly2tri/sweep/sweep.cc @@ -129,7 +129,7 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl EdgeEvent( tcx, ep, *p1, triangle, *p1 ); } else { // ASSIMP_CHANGE (aramis_acg) - std::runtime_error("EdgeEvent - collinear points not supported"); + throw std::runtime_error("EdgeEvent - collinear points not supported"); } return; } From 4b4d6b13268bda0c16bc5ba593b8b2abb81b1d3a Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 13:44:52 +0100 Subject: [PATCH 054/232] Standardise Clearcoat, Sheen and Transmission Also cleanup glTFv2 defaults, don't import/export if disabled --- code/AssetLib/glTF2/glTF2Exporter.cpp | 236 +++++++++++++++----------- code/AssetLib/glTF2/glTF2Exporter.h | 26 ++- code/AssetLib/glTF2/glTF2Importer.cpp | 39 ++--- code/Common/material.cpp | 12 +- include/assimp/material.h | 77 ++++++++- include/assimp/pbrmaterial.h | 28 +-- test/unit/utglTF2ImportExport.cpp | 15 ++ 7 files changed, 281 insertions(+), 152 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index dfa62372a..3cb3891b0 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -436,11 +436,11 @@ inline void SetSamplerWrap(SamplerWrap& wrap, aiTextureMapMode map) }; } -void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetTexSampler(const aiMaterial& mat, Ref texture, aiTextureType tt, unsigned int slot) { aiString aId; std::string id; - if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) { + if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) { id = aId.C_Str(); } @@ -455,49 +455,49 @@ void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, a SamplerMagFilter filterMag; SamplerMinFilter filterMin; - if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) { SetSamplerWrap(texture->sampler->wrapS, mapU); } - if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) { SetSamplerWrap(texture->sampler->wrapT, mapV); } - if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) { texture->sampler->magFilter = filterMag; } - if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) { texture->sampler->minFilter = filterMin; } aiString name; - if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) { + if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) { texture->sampler->name = name.C_Str(); } } } -void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot) { std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - mat->Get(textureKey.c_str(), tt, slot, prop); + mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot) { std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - mat->Get(textureKey.c_str(), tt, slot, prop); + mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) { - if (mat->GetTextureCount(tt) > 0) { + if (mat.GetTextureCount(tt) > 0) { aiString tex; - if (mat->Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { std::string path = tex.C_Str(); if (path.size() > 0) { @@ -568,7 +568,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; @@ -579,7 +579,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, TextureInfo& prop, aiTextur } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; @@ -591,7 +591,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, NormalTextureInfo& prop, ai } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; @@ -603,10 +603,10 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, OcclusionTextureInfo& prop, } } -aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const char* propName, int type, int idx) +aiReturn glTF2Exporter::GetMatColor(const aiMaterial& mat, vec4& prop, const char* propName, int type, int idx) const { aiColor4D col; - aiReturn result = mat->Get(propName, type, idx, col); + aiReturn result = mat.Get(propName, type, idx, col); if (result == AI_SUCCESS) { prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; prop[3] = col.a; @@ -615,30 +615,109 @@ aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const cha return result; } -aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec3& prop, const char* propName, int type, int idx) +aiReturn glTF2Exporter::GetMatColor(const aiMaterial& mat, vec3& prop, const char* propName, int type, int idx) const { aiColor3D col; - aiReturn result = mat->Get(propName, type, idx, col); + aiReturn result = mat.Get(propName, type, idx, col); if (result == AI_SUCCESS) { - prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; + prop[0] = col.r; + prop[1] = col.g; + prop[2] = col.b; } return result; } +bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG) { + bool result = false; + // If has Glossiness, a Specular Color or Specular Texture, use the KHR_materials_pbrSpecularGlossiness extension + // NOTE: This extension is being considered for deprecation (Dec 2020), may be replaced by KHR_material_specular + + if (mat.Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) == AI_SUCCESS) { + result = true; + } else { + // Don't have explicit glossiness, convert from pbr roughness or legacy shininess + float shininess; + if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way + } else if (mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = shininess / 1000; + } + } + + if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { + result = true; + } + // Add any appropriate textures + GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); + + result == result || pbrSG.specularGlossinessTexture.texture; + + if (result) { + // Likely to always have diffuse + GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); + GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); + } + + return result; +} + +bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen) { + // Return true if got any valid Sheen properties or textures + if (GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_SHEEN_COLOR_FACTOR) != aiReturn_SUCCESS) + return false; + + // Default Sheen color factor {0,0,0} disables Sheen, so do not export + if (sheen.sheenColorFactor == defaultSheenFactor) + return false; + + mat.Get(AI_MATKEY_SHEEN_ROUGHNESS_FACTOR, sheen.sheenRoughnessFactor); + + GetMatTex(mat, sheen.sheenColorTexture, AI_MATKEY_SHEEN_COLOR_TEXTURE); + GetMatTex(mat, sheen.sheenRoughnessTexture, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE); + + return true; +} + +bool glTF2Exporter::GetMatClearcoat(const aiMaterial &mat, glTF2::MaterialClearcoat &clearcoat) { + if (mat.Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat.clearcoatFactor) != aiReturn_SUCCESS) { + return false; + } + + // Clearcoat factor of zero disables Clearcoat, so do not export + if (clearcoat.clearcoatFactor == 0.0f) + return false; + + mat.Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat.clearcoatRoughnessFactor); + + GetMatTex(mat, clearcoat.clearcoatTexture, AI_MATKEY_CLEARCOAT_TEXTURE); + GetMatTex(mat, clearcoat.clearcoatRoughnessTexture, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE); + GetMatTex(mat, clearcoat.clearcoatNormalTexture, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE); + + return true; +} + +bool glTF2Exporter::GetMatTransmission(const aiMaterial &mat, glTF2::MaterialTransmission &transmission) { + bool result = mat.Get(AI_MATKEY_TRANSMISSION_FACTOR, transmission.transmissionFactor) == aiReturn_SUCCESS; + GetMatTex(mat, transmission.transmissionTexture, AI_MATKEY_TRANSMISSION_TEXTURE); + return result || transmission.transmissionTexture.texture; +} + void glTF2Exporter::ExportMaterials() { aiString aiName; for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) { - const aiMaterial* mat = mScene->mMaterials[i]; + ai_assert(mScene->mMaterials[i] != nullptr); + + const aiMaterial & mat = *(mScene->mMaterials[i]); std::string id = "material_" + ai_to_string(i); Ref m = mAsset->materials.Create(id); std::string name; - if (mat->Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) { name = aiName.C_Str(); } name = mAsset->FindUniqueID(name, "material"); @@ -660,20 +739,20 @@ void glTF2Exporter::ExportMaterials() GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); } - if (mat->Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { + if (mat.Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0 m->pbrMetallicRoughness.metallicFactor = 0; } // get roughness if source is gltf2 file - if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { + if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { // otherwise, try to derive and convert from specular + shininess values aiColor4D specularColor; ai_real shininess; if ( - mat->Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && - mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS + mat.Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && + mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS ) { // convert specular color to luminance float specularIntensity = specularColor[0] * 0.2125f + specularColor[1] * 0.7154f + specularColor[2] * 0.0721f; @@ -694,17 +773,17 @@ void glTF2Exporter::ExportMaterials() GetMatTex(mat, m->emissiveTexture, aiTextureType_EMISSIVE); GetMatColor(mat, m->emissiveFactor, AI_MATKEY_COLOR_EMISSIVE); - mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided); - mat->Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); + mat.Get(AI_MATKEY_TWOSIDED, m->doubleSided); + mat.Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); aiString alphaMode; - if (mat->Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { m->alphaMode = alphaMode.C_Str(); } else { float opacity; - if (mat->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { if (opacity < 1) { m->alphaMode = "BLEND"; m->pbrMetallicRoughness.baseColorFactor[3] *= opacity; @@ -713,86 +792,43 @@ void glTF2Exporter::ExportMaterials() } { - // If has a Specular color, use the KHR_materials_pbrSpecularGlossiness extension + // KHR_materials_pbrSpecularGlossiness extension + // NOTE: This extension is being considered for deprecation (Dec 2020) PbrSpecularGlossiness pbrSG; - if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { - if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { - mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; - } - - GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); - - // If don't have explicit glossiness then convert from roughness or shininess - if (mat->Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { - float shininess; - if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way - } else if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = shininess / 1000; - } - } - - // Add any appropriate textures - GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); - GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - + if (GetMatSpecGloss(mat, pbrSG)) { + mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; m->pbrSpecularGlossiness = Nullable(pbrSG); } } // glTFv2 is either PBR or Unlit aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; - mat->Get(AI_MATKEY_SHADING_MODEL, shadingMode); + mat.Get(AI_MATKEY_SHADING_MODEL, shadingMode); if (shadingMode == aiShadingMode_Unlit) { mAsset->extensionsUsed.KHR_materials_unlit = true; m->unlit = true; - } + } else { + // These extensions are not compatible with KHR_materials_unlit or KHR_materials_pbrSpecularGlossiness + if (!m->pbrSpecularGlossiness.isPresent) { + // Sheen + MaterialSheen sheen; + if (GetMatSheen(mat, sheen)) { + mAsset->extensionsUsed.KHR_materials_sheen = true; + m->materialSheen = Nullable(sheen); + } - bool hasMaterialSheen = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_SHEEN, hasMaterialSheen); + MaterialClearcoat clearcoat; + if (GetMatClearcoat(mat, clearcoat)) { + mAsset->extensionsUsed.KHR_materials_clearcoat = true; + m->materialClearcoat = Nullable(clearcoat); + } - if (hasMaterialSheen) { - mAsset->extensionsUsed.KHR_materials_sheen = true; - - MaterialSheen sheen; - - GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR); - mat->Get(AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR, sheen.sheenRoughnessFactor); - GetMatTex(mat, sheen.sheenColorTexture, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE); - GetMatTex(mat, sheen.sheenRoughnessTexture, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE); - - m->materialSheen = Nullable(sheen); - } - - bool hasMaterialClearcoat = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT, hasMaterialClearcoat); - - if (hasMaterialClearcoat) { - mAsset->extensionsUsed.KHR_materials_clearcoat= true; - - MaterialClearcoat clearcoat; - - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR, clearcoat.clearcoatFactor); - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat.clearcoatRoughnessFactor); - GetMatTex(mat, clearcoat.clearcoatTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE); - GetMatTex(mat, clearcoat.clearcoatRoughnessTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE); - GetMatTex(mat, clearcoat.clearcoatNormalTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE); - - m->materialClearcoat = Nullable(clearcoat); - } - - bool hasMaterialTransmission = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_TRANSMISSION, hasMaterialTransmission); - - if (hasMaterialTransmission) { - mAsset->extensionsUsed.KHR_materials_transmission = true; - - MaterialTransmission transmission; - - mat->Get(AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR, transmission.transmissionFactor); - GetMatTex(mat, transmission.transmissionTexture, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE); - - m->materialTransmission = Nullable(transmission); + MaterialTransmission transmission; + if (GetMatTransmission(mat, transmission)) { + mAsset->extensionsUsed.KHR_materials_transmission = true; + m->materialTransmission = Nullable(transmission); + } + } } } } diff --git a/code/AssetLib/glTF2/glTF2Exporter.h b/code/AssetLib/glTF2/glTF2Exporter.h index 86497516a..edc85d998 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.h +++ b/code/AssetLib/glTF2/glTF2Exporter.h @@ -72,6 +72,10 @@ namespace glTF2 struct OcclusionTextureInfo; struct Node; struct Texture; + struct PbrSpecularGlossiness; + struct MaterialSheen; + struct MaterialClearcoat; + struct MaterialTransmission; // Vec/matrix types, as raw float arrays typedef float (vec2)[2]; @@ -97,15 +101,19 @@ namespace Assimp protected: void WriteBinaryData(IOStream* outfile, std::size_t sceneLength); - void GetTexSampler(const aiMaterial* mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); - void GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTex(const aiMaterial* mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); - aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec4& prop, const char* propName, int type, int idx); - aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec3& prop, const char* propName, int type, int idx); + void GetTexSampler(const aiMaterial& mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); + void GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); + void GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); + void GetMatTex(const aiMaterial& mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); + aiReturn GetMatColor(const aiMaterial& mat, glTF2::vec4& prop, const char* propName, int type, int idx) const; + aiReturn GetMatColor(const aiMaterial& mat, glTF2::vec3& prop, const char* propName, int type, int idx) const; + bool GetMatSpecGloss(const aiMaterial& mat, glTF2::PbrSpecularGlossiness& pbrSG); + bool GetMatSheen(const aiMaterial& mat, glTF2::MaterialSheen& sheen); + bool GetMatClearcoat(const aiMaterial& mat, glTF2::MaterialClearcoat& clearcoat); + bool GetMatTransmission(const aiMaterial& mat, glTF2::MaterialTransmission& transmission); void ExportMetadata(); void ExportMaterials(); void ExportMeshes(); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index b5ba65857..b0f0955f5 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -291,36 +291,37 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL); - //KHR_materials_sheen + // KHR_materials_sheen if (mat.materialSheen.isPresent) { MaterialSheen &sheen = mat.materialSheen.value; - - aimat->AddProperty(&mat.materialSheen.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_SHEEN); - SetMaterialColorProperty(r, sheen.sheenColorFactor, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR); - aimat->AddProperty(&sheen.sheenRoughnessFactor, 1, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenColorTexture, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenRoughnessTexture, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE); + // Default value {0,0,0} disables Sheen + if (sheen.sheenColorFactor != defaultSheenFactor) { + SetMaterialColorProperty(r, sheen.sheenColorFactor, aimat, AI_MATKEY_SHEEN_COLOR_FACTOR); + aimat->AddProperty(&sheen.sheenRoughnessFactor, 1, AI_MATKEY_SHEEN_ROUGHNESS_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenColorTexture, aimat, AI_MATKEY_SHEEN_COLOR_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenRoughnessTexture, aimat, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE); + } } - //KHR_materials_clearcoat + // KHR_materials_clearcoat if (mat.materialClearcoat.isPresent) { MaterialClearcoat &clearcoat = mat.materialClearcoat.value; - - aimat->AddProperty(&mat.materialClearcoat.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT); - aimat->AddProperty(&clearcoat.clearcoatFactor, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR); - aimat->AddProperty(&clearcoat.clearcoatRoughnessFactor, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatRoughnessTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatNormalTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE); + // Default value 0.0 disables clearcoat + if (clearcoat.clearcoatFactor != 0.0f) { + aimat->AddProperty(&clearcoat.clearcoatFactor, 1, AI_MATKEY_CLEARCOAT_FACTOR); + aimat->AddProperty(&clearcoat.clearcoatRoughnessFactor, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatTexture, aimat, AI_MATKEY_CLEARCOAT_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatRoughnessTexture, aimat, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatNormalTexture, aimat, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE); + } } - //KHR_materials_transmission + // KHR_materials_transmission if (mat.materialTransmission.isPresent) { MaterialTransmission &transmission = mat.materialTransmission.value; - aimat->AddProperty(&mat.materialTransmission.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION); - aimat->AddProperty(&transmission.transmissionFactor, 1, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, transmission.transmissionTexture, aimat, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE); + aimat->AddProperty(&transmission.transmissionFactor, 1, AI_MATKEY_TRANSMISSION_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, transmission.transmissionTexture, aimat, AI_MATKEY_TRANSMISSION_TEXTURE); } return aimat; diff --git a/code/Common/material.cpp b/code/Common/material.cpp index 5230c8b43..6c90e66f0 100644 --- a/code/Common/material.cpp +++ b/code/Common/material.cpp @@ -47,10 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include // ------------------------------------------------------------------------------- -const char* TextureTypeToString(aiTextureType in) -{ - switch (in) - { +const char *TextureTypeToString(aiTextureType in) { + switch (in) { case aiTextureType_NONE: return "n/a"; case aiTextureType_DIFFUSE: @@ -87,6 +85,12 @@ const char* TextureTypeToString(aiTextureType in) return "DiffuseRoughness"; case aiTextureType_AMBIENT_OCCLUSION: return "AmbientOcclusion"; + case aiTextureType_SHEEN: + return "Sheen"; + case aiTextureType_CLEARCOAT: + return "Clearcoat"; + case aiTextureType_TRANSMISSION: + return "Transmission"; case aiTextureType_UNKNOWN: return "Unknown"; default: diff --git a/include/assimp/material.h b/include/assimp/material.h index 11cdef1f4..33e39529e 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -144,7 +144,9 @@ enum aiTextureMapMode { enum aiTextureMapping { /** The mapping coordinates are taken from an UV channel. * - * The #AI_MATKEY_UVWSRC key specifies from which UV channel + * #AI_MATKEY_UVWSRC property + * + * Specifies from which UV channel * the texture coordinates are to be taken from (remember, * meshes can have more than one UV channel). */ @@ -292,6 +294,32 @@ enum aiTextureType { aiTextureType_DIFFUSE_ROUGHNESS = 16, aiTextureType_AMBIENT_OCCLUSION = 17, + /** PBR Material Modifiers + * Some modern renderers have further PBR modifiers that may be overlaid + * on top of the 'base' PBR materials for additional realism. + * These use multiple texture maps, so only the base type is directly defined + */ + + /** Sheen + * Generally used to simulate textiles that are covered in a layer of microfibers + * eg velvet + * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_sheen + */ + aiTextureType_SHEEN = 19, + + /** Clearcoat + * Simulates a layer of 'polish' or 'laquer' layered on top of a PBR substrate + * https://autodesk.github.io/standard-surface/#closures/coating + * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat + */ + aiTextureType_CLEARCOAT = 20, + + /** Transmission + * Simulates transmission through the surface + * May include further information such as wall thickness + */ + aiTextureType_TRANSMISSION = 21, + /** Unknown texture * * A texture reference that does not match any of the definitions @@ -314,7 +342,7 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); // --------------------------------------------------------------------------- /** @brief Defines all shading models supported by the library * - * #AI_MATKEY_SHADING_MODEL + * Property: #AI_MATKEY_SHADING_MODEL * * The list of shading modes has been taken from Blender. * See Blender documentation for more information. The API does @@ -324,6 +352,7 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); * 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 modeler which wrote a particular model as closely as possible. + * */ enum aiShadingMode { /** Flat shading. Shading is done on per-face base, @@ -384,10 +413,11 @@ enum aiShadingMode { * There are multiple methods under this banner, and model files may provide * data for more than one PBR-BRDF method. * Applications should use the set of provided properties to determine which - * of their preferred PBDR methods are available + * of their preferred PBR rendering methods are likely to be available * eg: * - If AI_MATKEY_METALLIC_FACTOR is set, then a Metallic/Roughness is available - * - If AI_MATKEY_COLOR_SPECULAR is set, then a Specular/Glossiness is available + * - If AI_MATKEY_GLOSSINESS_FACTOR is set, then a Specular/Glossiness is available + * Note that some PBR methods allow layering of techniques */ aiShadingMode_PBR_BRDF = 0xb, @@ -942,28 +972,63 @@ extern "C" { // --------------------------------------------------------------------------- // PBR material support +// -------------------- +// Properties defining PBR rendering techniques #define AI_MATKEY_USE_COLOR_MAP "$mat.useColorMap", 0, 0 // Metallic/Roughness Workflow // --------------------------- -// Base color factor. Will be multiplied by final base color texture values if extant +// Base RGBA color factor. Will be multiplied by final base color texture values if extant +// Note: Importers may choose to copy this into AI_MATKEY_COLOR_DIFFUSE for compatibility +// with renderers and formats that do not support Metallic/Roughness PBR #define AI_MATKEY_BASE_COLOR "$clr.base", 0, 0 +#define AI_MATKEY_BASE_COLOR_TEXTURE aiTextureType_BASE_COLOR, 0 #define AI_MATKEY_USE_METALLIC_MAP "$mat.useMetallicMap", 0, 0 // Metallic factor. 0.0 = Full Dielectric, 1.0 = Full Metal #define AI_MATKEY_METALLIC_FACTOR "$mat.metallicFactor", 0, 0 +#define AI_MATKEY_METALLIC_TEXTURE aiTextureType_METALNESS, 0 #define AI_MATKEY_USE_ROUGHNESS_MAP "$mat.useRoughnessMap", 0, 0 // Roughness factor. 0.0 = Perfectly Smooth, 1.0 = Completely Rough #define AI_MATKEY_ROUGHNESS_FACTOR "$mat.roughnessFactor", 0, 0 +#define AI_MATKEY_ROUGHNESS_TEXTURE aiTextureType_DIFFUSE_ROUGHNESS, 0 // Specular/Glossiness Workflow // --------------------------- // Diffuse/Albedo Color. Note: Pure Metals have a diffuse of {0,0,0} // AI_MATKEY_COLOR_DIFFUSE -// Specular Color +// Specular Color. +// Note: Metallic/Roughness may also have a Specular Color // AI_MATKEY_COLOR_SPECULAR +#define AI_MATKEY_SPECULAR_FACTOR "$mat.specularFactor", 0, 0 // Glossiness factor. 0.0 = Completely Rough, 1.0 = Perfectly Smooth #define AI_MATKEY_GLOSSINESS_FACTOR "$mat.glossinessFactor", 0, 0 +// Sheen +// ----- +// Sheen base RGB color. Default {0,0,0} +#define AI_MATKEY_SHEEN_COLOR_FACTOR "$clr.sheen.factor", 0, 0 +// Sheen Roughness Factor. +#define AI_MATKEY_SHEEN_ROUGHNESS_FACTOR "$mat.sheen.roughnessFactor", 0, 0 +#define AI_MATKEY_SHEEN_COLOR_TEXTURE aiTextureType_SHEEN, 0 +#define AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE aiTextureType_SHEEN, 1 + +// Clearcoat +// --------- +#define AI_MATKEY_CLEARCOAT_FACTOR "$clr.clearcoat.factor", 0, 0 +#define AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR "$mat.clearcoat.roughnessFactor", 0, 0 +#define AI_MATKEY_CLEARCOAT_TEXTURE aiTextureType_CLEARCOAT, 0 +#define AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_CLEARCOAT, 1 +#define AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE aiTextureType_CLEARCOAT, 2 + +// Transmission +// ------------ +// https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission +// Base percentage of light transmitted through the surface. 0.0 = Opaque, 1.0 = Fully transparent +#define AI_MATKEY_TRANSMISSION_FACTOR "$mat.transmission.factor", 0, 0 +// Texture defining percentage of light transmitted through the surface. +// Multiplied by AI_MATKEY_TRANSMISSION_FACTOR +#define AI_MATKEY_TRANSMISSION_TEXTURE aiTextureType_TRANSMISSION, 0 + // Emissive // -------- #define AI_MATKEY_USE_EMISSIVE_MAP "$mat.useEmissiveMap", 0, 0 diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h index fd904e4fc..c67bcc3b8 100644 --- a/include/assimp/pbrmaterial.h +++ b/include/assimp/pbrmaterial.h @@ -60,20 +60,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 //#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 //#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN "$mat.gltf.materialSheen", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR "$mat.gltf.materialSheen.sheenColorFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR "$mat.gltf.materialSheen.sheenRoughnessFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE aiTextureType_UNKNOWN, 1 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 2 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT "$mat.gltf.materialClearcoat", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR "$mat.gltf.materialClearcoat.clearcoatFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR "$mat.gltf.materialClearcoat.clearcoatRoughnessFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE aiTextureType_UNKNOWN, 3 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 4 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE aiTextureType_NORMALS, 1 -#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION "$mat.gltf.materialTransmission", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR "$mat.gltf.materialTransmission.transmissionFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE aiTextureType_UNKNOWN, 5 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN "$mat.gltf.materialSheen", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR "$mat.gltf.materialSheen.sheenColorFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR "$mat.gltf.materialSheen.sheenRoughnessFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE aiTextureType_UNKNOWN, 1 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 2 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT "$mat.gltf.materialClearcoat", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR "$mat.gltf.materialClearcoat.clearcoatFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR "$mat.gltf.materialClearcoat.clearcoatRoughnessFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE aiTextureType_UNKNOWN, 3 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 4 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE aiTextureType_NORMALS, 1 +//#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION "$mat.gltf.materialTransmission", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR "$mat.gltf.materialTransmission.transmissionFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE aiTextureType_UNKNOWN, 5 #define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname" diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index e0ac10ad5..766372325 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -152,6 +152,20 @@ TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) { } #ifndef ASSIMP_BUILD_NO_EXPORT + +TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_pbrSpecularGlossiness) { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", + aiProcess_ValidateDataStructure); + EXPECT_NE(nullptr, scene); + // Export + EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb")); + + // And re-import + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb", true)); +} + TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) { Assimp::Importer importer; Assimp::Exporter exporter; @@ -169,6 +183,7 @@ TEST_F(utglTF2ImportExport, importglTF2EmbeddedAndExportToOBJ) { EXPECT_NE(nullptr, scene); EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "obj", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-Embedded/BoxTextured_out.obj")); } + #endif // ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, importglTF2PrimitiveModePointsWithoutIndices) { From c86522e552dc3edf0d2a3cff228956f3ce87b5c0 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 14:34:42 +0100 Subject: [PATCH 055/232] Add glTFv2 Clearcoat import/export tests Uses Clearcoat model from Khronos --- code/AssetLib/glTF2/glTF2Importer.cpp | 5 + include/assimp/material.h | 3 +- .../glTF2/ClearCoat-glTF/ClearCoatLabels.png | Bin 0 -> 10270 bytes .../glTF2/ClearCoat-glTF/ClearCoatTest.bin | Bin 0 -> 50328 bytes .../glTF2/ClearCoat-glTF/ClearCoatTest.gltf | 1669 +++++++++++++++++ .../glTF2/ClearCoat-glTF/PartialCoating.png | Bin 0 -> 5077 bytes .../ClearCoat-glTF/PartialCoating_Alpha.png | Bin 0 -> 5065 bytes .../ClearCoat-glTF/PlasticWrap_normals.jpg | Bin 0 -> 144210 bytes .../glTF2/ClearCoat-glTF/RibsNormal.png | Bin 0 -> 1605 bytes .../glTF2/ClearCoat-glTF/RoughnessStripes.png | Bin 0 -> 5033 bytes test/unit/utglTF2ImportExport.cpp | 57 + 11 files changed, 1733 insertions(+), 1 deletion(-) create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf create mode 100644 test/models/glTF2/ClearCoat-glTF/PartialCoating.png create mode 100644 test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png create mode 100644 test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg create mode 100644 test/models/glTF2/ClearCoat-glTF/RibsNormal.png create mode 100644 test/models/glTF2/ClearCoat-glTF/RoughnessStripes.png diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index b0f0955f5..b435f111d 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -208,6 +208,11 @@ inline void SetMaterialTextureProperty(std::vector &embeddedTexIdxs, Asset if (sampler->minFilter != SamplerMinFilter::UNSET) { mat->AddProperty(&sampler->minFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(texType, texSlot)); } + } else { + // Use glTFv2 default sampler + const aiTextureMapMode default_wrap = aiTextureMapMode_Wrap; + mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_U(texType, texSlot)); + mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_V(texType, texSlot)); } } } diff --git a/include/assimp/material.h b/include/assimp/material.h index 33e39529e..f348da369 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -1014,7 +1014,8 @@ extern "C" { // Clearcoat // --------- -#define AI_MATKEY_CLEARCOAT_FACTOR "$clr.clearcoat.factor", 0, 0 +// Clearcoat layer intensity. 0.0 = none (disabled) +#define AI_MATKEY_CLEARCOAT_FACTOR "$mat.clearcoat.factor", 0, 0 #define AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR "$mat.clearcoat.roughnessFactor", 0, 0 #define AI_MATKEY_CLEARCOAT_TEXTURE aiTextureType_CLEARCOAT, 0 #define AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_CLEARCOAT, 1 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png b/test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png new file mode 100644 index 0000000000000000000000000000000000000000..d47f2f5e1f06116fca5ddd9697001ffedcc5bd68 GIT binary patch literal 10270 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 literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin new file mode 100644 index 0000000000000000000000000000000000000000..e7c6a93a4995917f931d580f7d25c94f33d1639a GIT binary patch literal 50328 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 z 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 MV6NHY-@g5S0BBn6`Tzg` literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf new file mode 100644 index 000000000..ae12bfc11 --- /dev/null +++ b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf @@ -0,0 +1,1669 @@ +{ + "asset" : { + "generator" : "Khronos glTF Blender I/O v1.2.8 with hand-edits for clearcoat", + "version" : "2.0" + }, + "extensionsUsed": [ + "KHR_materials_clearcoat" + ], + "scene" : 0, + "scenes" : [ + { + "name" : "Scene", + "nodes" : [ + 3, + 7, + 11, + 15, + 19, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32 + ] + } + ], + "nodes" : [ + { + "mesh" : 0, + "name" : "BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 1, + "name" : "ClearCoatSample" + }, + { + "mesh" : 2, + "name" : "CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 0, + 1, + 2 + ], + "name" : "R0_SimpleCoatTest", + "translation" : [ + 0, + 5.25, + 0 + ] + }, + { + "mesh" : 3, + "name" : "R1_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 4, + "name" : "R1_ClearCoatSample" + }, + { + "mesh" : 5, + "name" : "R1_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 4, + 5, + 6 + ], + "name" : "R1_PartialCoatTest", + "translation" : [ + 0, + 3.1500000953674316, + 0 + ] + }, + { + "mesh" : 6, + "name" : "R2_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 7, + "name" : "R2_ClearCoatSample" + }, + { + "mesh" : 8, + "name" : "R2_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 8, + 9, + 10 + ], + "name" : "R2_RoughnessVariations", + "translation" : [ + 0, + 1.0499999523162842, + 0 + ] + }, + { + "mesh" : 9, + "name" : "R3_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 10, + "name" : "R3_ClearCoatSample" + }, + { + "mesh" : 11, + "name" : "R3_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 12, + 13, + 14 + ], + "name" : "R3_BaseNormals", + "translation" : [ + 0, + -1.0499999523162842, + 0 + ] + }, + { + "mesh" : 12, + "name" : "R4_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 13, + "name" : "R4_ClearCoatSample" + }, + { + "mesh" : 14, + "name" : "R4_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 16, + 17, + 18 + ], + "name" : "R4_CoatNormals", + "translation" : [ + 0, + -5.25, + 0 + ] + }, + { + "mesh" : 15, + "name" : "R5_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 16, + "name" : "R5_ClearCoatSample" + }, + { + "mesh" : 17, + "name" : "R5_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 20, + 21, + 22 + ], + "name" : "R5_SharedNormals", + "translation" : [ + 0, + -3.1500000953674316, + 0 + ] + }, + { + "mesh" : 18, + "name" : "X2_Label_CoatingOnly", + "translation" : [ + 2.0712804794311523, + 6.619500160217285, + 0 + ] + }, + { + "mesh" : 19, + "name" : "Y0_Label_SimpleCoating", + "translation" : [ + -5.3578033447265625, + 5.25, + 0 + ] + }, + { + "mesh" : 20, + "name" : "Y1_Label_PartialCoating", + "translation" : [ + -5.3578033447265625, + 3.1673200130462646, + 0 + ] + }, + { + "mesh" : 21, + "name" : "Y2_Label_Roughness", + "translation" : [ + -5.3578033447265625, + 1.1383899450302124, + 0 + ] + }, + { + "mesh" : 22, + "name" : "Y3_Label_BaseNormals", + "translation" : [ + -5.3578033447265625, + -1.099429965019226, + 0 + ] + }, + { + "mesh" : 23, + "name" : "Y4_Label_CoatNormals", + "translation" : [ + -5.3578033447265625, + -5.252500057220459, + 0 + ] + }, + { + "mesh" : 24, + "name" : "Y5_Label_SharedNormals", + "translation" : [ + -5.3578033447265625, + -3.1963000297546387, + 0 + ] + }, + { + "mesh" : 25, + "name" : "X0_Label_BaseLayer", + "translation" : [ + -2.087031602859497, + 6.616230010986328, + 0 + ] + }, + { + "mesh" : 26, + "name" : "X1_Label_Coated", + "translation" : [ + 0, + 6.614150047302246, + 0 + ] + } + ], + "materials" : [ + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.5, + 0.019999999552965164, + 0.009999999776482582, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.5, + 0.019999999552965164, + 0.009999999776482582, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatTexture": { + "index": 0, + "texCoord": 0 + } + } + } + }, + { + "alphaMode" : "BLEND", + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Coating", + "pbrMetallicRoughness" : { + "baseColorTexture" : { + "index" : 1, + "texCoord" : 0 + }, + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.6000000238418579 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.6000000238418579 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 1, + "clearcoatRoughnessTexture": { + "index": 2, + "texCoord": 0 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "metallicRoughnessTexture" : { + "index" : 2, + "texCoord" : 0 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Base", + "normalTexture" : { + "index" : 3, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Coated", + "normalTexture" : { + "index" : 4, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012848637998104095, + 0.021861059591174126, + 0.11068868637084961, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatNormalTexture": { + "index": 5, + "texCoord": 0, + "scale": 1 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Coating", + "normalTexture" : { + "index" : 5, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Base", + "normalTexture" : { + "index" : 6, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Coated", + "normalTexture" : { + "index" : 7, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatNormalTexture": { + "index": 7, + "texCoord": 0 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Coating", + "normalTexture" : { + "index" : 8, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 1, + 1, + 1 + ], + "emissiveTexture" : { + "index" : 9, + "texCoord" : 0 + }, + "name" : "LabelMaterial", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.8999999761581421 + } + } + ], + "meshes" : [ + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 0 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 1 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 2 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 3 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 4 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 5 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 6 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 7 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 8 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 9 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 10 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 11 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 12 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 13 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 14 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 15 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 16 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 17 + } + ] + }, + { + "name" : "Labels_Mesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 4, + "NORMAL" : 5, + "TEXCOORD_0" : 6 + }, + "indices" : 7, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.001", + "primitives" : [ + { + "attributes" : { + "POSITION" : 8, + "NORMAL" : 9, + "TEXCOORD_0" : 10 + }, + "indices" : 7, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.002", + "primitives" : [ + { + "attributes" : { + "POSITION" : 11, + "NORMAL" : 12, + "TEXCOORD_0" : 13 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.003", + "primitives" : [ + { + "attributes" : { + "POSITION" : 15, + "NORMAL" : 16, + "TEXCOORD_0" : 17 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.004", + "primitives" : [ + { + "attributes" : { + "POSITION" : 18, + "NORMAL" : 19, + "TEXCOORD_0" : 20 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.005", + "primitives" : [ + { + "attributes" : { + "POSITION" : 21, + "NORMAL" : 22, + "TEXCOORD_0" : 23 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.006", + "primitives" : [ + { + "attributes" : { + "POSITION" : 24, + "NORMAL" : 25, + "TEXCOORD_0" : 26 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.007", + "primitives" : [ + { + "attributes" : { + "POSITION" : 27, + "NORMAL" : 28, + "TEXCOORD_0" : 29 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.008", + "primitives" : [ + { + "attributes" : { + "POSITION" : 30, + "NORMAL" : 31, + "TEXCOORD_0" : 32 + }, + "indices" : 7, + "material" : 18 + } + ] + } + ], + "textures" : [ + { + "source" : 0 + }, + { + "source" : 1 + }, + { + "source" : 2 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 4 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 5 + } + ], + "images" : [ + { + "mimeType" : "image/png", + "name" : "PartialCoating", + "uri" : "PartialCoating.png" + }, + { + "mimeType" : "image/png", + "name" : "PartialCoating_Alpha", + "uri" : "PartialCoating_Alpha.png" + }, + { + "mimeType" : "image/png", + "name" : "RoughnessStripes", + "uri" : "RoughnessStripes.png" + }, + { + "mimeType" : "image/png", + "name" : "RibsNormal", + "uri" : "RibsNormal.png" + }, + { + "mimeType" : "image/jpeg", + "name" : "PlasticWrap_normals", + "uri" : "PlasticWrap_normals.jpg" + }, + { + "mimeType" : "image/png", + "name" : "ClearCoatLabels", + "uri" : "ClearCoatLabels.png" + } + ], + "accessors" : [ + { + "bufferView" : 0, + "componentType" : 5126, + "count" : 1113, + "max" : [ + 1, + 1, + 1.0499999523162842 + ], + "min" : [ + -1, + -1, + -0.06000000983476639 + ], + "type" : "VEC3" + }, + { + "bufferView" : 1, + "componentType" : 5126, + "count" : 1113, + "type" : "VEC3" + }, + { + "bufferView" : 2, + "componentType" : 5126, + "count" : 1113, + "type" : "VEC2" + }, + { + "bufferView" : 3, + "componentType" : 5123, + "count" : 6180, + "type" : "SCALAR" + }, + { + "bufferView" : 4, + "componentType" : 5126, + "count" : 8, + "max" : [ + 1.0280373096466064, + 0.23501670360565186, + 3.8289083903464416e-08 + ], + "min" : [ + -0.968224287033081, + -0.2350165843963623, + -0.010000125505030155 + ], + "type" : "VEC3" + }, + { + "bufferView" : 5, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 6, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 7, + "componentType" : 5123, + "count" : 12, + "type" : "SCALAR" + }, + { + "bufferView" : 8, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.23026323318481445, + 3.751463495405005e-08 + ], + "min" : [ + -2, + -0.23026317358016968, + -0.010000579059123993 + ], + "type" : "VEC3" + }, + { + "bufferView" : 9, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 10, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 11, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.2302631139755249, + 3.7514624295909016e-08 + ], + "min" : [ + -2, + -0.23026323318481445, + -0.010000428184866905 + ], + "type" : "VEC3" + }, + { + "bufferView" : 12, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 13, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 14, + "componentType" : 5123, + "count" : 12, + "type" : "SCALAR" + }, + { + "bufferView" : 15, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.22039484977722168, + 3.590687924770464e-08 + ], + "min" : [ + -2, + -0.22039473056793213, + -0.010000280104577541 + ], + "type" : "VEC3" + }, + { + "bufferView" : 16, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 17, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 18, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.21764808893203735, + 3.545937588000925e-08 + ], + "min" : [ + -2, + -0.21764802932739258, + -0.010000137612223625 + ], + "type" : "VEC3" + }, + { + "bufferView" : 19, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 20, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 21, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.20775499939918518, + 3.3847587843638394e-08 + ], + "min" : [ + -2, + -0.20775499939918518, + -0.009999996051192284 + ], + "type" : "VEC3" + }, + { + "bufferView" : 22, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 23, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 24, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.22341907024383545, + 3.6399587344249085e-08 + ], + "min" : [ + -2, + -0.22341907024383545, + -0.009999859146773815 + ], + "type" : "VEC3" + }, + { + "bufferView" : 25, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 26, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 27, + "componentType" : 5126, + "count" : 8, + "max" : [ + 0.9169960618019104, + 0.22670458257198334, + 3.69348676088066e-08 + ], + "min" : [ + -0.9199233651161194, + -0.22670456767082214, + -0.010000176727771759 + ], + "type" : "VEC3" + }, + { + "bufferView" : 28, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 29, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 30, + "componentType" : 5126, + "count" : 8, + "max" : [ + 0.8968609571456909, + 0.20853587985038757, + 3.397480696776256e-08 + ], + "min" : [ + -0.9147982001304626, + -0.2085357904434204, + -0.010000113397836685 + ], + "type" : "VEC3" + }, + { + "bufferView" : 31, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 32, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteLength" : 13356, + "byteOffset" : 0 + }, + { + "buffer" : 0, + "byteLength" : 13356, + "byteOffset" : 13356 + }, + { + "buffer" : 0, + "byteLength" : 8904, + "byteOffset" : 26712 + }, + { + "buffer" : 0, + "byteLength" : 12360, + "byteOffset" : 35616 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 47976 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48072 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48168 + }, + { + "buffer" : 0, + "byteLength" : 24, + "byteOffset" : 48232 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48256 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48352 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48448 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48512 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48608 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48704 + }, + { + "buffer" : 0, + "byteLength" : 24, + "byteOffset" : 48768 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48792 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48888 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48984 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49048 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49144 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49240 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49304 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49400 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49496 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49560 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49656 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49752 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49816 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49912 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 50008 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 50072 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 50168 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 50264 + } + ], + "buffers" : [ + { + "byteLength" : 50328, + "uri" : "ClearCoatTest.bin" + } + ] +} diff --git a/test/models/glTF2/ClearCoat-glTF/PartialCoating.png b/test/models/glTF2/ClearCoat-glTF/PartialCoating.png new file mode 100644 index 0000000000000000000000000000000000000000..d579bbebb8a423eedb16594a7ef715fe7521a752 GIT binary patch literal 5077 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 Q@JSA$qoJ>ky=fQyKcnMWr2qf` literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png b/test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..b1911d9f0a303a57181d44856f3f31ea18c11063 GIT binary patch literal 5065 zcmeHL`#aP98{cwDHOD-2Xvi^CIw7Pv3?n0QN;z~K_JndK=1|W=awt+1!zPnC6e6dk zro`j0g+jI#wVV>dFx&U@@qDlEfAIaSA9j6qy|4ST_v^mz*ZsO**HTY9+QPOeZi7G| zFgrBL1pu-m$aE%(79lMgTuN;2&RUAsJiT&L1&_uDsd*^Tz5MfEmcC0Qr-_Lo`5}BtOVlBkf-EMEJ?j8%n}{b%Ee5Lb zyxqLhGaWlxx4Mf>_H-%!c%mqU_aU64 zJG7zkJi_uy?iP2+;N|f_SyWlIS>kQ_UXELR->sBPFyn|8 z|G8oRLt3aQyUtsR+mQS${)~g0O}54$cO~pK=Ed|W2$Fx2|Ea|d2!%g++}EqMyj6!#VJCai`m3%z0LIltS^_Bz_o!ho z0jS1e<`#3gxD%v%$6IG@eC{ZIc`Go+8%{k~jwZXm*LdoIMxJ~F|ZrgOCl8NkBz)oew~TDft1`o5}4#E zhR5E2yIT&)C-c=J&Jcap$LXqP+x3u1k)QS)Q1z0jjV7S6DGx zL6PXc@)hc2Tdjg=KQDMm6G(y?F``nL*(BnS|&bPZ-uTR7_>k88Ot z1?*kG$)UAxd9P)$E)R&IQjsLEz6&12idsLQB}ZhfgUP$Ud8zm<5hUX(D(z zT>b9S{_}N|s|^^{Bo+Kce1zlKsPzKxVVa*C2uB|8uC*UE<7iRh@QBhDjV;+1&tvnV zL+C_o&Y&}V_L(j~t`Pp-#ZlQxbop#C|CRkg9v8{!OC}pOquHJ3Q#~C1`utXf?(5jr zh?S-F(t$~HT2I?DPcnC67sgB62JcYtql7aGU8j0<2%PZwaX}q5g}KrqQNr>@I8P-d zzRX0DsPfrxGxDJEr4KRHVCpQ~_8`o8*Lg%)%lpHhSvFE&l~$C69;Y^G!vYHrOJD6c zNC(fb3e`Ae#GSCPh4I-*8K4d;SfbVxCPm{y)@59A!($sDXda$t+F9cv>z#fgEyx7S zkeUhIXRg2Pc@HZ4>|yn$^O|zN@NS&`#pi@y`#jZS^tIdx3XBCeO*$^7aYCLH_8t~( zB1Z$e&RbMtR%v@pYg`!}mq=6pIW9S=-}@bFVAKCiD&mYBph!pYqcZ;l`uJgz&Yl)S zm^p3C;DD5R3F%G5hTB)`cn1qo;sXx!J2?rn-wLAf?k*}goe6)lFq0_y;pDi;slQe5 zTLu0`MQ-ru3=FfJ4>bJ|wLzxe>$4vsH|j%Zf3ujOLcKC58yv^hJGkT0Z!K{kJR5LGj!t|#JkFfI1$4UjA=SdNH0r>}QFFQC>)G@duQ z-d!a6EdS;&Et{CEuy$2eTYu?6O(ZHBf*;_zcp%cWB&Yb;gXP=w#K z#&~Q{xJ0*w=*7GgkLTUlXw5LMg_oHbzGyQ3GDg?}MhOf>W@m#Pw=tfiW>h8m(6`zD z9M7E5M|}PVDmF4#D)iY6aSnhz-Pg6f%mNjtk~h%XXOmz=fPuxw8zy*7Wz!=6eAcPV zc@3xuajn*w<%yA@)V^<#)eByIm5HPv^`Z`{d4`h?n;IlaZK~YkB4)NDL-k&ht|gV7 zo}eP&@Q9C#sI2#DzH;j+^N8ok01X%fQ+*fH$vy|+9LL!@t2N-hqtrp!{!m7FeMHQC zldVX84WDUQ#7e;?+9p5kw$l*@<#6u?M&g}#g*_tt671K65)_p8^Qt!6)nojFbqzwT z3LS)ihJDqyaa}wvVXVYvX6dql%Y1!8EW#JoBdN#ZdFOaT7tF65Laj%89m~S19oS?% z|5KwM0?r(rD8fX{7>WZ$KZVU__;Ld}Pk2?hUZ`kdmrI5!gtOwo~X0qsM5F z2H2;jh0FP>QBJasz3uVcAfgcdG3kxF-_ta05T~#uKh6-50Miz+B5vtNw2VvG83xke z=rKc~ULi7Zu%+R4&wDZe$X?(46pwUhd2)MG;4|X>rcE|vdL`8h<>bVWR~*6`jsR;E z`05jAlfoi;jczPUEP_rPLUgjC1LiHn`tLO?UoRBfRe zM?1OFW$EEyEqM^Q+FIyI95H!FV)Y-L@(SGX%@?I#)Qve0?d9aXNvXhTf=98OEgG5z zf$V^6B~zt1pqt~t+R%=aBzKd6hp@4wFi%jBYA?J}hXs~0NpCYoEr1wKU&&m&qP6Y5 zaQkG7xY&O+T{>2&7rJuo zoPtH$!Fo#We{R%0={-@?od}BbQbR11QrllQf)!qnKzScKuvy*lebB2lH;I{kU-xz$x7KzeL_`vLp&kw+Dlj+-(h zCSkUEKwGQcii0- zGTnc}JE(CdP?s!U(6>3zI#; z)!8$YU^b?r3`VlX+gqI}k)B@)bm(ISZXKD(C%)6-p7+Qerd&g>!q%T|nRoyPXJr@c z8^*28bsfO|iXaBRVqif@rq*_kXWdf4yW*vrE1j!1=KZCrZe4dWaPKeN0E}NWK7yBF zKrh}Gy9-fyB>T4q`|ZJgd$8Xg?6(K|?ZJL~u>XSxD`EaQ_G{>02O!vDbze<-5?y^B Q_!|ylXYGipvh+*(4_Os-!2kdN literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg b/test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg new file mode 100644 index 0000000000000000000000000000000000000000..73e6260ef7126ccb11113179c58cf6611d3f3e9d GIT binary patch literal 144210 zcmbTddpy&9{QtkM4mwFHL}gd0Tv3)p6n2#džOVOJDoNkR^>T_xnWONE@;Rbq*4 zIn0@rb2*>3a?BiOIgBl{!+XD%>+?JP@%`ib`wh3fUNvRhT{&|%F#j%c0KH#lWzblU9v1#^pwmR5Gx>>be89Wm}V zZ+Up$zT@TRAMh|RC^#hiX+&hyv*?)TFJ2}kr@Tr{dz!pljPw6`{4d9)2*&l> zx^-*U$*qoS&2NF=XRYG8_4{=<>^OTx?uPHq11BDD+;uMTO~L0)8hTe3l-wS4ZB{;b za`X^yb!h*M?0;`yPyT<6?EfCv|1++B*w(dcz~rq}gdt&#orEzV+B#P%5TW(|mBE@8 zS=JvGIDFd7SL`O?FBfPEX_wMR7@O`>;h^bu&`Yma)=B7aXdi*y(i>1J>Z1 zz1CTd`n94v@k@%hY{oRAvM!yOmqt`BfRxOCOs>p8Il6b?qs|=`>mHp9NH~CHPDbQ; z^oD7};Qa5Vbo$NjC2*@cyYwj$^JG%CMwAih&>(lyF~Q^4J92YNnA906$eL`kJ?7NQ}nL{3T+omFSO1Ea7EQ zTxSKZ_o0e>q!F98vd4Y}Z_XsjVCpFQd==<$452>{V<>~2^J#qB!-5X~DQ4yFd#eRL zee{YtiA-pEFG8mWeJ;wShuB0ZXK`FT*>#y$7;1}uAVP9Qo_nmJ9n&W9 z%!KlIG!>J9Ipm+Chwh$Od4VOo_sRCdsGROWTfQf}_nP$IDTD1KQ$tIoUujv&Au`yg z_2#Os@77RmP0}QEXm@!CKh-F{CTU7t6SNJ(xF_%PXyRhW>>q?OJe}z*9d#x>hDJp! zC*R&{li2yZPVQ*O%Fa&{l8X-unY5?d{?aG1!y~a2FX8dY4K#~gj8P*ITt6~X7};cb z$PY6$qSSpcnCjz<)DNArk@(FmO+d{zhKc+en0`%32(7!2T25EWd&|4auoC^mE|_}O zO;~+vk-_{siFsk#&v-s|0_ud^Fm+EuCW={JIL~gLW)oIaq!Zp7*?RnZAH@<+ipwsx z-u3<^4?ovFmsH^|PlOQlM0X_ne8@H8vX_B=BQZL9+Eot8Lr~{!!5Z~*R(XOPt7iq@ zXeg(h(Q};|Bv{^mA%oq@hIY1A-Pe1h$$URRlUR2b8{=d!cwv}Vc7+U9Uyd8j>gj^N zV8$FRT5q+iu@!XA;r8RJ;G4EC_w0f5Gvz61e`AsA6+!kBLfKtWTmx1~Y5dOTT4vz0vYu>_Lx!1B3foKu+rhynMZ%q} z1SUtDhCW@B!L}I3_y{@#l!3KS_`bhdAUZ^^{EN`}ye5WJ-!aj;NBXllz*qcOkiQ9~ z-|3Yl-OVzU6y%Z`Z^dA14)vs~z~e&>zc_r zt70#$&;vxQ!1umnQuh5ccv~KgSy_#WUCXB-R3#W(t5hyRMXY?-m8|&zE=-0~?vGD` zMaQ~^`-^Am6(+Jhg zP~0eXW#ZA@U(V`qK~l%`F%Y}jz4SuaR?`b6~~l7y`jt`!3FzW4P9%7`&Y-nERpCt&zbJ9|=GRiZ(OYB%peJ))q5GEEI3AM+2VT9(2^r%(Z>3wB0nS-s6MBo?zDb zFY-9mq6t?K#LfF#!gcP5PdV|1dUr>O#&{TZr)g-3b=%&C&kc!6af6AN0Uh{z6V~;pYpC zPcf_NZkKqzS*ZI?@E*1?SPBO63urDg7O z>xgW#FL()mE@@w;+~P8LGdP=V=!N{YK*+?#w@cW?|27eNawM^}zvPgTMLk*X7$&0N zcTpd5`_ch)fX`D2VISg`LsgeTWV?QlDV_UO2Gc?lg`s2iQC!!eJ>%xu?Rk3|5xpW? zC6dmL4@>5UKj^(Tx%rk*|8>orw)<7pUiBh(lrU|8_$wlyZq#tbEFA<-ABg;}UROk6)ZhT~SS>VDG9MhJlyrJh4{MWLdDWHNJb7i3Dpk

8#fXvU&U@TnHe66M)}` zPihkqpmSSX)QjqK`FYs|D@+$?3~86#haCISci1Q|Ew!!T>b;~6WHd?7NmwXQM$~AR zFObbm`)bL>JHsaVEi05Vn+zPCa84vYxeaV;ep ziq`CYyU?+QTY>5EG~M@LEH^~?=A?jY=D_Yo))rL^SKs!M!FW^f(d($O$1|PKhW#>_ z(~EBge@V2}H8Rga^sJoHIe;E*;h7g8`Vq`kW8=oq8Dj1L$6Z+li*PjYl0OscYxkPI zGHEoTsIffw+PF*&*F0{zKLgX`>KNRbG=o&MJ?2i+DVsct^?oda`H`myr+f#%AIHRY z&KvJyOtUMU?3U2`eq_sF7pOIz&;nC6fg*yxU-&Tb>FRzKW2^y68duSP4$Dbr1kxZG z>AP7Bcq>3XjrLQ||%ThDBF2{!wV>uTo@nj~N* ztY-ZHLIx{!taBnX+d=DHey$ydTa6yu5O5X}1b$TbCrmqQ-$K?W5QIV`?Qr&=U?G9E zj}Jk=3AKbzcS>;`D64ML!q_z4rvbZYkh>^}Z_|K`;}2&rI5JrL87uhoIZ_Ty6gcLZ z|IqxKePGmzdaRo-PKbUL^ls`Jp|Sd02V{cWyg0IR?@S?HfBecYId?h32O+fmx2nBo z^ch&AVNJ|`S*BvCS4W*92=@a-3Yx`WGMIVt(DIdFi^bFL8wXEC@Cm7!kqlVR9by(k3NH8Ek*a`xdlV z31#j()eR=%(kWr$z^Os6C7KeEpF+E+F%GgQLq$gi&T$?zm98WiuPA#zVpqJ4oSNZuUo@xm1u z%)BA6#6r54rF@g7w1~_KvH^5q*11I39XIqv@ZlF1%tl|HD+12B8s>H87MLnGR=M@9 zMWo(KpI59E$kW6J(2kD_Gerjb9h`5r$f*!rm`MP5f6GLLPlkk^l2}R_7FHTR2!@>0 zap9c_`VQ2YZ;RTKdfCAUGeM1N(tCaBBzB*C@O*sAq`T|W(`rAe?p5j1PW}Fna?n&a zenI{@<+e`~sNRh-cv2&YOs>Y;bfiUy;aZ!B|JPhk6l?Bj6-8e1lz8q}uz(HXW=*WlA_(5xG$ zPRo>D`9Xp209dKuWI>VycN8RnTQ8POOZUe=yO}Wj!}V4h27$7SY09J5kev*EGGp;vHedC}tQ)btn0 zsUIL*_Kzx?V@{X8Msuox2RS>nCzvZH9;$uh|0^lHFvmAlCH|^pp`<+)L!R zDq$DmBSGB!V@7k1X{1`Al$V>aKA)~y4!2rAKflcR1fv@#nCcM-1mQ@lmV|4Jiw4Om8~y3E!y0-bGVB49CRb; zTj9QJpTRC}O)i_&FIWYaTL4^6W7zra1N_wk>Rx1RM|?X==Wy&IHg)=V26@!6A4Jm& z`uN^ST|QW;+kNl$HKBy$Zg>69Z7X{-Jf|dH)UpR0yA5@aKK8oNP3qT^*cul$?O zPMYc)*3f7EF%7L)r0j_|M^ZupvL>FFW2zMA?~kwCwh4Yq7q-9cvpNX&Z#6?)OLN^i zbIg7eE6n&TP`cBP)aZ^gD%c{yEuHDAjWOq1h1gwZO=O~GHvW7z*Y5sX(FFFNKJkkr zrK=adlCr~;7bX3Z7bYsmO44cEur(NYsH%ZPTyLWt?w>^53G7l03)z10Xza!HM)>|mM zGaYLuC{9x=drLsJ-M@}XbY|O&HcuI^#{FGXA^;r>!;1O3Su&*qc6*q`WP%+Cf5h-> z{S+^`<;f3pzZSMBhQ{0d_ma!VQyHw6(Oq_k>oWC}C^|#(D*M^-rddUN*T!;>ys&SR zlk})mt9LeB*r#b!jK@A+Pf7K(xQiuH0#(KW%0%e(?+g%8;=XB>wFWKV)S#4o zQSVzb)_6wSa&}ZgrK;IA*Q8oWG|5vZt{n%pT$8k(bUIPg^QdSt&pPvp z;7p79u?EZG)H@AjWP%bN7t5@YqNXJ!2DNss7W;U++1;m?HW@vyKE*!U@Gf8y*TNe5 zi!gK~1m0TZM-8|Vq8{}hmFV|5l19`fLzR(T^8U%5%d^+r=`W=UeP!Mce*B?`YmzEL zNfT&oh$MskM!UvJ@Wygnu@8_vj34&jlk+ZH)bq&I@sEdYhf7uQNpFH1;z(9mZx#3f zB75%e(RZS2_RI=_#rIK{LK#dPkYPiTJdF|yM$RtFb(ft1K(WH4(a zhG@IH5S4wsyJ`N*GR^gjM&5?%8M!b|kfR6-YKx2=gyyR#N=P5s%;Y`NU*vZLjqf>> zy6aer72?8=`lNwxgj_g=ZEUp6E%_*e*}2+2f-D9WO2ri`kvW71fT4pzfk42^d)ucl z$Jjh6F(t5rdKV|4I;459s)R9$R)7u$)`vz`EjMb%MvBmHWH5zMSHhUS40h2cJNWxr zDL*B#rx7Q80^K66i#sc1U6&*})1Js+V`=gdia1(0F=1&67I7U*tDt<47&vxt3i&I z!Ny(VY)-NbL=HOd{qE!3QHJq(J1-lR>Cdr*s9HZ&LeW{J-Jv=E<{b=;%tIgewlZFP zW@J5l5FO@0=xPg=!JZK3qPi=&cCmiWsZT1f8X}blxcFe0>eTR2)$$iyOD_iRC^q{{ z!`f2{g4|;&&>?q@YwJnt@z7lwp|zzQOGXzX5$cukn(6DQhc+M%ideX;rJF!%k;5w7 zaj@<^iP!g_P68Z>=q~HC2~;LW&@$WlW~cGlb>dqCAAOz#IFFIeUQqDZ6=>V3as&IPBC8 z?&~f<{I&yjs>MFbI2B8{=9p8(KQh>P(A&A%b1pjteL>j$MAYMQTGX@}i=w{3E02h-6rp5wGdBlef6A(9$ zF)ipigLppGXSWxJYVPe?Ub;J^-}+jhoBMpjKNhQ_=DK+2jiL0vY}2t|Rs=bzh2bo3 zcc8Q${(2qPxez>*7c(cQi&dts{p=0)y|2q;jjvnZ7@IMi3nwoOlvui~6#dhSl31mc zbulZq%=za~k#DG9hZLIqn%W%HqY5Mjh6{Y`yc(BiHTEBvfXx!b!eTQg8S__> zt-&6N_M#r${cn1v!l~bAIL*xrOqPe=aa!PSl&7fPB|S9MpOSX!0K7t}F4S5EyRt9P zS7B)LC>|uuT&vNq^v2Sey)HRs`r~oPH46Mb5i9S)Cz{p*;4@o{z}`@)=QZvRgvM9E zCdv%E7rYUJw*^&48OQDhvC?k{@;l_IJN4&pBz=qyhHJ(xmqiz4{|!m(;X8GvKj&R8 z6?43_bBT^ z67K*cN zT6bC-U>r4%I-`_ck&c32b&Q=P3tgQ$ak)0J}Z8W=&n}( zyvT9&$?*-LPpi4lC2H$a^ah*BEgPy@&>E+s-@m~MGtUb0c5aE^N05O%sz+75H z?j!P-1o_@uSGmIpZ?d}=saD(7uK!03{a0R)ggEh&b%{-2OKnTBM$t7%UmJB#NO5Fl zx^8KAdA9RuDbC3&BK6U?mP~hpyJI4U`EZ3+LAzYrTLrJ_P-Q}pAo&=c?KyDkYK^BQ zPzh8d7m-2qHZKdcN31LCvrGG_XnVt+*9D)?bVkoiwIow~ELQfiq8Q@`lDdMTqR|}Z zkR(tV?rzR?pbCfV-tyQ zAwtW!umF7QlxknR+mSUYfcmRdA50VwDGnVXOVPEI_)ntV%_9t?)ilm*pwW6c`CvCm zrA|7-kJ>}AllCv|(=P}?4%1Mmv~J`wuBmaF-Qg1nKdF6wYQ9IRhRwb?9qL1=K1=AD z6ge0q99OU4RQ7n<+-ix-k))nP^7sNA200yV|1X?#%ns)%Rl3t6Ow_mirq$qbsK!Hd zXNz6vTnxk;8sNdQ-$gyUa14J7iz&o=@_)%c82wR| zfM8!)1&#&3Lm{W?>tZi*+wZPCuUTFT&02RtPGjDF{jr^V;syo2p3^T2>3;6c&w0>O zoJX?^mVHq>T_t}e1Wvi>I=6PK1r^SJC_<+V=^zMd{LK5%I4jeICh{3`jdW!^AV#t0 zAMH+Ho!|W!pY(7!-AF4Z7NmPFKQC~EH+9I}4k%fA+>dkL6vP3L#h#l;wi{y}9qj!T zQmXA&5WYf)`-b|%Mrw7$W z^nI-h#`IFeEDdk`T|aRQE)=0C-a!}L2Bq4a0Lq%nMerj@;z8?Elm7DEANA)eo!YKr zrdPBl3yI6&f}XV-XV^aN>goh z=Qu|ts2EUOLN&*gz6b{iZ**yyU-*qDA7xxBFZ{yLXhz7*-vNM(Az=c(Nd*RQK`s!7 zQ|bwaI)qVn?|2VLR$q93G`=0N>gy%~c~~1!KeDKZwCX3!iXR;3UHUEsn+liHI@q~%*PjKg8ek%UEE8l=!C&$|pxjQ}$605bHkl!vBA{s%ge z|7P&?uoV9Z>>NB;u-Axg0TnAKAXuPVlG*Kn+rnN2Y6VR7ue9z}LtE7PmmexvfrwOYB zB5%xA60yorY|o{`D#Ez#e>}c(Gg5tz`Tu+GaTDE-r5*ignOgG5@D)e-$~_%|+VqtI zGRx%m2G*EUWdXUbd&d7(|0AsVouGqLXw1Cvd0#hNhh+N*&j@s^z7^08RMcptifqkv zoS#%Kc^(OFe<^5sO=k@*Wt%+Ee1dyhGA(&=Dlk`WJY>1f4@e;rAcg$I#R~5e*k={Q zjg_{2E5GhC`ri?*E-)m~3nQc0L?U&sIRit^$?Vx&#NPSrWuJ*^sU`O86YS3;qSxwo z5JJ1Sux-zE7dMaAXsmL^T+iI*QPTr)^$e@U#dzQ6E9t(GsRvnaVt*(?8QQ%e=oH~P z0y##o;Rjx+FB5k)ZPA#z2ApSal2V(6b+7*X@EU&Ufb1BDrK#PH7E8?9t+^kRqa!K1BmFKo;9FkQB|kqUMNA8OZ7AhM6&I{96T5o<_CT1{fyQ1>cp?2ZKQRlP@0vo1>7)o2WzELwa^ z|FDewt^Vt>nLnfx5qR2c@|seeqwjs~!Y=Ic7kh=#*TX1n$KZ6T-utWPknCW2q$(r( zDWU$C)?|9|ArME>pO8Zm349*j(_!ccvHNvzlQ@T=moPQo;5^ZAv5waLZ`|Y+2Pa`o z5??=TKx6WcnqbS7*Jfj*@qbEjr0%j_HxcUs59WRaiJ9d0#0nY1w4wyw3cRJfF|``S z!;{e;GMh)NKHsW4<~@@6b;PPU_5LoFJsq*DoLjy}vBdiY#|81EWBzrybztij7j(G7 z{NN*p-Haa{oubxcbnET3;X~8(FJ4msGqCI{i@DTO*FiBFxE;=nvcu$di0QbH-KP8C zyc1T_#y0h_{Cov@qA-=>Bg~4zX8U(fy<&tgT2_KeJ-6#77 zC-k0(bvCs8iBI-l*S9KDKWtm@%#DpNI63m-a4DQ4Z%rZb19p|vEze%z#)}g%(qeb} z#UEa~{3E4fVOo>V_1;7Jq<5$0_i!*6#ObIp+nhF%{*z_$1wzo=&Sw$F4oDlaj9C+X z0U4xJspVPBOLStL)5{-u&7 zNwEFqei^%*S5`Pd*v$5BM6jeYv-w7L{JbW~#zmozrDbpiss2r-zA1fTiV{MqPXsDQ zF;EPvJ_^WZhhL&Pf+{cdvt4x*n1yv9Ti24`N{B`DGMxPM8PLa9I>80{^}kIG=*@R!(h$kVH7(p4lr6>)6U@0B;oxv?%F+j+&N{j&N)8KJZ^`8z8A?wbFaD* zDDOvCWmJq8#<(u3*j1gwA(Te4Th%!l`5t{WNyHqg3Xf7*0L6e{`_UOO z=IXEm`?JT@bC|6GeYaWJ*u7Ynw!W{c z5#}X`61@?8IECcy;iB&%`xGdTt^q-(Xp6+*ZtbaaeI3J8P-m&oKKp9;BVDPXQ^fKy z!4((%Z(}PkcorhMk;k$QgkBlB|MP} z--eQ0{0l^c$}7vbXTpaB_61P%`3sK`?j0f&7m11@k>gjQz|N4wA$OGgu4G*|TVX|K zzlgotr?AyviuUF+D7=b0QrgIOXB7Eo*290Ns<5}O$aPm1gUyI~hV&29Zu-I)o|tZ* z(|!2mUG-KU7@vrUPRXtzpWNcIia^iv!0Nj(`LyggquZeQDTBz?({wsUXBqh8TD zw{P~??FP4+s&C)96Z$NxvqrU8qAl+P9S&Nia1+y!wS|;- z|F%nv%f}o;UfZ*6bvl@&(a}_bnrC!b&5f(V+a4jHP?{1ob_{!>H1!*%DW-hmc<<%I z%H+R8CyVlqbqp3xKdAj9JAbbenVElQZ<5H~+wa*H;hQ(x~AkQ@Evijf^7&ON8Lv)Us|E+EZc2#?=Zu&^=vnUD5jr>V3KzIe5|Dt~eOxfdqrYt*(&9PQc zn+(>|u#%}ed+EiTn(wU5i@8(Xj5Cqb($`LUr`M)7xcYt;IRw0EYrA9F1-QhJ-eO48 zm{>rh>ES&SL;r+xR+g!5pV+?;%XzuNzmYveX1TRqY?%x;O#4Xu&bL^E0&=|VB-q^E z!IB?1Q_lTZ^xnA*|1w-Y(z+`hrp%n%ukQ2u9*v_d+yKQcLbk7ix46KC`~)b<(^Bnp zC5fs?9XhK$xhu?OWCwAG;;Z(h@wH%3_(FP_ssO|SgtOzlvfCtd1Af-3CK4SP8E}X) zqI93j&qrEU{>?b}ZEG zWciNTp8Rf_REPE%1zUValgOcSCJebv2CE5u)pnuAUex=rEfpm$PJ8i}CNtG*q#7uj zITp5BScW(fQ!4V5Y_2BQ7=4Td9?;5%pb7w=M;F|YGf*T{x^6=XalImS`9PX|u#(S) zztTC*0`gZNUmxmhJt5W32#*A{*vPa4foT~qM2FfaA|Y+a9O+tWU9@-t43FIeFoXES)mZ^g9*A; zL-{bp5zgn-(6P1d(sjMRusj(||2~sR@i~vL0~Xz*s>;*{7pdBp;T&ZA!!()>E}=I{ zWHIJiveW9eP<{V$-j|h3hgJEbg`cNlVbEb;>?8bz&2}WE2U39;u}c)=BaP8+p48aQ z0H?bUUr^%n{i+)Z%5?s#Ri=WGI^%Zu!@NV2euNz<&>?LB?Z`bk7r6rs9oizG9mW@b z9Qho*;Cv&;8+krpd))OpAm+Ryr9#EuqC{JfbKM+lFhrl@pZSAx8c%vN@SVh86*~f- zHHrKHltiS;U}piPqzWVaLPqA%={7i|9g|(!7V&Q&L!t3x)-_`w#=oq&`U z9nD{rhrqoXFsoP+*?7#M^u`QMrLIo7#p!Z@{zw9JFz$r+@t28BLGL|QOzB-gVGjpr z-R1K;m);|o6)uXtcTJP}hz~y@&Ibjr3oh#KXzj`m^OK&X#54m*zgKOgH4jip_7r^X zIp1g+*+;+KFYV2r#*aBf&<3rYCF_J9}9#QVP1hKE$1Aqec7g7WY8jZF}x zZ$0VtRx1w-n~5!l+?xHg!*H&85zh+-AzlrguMeo?wZ6DO{{WT`%vTYoM2*y#^{e*d zY`>m3t6s}T@6&j6!+n9_3hkyz)4#KOS((D)&;sC+>!7W3#O0IpMFdH_mhZVQ-%l?L z=VOf(CM#s$@XBZ(!A$#SgAjIGgtmA2ov5wl{7qC7H!9d4TaKq!j)sfFuUAyC7ct)! z3R;i3*qg2mM=bko2}wrBqj=QXLH0>15^}R=Igy4A1@9IF)r%#G{q|utd04@@@_97{ zVTy^*chV3rc910DX*Fn_o_$NEMM%~kBq3@aNS{tuK0^g~y8hIOv;@Uy6)x5lq-bTI z5l@@!IOitEn$^k(aTJpVxXltE$!qQZ`Yt<#sJB$cC=wbr-_LQKA4P&%3jx$xHkxP; z8v|@PAV`t=0T^u(e-HpB5&yrC1SJWV!GQB)4fIOh-*pvn2xHgP;c$*y#fcGME}rU} z3YqF$*%txSN^_s=TaE~LJg!~uE$rAZEo0ConDhpXodtG82QJv`hz7#q7JYIc zSl;^Sz%0H&O=N74kjgV^rA5|@^uF9{npdNIlfm+oF#H>B*9%A2KiUNbIU+A{@ZpSU z^+kWs(P?6q`qu_9$2LGc8p!S{^sI+Zsgh(XL~D4}P8 z%9H!5{gk&bwm{o*2oO$6>#v{|=z_wy`>N=H&K~qtr5P-MyosV&mdjvQ@kxCk@>X>l z&#mv#B;^?%^Dgouh79(+P}bl$L;2sUG=?j@O?3Yv_g9PNlb^2u%@JDC%a>l-A?#zQ zac-#=O0-`-Z2Sm3>&M$tM(v_|5;Ki(lbHcC!N?dx#*KLgV=X=3%Rzb2xnmBY2lN7B zIAYK7f6#5-(y{UgQ>%b7>1eJ7cBsd@@s`(k2K60&`6*HBaXNe3?^p_^ix4I4&tseJ z$5I?SWiZfT66Q$3CqYrvi}gn51G4KD?RalHAdqSc;3Vp1CC`lTM5gqT^$>@o-x%_M z;9|Q}Cub0C5nom$IZ~sHoX)l7btq#a&NGHtt@AHx%uY#rga0*hcNiVFH-rQ(^vS4v z;*@j}xyR=2Z3Oh3f%*Ek_%EMUYyb+9nzHY{jYsDKj4%72j~3Yxe+U&Vd!nC6=93+PuqKWu4e&? z=w$dk#NESI;HLfLwHkuK$7||khrhdse5N5iF2yC%k>IR{`zU;Z?EbP$Jv|cL!^OJn z%bF%m8ZTdB<}IDRb|yOOnzIw4JN;guZmuAp;jL7=dLQ4t&FWoMT#La9L2XvDi_i@+ zv{JgSkQMynMS2mRdI`0lIUQnrdj8cF!D-YEg~6Dmq1(V7X%eFOtZ8n2aAOIf3(j>` zb*fQ<-)VH6wG=5;9YwPL()Mf^vgj_X(qeFG=~dXq{&_O+0c}CV7h;I5ql1&?S*qsm zd}?|p;FTdQv+&JHlW6^cAT)HA$fs)3AHG%s-tHG1OwUXh;y(~cb-HG($;>M987>tU zdr@koUS|$VxnbJD_Z)PvvvA+N$6{$hrALRK`H?RGWu({}N?J8acy?~{Zzc>Wje>(w z#FFWYCnT}G{kY27vY{-;gfgp6O?R%DD_7SAU*JhT1){~_( z2Jf=(6(_CuO3jNcG9@LjaY$sVib+DACusPZ)ez9V z0@`LGf9+LA;7^qBUn>KRW3!VjVoJoyG{K?y7C+0df$PJeitSz&ClZ}s=ODY0YBRJ1 zPyJTlx!2&)+~pUteiSi69^gGKvUpw$s{(}NQ8l`|jE>E=Z8~n^XDuZ5iVTszt|Jhg zQz(@@0hMua!inP?w<;J`wC$Eq_~ZmgadO=DTXIz^Dp~_NIL39oD8;ex2Ni@bfSL53 zQT_tsGf~hh#Ge2M%;IWA=I?+gz;5$MT^`Ok3q4j4(>J8~YDjUz(*cNw{!*k_Ng;CD zg5w%MqxvWSgHE!hbgK+j>AbBhwE~G5&8g<7lNH56FU^1-5d+ywWK2+*e)*5C_~T*P z3Jsyo=xwT?PLhO86q#!oSDi8I#L6$$Rz5-s?CXS{^D%Sa6yJv?z2=*=gA;F{#UZwl zZpekJ3j>6Dju&=Ejs9Bus*|gl^YXux$e2Z`!q+0JjPJmYsydSl?qCVie#N`q%82j> z2_JMacz?Fmaj@{DJhzEye0+WXM0D^Q(e{Qi%I(u>EnShvnDpPpZ7#1m63iVPUWHs7 zlU&qW1&{dO-M-_KT7`dmUIE8vwZ9q)rP_DD46NT3oN;Q;)b8lu6m3l>c$pV7zuHH7 zmiD>@IjksB_l39!v-H?3hln?=cN{rg+;crr40?$9O;Wm9%J*@S!9M6W3juksKr>gO z{et(`idvO3C}0&gi*$ZRw_N#ruTLpGz~LOFoztj+(y^va3nD6=a_6 z^v5E`7KRX&l4U6UbMhg$Vc{K7n2hKycVf2A3aF))@KqZKF1mZ;7+CP9$ytWpcirYN zJqvGIZ5&0yhQXvqwHCT20jM9w`39+!s_y}1y}10pp$Y95gq4kGdpXfd5jr(q=oU6? zB^A}=^rcFh^AWoNXBy=_XrA0$%Ogs{U)P{vi~&-v;JX<3euKf!;-1gq{usC*R*-v7 z24kirtTt)+1Vu?aB5uY%5qbgdE|h@Xvma4Q=Rf|h8w&Ku{rmaUXo+SI3%ig>nAh0I zrv)vLC4V1Xb$!VFpCt189t5bzOz9Dqv1yXjWxOhZ!TKL<1SJW=>qm`_rl$??(2^h` z#Xac0M+ghigAjM*x9fL|E}rH@%_(#jQJR#<2+<3EzA#E@Nr)Eb()9Qk?s6yI+kIo#N?Uy*_ox+PF+Z!W9QN)mBPo;&UDcR(@ri!dy2+VxSBkROf=)S1gP+)= zmuXUy)aJDomp08GOIH@5ksqimrKj|ZD@SdMe@KW{3!55Wz?P}g{z{*PyT&d(eZ+dm z@JH8sdVLH3k3P^B2myGDQ)70qT8#_V9_|>((6AsMxz@l_pT8TJff43-LT3A*rA>(X zOOh`>sV<05(3*QYsq2g+8cFg#0RT3z-dPWOb~E zhwvqS6i;_IgW%a_>?`6VQE&hDQ6SxDLX8iuVi%~;h4{-J{g}mS_t^QyYBV@%6Yt=@ z2GE3%1Q)tR+G6S+Uwj#=$n1F9?)_p$3$>5uemUqO@BwM|UK=(ULrMN1i3h_}yGJDM zeaKqkNg@SXsOt4*-<>;k2+Pom61BAI^U*queYwS_8oH%-DxQq$UFzDSVkw|P??t<* zRtpxnce0GdcH0T{v!+Qp&rkgQUmM)+wG?n`z7IR($@lmT{*w=1POLh81u04qUH+dZ zbc5L#F?U&Dx+!G$=^Jev3Ma^pPbF9y)#!KHEGu-^#OifdnJ6TFaargAL_&DBR{DDy z`v?0baDa{RQsaGf2^1_P>_NZjGw5~)zjY=gb6;wUS~Z1w^rhHd-f*Jr4z5nR2A?!Y z#LB7PJ%%3lbBLF4s~3#gzKhT#415Y~^W8D?8qZ)U)*>Tp6% zFDiua=eN^DCc*d;LVzZ->gSu0&0$k}I7T4~3;)VsHWcLUt!wxn)OTU|6`efd5ZsEN z#Y32}k6$UVK-z^PLwgH|Lnap_9^|L^39cbm* zC~|lyB3aap%##jZ=}VuJdTFm>$dUd6F;ftdI4LTu`62{*02Ayj|3>gmN&G*k}c2#Y5m;Jm#lIph&##`>;(r z3>+@w8L1wfZm8Nq$=OaO<=<9P#4WB={$Z0a>M_8G2Oj1mAW(G&q0+OQC!#j?bq86b zM`rDsJ?GQR9-SW96XojpS4g*KngYg*$v_~n4x|Z@Qm@3)s0>AUP=S<$ z#Yj&)Sw2ELNci1XW3t1GF|W=T^@Sc^ph*ZFg6xIzwunQet4$iH1*kKcr8x<&j=}|< zl)$zYv{7E1EuP-x?C0Ot!Ckuy$}o>k8dx_AFR|=3Cw=Z@V2Ih)8J~cl9c2U7{eFJ# zoYNCqeXoS$j;Tzz9`>T zlR$aIb$?5%)N%!~$Tv7&4`8O_r9slF2-EgPgx36!b5DU;eg}cKpk(NTn(QoIcs|<` z)m<)w0TOHaOnp(er4)tD_QUJ2Ll!?KtR&g-2OAzNNR`IJ>-LsWXL_Xe_iI$d?8-cp zz`U$zsB7jvNnB_M-(?BpdV}|p;{Zdfhz+QCdyX+^8(N7DN!C>{%y=20^O+1DM5Z{A z{y`(zXHyxQhf9pk^79efw*sMn_x(YMmc`8Ax%reslhOW(&(_}_5O}@G*?QhI2|pst z%TmgJceK>%TGtnD`00N{EW5vS?=h?<^?frhh2cIFubOg#^GbGX92iby7?YYutHU~@%$qwHx=sgYi_Un8+!eO41!FgC5YstBXb;-L3v zZT4qz2{nf$wf9p$3a{|V4hjww4-(&Z*e`Y@P7#*63l&C?m_DDCJB}9fa7x%A*Qg~^ z#G76WLShX-Gy6yVtV>uLkc0{tLsWAyWGatC{w?t3m8hq@=?pT2q_|G1f=E$>&J=<| z-n-3w?W|Z0KGmzg4F2vu5Tc%PB<%*{t4QMcPC=MV?k-F15SfMH%DTXvB*2@!d)6i- zbz!PHK6knuULUUVz@8?Tk<~}o73X56)CjXlcU@Vf~b?*FXWX4DbnDm_iS2Ur#7S+RZG11-@|+ z$g1O|Sgy?l1yO^jceQ1q0$QvdlaBG+P*10F;XkWk`z$s!W zVFAhtwFk4O!F4p<3$LO0C=r_e5i!w`P4hN)9{1<~CTY`WZ(J49vw8eL%Db`*4H`Q~J*fkDA!q<3D67heO4^r)LQ7j#>Xppn16j5b*R0A+h91 zjcc{7!vZ1a8p}+&F6>Te9`x$Ya=3Iuv6EhXPBCTa$TX$YE>6s{V;|Amf!55V271i&g@rkrh2@Yy2&!sB!_N z7OF8^*#HVChz-138<(j9{4_1513lj*wW~ue>f}_Vj6Z|h&LfHE$0m%l_&6!dCsV(>VhW37xrPiR6}{-)e=vlzqhe~s zCL|7GKjXxSLvUH860_El0YRvjHGTFl@Q{fSZ_R{uuAJx+s8 znA?LZ$-S3p?dtcZugFrhD9>)!h1i%8lEnRFnt^z)bffmYHqdwBS)Q{bSbM$aF|XHG z6U*I%#hXVG3ZXX@Bt&&Snj%eB@ez?)${N#Ete@Ox+q$KZ|He#fNZ|U{4jM(MJjk;c zvM^O#fpkWKee}9qwA`uXUYhb;)+75)0ypRJjk-f_-&xTM&!j(~ZK3@ZLt^O<9O_64 z3U}Fsh)>B`<{=^a$4}O3%?aFYg;NX3=YF<@BmYIneW%t;H>jJ5sskm^#nPF(c;m~+ zOlsmGn(8?wL6}efdT9W>Kv}};*4rYjqjs&6vkP6WE92bB((G1cNE&&N$YxfetkNmj4377)1d zyk2^S=5y7$kBLqLVP2%oMttcU5i9`5PHNs#C{_MO=d}3es?4rB5%rgVzL0yhjGFQp zuL6LJ!8e)H%_M%8dn}Y14gTiK7Xd?!CPqn`wJ<9ZO+_w(52?{w8Mh)gGo%SAe#|+>ljXsX8ji z)ZF;yBMR<(_zZ}YR%WLz9_9|s6f7AlKx(Ya!rI@uppVg4c^@@vKDG*{ZeEWj)%K|s z_s$#Lv<(*EZLQ90Ny?FxE&WDM8MpeZ-OH`_39%6Q+9D;li!Wxz@{F2v%IGDHFRtL^ z#pmmf)B*{#UE{$yu7G!3X(j0xa-E88B2TZUSQK@%Mf^e2=A}GSC-_=lt{qLeXRZB2 zyJl~_Tr`Gdc}2Amy+S0eXXKN&5hihi^5(}l7~sZ`oWq_pW}imF@|JMuRCBEo(3h#IY4xp6u#B6b+z$2+AzFpgUE9M zw`x%0&1#T%E-gZ6H11cot|*-T{k)b`Oj?+QlL1uu&Gu>qcR_l{QNmqy=>1g5!Mez( z^cUn2F~`(oA|A}?dwNBcBr6oY7C;z!WlRh+9-CqRHRJQf!en>hvc`BNpZ;{RwzE85 zIO~5P#-65B@d>)hO;`zIRSi16m3Dn+KgJ!+q>@Xf z46VUWa58}27u@I|tRw`Bo!|yK*7Zb;8VzPW{`gJEf04O+SYuxrboV03LWGmcJ5R>P z?*w!M?+40!=2rM0&?2@z#q+KDu8b85*du(cJ4vV4eS#ke>pOqz#S*(Bz$?!)>&rZ$ zyxH85>>H~@tGewt!W#JE^$aic8mP(7_^lSbOb~Qpjt)|X$s#TBFc(4>r8xu-iI3iTFwiUxyF;hvk9IFMz5}OczAv0h_Xe-|e9!zCX*Twmyw?kgSEz)Jg2y0MtUe z@wj4?8DWd6KNCvrNOc6KWZohCdhKYf<3s9KO7nKt+g6nDIipscg>zfwY*0L2LXSqWc0<=0~9NRItL-*jkWZsa?0khE^ z-N_0cAk2F0vNEcj(N>T@N8;hR?1Sd!EikT7D;|YrnYCvAszL56{e6xT9b=`3tLxe{ z`mN6PM>m~oPoFhd&%URv9*UrMH7%Yv!389}Z)V9FRMdr^-jBBZa;6%~pe0tv0jV;w zpJn%?I#yfTd(dwqGBn@&ypuxzlJI-OEe_)q=leUvawsCs^ZhDJ@BIkG)f^*B3?xK= zxn8H9p)4XRdd3&b4b|IbK98m)B*uziiLKeA&$i zep!S&_Q!eN$ejOknk?pPTgb=*w1mnZj;3EjM?1i5qPhX>Nn~987+=*UXCe9ksqg0t z=A-$DR~b$dU$IwJR_C{ZY|29A=vDVxeIR@~F#X*!PGY6Q?`2J`k`czO82CrrwcH4L z>LSmPTGgT4^c4eDbJQu`Z3w=$w7JS8Ja4sW?pB!2m#n>x-(YfC;`jcGCMu*qH~6HE z_P5;V)1m7+qNf3{OexO4@s^98RL`;N2XlHNxXmheDm0Pp{2c6Fp!pw18((@`R>!>v z)YRwc#kUjWrFj(|_?d0-cL{P%c12HOjbP+s@5JZX`w1u5*Ft|%9kA=8I>~dk=-t1b z6JCTJOjv`@d~(BfjK>en9-}X9(GBq(j&AWWxLOqV+X%j#o~SQ91kcwabl+H$`U>f` z<|Yx=Q%K=?H9z$Un=Ujk3Ss;As!=BQVgydH%wjdoqiTPlUfZNexvq+t5pMaZFfQ5)nB5;=ho?M9*)s1bO(xe1|IH?5El!(*mvI5`kOelV^Xk~5rF)ccC zT+_7evh3|J6&&mDo*+_NWR#S9h^|aDQ*-+l z+rjdr&Tf$Ac3T0i&1=VghUyGK+p&S@=4{W0yR(=YI;bH*nZ50j-5^T^eu z)nK9UE6tubWgfYp9B!FGiLZ^0Iys8*TP^yF<+HI%+BeZVy_Fy^uISJ8TZj}9v-21r zboCi?SpW=F$lLooW$ikZX~XT8FMcW!J|l6@M4UV^`~XIktuB+QUPmUC{aJb>-`rUE zZAygBIR9Envd(14xWIRGRH=O4*xw)GIDQP8D|wm~2T@-jX}DbYjNkh7Ci!@rA*xunYFP zC=XiDzLg*avw0m>(MwNZ3SuAinjHw1&d7b)ccx5wb_adCKpc)qK164w6p!t<=DA*WghXImcoZ z`ZZr`L9bXPRvC3K^>KAqHfeWT;jS4hK2Wh~pu3zt8BwmA_=t7uoe4E6rSv4yaxB-6 zOKV0m-nqL75t}!8?X(+p4v3MYY$GZJrK;GZrbIB8p-kEX!6&5-53YVT7)NiV<)?e* z9b^csTeCXp^*k@uQ8Y^6+)6gAi$%Zfz0@@NS>pG)r?xQ(r{;>D<6vgAJovS4njON7 zEP|mf0{wDV!R)ACmjl6myO>fLTiaOzWAq-Y&LuTq{}Q+j|8zSE0!HuMMc7YR*C%6I z3#_D-o=?&V#|a$_j>UUjL-1Zp*M!>$-&Eyh*$E6S>K}< zX@b+X4O)>E^rK@L00)?tF(gaXPSdk(8wNC^O(}cAL%sWGd?>!pdJ*>3GlekAF$(i3 ztY)MQ9CD*mo>KW*staUcyhaOPEssq3j+6EPa&c6@1S1bJu8VW_HogB@@)yR>=`vuh z>N_^9W02=-5TmQ>Gw>vY%57Gj)eaxz^jJ2&XK2_3`Zs$|4J&c|E2(4+qhov}wgIGHe^*B~h>T7fK zOEf-Snxz^ZJIAL+JIcCO_!R$c@UUsNRyBGWZBOZ;u2NKA=wuAL?z&hqssF}8n`lB5 zey=IC0BhUJnjHP@!U^akiYs1>>Tb1KlQ+bFlaw?>(}_>gtcSCR${p_2V|}>5x(?f7 z{mi4G)4&Lt(a_9Ee3N%{HLWeBBIsA-R^U%paL&(8x8%-Jy2*0BRY*L;F~eW)dz{*i zdK-7WZOj9$k-5xHxp_C}!@zj35 zB`6=xL*_`wrEdobYYYpa@?$#MZ;a!qdHwX}@sG;Qo`;9=`y`iF9lx0=A-lPUp&Nu1 zzh78timfu!eiCy1Ho)L595}&&RY?3D@21{>jsfY*#qgLN3dRYU%QTTi8%T@3zid^z4NC4ab&^660+=>_a-!(%slRA zq1w$lh_=IWB{h0uHk2I5b-u<*uF`K*mh+k$fhJohyAgZ)lojK_~WjT zCIKPXi#BNEy%=u$iXfuqKNOSA@z2q`z)XvqlN9fOs;;Rty00QRJ*0PMfnCUWULZxr zP7aq5Mh%xA6T>rN+%rtzj&$qUww&gHSnL2$o&Uyus2MH6zi!~GP32DQ5vMRP!f24} zDR&FqO`b1+W(#U;D*&5|bLz)l*FVXd4Xf|yEqNqeb9Zjv`FRB5o1DVw?7}Y1x>OnG z_M37qWM9>(C7K)f^fL|a5IWR6r}Qk+da%PhH1#ZgL`?k)SpzV8dT|xyRQz$PSe?Kj zxvV-RW$-j6zlhJ6^jl|~*gC$e^GZm#m)FBO7Ie-A;T)Q^ejd6=c0Yis=NDid#SnXt z_b+>qr-;Sw9N=z`_u_vosJqRz!6iQ1|>dEWDvyVz@F^&81!Jd1o=bs`Ti(X1zKsCimQZPgDGKIcir()+>KjU7JiLmrT z#T4%XjfkFV+coqHYv>%Z$fhmf9I8uK`IAV}<=FO`VqDCi)`rjh8&a8KGLe#S-c~q;)oXa6g=HTUugO#4?(a zADn~MKHxbI)&H0#IH}=THuXAeUN=R%Kzbl>hL`y4j}TghDDZEMMF55_@t+?0-!aDq zEsasqlX`v)IS3HOlT#ZD+Dc&g(l%_n1Uzz$z`mJon(A~-n*RHj3ve&v4j6CXBvh*k z-j$u6B42VebI`V2+;a4-TP|@1P6j&80*sL{wk#ZC@&w-}haI)9e?7IWh^j!^Y5HkT z9tg@K4)9_QXn#!8XFAWV4*(7(NkVL?Swy=Vai7*tdr?CCf^!D-?(3TVe_(lyj9Tdn zLtQR@+c^M{r-00B_=6wwoTXYSnuTMXKMrQ*q#Wsk$j58bg0JX~kcQVOs!XBo_U8j7 z$>_3ZhvITQ09#e(tB-b@4#6Ka3;7?KNn!>o51c8r`nw#TG4^+ZI14n!qzyhF-c#z^ zq-Q@*MSeX-Yd>izZ0Y^aXYlk^U6q%Xl5024aC=ajv^h@ZkOxcveU-|cgzd-_yF7$% z7#F>UJUIt2jfWG~2AHPx!|@=K)%3>zXRLVi>6Gn9{yqjnSTY!WLV zi>N17Gg#%KeO8b$HfV`iHAfUA*hJ$QV|;7-i!YQmMM(YF&tDgZ?^ zfca=euKc`>zz|dNji! z;Jj7V2}I~8%hosz6qyGi5bKcG8R7F|+QU6Q$zosfRk*Y@M5c%NM{(bo51c!eTuvF! zEAKEGMN16u?&V87)}wsuRyNtNDHbw`Xpa~F7h4p?xkTPN%OUSQ6Dpm+OE&rwqFPB0 z6y|dF1(d$I-Cw`2!cmVPQtDE;fjraiaSN38YoU#( zRd>#ug6}#K-|3-Lc@x|`(lHuW;3S&FA;}isZPD12m()`t@t&jS66BA-^;K0Cz4oP9 zKjLN7>i0x~`9>K=B~Jwg43%x6$wQ-Zz{>{jZ8aV?j2_E|_X5pYo?AKrk*?*K&alj$ zDh9!|wb-haWywye9WZ|UAG}cwQ=1Hd!58%1R0L~WgwXsKTMNM=`~fOLEEOH$<3h%} z3OmcQ%MII7@^;)AC*S%oWVVsPJQTZmP;x+H5Ai#m3AqD~a&}~?t>y6Zp@}!Q%1>;4 zLx7UT*M?v46fM$ao~_vWW1277;clrY&{CFe=2L9vcZIeXB3iuA9IIDKmr+vmDeQ4) z^1xKe)-1HdB3XoOf|{4A9rm653^nN8D}_5H_N!pjJ?zBiiy)#j*Ji!Ns5QK{X);L8@LyGg6GT(ZRp`Vc*ye0P@i|0II>0)|L}-YYZh0{`ioIQNVMXp zUb3Bel>s?13d#s*?pNRtm2S8?D3>wA72{(Om%}rsHLEwCc%J&#biK1JFw4P_It5s2mJPGZgTwoy)J#givP zn0FOKIE@B5X`FdIR+W+&+TXR5(t>5JKc>MU#ohz*r7h|X zs|+2@#kia(o?S;NDxXIBH6Y?o?Y0@qJ(4qis?x7lJ}U^KJiac}eN!B~8FIAmwYWB#C_&cd2lX{6#^DXsmIwx=zjs8@mKIi9IqBH^xND$Sla6y%}~NFpqE` zS7{rS**(_v8RxDsErV6W1Qh2DD3!*|5_06zpKI2mG=WzHaniuTad^TE7Ch;;UE%}% zqon0b_tbLgH9b*)SXsXIOZ9!AbYW6+1%BW5WY-fET-Y4-dkaDAq^A{Ha9|=}lW<9O z93LM<2=z^h)iXLr!UciJ?!&blTHEGYDc9%*JQ@8D^SldzOLI91;wchBv0b6q7{>|d z(SOfPQ8Y!Py_FqCXCGB;GaPPOEiKes`NZg0l`KmytaM1R&6Xp?jPw1;!n7X%U zcSjfLc3E^L;GtB^{xR)o#!mE2e)&SJ6Xx){ibS6(=_;Vyz37_AcQTGKA1a0tp4|=D zymDfUX!M_3?aJLkD^*;x5*ExA9r=RUxGqk+XHpezawhA%2WB5_SPe~F%{EetNf5U+ zV*QE9ywqPQPsaXeP)DzI3Y{=c3q7%fl*|%^#D~b(7EXwmB4>ZDec(MuxCK?J$*f?P zgRdE+szv<`tuhDB)TRGf^%rkk59dtOg*E7(=(XKOWXjpI#$G1IZ1pkgJubPNTJ;P|+(I** z*sCMOWo~R*S;BzAX^T%n&X>^VhEi79d8d7}{4tHKi z0pV0_g7RI?+h4|$kR^Wf9s}*0xQSS0vZUF5`s5$@l)$lM$=NT(p=;8h5DqUF`|JZi zb6MicY+8t8o-MhIKB2SD_RtJ1WrX!f<~FAJt&!u{-L7yX3@#5X{w-a+pK|IxPVD@h z9TP(*`~=JF0=z4(;$M|A<7u#FZ~IFTamJ_2rwBg!)OUF5bv5gj&!~*1xSaa!t2oLq z1=iizf3y_l*r>bDOGuVH%JULy>;pF;uLJHRZW}m9^S7s=!UHho>_(R*s`xod;0LRo zMpNoxR^_gWzhMz=7{_-XjHvYKRhjZWPtiC)W3gAuj()4)cKre$9|c6tR%Z1_NMh2- z+!o3VJRrDjCzwC+lghLFA4ucQr_T#LL!s<51c+Ye`*9D0eA_7{ZN|Ax*Q?Lcta0V~ zf;DgE^iV^vl8TF~E>t%#GNdPxUCU>c{Z0rR4Vt{p*XcEhWRKTrT}Ymhn3LHo@F{DW zjHOAM7`S<(GFwv(+-OU{xQkOVh?snPmNC(wvs^i6%Y)kXNxa}6;5E}rWT*rU!_3wppH!F{Oe?~Ss%>S#sxG?Wqb0;kWdsOzdRGHVV zm>{WRql`OVX~lV+xZ$NaU+CQ_rAO+gR|q*{Et5BY@m`3XXn-~LmT=!;lu3E5*m+DD z527{x^=r8E83D;0FxzJIsjk1iGfY`$0k&3sR?a*_!jtD2|M41R4|k!{c&C~2rNi`Q zf0rNA{^rkT-5@GqXNFBrKqS1%*BJ#)u}ia=z$bXV*)RLGw|fJmd8S1|(X5^laVska zwHu>%MuiV*E*oe+4Z;nxK}+<%YW12gvzIq%_AZAp%?zAu7VJ5&0Tmvmam6XWRbL@H zA1ptmseZ%JvN`hhLFxs?)d1ykEj2Mi#Lhd^ts{(F>(t{Hb0;2^{>yRamXk|8&0)@} z{levb_x+#IT&ux$wA4v*@Z_h0^_Q=^o=Z0is`hn!u!C|-YT7^FesSmCF=Yw!>4OR< zT|^Mf`kgFsA6|Px{Z0P(Kd=*K-ZXezY7(2Fn}R4PDbgOe594rw!&^3825PrH;>Waj znaXoq$;eQh@Lzr`ufsTZTq4&WF_{a)12-;`VmJ$UZhoJ8o-{sSIFA>sq8ejPnZ2L; z>&=D^N{JggiC5_=muL#98aGVAy@}DTt>D`11XB<6%o0ndAeBj0_|0_>G$+{KbFF`s zh{niO0mI7%19y4PE6z_Nvrg+v;ls(Rp|8G`cnO|uyTpzS+NUq68`yb{ zv6Zsi$H$wr-7fTCl(M=g!(sljTQ5xlR%;xHy3YR)dG6J%Ws{5t^<)?}v%QgWB+4ABNB#l@4Lw&Kl`zuC$X-+;;M>Rk-Sy40X)h z(%yjKbGXIVw!jEJea3@8t1nb%u&8M8SI69Hn0__B7*G|An<4IndbNEQPv_Lnma(mC z%bi+Fmm^9AU>q93p3aOgB&lUsfVk`suwO2!`C|q5m!%xi}O18Q;P}`;2)b{4_}=wvMh`$ocanpqjIApO;8sSRf|XE@B_@tw`V* zX^~*tZrswtQEG5r_Qx!<_c&dSCmT&Z!tt{`5cVr%D)&3s4l9#O za3yBTa)2=UH>Q`o++8c=Wg?HVEx!9$zvbi3r~an>JmsaKJjm4ID<751np_scd98QF zVLLWQQ^+8F(@84NeYasS-K>+##VKVd^Wuje(-faj)Bf?qppq*S^dNeJvi?5;X zk#lx-+R`UD+e&a^_<*ndW2Te*>-^C>cDi6}c`auNj2?J#TAymx!XljK?PqOsiEvj? zi`x}FQ)ll#1vbB^-;}~Qmp~$7{}jcK9+P1h!a7&ilaxttiutv`&M)i9u&H8YS!UH- z^<)KC0;a6tosq%Ulk`B4!kc1qog`)F*dlG%7Fovi&SW7wOtfEIxd;r}H^VwP;hKPY zkGA!Y>oX=}rNSS#xG>+}`@G2Lz}@VTriRTB%ISCG6(AXOPH3=MYw?}dl3=I~Id{a) z_Xa(|BZf<62zBL|k^^7SP6pFQs;e5U$@ze?HS78{j_ZkPk>4=`oFu)JGmW=`G#60~ zvfEy{#{Q9?-nwnv@Z7*gxr5O}e5i~)<1ZAr#L~|qEI~<18`j0rlzhfN*z$lO`{Nrt zTz_r6`o3D0m-7BZ7nbN!smUihw|DAk4Z*eV=~-1JfBtr&e;g|@iGHFhD~2I^&b%A@ zTc5uZ!>&-Na@s-JJTte&ZTIU{M3yO6*^>o1GQ`mWxm$frbiFagcTGxD(+l z>q?;_IZJmb?&|iVU#vT`ZU8|>XL>w`4Jrz~cQ-~kCuQ8pwbtwO21G@lh^&)M&1wQJLEPKG>t)ZV#YuKX{Ur9$xf$^n6ABBd*KpY{kMMO7U|@{OAy*q0rVLBmYjO+lm&1OCVjaz~&ypIwVCkF>C^N z-v=R3jdK+Kyf3J&zd*Cb6YWNmzuEoNm}K}p8>-|m&Rdf)) z(scro#E2@@Y;KvZVUI$hOTUv=GP^-GONH! z?e6R|F^z$~_Sn}sYP*}VrpB|m9Ur+mU9fe`0>CzbfNhRTL>rOTGGm;&<+~wWeWhG? zbw|WzEoI#oxIUdP*|t+4ho5>K%X zx@q%73?kixc*z=;32Q8vk+BHuf;CEdBGy^DDSAy6MK?KVF7iF4N%iHG^j$xG+iOJ9 z%~AFY9(Nlfs|Ft-b7c1QF1&@*#__MTZ>LV99Y)d3^?O{o>T6}m+uD%hnae#V7d-fO zosSdFd~4O|x7Czw?He>ux-qUfNSVGdM_u85Cv}wjUs&7{F-`4$V8W#nfD(zVjl3<9S7(Q%MW)TJgH`&&F#n z_eIl!zA@b84nKP=zbTK(_BrcSVf_ZzD9^`D7LPe~~pKcn#|_?nD!}>T32B49$kwB{ptnY zVzkddNn$Af5bfuX0jGv%4K>v$3oz&7;|_1?Fo!#0A2MBOD)vpg)Q}sEzoGo+EM$r6 z;M;(1R-yVl`y#;vW1(9s@6LFfEJu45HpV4PuttwI{wDT*?AP6vj{jou%0p zUp$b*4a}eu^CQy;C6>#wzs{VdI`FBv{%GbiV_jzCQ}pXe#a*;_7qQt2;%o1S{j4Ai zv{S`@KFkBMsIm@xwZ>vd zi)QXrzg1aQJskbF!_O&|e`rh=@E4r+v~a|g!y&Zv2)3(8?3NCp`6KPNy%mXlM>;+SoH75j+V3umD#N4M+0GNS5}C(O zp;AzJ|L-tLIf#7*kIl$IRubG#g>*v4lr1fM@B%8GFoR*%63!Qmq;Z5N8Xzk2z8#;! zHn9eyE5l5dz!&IOb@FZ!NA8FZ`7ObT#o&7T6$ds}Yv742yWWT^JIePhisDC(!S#O( zANHEl%^vLS1>+!m);;pPazkku;ahU6++n|3`xj@kbR}(^40{a>Zem3?#okGtA8EV# z?pIn{joTmzd%6D5ygw6lz|p^~CF7@NopHDiWVEJW5boDURQzvBs3{3SKeP(ARv@+>il z>I9VmNpO1x8aT;9xF`;U+N2y~V5S)o-wS_#)?=NsMA;~iC^Lh6*C_H!-VVL%Dp(ek zo}4|kIZY~=r$S-+X}Nol7fpZQLIcK7@@MKg6Wi4zTN9bCCb7MZ`*F_SQcvzdW0vOt$DO{dLwjH{DbjG`2Ya@PMBDxBtv) zG8Lck@y2_G*C@7Pikc+p9Jc%&b@Tb)p)N9UT0(hH*B7-IKn+LD9qeAQtW-m3S%OxT zqg@<(Db4^*2zQU#Esu!xo=a@`m3&1`qUe9kxJD(og znpCd}T3STEb)7M;hqA)v2v+3lLrh~-7qJA-u)c6WtLwNTA(2d)ynOaa0f+b&za8t% z|6KVhe{*%@uaR|JykU~#J?7KiU+lhytK8Rbi>64@Uo`#mq?nR<9#?|5^aGYx;M^WR z5?9ZU8Cfp&TcD>E;||^Dd^e0TA?7dmU6t~#U|;}Y1yM}3!JW_7qoNTDRx}mcf|?6S zYG=8YxWm*4?e*DE0vbozzyAB6yY9rKEYD}R6vjJPJLbOAml2g3Vm8OE9qC&N@bn%O zJQc&ukC_v~1bo`$AXcT7nPL60GW%xZ;Q+{-{$?4!tRk_07!{s?v68>qluzTuJ5T#h z*hY9@D%i2Y7MiNNx#{zkCFVBWXmmbcvS8V*99B}spvQ%#(SfL6-eCQNyLE2kMkz<` z@6UXRGbLR`yLj%3ITS@*b6(EY#YGLrpSy;CDmX&9(UJN)pB3Yo89I(_%o(+L@HKNV z5OVkLXkjO_(G(X{F2=^Py%`C$LZ>IE98HL7ZXI<8PV7b^J-HDHG84cWy6n1Z5N16@ zD@?^7Yw9pQsTRxiCEYr~vg7)FxEh9+oH2tpvh-OMzW2mU6P=_Dhfhb`$0TFf@PnPJ8-kz zZ87A@4?-eps_!~@Hy|e}vFd2Q+1>f{_RKTtG<_^jxK9>H}fw~ z-B^RA+-94}Uydjf(Ux^9nzCwZwJEfi`78R`pUJYnoC`c4_PK+s$|)}G-JbTxoY8Ro zs@XnfS7wH3Rv{!<_ne%|y+2E_c)|)fRK%OvQt<((L{-{jxCcx-AXS?ziwD|oT`>|&=k6>TDe%?rXxuJ4!DNp&% z;H|>kW}p>ehAnk1ewv9Ox$Ub2f^%Am!7y~140xwB;@KjfS z4p|WSG8mvvp(d)Zm9rd3F(cG;0hIjPAu7waR=SgOE@K^0BXOb3?O1=L$zw!#0_o;> zwJZqBM!&GXj%vH7;7v-;s-W{`fNm7NhSH6yxDi<4{sl+bA{Iw;AafXB7=vvG2ga%U zQ0S6!9ccOq&m#B(O=i*nyDN7i{zRF&EQ`1p&EDU$BL0{*V zf)W0<-V=>Q@@Ah0UJ!@e5V5C#T^_l`KEi*qNPQvq_dCcT3dQ ze=7F-9j9JNsCLqp5Svf1s&9bFCi>U_L^*vk;%OoP`We7{B=Fx72wfhRBbHLVD3`ZG z)U|C=eXeL<7<5k-b4++w&nWA#?da90Hl>g{qA&P8w~TJhuIzo)AQNo&i9V0aFD@_A z;P0V7mm7|MSbE8pq#V`bm-_^FINY7n3c1vBWSDC&T?X3DsIxn191<6f;rOT>jAE53+ zg$Fxi4#ex#;(xMvC1+=3v;TobX)(A4rfYlU>^NAx7Y8t%awcDTmuU^_t)0bGT&wdy zDl;ry7l!qo+#5)?R)3x22LdOZQ&aF&cNCe^EVVFjk{s$ZQ!uWc`af)w{gFPJQbLA) zD^N>GhMX#rQ%^xWU^*A!@&0`uuOjYka`$+PH)R#_0+<>SmlRjid;Pr63&evAA$#72 zyRbEtw4Qg-)g-ysJuyb?OL$cLGMIU;dy(xyLSm)o!+ywA%}fW zfDsrHA6}&>^*s9#9HbZFHTz}A;kVIW7vS1&@%Tt`x{T;At%U5WOxTqql;~pPxJ+?8r=?z+T^BF>aNLJpRErzi{)&QP*)SVD#Y&aBy5^7@CGbn zIy#I012;xfs0UaAzi{E14U(3>tN)(THU>dHlq&IF#62dpdiW!2Ri>OVz!tF-BSEW< z%B2(cPmIR-Lnrnx(&|^Hp(xg=aKV)n#WF_^73n8YhWfSN-&*|NQi1BVAHfkD)9SKC zx|8~EV_plw^9r~~X_Sa+MA*fy`Fvd<_Iq+VpEVSz!9SUqIt~CQ4|7byc{SY_XIv#q zp^nA&H+2^LJD&KpylJ4mQv1D7$r!k3eI?b-9(BH;DQB>@_s)c$+7Z3(2mlH+{bF(V#zMESv7|GPjR7OHZ`o}rbK402LlC-2t3nV=zipHl4f{|U5Z9o7|O z4LhjKL)Wq6o&Rz7jw_6_ogOXk^m8n}Slv2zPMCgxdo18)^Pkp^{Z?MDG?fz1k(iOD z)|`Wk2c$MfuOQ_|wpWYYnu?+Pt_%0Q#a?bQ{WNG|feMT`NL1X7-gN{MSvbBAzg5Na zs(lL2%3EGHi*+o-qP=q~G_JU3K;<5nW=>vM%=?rnQKiS&)TAj%b6L9g`H^9pvS4Qi zivNJsW7^Va-Xc!d_Q;h_V&S)Paz>rK;oP54_f;rm7J{3%CAL@0& zUdPd#l4~2Yrm~yd3YGz|@$&`Yg4Yv1?D^&3PT8X;c>^J5^Ar-EB=o*C@|{=k6`jUY z_6N_s+aIw#67k}V+vp86Sqwz|oOhojcJtQO zy7_q#R9Z?Kb@2y}M~^B@PD*XI`V`5+OjZYX(e#_9hTW@P!xFQKJ!J z^V^P_tGzyr71z^c`K|%6w_nrx*29@`on{jS3V-~MX#u&s zvPO3yqKPn%Y`V730_@oaLMZjy$;`;67#kkWOAaeppkIvWf$xuLt8iZ~-n@-Ai&UiL zQaxaDo`REg9txWktxPAuuG4brDmt!Ol9(u0B zh+)FBWwirMYyOL3zTBR^2nY5}SLq+H)<)fVGsD_S#yFzF-S17(n4_O-Ln1;>g6D{D z)iaS6o|oCaz#Nvd^Tmvr#uApFuRg34jK1iR3y9L=HOI2qYEz>AyczS>)jZt1^x<`C z$HZUg*LS!_**~Un3x{@2^{ZEaJ&hZND*7E{E0#aDeSE6O;`1iX3%nHPIEqtIjJwKs zX{*e-Y)w?BB7U+JzV=|*x=5>L<BUh1K5%#}x(vE0RN0RU$Ts-HA^0^qnY3f=YhFO>Y9a4M>d>kxxMJFw_ zP`*ui``r}`cY0#?9ib6{^MOr#nAQ3u1|u|3xyRMRgQHuJOOAaMw9e9FQE?#mfs@i> zO_RAFZMN_3{EMBdqHToQ3NAOwG6UI{MnS6;d!v~Cx=4j}q=HN=tE#EiS z17}LJ8sMW+KsTc#=5S_Ta7|g~*wIN|H;3Cj3yTmQSHF!)(}kqP6^X&MPKsOfz`WiH ztDo~sY>^7O*o&xyy>v!{{&|Fr+jOWqK2Q;4>dv@&ZmuFBJbju?J73%DL+4}?%vPVT zhl>|$@;bDI@~?CUnpqys*SX5}ubz!vm-qCaX^lf3S$+s@&DQW7Z9=AdUVtjpx1@tf z|FY@?gv)O~O?ECs>}yY5$-(rlkjr2Itp*V~%Ns4aVdv&doOD&xff7b)e<~1Gb8Vg2 zcU}uRD%7k9p+UkSqPNrw+V&h(dTRd5^{~xw=@T&6!o<;hIK@@GG0wW8 zpzdc1_BuPaNyhQo&04KhUlikVQynB;<6ozO(uIE=gNK8Q8L;e-ty8xhJ;itoTN>PJ zQVm6R$-~I}VrK@SaHcGn!J+Osr3= zCNq@x>8$f!KTY0#&Kk2E+ch5F#H7b;)OW#Mgeo8KXB3l~)$HZuB2Wy7&HNdnXA^Tq8RcjFw&6B%@W1^>>$FL)75iML zE7b+E5_#|?E%_Q_fpi^_>7`?BCOL%7N2e0xUg*KTN|c+<9VcilyGXB(B;Cs8 zonw85LA=~s6TkXg*7{PrH>vMU5ESeEv>H%e+fumhf8*Ed$~swAu*O=N(TI*8-uoX% zJl6|`8;*X#b#ppjo&ar)vLH0Mv*dZ=V>&{J+PT;l%`y8_kl!@=H)|V5ZYDl-K1RDD znj*+#+2ZhViojQ5kP8a9Jh`ikwT#x2ucc_+qaijHEz1;7;1s~^9Lm3u&-1Eon;HB_ z)j9H%0*1O1ZUmfwP@Uqxd)-<%6byRaRxQca6zor|Yj<@hy)jvg&dv!*t z-QrzlJ@%w+!hstKbq(Vr()wu1ZiSWwdRr*f@!H{G1zKNQoQlIcc02V+xVIe$f&@p{k*kYZHvN&J!hFmrQdc*eaojYe=2%ZQ0FTl z4n!O(LqDEUJ>=WAIkgs1jstV8yUr zz&eNt|=ejlhRxYZgVoyKvxJYpVMld?&;$S>ii$)#oN72S*sg~n84RHv@* zC#;0^`nDF|NJXk4LE@3d5x_Y#GHEA${J_UP8Tez`3FZ*Zp;|EhSK$EtOv}C?Qxyp( zUDgY3@H5xZW;psyYC6hkEn7bac$akyOIf$s6sLo%xvklZ6y1^d-pfZ?yoz|&bT)#5 z&IcnnoumoeBpBb6HEw!U-rID?KGhQ^mcab?d63>bbk*E}lvfb420*Aa>^~p`-a!t@ z4hj}M*PbxPvwo9F%-Y;}l2>Z}L58#k;vI(PriBs!tZE`~%m4sE3+TCo_?XPV4}}9w z=2NgI_Huj0Lc=1>sq{5C@#uc#ALwJqlfq-mWr)R_T5Ks_ncO+dinj$#jPlydPW=C9 z`ucdL_y7IQ=~Sm%siY+9q*9ht4pQuW$|*(JDL2coov6gzCWY9}sU)POlH9c0Yte`vd;vZ%G16A5%>0rU;GG#Lrs!-+ zeOoB0Qw$Jq-8?|K^#BNmz}MRWCo8DH*2_;&%F{-@B!9{41-aB(^TW74Cw3UNnUovTf)hnW+(Jcz6faZ;+)e5@lCO8!PD{t}#;PcQrPbGK#ak!xne!6r(C zt8yow;19n_efX8BxO`IIzFqG;sFo}(liFefsRQo|G44V)^!m3F^k^_&9LCQlovPrYC3KBC+O zJ2Xpp@|$p=H7u=5k?>CWLBkDh`ZGI~ZKtGK)(*;XyP*7V4sHSWG>a$;-3SBQUJtKA zbWpMYzKEA_6szHw;S^k(X#ZEG85n#134!UH4EfQmmn8oH)}XUAqy=Q+GxxiI zgTk=|SPg`iYW96=(LPj|817%T#Xko4%17^}c5uH0LJzHqPp|sZ$rz zuzr^`T>rKg0%SPlL%-}O7-pKDYWgVUIw8RYludcWDSz@sFJ)dfxS48zy7m{vECk`) z0a8psW$JG-j9@C4)r`%gJt6TPf(5SM#T zMTDn}k(ZmS4MmsJo`J>kvVXsBSAlezZ*@JE^PVG?B6fSAj#D9#XMX$7x-{ciZJe>_iUg_k{iT1CSaM08>6~P%J$pW*e~Fq=Bd8-^B2`R;P|bt#;FOu z&FsAXhEfkhou(RZh02#h&P4r`u__AaYAuJR;Lq-*R5wHUiqbpe^@Bp_C9Xss^Z||OGH$72(NJr#ObHEK zX^#pm8LdlOiMhFlGVxZtH&B+iA=mf>3YJo?laIiYz9APmj;bKi{VyP4>pjK9!=T{V zqBLCw<%Xbx@B{d4c!p6B?g)PjO4ONvtWsbdqYA3pL>584KJz~(j9$x=@p5mz>zV$= z@H5|aj!LjLlf_~y`eK#r+amo6-{(g(BPTnHeYUQN9l1!HLbUmZN{D;oiSKD+-K80l zNG#BGtH7UnY=d&ra@Jrg35gl;?T!zCPKFmj88q+{=d_R~u1Fp$&DBx(rmglB@!ss= zkQ)$OEkqi{K1&g~)svg{Ic{?RM3JS4^jE32=q2<$Q7ARL?jBTVodC|X<^s<`rA-5Ym6Fs__ulOD4}ZOEoR6r*3OHhk%KF~ z3SKB$q}1j+kz^h#(@M1fxjD*A>O@g zE$oLsliWvcpxSX~*Ve~~a^3p(E7~b%|0E-i5w~WXe5dWa=FhOYNu=MX+~9eVhVN~; zY$xUU_uY=L8Fzk&OV=KdT$t{+nK_KCGoD;nSi9Xj3-31^LcxXQCwt03?9Elb1D;FJ zcMQK``NqYFO$r#vZDcn(!!mupVt~VurSffuQRN=;xnCyESxg>MxK~R@_2qBX8po>D z`%AAL*py4c!3#CSAnD$qBk@Bc&>LI|&suLkf|aAGVZM)X7cyrqNN{`Bk;gow35q_TR*2C-6kH*{amFyc9>pd#5E?12u*{P^_}ms%c{{ zLRrh@YH@S%ALYk}yU4L4!8+=!=wMXM`ez%<=PWi=pzyO-#Y(nSi~MGkA^Yo$MZpyd zu%l3*A4(pO1Ee^|zL{h*4A4C%k;4jf)q95IT=onCNbkp@P3@uEtppV;P&1ZA+bGQl z#YkBC9=K_OwWWkN(4&2*qJ21IMc681qq#WsY!JD^%+7LPp+l45i6TI}loXWYsZMLf z{UPE?lx>g`O3SHQb=03%eV8}k3?~j1Y%Q;jy5SJ~XsH8P?e2w~-E!)y52^0`#6;8j z+QkZ!EbOl;Jv6#8TpMO#m%+R99-JkSKE!S*P#6`$ir!)2I_Rr<^^iiY@k0;R&SOSk=gSgx3 z@_m1aDKUA%9d2ic|3f_JRtJg2Eh`#t#IKZVI=3ULTwdq!v(wL(MCuKVwtkcKKm`|8!hcGn1eeNtI0B@G0#}t z@8}o5KvXq9eYmSKwgGQss4C8V?IW2(EWiFvWW8eR=u*Cm<1*{Uw4sn{%$zw&>AwPF z9o_&OMH(}q$Nj+6BQ(Iwh5EiYn&2xoyygu-3IiLPK7scyv4I?`5|wDY$Jy}a{7SWb zPA~nG(id(F=p0YHlg&7nCH)Lag?#$gTd8kD`<+iTVDPRwBw_sP4NqC$)dY zf_dW9+Ex515hJeZh+X;^<(#W))3KuTQU9xy49ic#W%65{%$-Qdf0ws4D&KnoRgTOC z)A|lQQvy#zhs4d8@kdmJ3&cSD1J}yf4Z*aZo}fRaOB`w%<`T!9m)%jBx|mD$sqDdb z3&5>du{&M3X$bH7+n7Z^j3Nr>PA=T8m|1ic%;K*=M;M$xRt&-^ zW_61g^>ZPa4Gi$($(RX(Pw$Cg7^yr2dz?z_5v86HIs0A0P^+?3^Cm+Ikw|TA;f7#a(aDDSy({n80 z;kfbh>WkN{7?sE-=qIavB%(wxqs*?ZpNlw; z?Q;u@WzF)yw2Ei6$LYac1CYovgsg?@J78MdUGj!j6~P}NDK}OQRK_g~$pPy0tfPr~ z>ME=$^H31pkv^55q_mxJ3zz7O1)E-^-%T5({vptp|1Uq}{fd>=}37jg&nq zVJ%bX0;h)Uewwip1I%1r%CJBg@AV)4QIVgSM7ndnO#^}GG{yroc*Bp-Sh^a~QCmyK{gnwU{H!m&Eq-Rc zsLVd@!+0-Pz%tDph7Cm7EtG6Ei}P?#j_tod>BhcnM845dhD+bgUzT#$JcOkO6t{G% z)1_U~gb@*S{#QsP4^5QB$UjFCCmt@aLG5>c@2M&_``Z^FkcHgpKREP0&7#+>(@*#7 z=gkTFa!S2eZM1vGp;jBkrfO-IJ0oeyCnzs2ptMTsnE@N3k6r%d6p zo9YsQ0va+}tN{DXuwZ6ASoTeFRlZA~`Q|IQJLk2-xXqVPU~HhvDj{a%`kXg;5=zPt zl)|PiorDzl4Pj=lbbQ;UXjSOGy1jxEuxv8CHM40BUwQ0tdFKrB>NWioqJ3L52@NDsG$ zV0FJYtC~Ug=H|&4GmgaxIt1TLCmaxi6yf^Ih-CEz$;ls!(ieu!jy0Z}Q5SqXkRes!nWj_{4Z;smWxt1>)k=jtBByUw_jctzkPNd0Ih%M24G-C> zT@T6W?=F)P)ldRu_;Ln(A0Oz%WwdStx>0x?QOB63C?!)F`$cI)w!6(Uo=>(3u#BqF zKJ3~~pL>+b1KwUkoFI8gjw32Mp^gVVp^am?&kwg$@3HT6l>QD>}V9ToOjg~vzs7aO8F2EQ~jRkUuWgx2D1?13WWIHy@t0uL( zVljLN)XR*+65AeAxYdwc?#hlk(6;rIblEae^(=^(olZN0f)RvEXNYNjo!}C^XD+ww z71ACWi>7@nID6U4F^7T=*@h`c-ezHn+36MbO@{Rt(fnoC-#nKSqaM zkUsb7{})W8yVpX}LL4BWy`TF~5{ZX2T#kyF4_M+kLAl2h8n}R>#Wloq#8HmM*6Kxp z7{sUgc|cb0(`S^&^2kQFrkZB~ilO=E=~qqEoGZoWi^p`A$~XdBS}}EWW5w`UE?7Jm zx4;ODH4esBZFEidHh@I>Sw9x#`p`q~;*{SN*lN?`T&Di9xx3ao4LeEyu|F2!Yj*im zl_lFQjKFwGo58C$LYBY;Ec8oFv)Oo|d1o_tvMF( z)Gf^hec&jh`=F#VL^DST2ivh5`F3_*JxSTi+2TK!H5fj(jBiVyUwyuplWEmnuQf%` z3fpqM?iCzhr8bGKqTGW{IG1|G1_e5#6)Ato-l7{fnVtHid~=V4mpo<#Ew!td zVNtSKANG(uVg~FHyfpZ5s?ZbOJC(-#3$@c6teVkyTK3a&0^Lm#TjWf?#+6`??2p^G zfNlfpy)^*w08qY+RF%xhIILdQ{OaRkwGle|NdwZ80gs+mgO0#%YlxdQCCwQW6E@D9 z+h}~E&e2Y$zKyhOEhC)4mue=Z&+Ty_Z$pH84by&8blMZ|A%hyEiBeS5WuG}Uq{=U5 z)d$E?Z#tC14SmGUcp7NT(eG_ikC1A)Y z`rHz}+ydjMoX56LhMb1^286WQg5;w@zg9;Bfbhle;hb}d)_Iyu9c3y;Wbrz1b>&SD zQ62KByzE`HtN|}x3Z@VrMp!lAd`W+IcxW1;hz)@#eO*D!+qBwt(n9KLCp$3Byf#3S z8GqE8wb|m5Fmq9aNMB3OT3-MIKU@Puab#h-iv&ZX?9D>xg=>bfbbs9Kxm%?yb$z+D zRtc#u;y5`+xmeO;`2U%nvc62icX(ZF5=9$LHm`l}TiAFt+{DlEWzYGG zj|6h?VLJz@#{?+(Kcw_ln*4$l#5q>@DGi4}zV(|>G^zxF^E;&6LDtxioYY-MiXgR2 zUp;fMlzd;vS_@Ayo6etRb3|%_XFOS5IlJra2j!UlhnJLr zUe0Lc#91HlaBg*LUhUY+;6ul~eg0a2Iiy4#*xe?jmMlOXV1gP(rM&#dfUqq-3YQ7p z83y)KN;7P0NtwSgiG?4Xq71Yv&*V4RR4f9|A z(NH|op*?Sbi8I2aXKE&U3_mcSsB<$Be8tNdxX2H$p{@%Tx{gupk4~FIVrf50ev{FT zbJ#Thn-r%bR8d$`{T$lJu=_(}0%~8EC4aD{9WoLEf{)g~90lKUxfh3hopc%`L09Cf zB&(uM=?r_3*w3Ut7Oi%bfP6CzuuhmeiT;c&W5)LRD`S5wTCDcSKGLG-ndN*AD*n4b zvx;UJi51_adHDYvy2J=uNt#(fo^Sv|3+$*gDj^w(8$3{oGs{sCJtUFWup)td@DNgD;OPuW@5^SjJ*Z1Z>+|##4KC<)ZuHfM^sAe#Hq=cqkmQIASd2y zTM2xt6CFT~9dBDzEx}O(m$Uig zV)BKl7gr4ao|5>UXm!UvRmEKRVB1{&YdIU&f(+D>?n|Cf0zunyDaG$1%d%g&6VQxL5lT{uN~A{SH`W-<;B{vz*oE>N0thkP@U!$)^6^w)vEF{rkg% zdcn^rx#?53GM>dtW9eEgPI&HXOImQp=fsV)u$#}OZmvX~dwcSxX#j4}#&k^25ew8b zCCnw=c;dgyaK6)~2|ny%e^M`VkUqZwpyr8t<+}d9EbImqQ9@Kzl9OSmwFqM|B;w>h zkvBZwOX)JSk@z&{No*_iC^2Ccf_C7FEPLr`DzX5j#gZU@q1loJNRx(~{R_e8A(sD2 zc_Zz#QYwG5-suUF^638%%)&~}%@gopZR4}L80tU(b6MO<-d=!szp;AGiS({4 zTTiPh`zkC07{eABM6mk^VZO%h+I&J*r_RTV_`al|^bBEAQ(kn4`upEdz0FnBUEYdkO26B`nD;*JI&NWX z$8qe%_!!2%d4RdJ6lLfWsIN3XOI8cl>87)nz)9e(+v`X(c}iiOg+oWqzW7+_!13~2 z05azv=L9S*Fi2;$Rvvz=5Cf2DeG3EG1tjAXK6KWh@;4}p?%B=c(JZa)huX5IwC>5Z z55!G^Tk0JO*swb{J#3DI7jIz=3=cc~dzW6rf%&Q#_2~z-FNEX%U>nofv22E8MC#O)Wgm;a!Xjf-nW>MCCT3sIF#71VEO{$u(Db@lYO!@+Z zc?0t-SL1APt_MXGpe;a|@AC70X|U_}#4SO@Ru`thg4TiAoK*R4IXis-nHz;pKr5=( zUNJ!rPGRg49O)-v>A_7Q&qFH4$l+tpQAc_&m-7by?otuu7ob=z~LZU<@W2=c0St6ehzDO~EOO_kKQ}eMhcCOFx;3~|628@^_Nv$LvJJ|#2TC7$vRq2o{SuYG~FD;oBZlwYnz zQ(FLT4meYzmX53CBFFk?gt;L)0$igsX$%oC)JJd3!E6_z9HvogVS|N1EQ7>EWRgdf zz$2c{^zRj?BR1I&)G|JcEM7>Uy4Kv|IlK-XfzkYbK*j))IZuzlb1-9o+)4-9W+1Fb zhFLG)IlwKUsPDUA*ly_mT-WJckEL+h&C?Q7eNVgT--Lsv+b|9oW*(yYQDpeIPql`I z=awFNfR}1o7Z+BjsO)J&hc^n@YbhVBVce+?#s3-QiMr0*JL3#jJf|Gwp_z!DB3w}{$d3NFif~D<#0bw zb_pT2i;naqBZAy5c+?&#E+DAsA1Q7`S1Skl#lQbLG%HuZ z{b*kTr47(Ya2atu%=H#E#O}tL*>uGkpYl zXk83S#%e8=*0+!KQF~n~R7Z@%K^5gZ6|TO~PpS6K86`>u3Pd1)u<$5BR)#1hZgs9_ zNXM7mFv_yGp0=tcxkam><={A zjc#67_*Wzy5XteUX_uRi8>uRz^;;{4+}BS_83{~z{0L_IrgFoyK5MYWNbW~Tl!obj zWQgj`4DpnwL*3TKjRk}rL3br8?yd{wMFdKPt0f!;Lln?FNL8OgtY+MT)c zLrq<3&dw5vt#a2j%u=sX$ZeiYy8M%fu} z{1!2BExP9N8#|;w$gx!j{+k=GOzB#EM7(zk4nnbS_A1t8Y2aM zi7+YR{biqyG8bz0^faR~$25#Ly7HZ{HIM(JVamMFQD;TDsQ#mkQ6fh2d;QrlM!Ui| zQq(ufIHnRtkD6Z+%&O8M30LU|2Zrjg^Q1ZPdg0 zmLM|IOqJLXGe?vS z%mFIRR_HqPA)B@eoF~AvZ4-TWgIs^wRQKF$(pf9{Nzr_%tv)wBF%9RldA7DT_%zDsDJGU58aoyer*g9;6Q$h*New=$15m4rEu=h?{;;oCNvV>6>D16G zxeV*{l%6W#AUm#+FTZH)4vF%I`@e$?pWMDI2U#)GwcACY@VHvBKaY^!A=It(UmmO# zowf?xYI0sE_*qio#s*RL0csMDZLi$H<^W`A6QO)5*F27#oi;q&tDJnqQ#hVoy*s2C z&q&-*vBpDOIJR6VzTQ&kSubN&jd>L+z!jynYvv+A)HG^z-znOjI9sOY>C?RcR30z@ zsQ5dFxbEXLc?c=#mn&ANbYGF&uhJCt%Nv6Z73b*L4v#9(!GkWrBjUxSqdYsxKa!BX zPMUuFDpnxG&Aq2gZ;8xHmoF6|hFd$x^E`g5i9CW;+6PY5HL$NobW zgCj4$`tT7IkKPmjEi}An^?tXLK572uuC;x&!TBP$WDj$7syI7gc}jKHbi0gpKNxmR3xx35;vAK}-0=4U z3PUop3G2Tq-N{SehAxcLI4{LLOAEdcedxb=(+2+eYCOn}&q5f%e!{&e(+!g10x2)| z_B+NF0ja;A4PFd}kj)xWM`qmt5xQtfWvd`oJ$=x%3#i>L=R#Deq4ks!Q%krFA_yMF zK+%b4k6)dd>yYN%Y=1%iuFysLGyj=i9U>y{2=(#-uN=n#+qze0{50OwWQ%fTb^NGa z(jD%Joq$}jmjxdnpHv=ar#3{=mD(v1FQD$)LeZ@+PzJb!5MzYjszA5>t(hRqSHCok zIQCu&sibA^b_S_{$B6i+x$GCf-#c-_GF4);ix$6~*J}1)G|iak9sRMWRo79a+#^j! zdBurBRS?>~$&ckQBI7bo!10krgAIHQ)AV+nP9UX~3Qj<)#SrdU`FgYCk44_|$;OdO zJ?)FTHo3qt+*acn@QAnNm5?IgZo>8|Mmk2Ulxo`)Ym@>?4ag+R2qyjOl}ViMH$wOj z7XZisKBgJmu9XrSvGD-Y!!}g4J8%KK!1jCxKa|LKviK)axpOa2pRFt^-^t{qq6-^0 z8XCT$tffjyi4QGk<-Pg!}t>6cTfDpjspKHP#SfGo{| zjA0`E3NiE}iJv_lP0=2?I&m-~gsmlnU7$-+E@T{FYqXWS)`gPx2dGW3``u==3>5w8 z*~TGZr}*2;HutuB^8z-FArt`+{7?se_qi*44Qb4xo__2s~yQquwk|!n=Ev zG!*^jX2O#lA-Pu>5$R)&?ag{BttB_jl?fPTn@2jdh5j3fLMethaS8f*pZ58XABz;z zK*Kni%kpfQj1q}mnfdgJbb9EgY2R^0hIH@EAB!%ytegzTW^Xp}6rUS64=U9CU2iScI;^LQ#)c1Ir4JKQMuRT znf->Xm@-CnE*RdejIEQK zj{@)e-6RrrnJH`8uwRHL*2bkG9&FLaSBf>}*2HJD;IGsncv!3xSo=c99VaIk-jQ>v zf}qo0#}9hhR>OR3C8eLd=bKldBw^4kXlEH+` z)Iv`esa*+rI>b75Oy7uduFq_2L(e1mJ;#cRW2yJKnNto4FFt8ZHY8`gG*AoPJIu+~ zUh#B|*N_$mZ`4#B$@E100jelH8YYn)QNtNZwqC!d7>hi_T z2PE;3vZOrmi0nD;y7`X1bwDxbe6GHFz&@k0Vfy-%??0xm&-jlJJ7p?ZPdW zV7swEHL;dac=sV8yf2VVF(#zH;>#VuznrD|;1V(IhJDf>=-ZNr21aY8|4+HPsbbhJ zR~-DkVg+%vqehhxqJJ_N`NaSlAn5XGRiyH9+|SpTa6R< zIbEmxJxQ3wcRWP=%3z}zGbO>2H@zNfQue55C!Aa6=QSLfzQwk{tU~Konf5NYS;$PS_P}+ z&t&#G5kv#-T(zq5`9jwvq*c)*#KDj{ydr%)*rl26bDic~&m57x&gP9gF+wu4Hg}H5 z1J5WY0-Mv7d6mrXoxK0uax)ih=Rg=yCPrwce{d_1V`heVhveusPmI_8P;|q0YaKPd z6x;L~)SoK77@NBRNdtqvWb|n#`<{O=wg|pa=s}hlruE)_qeAyNOGv6vaotYMXYUJ8 zYH!KGwwBAzoF<~^!CD2z4zL_&kBQI z`#@8O*yn+#;$W2GoBG3s)ai4buyEnw)zg*UN$oOjjn2@N^;cT_MmPF8w9wvc!8HK~ zQRYOb^b3S35zb9V`ZJ8fx_6njXVRA6$!v(MN%1PVo@0jB7v*q=;u`Dn1Ar|IO~|t5 z>k1C<8eattNZ-> zX4y}XKpL| zBZV1Ob}3RRBW|JEucEb5}yB`3(Ve?P8aK zgk*AG#uDqKw7>)S*P+L6T=_ky=fQf#UX7UvUl-UDcqQpW$gg2%dht4)R@PrHroyA9 zEvD1gS;VP1wttaarL+x6aT)Ip84j`5JTP32i5Fl0C|{NOVod@N*1?ov!nQyGY1 zI1zwM#=QY9t#%!Ao%0dpe&5oHxi_0{7=XmTCl*N^vajom$k9js@`2fN!Coxv+0XXW zu-|=SM8sttTYxGkdH{I~;y;FdRDtj<#+`}bDxaXgEW2x`RLmT1x{+IT z3sGy~mWPVf#yeFGp987UI#@}B1*lsUW7CAwUjdcdoO>=-i<4$O;f+XW0}buUr|+{b z42;@8p3+G8Flf6jX}!()=jg1L6-yh}GcQJXR}NjML0VUPe^XSS&r|7vT4CbPT&}hB za!KA_>gNwJ<>>e~@cHD4bK{&2F>^`QaKospysZzd7sX)Epv!*+spY#T`;%^NA^EIL zht?8%uCkZ0FS2ltmeK@F+)oY|V#No!OCKO@u+$@ENFzuqVKPnqEM{bmQQsdRDGF)g z_9=J0E)mBwfec>+-;d8y`>q zd8RdA{F^Kvzq&PJ%=8^8^`|sjdlSNAFadQmexR!;I&jh~`(PKV?VEiXs9RfE=Gl3Y zW(}t{a?T8OEmab~e~y0U*9&Ol8f1FC58ZvVT6IM?YCUr39Z=wPsWWbl-Ui_8Wt_ok z6t{L#MF&H(pR{k8YepY-{2)u3b$WJ~d}VmbV#*^WyJ%pY-L5I8DSu(S+>`mTvden? z6k-0g(FDdo*%TssUHEmBDzlrCqP<)DkngK7s!# zV=dJHE5$m)g!(Tys0G4iF+Ui7^7d}0Vvladv}s$-vVA>xdf)rRg3N&71{OE5@885U zDX+WYw`pW)94oONgzD?o{3b`=l7uy=q}|A@LBf2IwI&xtjHHIx@iS7|YvI#)3;mQB zfjBQGayAG%snS|*^X^QM1Na7?4xdAXU3EjxJt2xq8u}(n?CeIQo8!h4Z5)UJXFLh} zyw13M@QrppZ_%{0oSju`@$}TZ>S(?3Rh)&6JXopLc;!rD2W+Y=U>4?6_p~b^_osD2 z_A1@f`;W@rgDc-DPwdWXWNl(nC_28axnoeu<6IEGq&E3u{ZW*Iard?a)L!dZ1u6jr z`Uy8N;6rX%AG{nV{U-?C29f|u75mW#R6)NbR#yKKJ9h@REbV;aq(*j9q-53MQY0%& zw3~zo2lQLZ{p~m1kn-|8ja(*At&k*(-L-fM*lWeLhH zv1DuE3*(NW)?&v?b+5?GZfoOCNgCb0XyUeg(@Yl_@=?X^*KN>9}%JW**(wRyZb!tnRDM;@!snU)ytdvI0v%n8$5 zq)0h4I-{PpD!$s|_8QKoCS^$_lS^8>iu2K`u}PdOh4CP=yBVG|p96EK>qwfXLLg02hgi-e9!-2A z<-NKxpaifRc2)o1xT;TLx6z=fdpPw82?uQ+0lLd-_2GQIX03v!k1-_m$$q!7fV^m~ zE=k>C0jFGV_ip3nG2h>78^?c?<%opH2aItG*WA%IMheM4a^Q<-)tgEYChS>@r_X$HP(gNuU z0cDNRK04B)WKPbG?5`K8&CG*Rm5+d^WN~!_AvQpX`p{U8@U!u!l&|ca``|FNY)6gR zmw&(hEZFVaCHw#>IKrmB^~HCOD`$c>a5(H2wVygQeWD-leO=yGLkfRTVN(|~WXpRN z7{C7`@nzpuL7oUnD9+L>PGC2wW3)6qR0s@jj5z@-kn}6Yx%BYK@lQ=%nKgE5e51W$n<*H2o&D^_ebohnbOJwg!^$Z&4Ioy?qsGvP^!3{b^=K)IJz*i zDA|uHtI}mtA5mNHw&}{)zb&AYGZgy8IXYEUMN}x|p3;n*9VtWN^H}wnV~%YP_HC?) zs)ilq<;1kWu87(y-rK+%$-iM@-$z|fnX%{*Y_HT^pjJZ6&pnS_oi5Evs9dQk2jhrV z$t8~iB3(B3Ra^7XjFBnpHfw)E6t3DRC_gq&^>|s#G}qL8jxMH0J&%oBs~89+g{)*Q z4JsaLiimI|AfwD?ch*AeSBoS(>VlIpPFc)r1x95uy=DYI3{9xr#b8^Fj`=;aZmV?l zJmt5F_n=+QP7I)As~#ZsKNg+c>2jqBVd& zt3-P`4EX>w0$^4$O`0Q6Ak%$FVZe0suK!C5<;35Gg!ERyS4H=4lxs7Ykljn^ zW}N*TF8u`+7Icj@t%|@u8y(oV7Lo#iO?)1=tWJ`};@P}jfFq7I(YKwk0!C{IvcYH* zuHoV*Nd&6Y?{vvkt}l6q3EJVQ>u=dsHOmg=$bPC0MUR8u_&j#~luO5nsR)nZe<=MI zw%S!}dJ?MJe9JGx;+Ui{L%*k;QfBz0UjPEOBtBJ-^l1gw@BA1-b_1jzGdo84~;?Iz(4CCO>?RO7|b4I4lSFk^f z2;UTV6&;|>X7|Vse=P`9kr~c>w^)}lr#4l&!K+hqnIdw=z%azP36(7g)t~*8r{wQg z&`qvj0+FXRCCb>2;+JHC8mmhgJM*k4%(-{HTg8;xoy~7LZlzh zMRlJNXUex5_^MQpZ<}pXsJzR__3U5C7Mm@8MGW)RGBp8L;+ch+(oX}=VfI98P-pET zKL(w;!)eocIiUMHM*q;1 zDYaK)t1^AQ0lD(?+`U9TR{uun45nefeY8B1tmWBr%J~{gIPTehV*#WWEl8_~x@F1zAGh!nFq_ScRCOty3EzA1l0j{mj-YlFWmz--_Da_8%7Q z8OIKW{?2>bxKrP}9Q%C1`)5gA7C5nX$Y-NPxz24GnP9jYcHQ5ieHcyqf^8g~piXiN z`cQh8s(iE1_%*h;h)8>j&c3vuorCt}TXoN$*p66U`Z8!6bN^3r?46#;m)Fh`S&t7P z8~l7(Q}$uy&)#Cw+$7nGVCZzt)$iNe#wR+e1^XddHSEWm?;UpjbGYP<;ftB$+T zmnR@M5Etz*%{7zf`S+=gBz&nh^qZ#8edyPN1@Arjdmssp)J7MiPOqA?XDC|BN9+z`pI;~++%d6iPLhd*{QecdDOxq*`)HO7I;B2Qp&M9oO=_#rvym!d z#H1?4ryHE~b)IhNXMCn;*t=vG-1F$I?BNOM?TWeIw?z7e$Vnu7xopZ;vzO z*0L#a(qf4-DL z=SY$AD)9b3>av&Ri~sa=cBHTrq#jReub0U8NJ0KoZ<|=5QY`4=-y`-J_^3#OLrIQG zhZXgZfP4T;_e6=U2$wIUt+41Gln$V!(Ppbp{nX&{(x=#+TR-EeufT*nriD)KY3A#yR6<9{t^?enF~pj zyV}`OKPmV5a}c&!HgK%<6UXs~-4*=`bT@Gek_5y@XWE!eCBpZDn1rK;?m}3G@&AmS z#(&?p?ZL|QaBSqGNfkN2n5f@YZg&g*OtHJT$3p&l8AXiB2Pg01iPv-pGyeY-k0 z;a&%ZjNE9w-vROC^IG9cKTSR|qFQE>4HheOhwLj*kOC2+rPi4fitX@QAxazd4_;f_ zNWnjO9y7QGwmxRZy#nu0UKMfk5+$Us znPo`A0aiUNQ!k}voY2(AnqOQmVr(h|eu{>lrL2NRbt5EHf9cx)W6|&Auy-pYpJ;tX z;O1}3S~*&&m6P zn*TbEe+(UG^U)cm+nwt#Kh$LRZ*K&zS$Hkp|fz z5}DfMEV8lo$0C-^k@M6B2aPWvOt#Ni7TLs&I{@$`MTl~Ez+1z{XgV#d$DELlxV7eg zRbsF*&h_tujMle4U2cP`JCi>Df{eF~6B0K!M*%V!{tI^YiS%GwJvi5)7IbAkXlR!{ z&?HY#1WBt{)9V({2p`(RWqkw(ox?E94X&`U+LU)&kb{=1w9f?|L?#Sbx#<>iZN3xO zXKUgJm{BQibN_Hn6w%-jMa$xQ(uI9a1^ofte`E#w&J*Y)i*2@tU52zOib`+sWsq#zZ1rnKv{ z7Bf)(*I4+zm#j{mQ8F96K(%kp9KA>r>fGmEFPi?U=ZV*mpK>*f?q3{roN;xPaLi_~ zp-tIu;9|lwym6vWg_}?RYDC9t9IZmp4NIoKn33MkYo+x)1%3$Mk)yu~CHl!RHI3|= zfOdhMOPe8$UiaYDw22ae`IOKEeatL(e#VR(s<|2g0^ZT$Ez$%UjfFrepQ> z5b|LeWA3m$QN)zfI&5f&zPl2Yl#P>#Zp^UHzGktHb}qKb${0 zms2!Kay*ABL*YM?@21EthB%mmJCOY_;t4dIyRvb?5$?7=eh&G$a!Tdv!u*zek#U!t zp*Zr-PR}}$>~(>;V&Ch9!g51u>j$#aM>9&Zo>9_}-#$qs1z40Hi+-Ws1tku3H*d|3 zxN&>AhQUFO*-Zs}{1v@<@yE##I0?2zzIbU#euh5MJ~<46baL#wz^_R-iof#Jf{7+= z8!kLnuS2T7nKiqsVwL{#kkWDreimk*sVZrK?7K@1&nubVfxSL~vK(oCT5wv9A%0hj zd-jn65HR+3hTq~)bg;D-yY&qdqX#w_^Zs+R%-NEcOf;xRKL{G!&OI#)g6!pD?8+kf znH{;4Sc#ZELmOzD*V12!@!X1yZwUN{qT9F9D*-m{OC=vb(fZo+j6S!Ly7b4QtJG#m z`Zvbau&K`WbB4cK-ofT>Zh$BP5DN)4^rP1*LUA>_^0A{ zaQLm}aa9pxpACW5>k8xAEJ{_(v?jFk+P{?Fl_Vd$`-c9YAtO6gs5@-n)N({$J6F0! z^jRGLXf`m@s%1*VO88tE%eF4&P&9g4n#WJ%+=K%;B7J}mpzgs@6>kb-ho|TpoLOVU zZA6XLURZn=-%|uCS@fqm^ikBATd`vw*E+8`ZgS|JVvjd2VUeOEck_{|-D19e_o*Zm z81e9^^kX|}vuu@nQI$lxTluphyv&kPZP6G1s^oxOb9L3k6SY>ST=+Ihh|4Sg!Z{xt z1{aBuJnGXdH{L}prOIGX{k;<_ajH6y-FAbp!)ar#no<@@;%4TYonQL@nEKLyrmn8* zwpMAKs9Ho&Nh?*WQ7Iy$kZ2L3pi*QA21pbXgrJCk0+QS+3Ib~CfXJW`5SfVxLFOo< zD2NP6L>U7D2{Hr{$$0zi^m%`Le>7JGOzyeotiASLYtaj@Z%k5?U=Rfe!&!YwmbYaq zU=j{slG8M-4(F}JVkaVB;{>~rP0Rvi13-~!jpbt23r*ckU@ax#J8O@ml4xx{?=~P^h<;LopL+FH7HqwTRWQREa%LH=09}n3btlenk)o%#6q5^yLJ|PQeLABb z-C-2fUQ+X$x26_{(7Se@YdFhwn{V)SGb$t^>=SWLqlj-&6EIdWXIb>%O+xErC^?i} z(7s|R0mEbOS`M$LSKffvrO3BJsP%#!Th>i2yny?GlBT9Iin?AcX~9WvTu4f{wqPYY zTWRaw5XK70`@=WYTxv{sXracwPL08n-Z5r;=5Vn$k%0_V!pGg-q5-u+vB1%?1m;1X zVaE!$7l&ynr4obtAwF8lE7hL!vY!B%Je%oJez-Pr<5L%u6}uF?4rPM-#cXubZ8bHx z7Eb&(Z~#R+yjL$Vj84RJ?)4Y<`(a^CL5wVUGy6%UU+nT)$4`9-|- zEE{0Sl@mWJiG>prmAqL;`{PbK)B)RQT*T}P;iwq1K(rr;O^=bh*)M3 zi;5eSfO3hY^~t(Im#`wE5e2tx8!DzJN|p@!&7DO0KZ{&Ac%k2fm+y6?W4OoxjweQw zf=|p~N=^$2g4>@Gk($bg4%!p*1lw$Q-V?{Y= zI)ZNVBtrzZ_wvwJU@YR7kfb)QW!L}RpEqZ8Bgt%K!tCCm3$ILt{c8iK6vX?oGtZi8 zcGpeb8L+SBBF`P(*Dgz%-eL$bXj)SqLg#LC&$3c#NbDkK%O)iBL#hq-%PXWvX6q8d<#B7d8UM-ts`dR>&}p11T(kU z_Akxv`3th5HX?!p=-o_}#XZ)MpS!J$TkxbikfUz8=sI}b&X4C0jzpF1@GO@Z1_TLR zeu`?77XBEaV@KF7W#N~@ckbvrM$Ypk#t{@dc?SRBd<)w(c>XzW#~sT6#g|q8e&a)U z>1!{ql$cI_S{3j6obI$wOZ|&;SoCZ>R4@rv!RO(hFmqdBCRzN2dou$(Jp=#0*O!*0 z%TOb=BDrTz!2M?x;G08wVa=tv`|9Bm<7JGThKaq8&Z|7lmE3@l&q+epEV$J%fG@Hi ze2(pWcE441A?Toy@iWi@06sCTfK6^E4f}2gQ$v!h^@S_2OMYB4kSz7YeaTa-b706) znDhDE&{qk+Lk-Xbx#ZKtJpcODf6;Z`#&u#$L>61k8;A&RY>x(@tPsp8hrB9bWVoEN z#(esIZHOVIHWlwY?C-IipqHPP^Q3dX&z-xjmfvM{5RDH~+&I^fo%Q{V$&rui3pd>W z^5kg1@Y2NPH=$qs!-Spqs!p-7l)xI#RsH>myc)@FD`Q_=(WtA}pLq z8Sl4;TghW%I=G!WEEvyMR^y?!Vn^*k*VTspP?Mivz=}~u zxRi56tv&Xof?~~Ji7xOdBFD~hjPC5hi!Tt7htN|uUjAKmVD6;?W$n(3^}D=fp=Y{> zaZ-&PKqhoZ7<)YfjMY@0rUqtp7Qf?_(i{eL0@t6!BF!M_TbPAM$-p#2wHV+SgVmn2(M{DcZm@25!N-!RE_Bne`TYLX zmd^tU!^^>IW$y9h^&Cc?L+XN9`tiv)$ne|vLcuN~`!?2TM~coM-K@^?Vo|psQXVKT z*7@n}<<6Ae{Q>?l0eXvm;@^7M?If9T8}(Z#XqQG$xOd^F+{&s+xL|2fto1P^;R1MwH^>2DYi_e-+Qz{(1Iq;-gawtCP+6Dqbf_J)>jPC2XwYy zgO*xfKNpb7Av}q#S(~M%Url7kR5hE?cA`S$>u;+z#&(u+q>F9x+a$MByIBXj!mrU2 z+ciifW5_Z7kRa-u@db7(izJE)H4bIJ%_ikyOwzDE0g8vL*orPKUQYO9x(HuLnc-Xc z4ZEn0;=?e!LX{TH>->o7cH8odD#rJ$Hv=TWPf>MM?F)+xeWu-;hyIbBpcr3%Aqf5t zq5L-VNksh+wa+A*nH4S>n1Jy}s1Fxn_BJu6&(Zg#gm_P3<|oDvuN!lWR==WHyQg{C zp=#QX8EL|Qka8?AATweB1>-b)3~Rm|0EmmIa(t$*uxRQ8L-V*HSboP)@AQP z)9&24KG=dOUU4M&TSfQk?dbggkD_1jq{8DpHM?eVnMI5Rw-!TSz`O zyifgs<<4Dn#<8=ys;TArwMe-&%Z}wdWbp=5b+7t1WAH@6bW;ueTV3$RflwI(t#o%85;5CO2Ey>LK zb*GEfSj~WjhF4)M)t21>p2Y}*PRh<`)& zccSa;^%*OeS+4I>w+#Ttvu;1vVGn*cjVk;@B-6vJWh9ntJw4tV?DFz+-b83#_y|AF z?&Ed?sonG@AK5=h=5`-{+iACf>zv^aZn*n6Decx0bsOee2C*$E{k&nq<-^?ezI1KH zfu{j&^VHL<{^s}Xx<+3~39VyNL?#Vr>c9_+%GhVmkcxLXqlx5{ak>)_)U>Bzys;GD z9Fy;v_S@TcO&;*=cbumhmD08o+V≫PJyOL#2noO42?ak;#zU>=*qI;o{}k?8jYM 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�-h@x9GW3t>NHF#%4~dx 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 literal 0 HcmV?d00001 diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 766372325..ee8b1a742 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -151,8 +151,65 @@ TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) { EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", true)); } +void VerifyClearCoatScene(const aiScene *scene) { + ASSERT_NE(nullptr, scene); + + ASSERT_TRUE(scene->HasMaterials()); + + // Find a specific Clearcoat material and check the values + const aiString partial_coated("Partial_Coated"); + bool found_partial_coat = false; + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + const aiMaterial *material = scene->mMaterials[i]; + ASSERT_NE(nullptr, material); + if (material->GetName() == partial_coated) { + found_partial_coat = true; + + ai_real clearcoat_factor(0.0f); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat_factor)); + EXPECT_EQ(ai_real(1.0f), clearcoat_factor); + + ai_real clearcoat_rough_factor(0.0f); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat_rough_factor)); + EXPECT_EQ(ai_real(0.03f), clearcoat_rough_factor); + + // Should import the texture as diffuse and as base color + aiString path; + std::array modes; + static const std::array exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_CLEARCOAT_TEXTURE, &path, nullptr, nullptr, + nullptr, nullptr, modes.data())); + EXPECT_STREQ(path.C_Str(), "PartialCoating.png"); + EXPECT_EQ(exp_modes, modes); + } + } + EXPECT_TRUE(found_partial_coat); +} + +TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_clearcoat) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure); + VerifyClearCoatScene(scene); +} + #ifndef ASSIMP_BUILD_NO_EXPORT +TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_clearcoat) { + { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(nullptr, scene); + // Export + EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb")); + } + + // And re-import + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb", aiProcess_ValidateDataStructure); + VerifyClearCoatScene(scene); +} + TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_pbrSpecularGlossiness) { Assimp::Importer importer; Assimp::Exporter exporter; From 230f367ef92c9b9d187a64bf8308e6a56b12ade1 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 15:17:25 +0100 Subject: [PATCH 056/232] Fix glTFv2 texcoord/uv mapping Use the standard property to indicate the UV map index --- code/AssetLib/glTF2/glTF2Exporter.cpp | 21 ++++++----- code/AssetLib/glTF2/glTF2Exporter.h | 2 +- code/AssetLib/glTF2/glTF2Importer.cpp | 3 +- include/assimp/material.h | 4 +-- include/assimp/pbrmaterial.h | 4 +-- test/unit/utglTF2ImportExport.cpp | 50 ++++++++++++++++++++------- 6 files changed, 56 insertions(+), 28 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 3cb3891b0..7e0966aff 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -492,11 +492,14 @@ void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, float& prop, const char mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTex(const aiMaterial& mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, Ref& texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot = 0) { if (mat.GetTextureCount(tt) > 0) { aiString tex; + // Read texcoord (UV map index) + mat.Get(AI_MATKEY_UVWSRC(tt, slot), texCoord); + if (mat.Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { std::string path = tex.C_Str(); @@ -572,21 +575,21 @@ void glTF2Exporter::GetMatTex(const aiMaterial& mat, TextureInfo& prop, aiTextur { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); - if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); - } + //if (texture) { + // GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //} } void glTF2Exporter::GetMatTex(const aiMaterial& mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); GetMatTexProp(mat, prop.scale, "scale", tt, slot); } } @@ -595,10 +598,10 @@ void glTF2Exporter::GetMatTex(const aiMaterial& mat, OcclusionTextureInfo& prop, { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); GetMatTexProp(mat, prop.strength, "strength", tt, slot); } } diff --git a/code/AssetLib/glTF2/glTF2Exporter.h b/code/AssetLib/glTF2/glTF2Exporter.h index edc85d998..f5238297f 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.h +++ b/code/AssetLib/glTF2/glTF2Exporter.h @@ -104,7 +104,7 @@ namespace Assimp void GetTexSampler(const aiMaterial& mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); void GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); void GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTex(const aiMaterial& mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::Ref& texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot); void GetMatTex(const aiMaterial& mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); void GetMatTex(const aiMaterial& mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); void GetMatTex(const aiMaterial& mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index b435f111d..aadc9fb16 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -165,7 +165,8 @@ inline void SetMaterialTextureProperty(std::vector &embeddedTexIdxs, Asset } mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot)); - mat->AddProperty(&prop.texCoord, 1, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(texType, texSlot)); + const int uvIndex = static_cast(prop.texCoord); + mat->AddProperty(&uvIndex, 1, AI_MATKEY_UVWSRC(texType, texSlot)); if (prop.textureTransformSupported) { aiUVTransform transform; diff --git a/include/assimp/material.h b/include/assimp/material.h index f348da369..2024be07f 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -144,9 +144,7 @@ enum aiTextureMapMode { enum aiTextureMapping { /** The mapping coordinates are taken from an UV channel. * - * #AI_MATKEY_UVWSRC property - * - * Specifies from which UV channel + * #AI_MATKEY_UVWSRC property specifies from which UV channel * the texture coordinates are to be taken from (remember, * meshes can have more than one UV channel). */ diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h index c67bcc3b8..93e7e3095 100644 --- a/include/assimp/pbrmaterial.h +++ b/include/assimp/pbrmaterial.h @@ -75,7 +75,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR "$mat.gltf.materialTransmission.transmissionFactor", 0, 0 //#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE aiTextureType_UNKNOWN, 5 -#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" +//#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname" #define _AI_MATKEY_GLTF_MAPPINGID_BASE "$tex.mappingid" #define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE "$tex.mappingfiltermag" @@ -83,7 +83,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define _AI_MATKEY_GLTF_SCALE_BASE "$tex.scale" #define _AI_MATKEY_GLTF_STRENGTH_BASE "$tex.strength" -#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD(type, N) _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N +//#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD(type, N) _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index ee8b1a742..2c000bb37 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -603,32 +603,58 @@ TEST_F(utglTF2ImportExport, sceneMetadata) { } TEST_F(utglTF2ImportExport, texcoords) { + Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", - aiProcess_ValidateDataStructure); + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(scene, nullptr); + ASSERT_TRUE(scene->HasMaterials()); + const aiMaterial *material = scene->mMaterials[0]; + + aiString path; + unsigned int uvIndex = 255; + aiTextureMapMode modes[2]; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 0); + + uvIndex = 255; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 1); +} + +#ifndef ASSIMP_BUILD_NO_EXPORT + +TEST_F(utglTF2ImportExport, texcoords_export) { + { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(scene, nullptr); + ASSERT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf_out.glb")); + } + + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); ASSERT_NE(scene, nullptr); ASSERT_TRUE(scene->HasMaterials()); const aiMaterial *material = scene->mMaterials[0]; aiString path; + unsigned int uvIndex = 255; aiTextureMapMode modes[2]; - EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, - nullptr, nullptr, modes)); + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); EXPECT_STREQ(path.C_Str(), "texture.png"); - - int uvIndex = -1; - EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_DIFFUSE, 0), &uvIndex), aiReturn_SUCCESS); EXPECT_EQ(uvIndex, 0); - // Using manual macro expansion of AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE here. - // The following works with some but not all compilers: - // #define APPLY(X, Y) X(Y) - // ..., APPLY(AI_MATKEY_GLTF_TEXTURE_TEXCOORD, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE), ... - EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_UNKNOWN, 0), &uvIndex), aiReturn_SUCCESS); + uvIndex = 255; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); EXPECT_EQ(uvIndex, 1); } +#endif // ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, recursive_nodes) { Assimp::Importer importer; const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/RecursiveNodes/RecursiveNodes.gltf", aiProcess_ValidateDataStructure); From fbf75963dc73c4bb70001f358989ed8ebb0ce6ac Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 15:25:02 +0100 Subject: [PATCH 057/232] Fix typo Thank you clang! --- code/AssetLib/glTF2/glTF2Exporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 7e0966aff..40ba41c20 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -655,7 +655,7 @@ bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlo // Add any appropriate textures GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - result == result || pbrSG.specularGlossinessTexture.texture; + result = result || pbrSG.specularGlossinessTexture.texture; if (result) { // Likely to always have diffuse From 36c8cdf3dee0f846eef2bd82e8564925ef2077bc Mon Sep 17 00:00:00 2001 From: Evangel Date: Sat, 12 Jun 2021 11:44:28 +1000 Subject: [PATCH 058/232] Add scene metadata for glTF2 files as allowed by the glTF2 specification. --- code/AssetLib/glTF2/glTF2Asset.h | 2 ++ code/AssetLib/glTF2/glTF2Asset.inl | 3 +++ code/AssetLib/glTF2/glTF2Importer.cpp | 6 +++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 6aa0f92ed..b1fba7bd5 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -922,6 +922,8 @@ struct Scene : public Object { std::string name; std::vector> nodes; + CustomExtension extensions; + Scene() {} void Read(Value &obj, Asset &r); }; diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index c4b1e72ee..14088353c 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1803,6 +1803,9 @@ inline void Scene::Read(Value &obj, Asset &r) { this->nodes.push_back(node); } } + if (Value *extensions = FindObject(obj, "extensions")) { + this->extensions = ReadExtensions("extensions", *extensions); + } } inline void Skin::Read(Value &obj, Asset &r) { diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index c62989c3b..9ba6270ed 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1498,7 +1498,8 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) { const bool hasVersion = !a.asset.version.empty(); const bool hasGenerator = !a.asset.generator.empty(); const bool hasCopyright = !a.asset.copyright.empty(); - if (hasVersion || hasGenerator || hasCopyright) { + const bool hasSceneMetadata = a.scene->extensions; + if (hasVersion || hasGenerator || hasCopyright || hasSceneMetadata) { mScene->mMetaData = new aiMetadata; if (hasVersion) { mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version)); @@ -1509,6 +1510,9 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) { if (hasCopyright) { mScene->mMetaData->Add(AI_METADATA_SOURCE_COPYRIGHT, aiString(a.asset.copyright)); } + if (hasSceneMetadata) { + ParseExtensions(mScene->mMetaData, a.scene->extensions); + } } } From 5be2330fbb969e8e8f669fc2f225cd6cb594674c Mon Sep 17 00:00:00 2001 From: Evangel Date: Sat, 12 Jun 2021 12:20:40 +1000 Subject: [PATCH 059/232] Added CustomExtension to glTF2::Object so that all subclasses have it instead of doing it piecemeal. --- code/AssetLib/glTF2/glTF2Asset.h | 95 ++++++++++++++++---------------- 1 file changed, 47 insertions(+), 48 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index b1fba7bd5..4aec48586 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -356,6 +356,51 @@ struct Nullable { isPresent(true) {} }; +struct CustomExtension { + // + // A struct containing custom extension data added to a glTF2 file + // Has to contain Object, Array, String, Double, Uint64, and Int64 at a minimum + // String, Double, Uint64, and Int64 are stored in the Nullables + // Object and Array are stored in the std::vector + // + std::string name; + + Nullable mStringValue; + Nullable mDoubleValue; + Nullable mUint64Value; + Nullable mInt64Value; + Nullable mBoolValue; + + // std::vector handles both Object and Array + Nullable> mValues; + + operator bool() const { + return Size() != 0; + } + + size_t Size() const { + if (mValues.isPresent) { + return mValues.value.size(); + } else if (mStringValue.isPresent || mDoubleValue.isPresent || mUint64Value.isPresent || mInt64Value.isPresent || mBoolValue.isPresent) { + return 1; + } + return 0; + } + + CustomExtension() = default; + + CustomExtension(const CustomExtension &other) + : name(other.name) + , mStringValue(other.mStringValue) + , mDoubleValue(other.mDoubleValue) + , mUint64Value(other.mUint64Value) + , mInt64Value(other.mInt64Value) + , mBoolValue(other.mBoolValue) + , mValues(other.mValues) + { + } +}; + //! Base class for all glTF top-level objects struct Object { int index; //!< The index of this object within its property container @@ -363,6 +408,8 @@ struct Object { std::string id; //!< The globally unique ID used to reference this object std::string name; //!< The user-defined name of this object + CustomExtension extensions; + //! Objects marked as special are not exported (used to emulate the binary body buffer) virtual bool IsSpecial() const { return false; } @@ -834,50 +881,6 @@ struct Mesh : public Object { void Read(Value &pJSON_Object, Asset &pAsset_Root); }; -struct CustomExtension : public Object { - // - // A struct containing custom extension data added to a glTF2 file - // Has to contain Object, Array, String, Double, Uint64, and Int64 at a minimum - // String, Double, Uint64, and Int64 are stored in the Nullables - // Object and Array are stored in the std::vector - // - - Nullable mStringValue; - Nullable mDoubleValue; - Nullable mUint64Value; - Nullable mInt64Value; - Nullable mBoolValue; - - // std::vector handles both Object and Array - Nullable> mValues; - - operator bool() const { - return Size() != 0; - } - - size_t Size() const { - if (mValues.isPresent) { - return mValues.value.size(); - } else if (mStringValue.isPresent || mDoubleValue.isPresent || mUint64Value.isPresent || mInt64Value.isPresent || mBoolValue.isPresent) { - return 1; - } - return 0; - } - - CustomExtension() = default; - - CustomExtension(const CustomExtension &other) - : Object(other) - , mStringValue(other.mStringValue) - , mDoubleValue(other.mDoubleValue) - , mUint64Value(other.mUint64Value) - , mInt64Value(other.mInt64Value) - , mBoolValue(other.mBoolValue) - , mValues(other.mValues) - { - } -}; - struct Node : public Object { std::vector> children; std::vector> meshes; @@ -896,8 +899,6 @@ struct Node : public Object { Ref parent; //!< This is not part of the glTF specification. Used as a helper. - CustomExtension extensions; - Node() {} void Read(Value &obj, Asset &r); }; @@ -922,8 +923,6 @@ struct Scene : public Object { std::string name; std::vector> nodes; - CustomExtension extensions; - Scene() {} void Read(Value &obj, Asset &r); }; From 7f0efa0866920ec15dd5e268befb67125ff7f6c0 Mon Sep 17 00:00:00 2001 From: Evangel Date: Sat, 12 Jun 2021 12:50:44 +1000 Subject: [PATCH 060/232] Added ReadExtensions to glTF2::Object, so all objects now have their extensions read. Importer is the only place that needs to be modified to make them available on the Assimp side now. --- code/AssetLib/glTF2/glTF2Asset.h | 2 + code/AssetLib/glTF2/glTF2Asset.inl | 85 ++++++++++++++++-------------- 2 files changed, 46 insertions(+), 41 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 4aec48586..759411b13 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -424,6 +424,8 @@ struct Object { inline Value *FindArray(Value &val, const char *id); inline Value *FindObject(Value &val, const char *id); inline Value *FindExtension(Value &val, const char *extensionId); + + inline void ReadExtensions(Value &val); }; // diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 135def597..24fc48b3e 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -304,6 +304,43 @@ inline Value *FindObject(Document &doc, const char *memberId) { inline Value *FindExtension(Value &val, const char *extensionId) { return FindExtensionInContext(val, extensionId, "the document"); } + +inline CustomExtension ReadExtensions(const char *name, Value &obj) { + CustomExtension ret; + ret.name = name; + if (obj.IsObject()) { + ret.mValues.isPresent = true; + for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) { + auto &val = it->value; + ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val)); + } + } else if (obj.IsArray()) { + ret.mValues.value.reserve(obj.Size()); + ret.mValues.isPresent = true; + for (unsigned int i = 0; i < obj.Size(); ++i) { + ret.mValues.value.push_back(ReadExtensions(name, obj[i])); + } + } else if (obj.IsNumber()) { + if (obj.IsUint64()) { + ret.mUint64Value.value = obj.GetUint64(); + ret.mUint64Value.isPresent = true; + } else if (obj.IsInt64()) { + ret.mInt64Value.value = obj.GetInt64(); + ret.mInt64Value.isPresent = true; + } else if (obj.IsDouble()) { + ret.mDoubleValue.value = obj.GetDouble(); + ret.mDoubleValue.isPresent = true; + } + } else if (obj.IsString()) { + ReadValue(obj, ret.mStringValue); + ret.mStringValue.isPresent = true; + } else if (obj.IsBool()) { + ret.mBoolValue.value = obj.GetBool(); + ret.mBoolValue.isPresent = true; + } + return ret; +} + } // namespace inline Value *Object::FindString(Value &val, const char *memberId) { @@ -330,6 +367,12 @@ inline Value *Object::FindExtension(Value &val, const char *extensionId) { return FindExtensionInContext(val, extensionId, id.c_str(), name.c_str()); } +inline void Object::ReadExtensions(Value &val) { + if (Value *extensions = FindObject(val, "extensions")) { + this->extensions = glTF2::ReadExtensions("extensions", *extensions); + } +} + #ifdef ASSIMP_ENABLE_DRACO template @@ -569,6 +612,7 @@ Ref LazyDict::Retrieve(unsigned int i) { inst->oIndex = i; ReadMember(obj, "name", inst->name); inst->Read(obj, mAsset); + inst->ReadExtensions(obj); Ref result = Add(inst.release()); mRecursiveReferenceCheck.erase(i); @@ -1683,42 +1727,6 @@ inline void Light::Read(Value &obj, Asset & /*r*/) { } } -inline CustomExtension ReadExtensions(const char *name, Value &obj) { - CustomExtension ret; - ret.name = name; - if (obj.IsObject()) { - ret.mValues.isPresent = true; - for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) { - auto &val = it->value; - ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val)); - } - } else if (obj.IsArray()) { - ret.mValues.value.reserve(obj.Size()); - ret.mValues.isPresent = true; - for (unsigned int i = 0; i < obj.Size(); ++i) { - ret.mValues.value.push_back(ReadExtensions(name, obj[i])); - } - } else if (obj.IsNumber()) { - if (obj.IsUint64()) { - ret.mUint64Value.value = obj.GetUint64(); - ret.mUint64Value.isPresent = true; - } else if (obj.IsInt64()) { - ret.mInt64Value.value = obj.GetInt64(); - ret.mInt64Value.isPresent = true; - } else if (obj.IsDouble()) { - ret.mDoubleValue.value = obj.GetDouble(); - ret.mDoubleValue.isPresent = true; - } - } else if (obj.IsString()) { - ReadValue(obj, ret.mStringValue); - ret.mStringValue.isPresent = true; - } else if (obj.IsBool()) { - ret.mBoolValue.value = obj.GetBool(); - ret.mBoolValue.isPresent = true; - } - return ret; -} - inline void Node::Read(Value &obj, Asset &r) { if (name.empty()) { name = id; @@ -1775,8 +1783,6 @@ inline void Node::Read(Value &obj, Asset &r) { Value *curExtensions = FindObject(obj, "extensions"); if (nullptr != curExtensions) { - this->extensions = ReadExtensions("extensions", *curExtensions); - if (r.extensionsUsed.KHR_lights_punctual) { if (Value *ext = FindObject(*curExtensions, "KHR_lights_punctual")) { Value *curLight = FindUInt(*ext, "light"); @@ -1805,9 +1811,6 @@ inline void Scene::Read(Value &obj, Asset &r) { this->nodes.push_back(node); } } - if (Value *extensions = FindObject(obj, "extensions")) { - this->extensions = ReadExtensions("extensions", *extensions); - } } inline void Skin::Read(Value &obj, Asset &r) { From a7a30baf27fd0e9dac8e512aff1c4a37cefa93eb Mon Sep 17 00:00:00 2001 From: Evangel Date: Sat, 12 Jun 2021 13:08:14 +1000 Subject: [PATCH 061/232] Renamed local variable to avoid shadowing member variable. --- code/AssetLib/glTF2/glTF2Asset.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 24fc48b3e..8175ab428 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -368,8 +368,8 @@ inline Value *Object::FindExtension(Value &val, const char *extensionId) { } inline void Object::ReadExtensions(Value &val) { - if (Value *extensions = FindObject(val, "extensions")) { - this->extensions = glTF2::ReadExtensions("extensions", *extensions); + if (Value *curExtensions = FindObject(val, "extensions")) { + this->extensions = glTF2::ReadExtensions("extensions", *curExtensions); } } From 3de20af3cc214c2459bce5013be61518ef49d7fc Mon Sep 17 00:00:00 2001 From: Evangel Date: Sat, 12 Jun 2021 13:16:53 +1000 Subject: [PATCH 062/232] Renamed glTF2::Object::extensions to customExtensions to avoid shadowing in other subclasses. --- code/AssetLib/glTF2/glTF2Asset.h | 2 +- code/AssetLib/glTF2/glTF2Asset.inl | 2 +- code/AssetLib/glTF2/glTF2Importer.cpp | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 759411b13..25e917712 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -408,7 +408,7 @@ struct Object { std::string id; //!< The globally unique ID used to reference this object std::string name; //!< The user-defined name of this object - CustomExtension extensions; + CustomExtension customExtensions; //! Objects marked as special are not exported (used to emulate the binary body buffer) virtual bool IsSpecial() const { return false; } diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 8175ab428..f88ea359d 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -369,7 +369,7 @@ inline Value *Object::FindExtension(Value &val, const char *extensionId) { inline void Object::ReadExtensions(Value &val) { if (Value *curExtensions = FindObject(val, "extensions")) { - this->extensions = glTF2::ReadExtensions("extensions", *curExtensions); + this->customExtensions = glTF2::ReadExtensions("extensions", *curExtensions); } } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index 9ba6270ed..8ccc6b058 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -489,7 +489,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { "\" does not match the vertex count"); continue; } - + auto componentType = attr.color[c]->componentType; if (componentType == glTF2::ComponentType_FLOAT) { attr.color[c]->ExtractData(aim->mColors[c]); @@ -1002,9 +1002,9 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & } } - if (node.extensions) { + if (node.customExtensions) { ainode->mMetaData = new aiMetadata; - ParseExtensions(ainode->mMetaData, node.extensions); + ParseExtensions(ainode->mMetaData, node.customExtensions); } GetNodeTransform(ainode->mTransformation, node); @@ -1498,7 +1498,7 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) { const bool hasVersion = !a.asset.version.empty(); const bool hasGenerator = !a.asset.generator.empty(); const bool hasCopyright = !a.asset.copyright.empty(); - const bool hasSceneMetadata = a.scene->extensions; + const bool hasSceneMetadata = a.scene->customExtensions; if (hasVersion || hasGenerator || hasCopyright || hasSceneMetadata) { mScene->mMetaData = new aiMetadata; if (hasVersion) { @@ -1511,7 +1511,7 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) { mScene->mMetaData->Add(AI_METADATA_SOURCE_COPYRIGHT, aiString(a.asset.copyright)); } if (hasSceneMetadata) { - ParseExtensions(mScene->mMetaData, a.scene->extensions); + ParseExtensions(mScene->mMetaData, a.scene->customExtensions); } } } From 064ffc625bef301a03a91a0e3ab7104466101605 Mon Sep 17 00:00:00 2001 From: Evangel Date: Mon, 14 Jun 2021 12:21:29 +1000 Subject: [PATCH 063/232] Const qualify aiMetadata::HasKey --- include/assimp/metadata.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/assimp/metadata.h b/include/assimp/metadata.h index 551a9aba4..57fedfbfc 100644 --- a/include/assimp/metadata.h +++ b/include/assimp/metadata.h @@ -432,7 +432,7 @@ struct aiMetadata { /// Check whether there is a metadata entry for the given key. /// \param [in] Key - the key value value to check for. - inline bool HasKey(const char *key) { + inline bool HasKey(const char *key) const { if (nullptr == key) { return false; } From 44763528829fec49c46b6462df0d42a6451191e1 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Thu, 10 Jun 2021 18:13:46 +0100 Subject: [PATCH 064/232] First pass at simplifying glTFv2 PBR Removed 'core' set of GLTF-specific properties --- code/AssetLib/glTF2/glTF2Exporter.cpp | 60 ++++++++++++++------------- code/AssetLib/glTF2/glTF2Importer.cpp | 21 ++++++---- include/assimp/material.h | 40 +++++++++++++++++- include/assimp/pbrmaterial.h | 14 +++---- test/unit/utglTF2ImportExport.cpp | 55 ++++++++++++++++++++---- 5 files changed, 138 insertions(+), 52 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 751508225..dfa62372a 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -645,7 +645,7 @@ void glTF2Exporter::ExportMaterials() m->name = name; - GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_BASE_COLOR); if (!m->pbrMetallicRoughness.baseColorTexture.texture) { //if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture @@ -654,19 +654,19 @@ void glTF2Exporter::ExportMaterials() GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR) != AI_SUCCESS) { + if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_BASE_COLOR) != AI_SUCCESS) { // if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material. //a fallback to any diffuse color should be used instead GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); } - if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { + if (mat->Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0 m->pbrMetallicRoughness.metallicFactor = 0; } // get roughness if source is gltf2 file - if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { + if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { // otherwise, try to derive and convert from specular + shininess values aiColor4D specularColor; ai_real shininess; @@ -712,36 +712,38 @@ void glTF2Exporter::ExportMaterials() } } - bool hasPbrSpecularGlossiness = false; - mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS, hasPbrSpecularGlossiness); - - if (hasPbrSpecularGlossiness) { - - if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { - mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; - } - + { + // If has a Specular color, use the KHR_materials_pbrSpecularGlossiness extension PbrSpecularGlossiness pbrSG; - - GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); - GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR); - - if (mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { - float shininess; - - if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = shininess / 1000; + if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { + if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { + mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; } + + GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); + + // If don't have explicit glossiness then convert from roughness or shininess + if (mat->Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { + float shininess; + if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way + } else if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = shininess / 1000; + } + } + + // Add any appropriate textures + GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); + GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); + + m->pbrSpecularGlossiness = Nullable(pbrSG); } - - GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); - GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - - m->pbrSpecularGlossiness = Nullable(pbrSG); } - bool unlit; - if (mat->Get(AI_MATKEY_GLTF_UNLIT, unlit) == AI_SUCCESS && unlit) { + // glTFv2 is either PBR or Unlit + aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; + mat->Get(AI_MATKEY_SHADING_MODEL, shadingMode); + if (shadingMode == aiShadingMode_Unlit) { mAsset->extensionsUsed.KHR_materials_unlit = true; m->unlit = true; } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index c62989c3b..b5ba65857 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -238,16 +238,18 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M aimat->AddProperty(&str, AI_MATKEY_NAME); } + // Set Assimp DIFFUSE and BASE COLOR to the pbrMetallicRoughness base color and texture for backwards compatibility + // Technically should not load any pbrMetallicRoughness if extensionsRequired contains KHR_materials_pbrSpecularGlossiness SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); - SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR); + SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_BASE_COLOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_BASE_COLOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR); - aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR); + aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_METALLIC_FACTOR); + aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_ROUGHNESS_FACTOR); float roughnessAsShininess = 1 - mat.pbrMetallicRoughness.roughnessFactor; roughnessAsShininess *= roughnessAsShininess * 1000; @@ -268,22 +270,27 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M if (mat.pbrSpecularGlossiness.isPresent) { PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value; - aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS); SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_COLOR_SPECULAR); float glossinessAsShininess = pbrSG.glossinessFactor * 1000.0f; aimat->AddProperty(&glossinessAsShininess, 1, AI_MATKEY_SHININESS); - aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR); + aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLOSSINESS_FACTOR); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, aiTextureType_DIFFUSE); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, aiTextureType_SPECULAR); } + + // glTFv2 is either PBR or Unlit + aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; if (mat.unlit) { - aimat->AddProperty(&mat.unlit, 1, AI_MATKEY_GLTF_UNLIT); + shadingMode = aiShadingMode_Unlit; } + aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL); + + //KHR_materials_sheen if (mat.materialSheen.isPresent) { MaterialSheen &sheen = mat.materialSheen.value; diff --git a/include/assimp/material.h b/include/assimp/material.h index 08c0491c0..11cdef1f4 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -202,11 +202,15 @@ enum aiTextureType { /** The texture is combined with the result of the diffuse * lighting equation. + * OR + * PBR Specular/Glossiness */ aiTextureType_DIFFUSE = 1, /** The texture is combined with the result of the specular * lighting equation. + * OR + * PBR Specular/Glossiness */ aiTextureType_SPECULAR = 2, @@ -309,7 +313,9 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); // --------------------------------------------------------------------------- /** @brief Defines all shading models supported by the library - * + * + * #AI_MATKEY_SHADING_MODEL + * * 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 @@ -364,13 +370,27 @@ enum aiShadingMode { aiShadingMode_CookTorrance = 0x8, /** No shading at all. Constant light influence of 1.0. + * Also known as "Unlit" */ aiShadingMode_NoShading = 0x9, + aiShadingMode_Unlit = aiShadingMode_NoShading, // Alias /** Fresnel shading */ aiShadingMode_Fresnel = 0xa, + /** Physically-Based Rendering (PBR) shading using + * Bidirectional scattering/reflectance distribution function (BSDF/BRDF) + * There are multiple methods under this banner, and model files may provide + * data for more than one PBR-BRDF method. + * Applications should use the set of provided properties to determine which + * of their preferred PBDR methods are available + * eg: + * - If AI_MATKEY_METALLIC_FACTOR is set, then a Metallic/Roughness is available + * - If AI_MATKEY_COLOR_SPECULAR is set, then a Specular/Glossiness is available + */ + aiShadingMode_PBR_BRDF = 0xb, + #ifndef SWIG _aiShadingMode_Force32Bit = INT_MAX #endif @@ -923,11 +943,29 @@ extern "C" { // --------------------------------------------------------------------------- // PBR material support #define AI_MATKEY_USE_COLOR_MAP "$mat.useColorMap", 0, 0 + +// Metallic/Roughness Workflow +// --------------------------- +// Base color factor. Will be multiplied by final base color texture values if extant #define AI_MATKEY_BASE_COLOR "$clr.base", 0, 0 #define AI_MATKEY_USE_METALLIC_MAP "$mat.useMetallicMap", 0, 0 +// Metallic factor. 0.0 = Full Dielectric, 1.0 = Full Metal #define AI_MATKEY_METALLIC_FACTOR "$mat.metallicFactor", 0, 0 #define AI_MATKEY_USE_ROUGHNESS_MAP "$mat.useRoughnessMap", 0, 0 +// Roughness factor. 0.0 = Perfectly Smooth, 1.0 = Completely Rough #define AI_MATKEY_ROUGHNESS_FACTOR "$mat.roughnessFactor", 0, 0 + +// Specular/Glossiness Workflow +// --------------------------- +// Diffuse/Albedo Color. Note: Pure Metals have a diffuse of {0,0,0} +// AI_MATKEY_COLOR_DIFFUSE +// Specular Color +// AI_MATKEY_COLOR_SPECULAR +// Glossiness factor. 0.0 = Completely Rough, 1.0 = Perfectly Smooth +#define AI_MATKEY_GLOSSINESS_FACTOR "$mat.glossinessFactor", 0, 0 + +// Emissive +// -------- #define AI_MATKEY_USE_EMISSIVE_MAP "$mat.useEmissiveMap", 0, 0 #define AI_MATKEY_EMISSIVE_INTENSITY "$mat.emissiveIntensity", 0, 0 #define AI_MATKEY_USE_AO_MAP "$mat.useAOMap", 0, 0 diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h index 2e41b8b6d..fd904e4fc 100644 --- a/include/assimp/pbrmaterial.h +++ b/include/assimp/pbrmaterial.h @@ -50,16 +50,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # pragma GCC system_header #endif -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0 #define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 #define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0 -#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 -#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 -#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0 +//#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 +//#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 +//#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0 #define AI_MATKEY_GLTF_MATERIAL_SHEEN "$mat.gltf.materialSheen", 0, 0 #define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR "$mat.gltf.materialSheen.sheenColorFactor", 0, 0 #define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR "$mat.gltf.materialSheen.sheenRoughnessFactor", 0, 0 diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 4110edcfc..e0ac10ad5 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -57,10 +57,9 @@ using namespace Assimp; class utglTF2ImportExport : public AbstractImportExportBase { public: - virtual bool importerTest() { + virtual bool importerMatTest(const char *file, bool spec_gloss, std::array exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", - aiProcess_ValidateDataStructure); + const aiScene *scene = importer.ReadFile(file, aiProcess_ValidateDataStructure); EXPECT_NE(scene, nullptr); if (!scene) { return false; @@ -72,13 +71,49 @@ public: } const aiMaterial *material = scene->mMaterials[0]; + // This Material should be a PBR + aiShadingMode shadingMode; + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_SHADING_MODEL, shadingMode)); + EXPECT_EQ(aiShadingMode_PBR_BRDF, shadingMode); + + // Should import the texture as diffuse and as base color aiString path; - aiTextureMapMode modes[2]; + std::array modes; EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, - nullptr, nullptr, modes)); + nullptr, nullptr, modes.data())); EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png"); - EXPECT_EQ(modes[0], aiTextureMapMode_Mirror); - EXPECT_EQ(modes[1], aiTextureMapMode_Clamp); + EXPECT_EQ(exp_modes, modes); + + // Also as Base Color + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_BASE_COLOR, 0, &path, nullptr, nullptr, + nullptr, nullptr, modes.data())); + EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png"); + EXPECT_EQ(exp_modes, modes); + + // Should have a MetallicFactor (default is 1.0) + ai_real metal_factor = ai_real(0.5); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_METALLIC_FACTOR, metal_factor)); + EXPECT_EQ(ai_real(0.0), metal_factor); + + // And a roughness factor (default is 1.0) + ai_real roughness_factor = ai_real(0.5); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_ROUGHNESS_FACTOR, roughness_factor)); + EXPECT_EQ(ai_real(1.0), roughness_factor); + + aiColor3D spec_color = { 0, 0, 0 }; + ai_real glossiness = ai_real(0.5); + if (spec_gloss) { + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color)); + constexpr ai_real spec_val(0.20000000298023225); // From the file + EXPECT_EQ(spec_val, spec_color.r); + EXPECT_EQ(spec_val, spec_color.g); + EXPECT_EQ(spec_val, spec_color.b); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness)); + EXPECT_EQ(ai_real(1.0), glossiness); + } else { + EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color)); + EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness)); + } return true; } @@ -105,13 +140,17 @@ public: }; TEST_F(utglTF2ImportExport, importglTF2FromFileTest) { - EXPECT_TRUE(importerTest()); + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", false, {aiTextureMapMode_Mirror, aiTextureMapMode_Clamp})); } TEST_F(utglTF2ImportExport, importBinaryglTF2FromFileTest) { EXPECT_TRUE(binaryImporterTest()); } +TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) { + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", true)); +} + #ifndef ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) { Assimp::Importer importer; From 4a66ec25d0371f694e3aa41f3a0996c24a606651 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 13:44:52 +0100 Subject: [PATCH 065/232] Standardise Clearcoat, Sheen and Transmission Also cleanup glTFv2 defaults, don't import/export if disabled --- code/AssetLib/glTF2/glTF2Exporter.cpp | 236 +++++++++++++++----------- code/AssetLib/glTF2/glTF2Exporter.h | 26 ++- code/AssetLib/glTF2/glTF2Importer.cpp | 39 ++--- code/Common/material.cpp | 12 +- include/assimp/material.h | 77 ++++++++- include/assimp/pbrmaterial.h | 28 +-- test/unit/utglTF2ImportExport.cpp | 15 ++ 7 files changed, 281 insertions(+), 152 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index dfa62372a..3cb3891b0 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -436,11 +436,11 @@ inline void SetSamplerWrap(SamplerWrap& wrap, aiTextureMapMode map) }; } -void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetTexSampler(const aiMaterial& mat, Ref texture, aiTextureType tt, unsigned int slot) { aiString aId; std::string id; - if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) { + if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) { id = aId.C_Str(); } @@ -455,49 +455,49 @@ void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, a SamplerMagFilter filterMag; SamplerMinFilter filterMin; - if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) { SetSamplerWrap(texture->sampler->wrapS, mapU); } - if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) { SetSamplerWrap(texture->sampler->wrapT, mapV); } - if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) { texture->sampler->magFilter = filterMag; } - if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) { texture->sampler->minFilter = filterMin; } aiString name; - if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) { + if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) { texture->sampler->name = name.C_Str(); } } } -void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot) { std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - mat->Get(textureKey.c_str(), tt, slot, prop); + mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot) { std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - mat->Get(textureKey.c_str(), tt, slot, prop); + mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) { - if (mat->GetTextureCount(tt) > 0) { + if (mat.GetTextureCount(tt) > 0) { aiString tex; - if (mat->Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { std::string path = tex.C_Str(); if (path.size() > 0) { @@ -568,7 +568,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; @@ -579,7 +579,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, TextureInfo& prop, aiTextur } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; @@ -591,7 +591,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, NormalTextureInfo& prop, ai } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; @@ -603,10 +603,10 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, OcclusionTextureInfo& prop, } } -aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const char* propName, int type, int idx) +aiReturn glTF2Exporter::GetMatColor(const aiMaterial& mat, vec4& prop, const char* propName, int type, int idx) const { aiColor4D col; - aiReturn result = mat->Get(propName, type, idx, col); + aiReturn result = mat.Get(propName, type, idx, col); if (result == AI_SUCCESS) { prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; prop[3] = col.a; @@ -615,30 +615,109 @@ aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const cha return result; } -aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec3& prop, const char* propName, int type, int idx) +aiReturn glTF2Exporter::GetMatColor(const aiMaterial& mat, vec3& prop, const char* propName, int type, int idx) const { aiColor3D col; - aiReturn result = mat->Get(propName, type, idx, col); + aiReturn result = mat.Get(propName, type, idx, col); if (result == AI_SUCCESS) { - prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; + prop[0] = col.r; + prop[1] = col.g; + prop[2] = col.b; } return result; } +bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG) { + bool result = false; + // If has Glossiness, a Specular Color or Specular Texture, use the KHR_materials_pbrSpecularGlossiness extension + // NOTE: This extension is being considered for deprecation (Dec 2020), may be replaced by KHR_material_specular + + if (mat.Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) == AI_SUCCESS) { + result = true; + } else { + // Don't have explicit glossiness, convert from pbr roughness or legacy shininess + float shininess; + if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way + } else if (mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = shininess / 1000; + } + } + + if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { + result = true; + } + // Add any appropriate textures + GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); + + result == result || pbrSG.specularGlossinessTexture.texture; + + if (result) { + // Likely to always have diffuse + GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); + GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); + } + + return result; +} + +bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen) { + // Return true if got any valid Sheen properties or textures + if (GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_SHEEN_COLOR_FACTOR) != aiReturn_SUCCESS) + return false; + + // Default Sheen color factor {0,0,0} disables Sheen, so do not export + if (sheen.sheenColorFactor == defaultSheenFactor) + return false; + + mat.Get(AI_MATKEY_SHEEN_ROUGHNESS_FACTOR, sheen.sheenRoughnessFactor); + + GetMatTex(mat, sheen.sheenColorTexture, AI_MATKEY_SHEEN_COLOR_TEXTURE); + GetMatTex(mat, sheen.sheenRoughnessTexture, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE); + + return true; +} + +bool glTF2Exporter::GetMatClearcoat(const aiMaterial &mat, glTF2::MaterialClearcoat &clearcoat) { + if (mat.Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat.clearcoatFactor) != aiReturn_SUCCESS) { + return false; + } + + // Clearcoat factor of zero disables Clearcoat, so do not export + if (clearcoat.clearcoatFactor == 0.0f) + return false; + + mat.Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat.clearcoatRoughnessFactor); + + GetMatTex(mat, clearcoat.clearcoatTexture, AI_MATKEY_CLEARCOAT_TEXTURE); + GetMatTex(mat, clearcoat.clearcoatRoughnessTexture, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE); + GetMatTex(mat, clearcoat.clearcoatNormalTexture, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE); + + return true; +} + +bool glTF2Exporter::GetMatTransmission(const aiMaterial &mat, glTF2::MaterialTransmission &transmission) { + bool result = mat.Get(AI_MATKEY_TRANSMISSION_FACTOR, transmission.transmissionFactor) == aiReturn_SUCCESS; + GetMatTex(mat, transmission.transmissionTexture, AI_MATKEY_TRANSMISSION_TEXTURE); + return result || transmission.transmissionTexture.texture; +} + void glTF2Exporter::ExportMaterials() { aiString aiName; for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) { - const aiMaterial* mat = mScene->mMaterials[i]; + ai_assert(mScene->mMaterials[i] != nullptr); + + const aiMaterial & mat = *(mScene->mMaterials[i]); std::string id = "material_" + ai_to_string(i); Ref m = mAsset->materials.Create(id); std::string name; - if (mat->Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) { name = aiName.C_Str(); } name = mAsset->FindUniqueID(name, "material"); @@ -660,20 +739,20 @@ void glTF2Exporter::ExportMaterials() GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); } - if (mat->Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { + if (mat.Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0 m->pbrMetallicRoughness.metallicFactor = 0; } // get roughness if source is gltf2 file - if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { + if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { // otherwise, try to derive and convert from specular + shininess values aiColor4D specularColor; ai_real shininess; if ( - mat->Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && - mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS + mat.Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && + mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS ) { // convert specular color to luminance float specularIntensity = specularColor[0] * 0.2125f + specularColor[1] * 0.7154f + specularColor[2] * 0.0721f; @@ -694,17 +773,17 @@ void glTF2Exporter::ExportMaterials() GetMatTex(mat, m->emissiveTexture, aiTextureType_EMISSIVE); GetMatColor(mat, m->emissiveFactor, AI_MATKEY_COLOR_EMISSIVE); - mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided); - mat->Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); + mat.Get(AI_MATKEY_TWOSIDED, m->doubleSided); + mat.Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); aiString alphaMode; - if (mat->Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { m->alphaMode = alphaMode.C_Str(); } else { float opacity; - if (mat->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { if (opacity < 1) { m->alphaMode = "BLEND"; m->pbrMetallicRoughness.baseColorFactor[3] *= opacity; @@ -713,86 +792,43 @@ void glTF2Exporter::ExportMaterials() } { - // If has a Specular color, use the KHR_materials_pbrSpecularGlossiness extension + // KHR_materials_pbrSpecularGlossiness extension + // NOTE: This extension is being considered for deprecation (Dec 2020) PbrSpecularGlossiness pbrSG; - if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { - if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { - mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; - } - - GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); - - // If don't have explicit glossiness then convert from roughness or shininess - if (mat->Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { - float shininess; - if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way - } else if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = shininess / 1000; - } - } - - // Add any appropriate textures - GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); - GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - + if (GetMatSpecGloss(mat, pbrSG)) { + mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; m->pbrSpecularGlossiness = Nullable(pbrSG); } } // glTFv2 is either PBR or Unlit aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; - mat->Get(AI_MATKEY_SHADING_MODEL, shadingMode); + mat.Get(AI_MATKEY_SHADING_MODEL, shadingMode); if (shadingMode == aiShadingMode_Unlit) { mAsset->extensionsUsed.KHR_materials_unlit = true; m->unlit = true; - } + } else { + // These extensions are not compatible with KHR_materials_unlit or KHR_materials_pbrSpecularGlossiness + if (!m->pbrSpecularGlossiness.isPresent) { + // Sheen + MaterialSheen sheen; + if (GetMatSheen(mat, sheen)) { + mAsset->extensionsUsed.KHR_materials_sheen = true; + m->materialSheen = Nullable(sheen); + } - bool hasMaterialSheen = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_SHEEN, hasMaterialSheen); + MaterialClearcoat clearcoat; + if (GetMatClearcoat(mat, clearcoat)) { + mAsset->extensionsUsed.KHR_materials_clearcoat = true; + m->materialClearcoat = Nullable(clearcoat); + } - if (hasMaterialSheen) { - mAsset->extensionsUsed.KHR_materials_sheen = true; - - MaterialSheen sheen; - - GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR); - mat->Get(AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR, sheen.sheenRoughnessFactor); - GetMatTex(mat, sheen.sheenColorTexture, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE); - GetMatTex(mat, sheen.sheenRoughnessTexture, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE); - - m->materialSheen = Nullable(sheen); - } - - bool hasMaterialClearcoat = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT, hasMaterialClearcoat); - - if (hasMaterialClearcoat) { - mAsset->extensionsUsed.KHR_materials_clearcoat= true; - - MaterialClearcoat clearcoat; - - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR, clearcoat.clearcoatFactor); - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat.clearcoatRoughnessFactor); - GetMatTex(mat, clearcoat.clearcoatTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE); - GetMatTex(mat, clearcoat.clearcoatRoughnessTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE); - GetMatTex(mat, clearcoat.clearcoatNormalTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE); - - m->materialClearcoat = Nullable(clearcoat); - } - - bool hasMaterialTransmission = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_TRANSMISSION, hasMaterialTransmission); - - if (hasMaterialTransmission) { - mAsset->extensionsUsed.KHR_materials_transmission = true; - - MaterialTransmission transmission; - - mat->Get(AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR, transmission.transmissionFactor); - GetMatTex(mat, transmission.transmissionTexture, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE); - - m->materialTransmission = Nullable(transmission); + MaterialTransmission transmission; + if (GetMatTransmission(mat, transmission)) { + mAsset->extensionsUsed.KHR_materials_transmission = true; + m->materialTransmission = Nullable(transmission); + } + } } } } diff --git a/code/AssetLib/glTF2/glTF2Exporter.h b/code/AssetLib/glTF2/glTF2Exporter.h index 86497516a..edc85d998 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.h +++ b/code/AssetLib/glTF2/glTF2Exporter.h @@ -72,6 +72,10 @@ namespace glTF2 struct OcclusionTextureInfo; struct Node; struct Texture; + struct PbrSpecularGlossiness; + struct MaterialSheen; + struct MaterialClearcoat; + struct MaterialTransmission; // Vec/matrix types, as raw float arrays typedef float (vec2)[2]; @@ -97,15 +101,19 @@ namespace Assimp protected: void WriteBinaryData(IOStream* outfile, std::size_t sceneLength); - void GetTexSampler(const aiMaterial* mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); - void GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTex(const aiMaterial* mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); - aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec4& prop, const char* propName, int type, int idx); - aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec3& prop, const char* propName, int type, int idx); + void GetTexSampler(const aiMaterial& mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); + void GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); + void GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); + void GetMatTex(const aiMaterial& mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); + aiReturn GetMatColor(const aiMaterial& mat, glTF2::vec4& prop, const char* propName, int type, int idx) const; + aiReturn GetMatColor(const aiMaterial& mat, glTF2::vec3& prop, const char* propName, int type, int idx) const; + bool GetMatSpecGloss(const aiMaterial& mat, glTF2::PbrSpecularGlossiness& pbrSG); + bool GetMatSheen(const aiMaterial& mat, glTF2::MaterialSheen& sheen); + bool GetMatClearcoat(const aiMaterial& mat, glTF2::MaterialClearcoat& clearcoat); + bool GetMatTransmission(const aiMaterial& mat, glTF2::MaterialTransmission& transmission); void ExportMetadata(); void ExportMaterials(); void ExportMeshes(); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index b5ba65857..b0f0955f5 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -291,36 +291,37 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL); - //KHR_materials_sheen + // KHR_materials_sheen if (mat.materialSheen.isPresent) { MaterialSheen &sheen = mat.materialSheen.value; - - aimat->AddProperty(&mat.materialSheen.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_SHEEN); - SetMaterialColorProperty(r, sheen.sheenColorFactor, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR); - aimat->AddProperty(&sheen.sheenRoughnessFactor, 1, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenColorTexture, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenRoughnessTexture, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE); + // Default value {0,0,0} disables Sheen + if (sheen.sheenColorFactor != defaultSheenFactor) { + SetMaterialColorProperty(r, sheen.sheenColorFactor, aimat, AI_MATKEY_SHEEN_COLOR_FACTOR); + aimat->AddProperty(&sheen.sheenRoughnessFactor, 1, AI_MATKEY_SHEEN_ROUGHNESS_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenColorTexture, aimat, AI_MATKEY_SHEEN_COLOR_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenRoughnessTexture, aimat, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE); + } } - //KHR_materials_clearcoat + // KHR_materials_clearcoat if (mat.materialClearcoat.isPresent) { MaterialClearcoat &clearcoat = mat.materialClearcoat.value; - - aimat->AddProperty(&mat.materialClearcoat.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT); - aimat->AddProperty(&clearcoat.clearcoatFactor, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR); - aimat->AddProperty(&clearcoat.clearcoatRoughnessFactor, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatRoughnessTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatNormalTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE); + // Default value 0.0 disables clearcoat + if (clearcoat.clearcoatFactor != 0.0f) { + aimat->AddProperty(&clearcoat.clearcoatFactor, 1, AI_MATKEY_CLEARCOAT_FACTOR); + aimat->AddProperty(&clearcoat.clearcoatRoughnessFactor, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatTexture, aimat, AI_MATKEY_CLEARCOAT_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatRoughnessTexture, aimat, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatNormalTexture, aimat, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE); + } } - //KHR_materials_transmission + // KHR_materials_transmission if (mat.materialTransmission.isPresent) { MaterialTransmission &transmission = mat.materialTransmission.value; - aimat->AddProperty(&mat.materialTransmission.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION); - aimat->AddProperty(&transmission.transmissionFactor, 1, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, transmission.transmissionTexture, aimat, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE); + aimat->AddProperty(&transmission.transmissionFactor, 1, AI_MATKEY_TRANSMISSION_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, transmission.transmissionTexture, aimat, AI_MATKEY_TRANSMISSION_TEXTURE); } return aimat; diff --git a/code/Common/material.cpp b/code/Common/material.cpp index 5230c8b43..6c90e66f0 100644 --- a/code/Common/material.cpp +++ b/code/Common/material.cpp @@ -47,10 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include // ------------------------------------------------------------------------------- -const char* TextureTypeToString(aiTextureType in) -{ - switch (in) - { +const char *TextureTypeToString(aiTextureType in) { + switch (in) { case aiTextureType_NONE: return "n/a"; case aiTextureType_DIFFUSE: @@ -87,6 +85,12 @@ const char* TextureTypeToString(aiTextureType in) return "DiffuseRoughness"; case aiTextureType_AMBIENT_OCCLUSION: return "AmbientOcclusion"; + case aiTextureType_SHEEN: + return "Sheen"; + case aiTextureType_CLEARCOAT: + return "Clearcoat"; + case aiTextureType_TRANSMISSION: + return "Transmission"; case aiTextureType_UNKNOWN: return "Unknown"; default: diff --git a/include/assimp/material.h b/include/assimp/material.h index 11cdef1f4..33e39529e 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -144,7 +144,9 @@ enum aiTextureMapMode { enum aiTextureMapping { /** The mapping coordinates are taken from an UV channel. * - * The #AI_MATKEY_UVWSRC key specifies from which UV channel + * #AI_MATKEY_UVWSRC property + * + * Specifies from which UV channel * the texture coordinates are to be taken from (remember, * meshes can have more than one UV channel). */ @@ -292,6 +294,32 @@ enum aiTextureType { aiTextureType_DIFFUSE_ROUGHNESS = 16, aiTextureType_AMBIENT_OCCLUSION = 17, + /** PBR Material Modifiers + * Some modern renderers have further PBR modifiers that may be overlaid + * on top of the 'base' PBR materials for additional realism. + * These use multiple texture maps, so only the base type is directly defined + */ + + /** Sheen + * Generally used to simulate textiles that are covered in a layer of microfibers + * eg velvet + * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_sheen + */ + aiTextureType_SHEEN = 19, + + /** Clearcoat + * Simulates a layer of 'polish' or 'laquer' layered on top of a PBR substrate + * https://autodesk.github.io/standard-surface/#closures/coating + * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat + */ + aiTextureType_CLEARCOAT = 20, + + /** Transmission + * Simulates transmission through the surface + * May include further information such as wall thickness + */ + aiTextureType_TRANSMISSION = 21, + /** Unknown texture * * A texture reference that does not match any of the definitions @@ -314,7 +342,7 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); // --------------------------------------------------------------------------- /** @brief Defines all shading models supported by the library * - * #AI_MATKEY_SHADING_MODEL + * Property: #AI_MATKEY_SHADING_MODEL * * The list of shading modes has been taken from Blender. * See Blender documentation for more information. The API does @@ -324,6 +352,7 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); * 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 modeler which wrote a particular model as closely as possible. + * */ enum aiShadingMode { /** Flat shading. Shading is done on per-face base, @@ -384,10 +413,11 @@ enum aiShadingMode { * There are multiple methods under this banner, and model files may provide * data for more than one PBR-BRDF method. * Applications should use the set of provided properties to determine which - * of their preferred PBDR methods are available + * of their preferred PBR rendering methods are likely to be available * eg: * - If AI_MATKEY_METALLIC_FACTOR is set, then a Metallic/Roughness is available - * - If AI_MATKEY_COLOR_SPECULAR is set, then a Specular/Glossiness is available + * - If AI_MATKEY_GLOSSINESS_FACTOR is set, then a Specular/Glossiness is available + * Note that some PBR methods allow layering of techniques */ aiShadingMode_PBR_BRDF = 0xb, @@ -942,28 +972,63 @@ extern "C" { // --------------------------------------------------------------------------- // PBR material support +// -------------------- +// Properties defining PBR rendering techniques #define AI_MATKEY_USE_COLOR_MAP "$mat.useColorMap", 0, 0 // Metallic/Roughness Workflow // --------------------------- -// Base color factor. Will be multiplied by final base color texture values if extant +// Base RGBA color factor. Will be multiplied by final base color texture values if extant +// Note: Importers may choose to copy this into AI_MATKEY_COLOR_DIFFUSE for compatibility +// with renderers and formats that do not support Metallic/Roughness PBR #define AI_MATKEY_BASE_COLOR "$clr.base", 0, 0 +#define AI_MATKEY_BASE_COLOR_TEXTURE aiTextureType_BASE_COLOR, 0 #define AI_MATKEY_USE_METALLIC_MAP "$mat.useMetallicMap", 0, 0 // Metallic factor. 0.0 = Full Dielectric, 1.0 = Full Metal #define AI_MATKEY_METALLIC_FACTOR "$mat.metallicFactor", 0, 0 +#define AI_MATKEY_METALLIC_TEXTURE aiTextureType_METALNESS, 0 #define AI_MATKEY_USE_ROUGHNESS_MAP "$mat.useRoughnessMap", 0, 0 // Roughness factor. 0.0 = Perfectly Smooth, 1.0 = Completely Rough #define AI_MATKEY_ROUGHNESS_FACTOR "$mat.roughnessFactor", 0, 0 +#define AI_MATKEY_ROUGHNESS_TEXTURE aiTextureType_DIFFUSE_ROUGHNESS, 0 // Specular/Glossiness Workflow // --------------------------- // Diffuse/Albedo Color. Note: Pure Metals have a diffuse of {0,0,0} // AI_MATKEY_COLOR_DIFFUSE -// Specular Color +// Specular Color. +// Note: Metallic/Roughness may also have a Specular Color // AI_MATKEY_COLOR_SPECULAR +#define AI_MATKEY_SPECULAR_FACTOR "$mat.specularFactor", 0, 0 // Glossiness factor. 0.0 = Completely Rough, 1.0 = Perfectly Smooth #define AI_MATKEY_GLOSSINESS_FACTOR "$mat.glossinessFactor", 0, 0 +// Sheen +// ----- +// Sheen base RGB color. Default {0,0,0} +#define AI_MATKEY_SHEEN_COLOR_FACTOR "$clr.sheen.factor", 0, 0 +// Sheen Roughness Factor. +#define AI_MATKEY_SHEEN_ROUGHNESS_FACTOR "$mat.sheen.roughnessFactor", 0, 0 +#define AI_MATKEY_SHEEN_COLOR_TEXTURE aiTextureType_SHEEN, 0 +#define AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE aiTextureType_SHEEN, 1 + +// Clearcoat +// --------- +#define AI_MATKEY_CLEARCOAT_FACTOR "$clr.clearcoat.factor", 0, 0 +#define AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR "$mat.clearcoat.roughnessFactor", 0, 0 +#define AI_MATKEY_CLEARCOAT_TEXTURE aiTextureType_CLEARCOAT, 0 +#define AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_CLEARCOAT, 1 +#define AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE aiTextureType_CLEARCOAT, 2 + +// Transmission +// ------------ +// https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission +// Base percentage of light transmitted through the surface. 0.0 = Opaque, 1.0 = Fully transparent +#define AI_MATKEY_TRANSMISSION_FACTOR "$mat.transmission.factor", 0, 0 +// Texture defining percentage of light transmitted through the surface. +// Multiplied by AI_MATKEY_TRANSMISSION_FACTOR +#define AI_MATKEY_TRANSMISSION_TEXTURE aiTextureType_TRANSMISSION, 0 + // Emissive // -------- #define AI_MATKEY_USE_EMISSIVE_MAP "$mat.useEmissiveMap", 0, 0 diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h index fd904e4fc..c67bcc3b8 100644 --- a/include/assimp/pbrmaterial.h +++ b/include/assimp/pbrmaterial.h @@ -60,20 +60,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 //#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 //#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN "$mat.gltf.materialSheen", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR "$mat.gltf.materialSheen.sheenColorFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR "$mat.gltf.materialSheen.sheenRoughnessFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE aiTextureType_UNKNOWN, 1 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 2 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT "$mat.gltf.materialClearcoat", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR "$mat.gltf.materialClearcoat.clearcoatFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR "$mat.gltf.materialClearcoat.clearcoatRoughnessFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE aiTextureType_UNKNOWN, 3 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 4 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE aiTextureType_NORMALS, 1 -#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION "$mat.gltf.materialTransmission", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR "$mat.gltf.materialTransmission.transmissionFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE aiTextureType_UNKNOWN, 5 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN "$mat.gltf.materialSheen", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR "$mat.gltf.materialSheen.sheenColorFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR "$mat.gltf.materialSheen.sheenRoughnessFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE aiTextureType_UNKNOWN, 1 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 2 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT "$mat.gltf.materialClearcoat", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR "$mat.gltf.materialClearcoat.clearcoatFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR "$mat.gltf.materialClearcoat.clearcoatRoughnessFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE aiTextureType_UNKNOWN, 3 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 4 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE aiTextureType_NORMALS, 1 +//#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION "$mat.gltf.materialTransmission", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR "$mat.gltf.materialTransmission.transmissionFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE aiTextureType_UNKNOWN, 5 #define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname" diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index e0ac10ad5..766372325 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -152,6 +152,20 @@ TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) { } #ifndef ASSIMP_BUILD_NO_EXPORT + +TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_pbrSpecularGlossiness) { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", + aiProcess_ValidateDataStructure); + EXPECT_NE(nullptr, scene); + // Export + EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb")); + + // And re-import + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb", true)); +} + TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) { Assimp::Importer importer; Assimp::Exporter exporter; @@ -169,6 +183,7 @@ TEST_F(utglTF2ImportExport, importglTF2EmbeddedAndExportToOBJ) { EXPECT_NE(nullptr, scene); EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "obj", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-Embedded/BoxTextured_out.obj")); } + #endif // ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, importglTF2PrimitiveModePointsWithoutIndices) { From fb039bb9ebcba96602f7ba63bcee8d481f6eccc5 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 14:34:42 +0100 Subject: [PATCH 066/232] Add glTFv2 Clearcoat import/export tests Uses Clearcoat model from Khronos --- code/AssetLib/glTF2/glTF2Importer.cpp | 5 + include/assimp/material.h | 3 +- .../glTF2/ClearCoat-glTF/ClearCoatLabels.png | Bin 0 -> 10270 bytes .../glTF2/ClearCoat-glTF/ClearCoatTest.bin | Bin 0 -> 50328 bytes .../glTF2/ClearCoat-glTF/ClearCoatTest.gltf | 1669 +++++++++++++++++ .../glTF2/ClearCoat-glTF/PartialCoating.png | Bin 0 -> 5077 bytes .../ClearCoat-glTF/PartialCoating_Alpha.png | Bin 0 -> 5065 bytes .../ClearCoat-glTF/PlasticWrap_normals.jpg | Bin 0 -> 144210 bytes .../glTF2/ClearCoat-glTF/RibsNormal.png | Bin 0 -> 1605 bytes .../glTF2/ClearCoat-glTF/RoughnessStripes.png | Bin 0 -> 5033 bytes test/unit/utglTF2ImportExport.cpp | 57 + 11 files changed, 1733 insertions(+), 1 deletion(-) create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf create mode 100644 test/models/glTF2/ClearCoat-glTF/PartialCoating.png create mode 100644 test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png create mode 100644 test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg create mode 100644 test/models/glTF2/ClearCoat-glTF/RibsNormal.png create mode 100644 test/models/glTF2/ClearCoat-glTF/RoughnessStripes.png diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index b0f0955f5..b435f111d 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -208,6 +208,11 @@ inline void SetMaterialTextureProperty(std::vector &embeddedTexIdxs, Asset if (sampler->minFilter != SamplerMinFilter::UNSET) { mat->AddProperty(&sampler->minFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(texType, texSlot)); } + } else { + // Use glTFv2 default sampler + const aiTextureMapMode default_wrap = aiTextureMapMode_Wrap; + mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_U(texType, texSlot)); + mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_V(texType, texSlot)); } } } diff --git a/include/assimp/material.h b/include/assimp/material.h index 33e39529e..f348da369 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -1014,7 +1014,8 @@ extern "C" { // Clearcoat // --------- -#define AI_MATKEY_CLEARCOAT_FACTOR "$clr.clearcoat.factor", 0, 0 +// Clearcoat layer intensity. 0.0 = none (disabled) +#define AI_MATKEY_CLEARCOAT_FACTOR "$mat.clearcoat.factor", 0, 0 #define AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR "$mat.clearcoat.roughnessFactor", 0, 0 #define AI_MATKEY_CLEARCOAT_TEXTURE aiTextureType_CLEARCOAT, 0 #define AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_CLEARCOAT, 1 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png b/test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png new file mode 100644 index 0000000000000000000000000000000000000000..d47f2f5e1f06116fca5ddd9697001ffedcc5bd68 GIT binary patch literal 10270 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 literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin new file mode 100644 index 0000000000000000000000000000000000000000..e7c6a93a4995917f931d580f7d25c94f33d1639a GIT binary patch literal 50328 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 z z$~|zh+yiUwf#u%V$i1=V-dOILjodS9?wRFY+Q_}M=3ZLvv8~)=2hSHxc9pSm@BQEN z#R+*Y>edRLFXUd`%DsB0Ji`Xh-STW}<=HlP?v`g>E6=?0JM|~e+*Y22)>34R#mE|qW{pMGUd*bFtu$*dvL<6?O(s~6lC>HuYc;`o zl&s-cS;Gm|qhxKz%Gyq_9wlo&R@Qui^(a{jva%MWSPPOhA}eb|iZvoxJ7P`BXYEMV zlvtArv8E*JPOM28x9*g_o)+|RE@MCA7|&SdV_xRO4)(Bxef+=&{K7xhqwp8MS&t$P z;$c0C_=uDBDB>o5)}!D77nr~aUa*269AOAo_`(+6aEJLL>zu3^Dp@nstQpE$qLQ^l z&03K)*dx$kFq9dWKB}DCMj!`M%F4dYn8HwX=M#l*ON6&S=+R- zwi&F`%9^K@HP2w3R@OqTtc5DpLS>EA${ML+ja1f7t*o6Y)=p(jm9^R!)>LJ!m9<*K zTC1$bvQ}%{dTjbSbI`}RjQxybJY$)Ud6^SC*uxg~@dF?53;*y9fAO1jTH+uc)@g~4 zIEj(CiJ#cv0T-CS30|;*9~@x_SNOsf-f)NcBkTXHr7KxW*Q}+>8oQD;cFh{Qti8v` z+Ph}$UDo7{tjTNE{s*eDE0{Uujp=N??AD4AbSc{_7oI*3bNN=Wv?Nby$0EXU=PEv2O;|u>|q$U zPm#X=67+E{V?W~<&sgSTUgpFO_OOM0{J;nN!asb&U;JkOf;fnWScs1}*}oue;wN@^ zzy&67f)}jd2S*sf6~3^AH{4QnFVS zlD(?nep&XgjO<})_ON7c%gElAW^YUOysYeb1^ark7iMKIEZEnRJu)kMWWm0k?47Yc zsMtG`JvA$PYVu66!^mEnmAy8fy*Al{v$6*lVh>LC=Gfaa?9Ivk9eaDm?cb&EO9p+M z%h=C2#xs`rn3p-RgFS3vA3yK`zwi&=@E50w;L! zYym$w!Vs?Tg)O|{4)aI$i&>{sviGWkz1MykfTnHn%5Sm;F$&s*TqZ*o5VuuG@U;-z2!3utGgdtqv z3tM=@9p;bZ-I2qlC5KIu!zQ_HVmvM$+feRYZv4VZnz^kY+YslKyvUTH_u9Lo+39-a`x=*`CSz` zdy>m%C6_OTTt3P1vz;qeHa|*^pXB~o$^A3r{z-nImHa?k=D)l_>+&ASC$yZ)*v~k| zGnV<7mpQS6J#1kgKkxy+@DJbc7r*hDIEaV5LEU>;e3DbE$@!FAP;zQDxuB9Gx?gfcH94Y^J8C3%RFgX@ zIi*H&N;Ns9l51)u*EGndl^j$nIjHivC{j;yQ?2Bt2Kls-vuY)0RgtqQxvW-lSrxgg zlH+P6$2Eo=SIK>~lKX1NedT*aa$*fRv644yC2!WcyxH`8VDfA&=Q8#)j`56TKIUak z>|hUD*vAiiz%Ts6H~htK@@a{Kc!-7gh?9I;;wFA#hX-6>0w;LE3Vv{eAza}LTX@4A z=8xo!lk==4=UJ2UEP2sN@}f0)(ULo@BzIZ|Is9P>dnMOelWQ&c*hccPHTl?*vuz}2 zTa&Y0A~mO#yl$?E7|H#%lJ70+j)`?8Kio=wxF$bba>%XZkgG_^At&cl^2`-^=8}tU zB^N!qxa6WszPgosbwj?ojMuxu^kiRLg@;3$Yw;px*djua1YCl@5fD!Bf))JW z2t&BS7q;+*yNvO}Jdgb(9aS?^P)-~luWQROdrOZ!(qBXRIQOZP&!nWkE`2<6Fed$V z>GP$x)6YM6DA$U-E`6SKP47>)n_+(M--nWuCN_8-e(^eKM@f-?^8f7q_m4hbiqrJ| zG|o$ZpmKQ9wF8xQifacd?QO0dsI-$@J5XsSx^|$_PH^o&r5*3ufl52pwF8xQxN8S0 zZO^sSHH8@DU!ZcF;M#$5?An2*9|t4;J2!^- z9sHf1%onJ%+qmjHP&lA)K;eMG0fhq!2NVwBqHsXrfTlTx1ML*2aG;&!6b`f#oW?ti zb;>!4=M)aK;SltvtGt9Z9D?&s;XoS>C>&5Ypm0FpfWiTV0}2Ne4k#Q@IG}Jq;Sehd z2NVuyno~H?PH_qc+DT5~Ks&){yi@up;Z8lLaG)O!sF$vCNE;5pd8cro4F?nsC>&5Y zpm0FpfWiTV0}2Ne4k#Q@IG}Kd7li`~2QMq* z9u6oR0+mA)4#9b+aG(tb6b>jHP&lA)K;eMG0fhq!2NVt{98fr*a7Yk^0}2N;%_$sc zr#OWJ?Ifpgpq*ag-EpkbaHpPAI3#fF6b`ichr%H^?-UNS;ef&cg#!u)6b>jHP&lA) zK;eMG0fhq!2NVv(0tfVGDQKG0lTK5dZgZOCl-E+23ypUg>onY{=M)aX>z%@ZHvdpK z1m~T?fi@gaIG}Jq;ef&cg#!u)6b>jHP&lA)K;eMGAxRVt=+9EnG^Zz>ra0Z^l-E$0 z15I!m?=;qFxKqz59FpWX!6_VQ^ACkXaNa2#Xu|=80}2Ne4k#Q@IG}Jq;ef&cg#!u) z6b>jHhz$x1gG&%W1WUO^_;?iad1H45U3oYa0t#jg#&Fk zpm0FpfWiTV0}2Ne4k#Q@IG}Jq;ef&cg+q!c9MGSoplMD|I;D@o7&OUgqSFMY@lIo% zhCB6~!XZVD;ef&+P&q{55S(`k2ikBz;ef&cg#!u)6b>jHP&lA)K;eMG0fhq!2V#W- z`m+=?%_+weUW;yXn&dRmX@b*ur?F1MoqA5;z&vn3;Si`CqHqY#JB0&nIG}Jq;ef&c zg#!u)6b>jHP&lA)K;eMG0fj@FC>+qArJ(7B*KnNTbeq#8r-@DzoW?tibsFx}a|(wv zIferYhd|{Jg+p-ODI93S0fhq!2NVt{98fr*a6sXJ!U2T?3I`MpC>)3#4rqGe9LFb} zra0Z^G|6eA(*&pSPGg;hJN2BxfqCJ8!XZ#OMBxyecM1pEa6sXJ!U2T?3I`MpC>&5Y zpm0FpfWiTV0}2Niq!;=)PIG$FX^PWrPLrG_I!$mI?=;qFxKqz59GD*tfz5Q4CT%zb z=bgfVHXP7+r*NPR2NVuytW!A9h64%*G~6j1Xu|=8110?!4z$yp!hv>*Q#jC0ata69 z2~Ojk#ySmm>N$l&@Or0Q548D*!XY^C6b`ieJB@b=2m0ZFHgy{76b|&m0j=OP+$kLB zhXa~^3emJ1%1rGGV0fhsa<`fRJQ=Gzqc9K&#&`xj~?=;qF zxKqz59D>(7g#&H=p<|>3=ba973J3b(fOdA;#wi@=hXY#2X?3S?pdSusdVvGSaNwNh z6b`i0oWg;2ic>hyPI3wd+6hkMoyIy1cj`HXL-2a1aG=dUbc~eXyi+*Ph65V!w6jw< z&<_VR)@dE5aG)O!XnG-bj^Tj9f!BCW;XpghDI930IE4f4B&Tqoo!~UyX{^(5r=C+d z1h01r2ip8Y;Sij63J2Q#oyI$b1O0G7n>vkk3J3b(fTkB><`@nr9MC7F@LD*aa6r?X z!hv>*Q#jC0ata692~Ojk#ySmm>N$l&@Or0kpv^xN4#9b+!<_ba3J1=^0d3>7sZ%)6 z4+k{85G%)UK;eMqaS8{qC2 zK*OCDaS8|e;lLc8Q#jC0a|#FADNf-)JIN^=XeT(0cN*(7+^Odj4#Df4!htsbP&fqV zox*{3f2Z+I;Xpqe(DXt~9K!*H16sjpxKlXL4+r!~Da-{26b@*bQ#jC0aS8|8NlxKF zJHctZ(^#kBPCch^2wv|L4z&4)!XY^CbePlrPT|0LIH2i;SU83Q3J0{h(+W=EKtCMN zJWk=joNz$lfTlTx1ML*2aG;&!6b`f#oW?tibsFx}a|(yx^-keHn}6sSDZzQC!<@o_ zemJ1%g;+R-0}2PUj??N+;Xpqe&>~LZKpPIs?Ky=5?KG!wpq=6r4z!b;!hv>z(|D(` zPQ#sgPT>%|-YFbt^A8;(B{=UC4z%HbrWbtY7!D{L&{(H+oWg;AIH2K9i#UY?{cymB z=M)aK)11PAc8XIt&`xp+2igfvqC2 zK$|*^bqWXi;eb|f8txPh^uqytQVO=B?9MCpSn>vL9{cu35JFVap4)ntT&Epgf*oFfN2QMpkuXhRu+WbS)OM*MbSNh<9#yjoow2f0Za6Z;) z9jDcu!h!SQPK!8&1O0Horsot6w9}lzfp&^hIM7aV3J2N=PUD@%It_Q~IfX;;dZ%!p z%|8^Mg7Z${KpPHdywlE3;Xpqe&{(H+oWg;AIH2K9i#UY?{cs5LBriltChu`CvzJBc zEZ!5|lU`OYo7CC7>|PEpr zt=gi`diA_$FGk*$dQvM-ORJt}v={5eNlS@pufEqnTCt*WUPG^uwCamC@EUtfq}5Qg zk=N8~CauPzO}yq_3u!eKZRS1awUkzK(H34SueG$E6K(0W@!CqOm1t|Po!4GkZA9C8 z9pt#3)a|^EUMErNj^6WLXHn|sy%&Ud=b(NepkB=DBE-9RU4?b=pzi8*6V^`!bvLiO zur3wU-Mtrub?Kmf(d!|s%LH`~FJ4%e3+i~Ur?8F)>YiRNVO=q(dwIQu^)o@;+v_8& zs|0l)?}Lg?FqR4V8MB@UAaM!=xTAyc^2VaH(Gr-i_tx6{$xE@1}Az zLh6yiySW^Vl=@ZS{hS=VD)lJg-Aax|Nj+L2lW(hs_^a;)Kk4_Li;7Dr;ASWW(e=T zay&!onZmoD9L0$kA4*w+Ziga+M?0k6DZJm3qn%Rk65b2sXqVI<3Gan+^pVuNh4&&k+AZ}SVZB)D#nRdr9L6#H_OopsXr6mTfEOj zw|FOo_Xl!xQtDH}d#fCslKKnby-kk3kovUn{!oriOMOOoZ~1oai~s_;G-)K|T0!uwEAU-PaD>BB*N-TOgEACdZo=nq2rs2trCz2V&w z-p8aqChgn8`x807E%hDY{ptVe(i>?{06 z>nrdIr|-1B3a@hhLF;Sq8s{7M56e&X2d!^lzu2GjzKQ*2e^LDwwvR8f`fW=xOy)OK zcUhAbA;dlEn-oHd`_wlng%S^_Z&C?EJfyx!Ls*GN)Hi7*ti@yMo3s`-;tBOl+6Y_m zl=`;9jFg$H+@;v@AvgeP}i;uG~fg%@|} z#b@e!iS*ogi!anqFTAuWvulP=RUv7MapZGy}KW==*PrCbw zjI5vdMfZ%tpUo(K)7@VLu>RsUs^Ta@yCSQJ}~ z@)@ycmj4Akj21C$G{pn3KsJWnfugu5!4{`?5SEFrlJw4`SyGgucd%wDQJUT%nx#b< zdWUM35oPHerdd{$qjY9io-Ie|ELa7$Jf*W@v1|oOXTvJ8u_BJv*75^|4eQXm z09FUqrFSG&7uKV7L98CEPwPUk5T^#TE({BEZb<7Qun6a3ctg>MZAj^2SYx&krK7MW zY-4&yV@=s66pz7*vrTDTTr?BS*=F=Efwf?p)4L>Aim#URE~VL0w4!%u%~ql{y~}8} z7Hue97Pe*EP`Vt}j%`cn@>qMe9i=N^9oY7ij>S5%9YiNuSA-Qgccyn7jN{ye-j(n! zuq(YQV_ji4%2&ao*7ae1&Vy*(05;&<2p=Q{vx6wz2phrsg&=EO@-6w-9t=gdx#nI?vBlXGwIzEn+a#pyB9VK&Zc*7Y&M)j?>^WZIG5Ia zvAJ*_t^2`#oaWQIKkUzW0j&qX0h|}odLSIgc`&|EEMgZ@dN8(_T}0_2*b;UzrH5im z*(DVJ3me8RrSvedOe|-YQF=JGf?ZDO5!gz01*Jz~tJsy4{u^7(uA=lPYz@1b(xb7p z>>5gs!Pc>BDLocj&#t5NIBWyEUTmcGcsQQ(CR$H`6F6_C_e406^A>tf!neS9dQZmU z;a18|!M4I}^qwxZv(v>6dQZc4z@7A-f$fC5=sgqL1$WbX7PcGiq4#WT58O-ZIdBfA zeYBnn=W#;NJX>pd;8{h`c=V-kVZsdHP-kabi&KKyt8NUE8(t8Vb5niHv zJa!3QruTMnh21W$(t8_r6<(wF4(uAdPVb%Ab$El`yRaMZCarg4H{mT>?}2+b-KO)CTJMAVINzmp0!-kXgx?kS*t?WY!tS&8D7_zhz}~0y0qi0BfYSe9kJyJ4KZqS- zA5r>{cr2cv zk#{&*28ri-i7x#2`O12@6%mM#Y*{r?n)Z0k`L)_NGsNmkLYeCtywF1i0;<% zG3Bl06S~_-Th>NCrMs=PV{PR#y4y*6)=oaByS;Q^?d1!)J4i>?LB6EBqjX{&B*g!{6u|E>BU`o`I-7&GCgUSc6^Fy$2%%7#$hz{1#2%3EQX*)YmmV>WDN%G<~+GAo;f^0rtuHY?@ruy=H_rJf?~dn(1t{-<6@ZbH_rxM$LCSk!1z{n|XOM;246+F2y|E&&DCK>yqOcg{ zeX(LNit>KYk5jbt=M>FXl#JmM@Q41Ki}MKtzyQuccyU>REl%kmtR!24(wVSQY)MK7 zW2M!MgA z*qGMEU@=ZjXdMNkI5(wrG>qoljMgzQhI0wLnQYEBqjU+Z1>2m`C9#%l3rd&5TCpuD zT^ei6wxVY#WM~!^*R5DP3N+lkM4dl&*kvVB1qV7VF4%pmas76Wfu} zaad=z6QwI*UD(c)u8ehMyHL6c){X5dyVJTVtjf6ut*gOmoO{x`I;_sQ7rkrXy1I^?`lqT^s8Q`%%6Q)(`flcYQg4tuF`CyB;xS51IE2=X zU?WaLY26q$=KL3}o4_WVhtawzY|6PgK1>d0hf%sYHi8{a=@!^Xb_AtcVt=zEDcuSi z#r{p{*4Suv6s6l>W7yFYZ;Q2K$56VR94p7MV=3Jp8_$lTbO&q#JD$=Vv5D*iN_WC0 zu@foX8Jo;bqI4H*3OiX&rFB=>mGd-OcZ1zHPp5Tv*q!qXdiQ`mIM1YaPkbhvMeknN zEI6Cqy|LMF4!!$ebKqRc_r>PIdGsD2=d%Ok0($qy7Qlt{9*8Z3i)cLvTLc%=dN3T! zX$h@|z#*KM(t0Qy%6S;RR4!weQhFG+oLxrg;n)gxIi*KnE7=v49*M1DS5o?KY&E-z z(xb37>}pDn#@4cHC_V-o%dVyLSh-HFXV+1B9JYa7PwDa4Ms@?GCt#b{jg+26O?q_9&%SVaM5HlwOUU zV2@LJ4R(?}LGiWNI`$-`*U3}zG<%BD>#;NJX-aRv&a!7Hy%9Udo~86A>^ysp(wngh z?0I>S)?45f&X;H%592vsruA01mGc#PZ-d)7U#0hU{3^Ug?;Y4Rc%9xmvFq>#y?0?Z z;7xk(#%{t}l;4Bhg170NAn&jV@-DsiVRzv@S|?)n;C)&r!6Z%(XuTiq=lqb?2jBtD z2l0pU5&MwR2eHTOBT65_p0JN8eHeSnKB4px>>2x%(nqo9>@!Lq!(On@DSaG!$-bcU z3G5a7lHw<^Q|v2BpOUZT8}>D&Ph)S{H>K<}@2l8%_=DcpupjUzy{}_G;V*jM zz<$Bsl)s7nhRO84BP}W!3U!xLGJBifj^CrcP?9^P?o(cIBbA{ZP~PB1DJ%7m@>bj! z>JiJ{Cclr!t3PSM?2y{5dgdPDbg%7sm*-qPJgxw0#jW2N6LHR9?DC7qP!P3o+`ciO!@TOcqwo7h4S9qq*oc#SL%DSU!;%v zM*R%RhdW>Oo%%k?mpec8gZjS8kGqWOC-wbQM(+I8FY0Gh{@ewq-_-Y40o(;r-lBq3 zpt7h;N^lBflVvdF1r`iLl;RY^SFj4@6Hribw!%YI7#m7?D=ae`MtN&23!9npHdt0R z3*~LGY;0D_+hO*6Wv9HoW_Fc>@(!9gR8Gn}YUWhADDMPwv$-hmjHP39Q$C#vS9#cQ z%DZ5B**uhY#Uj|ely}4Ou@RJa$MUoJQ~^E#59q-;l25=BdU7tvC*TFWI2Ym*NRJnS zg(>fi6^2D9p8+cZi&EYPD+-HI-WMwdqbTo(MZsvwXT+jm4CVc?7+9S0L8=5Bq)O5| z5Gx5w(K-`W3YMmIFbw8YhSni4gmV~PMwMmDP&y1N$CjmZW~@A0j?!7M3T$~=XT`Gd z6-)1Inz5=Py|ZgpRB`mqp&6$tQ97q)B~_W;xil-QDiqHR!`Uj74p&uGHMT0H^I+B4 zYLw24)nKbrIs&W7)}VAgtQK2S)uwfRn4fbUS{Hx?IM=0jB#h)-kKP6Gdaypd3t{zP z19}(68o-A1E`l|Ljp$tzYXlq9yBO9OHlcSE)&w@Cd^FY+Hlue5)toJ%TF|>V)&jPq zbxEuxY(?u*uoS1(v@Q)xb1sXwR&ChUlrD?4W!q4?9M+C)OY8Dj1-{zTyMktW)q&oz znjKU}dRNr!s5;R*PP3EhOz%pXomCe~SJv#Jx>CG~W>;Ols_I7XZmK(_tHB;@cS={s zda^wzT?6aI_M~)8tT)?B^`UhwSc`LCTGxiPIrpP?9lRgxPw%=|e>i~N^{@eOAie8j z1K}WgH^2tL!Srs34TeML-3S{3htj(-HWdCv`6k$3a2UOttKn>OHG!!7Cdbigcr^eH}gXVZO zf!-Z8C#Z?^?xZ;a%}Hu9y}M{mR#T|nRdb52-c3!V_f$2F(%s>7b{eI7U^Ce1 zl>(vH&kJsFwHqv{7 z=0>%N-V-%9sm=7Bq`6sbq4#9XEh?VsQ#9js^{Hwry|=1ul%A%!O>L+2bhv}vPU#ug zPIiadMeCVxCgo&{%d-b3%%_#U{I-gB_Ma38(rV*6kMz2{*GFp=K#u|$|e?*&*A z+)wX?*nW6`-ixpU@E>|F#{Pi^DZd0e2oKSFxjM`)S4Zf*3_Ajk(s~7U6dt4XO1P5K zYW$cw&K{%nYU~7ioZf4&wS1kV_gc-9>J+`#X`WK2>Aha_v^qoY4Vq`vS$c2OJgd&p zdz0olb)MdvHP5RH^xmR*L0zPJyyit+eXF`e^-Jn9rMGEbR#zy!9bRRxP0@Y~N1RvA;uzJECR!`}D2zv^j(fSDX3_hpzQFxToas0V@ z!9J(;aqI+NFX??k^QC%4?~|IZ)N6X5(tNGn(EGIJ8}*jnXEficcl181`A)s3_c_h? z>I1#cYkp84>3u=-qxwYki<+Nw^-JnA)jz8*l)kL_MSZ3870s{e8>O$p@9a1AgVxvJ zHO@cjeI5S^f6@B}_6z=|_f70KOs4lOEE!siJDif)+e#RBsc$g^cha~=ePKxMlyRT> z(oozP#slgz+*uh9sc#ro+*uossBdLhb7x~broOFVV?0rI##8Fs7Zf4l9MBJG@OiA)Hms5I2*62Z_?RFXS|`l$#jN`@s|1~T?|*_ z9raDR8g9mW>YH>k+>H;^H|cJ87$2!`(!=mHK2hJKr{QH>qP|Hl!>s;Ur8mCtTa45Mn0;$YvwcZQ{6)|zfpkdo|*-WNUD2jMjB@I^hQBg z&?rQ8Z_PqRVX9})ENm2^ypLuPqo`4gPrz5NuMtIgKW?I6H03j5(J+Sc{#XnwPWb?= zI4nW$K&%8TN$()6BrHYmOjs#cn%==!X;_BdAy^q$mfoRQSy+zpVOTj>p59rF3T##* zmfl&gSXhzX*|3T*j@sF=IG7WUgKt$bb)yEoBQ$FmHR+vCv!+pt-uX3a8MWzMK(n?{hvJc%b*Mh>R`0}utGYzR zb`Oa#J$zM{gwowZ;&;y&7X6=|_KbON`s6FYd()nY#?$`ZVV6_feC85v;jfBsa&5Kf zKezDj9#n5{{F)y6`NTi*!mh)kzCUjoZLaz6Xn3M{w7Ct~wEMLEKYhF9zx+80zS~KD z$J<982?&Zv^#nW~kKz75J?)v8K%Vb&O~12#CQFK&|D1$PG&27=<};S7|L;$C<8505dQ zADQxQ=6fbMr@piKOrsPxpNYE6Gn>ASSrME1HcN!&piS^d+ABg`-P`AlT$p#e7;)RkAM97rIeow=JR2x?{7Z8 zfM*U^PYsJp{c-UpZvI@bo|W?J#hfA?huiyVGekjFb z%;!T>-=F)0^%qk#Hs)KcD}^)4G_y&i#phi5?TRu29)%mraGE z)7I?piF%=t$#iH6F1*E?Y|>`;t!vsyuZ2T?+bFYDx`m( MV6NHY-@g5S0BBn6`Tzg` literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf new file mode 100644 index 000000000..ae12bfc11 --- /dev/null +++ b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf @@ -0,0 +1,1669 @@ +{ + "asset" : { + "generator" : "Khronos glTF Blender I/O v1.2.8 with hand-edits for clearcoat", + "version" : "2.0" + }, + "extensionsUsed": [ + "KHR_materials_clearcoat" + ], + "scene" : 0, + "scenes" : [ + { + "name" : "Scene", + "nodes" : [ + 3, + 7, + 11, + 15, + 19, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32 + ] + } + ], + "nodes" : [ + { + "mesh" : 0, + "name" : "BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 1, + "name" : "ClearCoatSample" + }, + { + "mesh" : 2, + "name" : "CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 0, + 1, + 2 + ], + "name" : "R0_SimpleCoatTest", + "translation" : [ + 0, + 5.25, + 0 + ] + }, + { + "mesh" : 3, + "name" : "R1_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 4, + "name" : "R1_ClearCoatSample" + }, + { + "mesh" : 5, + "name" : "R1_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 4, + 5, + 6 + ], + "name" : "R1_PartialCoatTest", + "translation" : [ + 0, + 3.1500000953674316, + 0 + ] + }, + { + "mesh" : 6, + "name" : "R2_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 7, + "name" : "R2_ClearCoatSample" + }, + { + "mesh" : 8, + "name" : "R2_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 8, + 9, + 10 + ], + "name" : "R2_RoughnessVariations", + "translation" : [ + 0, + 1.0499999523162842, + 0 + ] + }, + { + "mesh" : 9, + "name" : "R3_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 10, + "name" : "R3_ClearCoatSample" + }, + { + "mesh" : 11, + "name" : "R3_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 12, + 13, + 14 + ], + "name" : "R3_BaseNormals", + "translation" : [ + 0, + -1.0499999523162842, + 0 + ] + }, + { + "mesh" : 12, + "name" : "R4_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 13, + "name" : "R4_ClearCoatSample" + }, + { + "mesh" : 14, + "name" : "R4_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 16, + 17, + 18 + ], + "name" : "R4_CoatNormals", + "translation" : [ + 0, + -5.25, + 0 + ] + }, + { + "mesh" : 15, + "name" : "R5_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 16, + "name" : "R5_ClearCoatSample" + }, + { + "mesh" : 17, + "name" : "R5_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 20, + 21, + 22 + ], + "name" : "R5_SharedNormals", + "translation" : [ + 0, + -3.1500000953674316, + 0 + ] + }, + { + "mesh" : 18, + "name" : "X2_Label_CoatingOnly", + "translation" : [ + 2.0712804794311523, + 6.619500160217285, + 0 + ] + }, + { + "mesh" : 19, + "name" : "Y0_Label_SimpleCoating", + "translation" : [ + -5.3578033447265625, + 5.25, + 0 + ] + }, + { + "mesh" : 20, + "name" : "Y1_Label_PartialCoating", + "translation" : [ + -5.3578033447265625, + 3.1673200130462646, + 0 + ] + }, + { + "mesh" : 21, + "name" : "Y2_Label_Roughness", + "translation" : [ + -5.3578033447265625, + 1.1383899450302124, + 0 + ] + }, + { + "mesh" : 22, + "name" : "Y3_Label_BaseNormals", + "translation" : [ + -5.3578033447265625, + -1.099429965019226, + 0 + ] + }, + { + "mesh" : 23, + "name" : "Y4_Label_CoatNormals", + "translation" : [ + -5.3578033447265625, + -5.252500057220459, + 0 + ] + }, + { + "mesh" : 24, + "name" : "Y5_Label_SharedNormals", + "translation" : [ + -5.3578033447265625, + -3.1963000297546387, + 0 + ] + }, + { + "mesh" : 25, + "name" : "X0_Label_BaseLayer", + "translation" : [ + -2.087031602859497, + 6.616230010986328, + 0 + ] + }, + { + "mesh" : 26, + "name" : "X1_Label_Coated", + "translation" : [ + 0, + 6.614150047302246, + 0 + ] + } + ], + "materials" : [ + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.5, + 0.019999999552965164, + 0.009999999776482582, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.5, + 0.019999999552965164, + 0.009999999776482582, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatTexture": { + "index": 0, + "texCoord": 0 + } + } + } + }, + { + "alphaMode" : "BLEND", + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Coating", + "pbrMetallicRoughness" : { + "baseColorTexture" : { + "index" : 1, + "texCoord" : 0 + }, + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.6000000238418579 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.6000000238418579 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 1, + "clearcoatRoughnessTexture": { + "index": 2, + "texCoord": 0 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "metallicRoughnessTexture" : { + "index" : 2, + "texCoord" : 0 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Base", + "normalTexture" : { + "index" : 3, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Coated", + "normalTexture" : { + "index" : 4, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012848637998104095, + 0.021861059591174126, + 0.11068868637084961, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatNormalTexture": { + "index": 5, + "texCoord": 0, + "scale": 1 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Coating", + "normalTexture" : { + "index" : 5, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Base", + "normalTexture" : { + "index" : 6, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Coated", + "normalTexture" : { + "index" : 7, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatNormalTexture": { + "index": 7, + "texCoord": 0 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Coating", + "normalTexture" : { + "index" : 8, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 1, + 1, + 1 + ], + "emissiveTexture" : { + "index" : 9, + "texCoord" : 0 + }, + "name" : "LabelMaterial", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.8999999761581421 + } + } + ], + "meshes" : [ + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 0 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 1 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 2 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 3 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 4 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 5 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 6 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 7 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 8 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 9 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 10 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 11 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 12 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 13 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 14 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 15 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 16 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 17 + } + ] + }, + { + "name" : "Labels_Mesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 4, + "NORMAL" : 5, + "TEXCOORD_0" : 6 + }, + "indices" : 7, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.001", + "primitives" : [ + { + "attributes" : { + "POSITION" : 8, + "NORMAL" : 9, + "TEXCOORD_0" : 10 + }, + "indices" : 7, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.002", + "primitives" : [ + { + "attributes" : { + "POSITION" : 11, + "NORMAL" : 12, + "TEXCOORD_0" : 13 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.003", + "primitives" : [ + { + "attributes" : { + "POSITION" : 15, + "NORMAL" : 16, + "TEXCOORD_0" : 17 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.004", + "primitives" : [ + { + "attributes" : { + "POSITION" : 18, + "NORMAL" : 19, + "TEXCOORD_0" : 20 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.005", + "primitives" : [ + { + "attributes" : { + "POSITION" : 21, + "NORMAL" : 22, + "TEXCOORD_0" : 23 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.006", + "primitives" : [ + { + "attributes" : { + "POSITION" : 24, + "NORMAL" : 25, + "TEXCOORD_0" : 26 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.007", + "primitives" : [ + { + "attributes" : { + "POSITION" : 27, + "NORMAL" : 28, + "TEXCOORD_0" : 29 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.008", + "primitives" : [ + { + "attributes" : { + "POSITION" : 30, + "NORMAL" : 31, + "TEXCOORD_0" : 32 + }, + "indices" : 7, + "material" : 18 + } + ] + } + ], + "textures" : [ + { + "source" : 0 + }, + { + "source" : 1 + }, + { + "source" : 2 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 4 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 5 + } + ], + "images" : [ + { + "mimeType" : "image/png", + "name" : "PartialCoating", + "uri" : "PartialCoating.png" + }, + { + "mimeType" : "image/png", + "name" : "PartialCoating_Alpha", + "uri" : "PartialCoating_Alpha.png" + }, + { + "mimeType" : "image/png", + "name" : "RoughnessStripes", + "uri" : "RoughnessStripes.png" + }, + { + "mimeType" : "image/png", + "name" : "RibsNormal", + "uri" : "RibsNormal.png" + }, + { + "mimeType" : "image/jpeg", + "name" : "PlasticWrap_normals", + "uri" : "PlasticWrap_normals.jpg" + }, + { + "mimeType" : "image/png", + "name" : "ClearCoatLabels", + "uri" : "ClearCoatLabels.png" + } + ], + "accessors" : [ + { + "bufferView" : 0, + "componentType" : 5126, + "count" : 1113, + "max" : [ + 1, + 1, + 1.0499999523162842 + ], + "min" : [ + -1, + -1, + -0.06000000983476639 + ], + "type" : "VEC3" + }, + { + "bufferView" : 1, + "componentType" : 5126, + "count" : 1113, + "type" : "VEC3" + }, + { + "bufferView" : 2, + "componentType" : 5126, + "count" : 1113, + "type" : "VEC2" + }, + { + "bufferView" : 3, + "componentType" : 5123, + "count" : 6180, + "type" : "SCALAR" + }, + { + "bufferView" : 4, + "componentType" : 5126, + "count" : 8, + "max" : [ + 1.0280373096466064, + 0.23501670360565186, + 3.8289083903464416e-08 + ], + "min" : [ + -0.968224287033081, + -0.2350165843963623, + -0.010000125505030155 + ], + "type" : "VEC3" + }, + { + "bufferView" : 5, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 6, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 7, + "componentType" : 5123, + "count" : 12, + "type" : "SCALAR" + }, + { + "bufferView" : 8, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.23026323318481445, + 3.751463495405005e-08 + ], + "min" : [ + -2, + -0.23026317358016968, + -0.010000579059123993 + ], + "type" : "VEC3" + }, + { + "bufferView" : 9, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 10, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 11, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.2302631139755249, + 3.7514624295909016e-08 + ], + "min" : [ + -2, + -0.23026323318481445, + -0.010000428184866905 + ], + "type" : "VEC3" + }, + { + "bufferView" : 12, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 13, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 14, + "componentType" : 5123, + "count" : 12, + "type" : "SCALAR" + }, + { + "bufferView" : 15, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.22039484977722168, + 3.590687924770464e-08 + ], + "min" : [ + -2, + -0.22039473056793213, + -0.010000280104577541 + ], + "type" : "VEC3" + }, + { + "bufferView" : 16, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 17, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 18, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.21764808893203735, + 3.545937588000925e-08 + ], + "min" : [ + -2, + -0.21764802932739258, + -0.010000137612223625 + ], + "type" : "VEC3" + }, + { + "bufferView" : 19, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 20, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 21, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.20775499939918518, + 3.3847587843638394e-08 + ], + "min" : [ + -2, + -0.20775499939918518, + -0.009999996051192284 + ], + "type" : "VEC3" + }, + { + "bufferView" : 22, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 23, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 24, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.22341907024383545, + 3.6399587344249085e-08 + ], + "min" : [ + -2, + -0.22341907024383545, + -0.009999859146773815 + ], + "type" : "VEC3" + }, + { + "bufferView" : 25, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 26, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 27, + "componentType" : 5126, + "count" : 8, + "max" : [ + 0.9169960618019104, + 0.22670458257198334, + 3.69348676088066e-08 + ], + "min" : [ + -0.9199233651161194, + -0.22670456767082214, + -0.010000176727771759 + ], + "type" : "VEC3" + }, + { + "bufferView" : 28, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 29, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 30, + "componentType" : 5126, + "count" : 8, + "max" : [ + 0.8968609571456909, + 0.20853587985038757, + 3.397480696776256e-08 + ], + "min" : [ + -0.9147982001304626, + -0.2085357904434204, + -0.010000113397836685 + ], + "type" : "VEC3" + }, + { + "bufferView" : 31, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 32, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteLength" : 13356, + "byteOffset" : 0 + }, + { + "buffer" : 0, + "byteLength" : 13356, + "byteOffset" : 13356 + }, + { + "buffer" : 0, + "byteLength" : 8904, + "byteOffset" : 26712 + }, + { + "buffer" : 0, + "byteLength" : 12360, + "byteOffset" : 35616 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 47976 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48072 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48168 + }, + { + "buffer" : 0, + "byteLength" : 24, + "byteOffset" : 48232 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48256 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48352 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48448 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48512 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48608 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48704 + }, + { + "buffer" : 0, + "byteLength" : 24, + "byteOffset" : 48768 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48792 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48888 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48984 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49048 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49144 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49240 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49304 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49400 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49496 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49560 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49656 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49752 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49816 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49912 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 50008 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 50072 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 50168 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 50264 + } + ], + "buffers" : [ + { + "byteLength" : 50328, + "uri" : "ClearCoatTest.bin" + } + ] +} diff --git a/test/models/glTF2/ClearCoat-glTF/PartialCoating.png b/test/models/glTF2/ClearCoat-glTF/PartialCoating.png new file mode 100644 index 0000000000000000000000000000000000000000..d579bbebb8a423eedb16594a7ef715fe7521a752 GIT binary patch literal 5077 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 Q@JSA$qoJ>ky=fQyKcnMWr2qf` literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png b/test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..b1911d9f0a303a57181d44856f3f31ea18c11063 GIT binary patch literal 5065 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 Q_!|ylXYGipvh+*(4_Os-!2kdN literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg b/test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg new file mode 100644 index 0000000000000000000000000000000000000000..73e6260ef7126ccb11113179c58cf6611d3f3e9d GIT binary patch literal 144210 zcmbTddpy&9{QtkM4mwFHL}gd0Tv3)p6n2#džOVOJDoNkR^>T_xnWONE@;Rbq*4 zIn0@rb2*>3a?BiOIgBl{!+XD%>+?JP@%`ib`wh3fUNvRhT{&|%F#j%c0KH#lWzblU9v1#^pwmR5Gx>>be89Wm}V zZ+Up$zT@TRAMh|RC^#hiX+&hyv*?)TFJ2}kr@Tr{dz!pljPw6`{4d9)2*&l> zx^-*U$*qoS&2NF=XRYG8_4{=<>^OTx?uPHq11BDD+;uMTO~L0)8hTe3l-wS4ZB{;b za`X^yb!h*M?0;`yPyT<6?EfCv|1++B*w(dcz~rq}gdt&#orEzV+B#P%5TW(|mBE@8 zS=JvGIDFd7SL`O?FBfPEX_wMR7@O`>;h^bu&`Yma)=B7aXdi*y(i>1J>Z1 zz1CTd`n94v@k@%hY{oRAvM!yOmqt`BfRxOCOs>p8Il6b?qs|=`>mHp9NH~CHPDbQ; z^oD7};Qa5Vbo$NjC2*@cyYwj$^JG%CMwAih&>(lyF~Q^4J92YNnA906$eL`kJ?7NQ}nL{3T+omFSO1Ea7EQ zTxSKZ_o0e>q!F98vd4Y}Z_XsjVCpFQd==<$452>{V<>~2^J#qB!-5X~DQ4yFd#eRL zee{YtiA-pEFG8mWeJ;wShuB0ZXK`FT*>#y$7;1}uAVP9Qo_nmJ9n&W9 z%!KlIG!>J9Ipm+Chwh$Od4VOo_sRCdsGROWTfQf}_nP$IDTD1KQ$tIoUujv&Au`yg z_2#Os@77RmP0}QEXm@!CKh-F{CTU7t6SNJ(xF_%PXyRhW>>q?OJe}z*9d#x>hDJp! zC*R&{li2yZPVQ*O%Fa&{l8X-unY5?d{?aG1!y~a2FX8dY4K#~gj8P*ITt6~X7};cb z$PY6$qSSpcnCjz<)DNArk@(FmO+d{zhKc+en0`%32(7!2T25EWd&|4auoC^mE|_}O zO;~+vk-_{siFsk#&v-s|0_ud^Fm+EuCW={JIL~gLW)oIaq!Zp7*?RnZAH@<+ipwsx z-u3<^4?ovFmsH^|PlOQlM0X_ne8@H8vX_B=BQZL9+Eot8Lr~{!!5Z~*R(XOPt7iq@ zXeg(h(Q};|Bv{^mA%oq@hIY1A-Pe1h$$URRlUR2b8{=d!cwv}Vc7+U9Uyd8j>gj^N zV8$FRT5q+iu@!XA;r8RJ;G4EC_w0f5Gvz61e`AsA6+!kBLfKtWTmx1~Y5dOTT4vz0vYu>_Lx!1B3foKu+rhynMZ%q} z1SUtDhCW@B!L}I3_y{@#l!3KS_`bhdAUZ^^{EN`}ye5WJ-!aj;NBXllz*qcOkiQ9~ z-|3Yl-OVzU6y%Z`Z^dA14)vs~z~e&>zc_r zt70#$&;vxQ!1umnQuh5ccv~KgSy_#WUCXB-R3#W(t5hyRMXY?-m8|&zE=-0~?vGD` zMaQ~^`-^Am6(+Jhg zP~0eXW#ZA@U(V`qK~l%`F%Y}jz4SuaR?`b6~~l7y`jt`!3FzW4P9%7`&Y-nERpCt&zbJ9|=GRiZ(OYB%peJ))q5GEEI3AM+2VT9(2^r%(Z>3wB0nS-s6MBo?zDb zFY-9mq6t?K#LfF#!gcP5PdV|1dUr>O#&{TZr)g-3b=%&C&kc!6af6AN0Uh{z6V~;pYpC zPcf_NZkKqzS*ZI?@E*1?SPBO63urDg7O z>xgW#FL()mE@@w;+~P8LGdP=V=!N{YK*+?#w@cW?|27eNawM^}zvPgTMLk*X7$&0N zcTpd5`_ch)fX`D2VISg`LsgeTWV?QlDV_UO2Gc?lg`s2iQC!!eJ>%xu?Rk3|5xpW? zC6dmL4@>5UKj^(Tx%rk*|8>orw)<7pUiBh(lrU|8_$wlyZq#tbEFA<-ABg;}UROk6)ZhT~SS>VDG9MhJlyrJh4{MWLdDWHNJb7i3Dpk

8#fXvU&U@TnHe66M)}` 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≫PJyOL#2noO42?ak;#zU>=*qI;o{}k?8jYM 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�-h@x9GW3t>NHF#%4~dx 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 literal 0 HcmV?d00001 diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 766372325..ee8b1a742 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -151,8 +151,65 @@ TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) { EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", true)); } +void VerifyClearCoatScene(const aiScene *scene) { + ASSERT_NE(nullptr, scene); + + ASSERT_TRUE(scene->HasMaterials()); + + // Find a specific Clearcoat material and check the values + const aiString partial_coated("Partial_Coated"); + bool found_partial_coat = false; + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + const aiMaterial *material = scene->mMaterials[i]; + ASSERT_NE(nullptr, material); + if (material->GetName() == partial_coated) { + found_partial_coat = true; + + ai_real clearcoat_factor(0.0f); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat_factor)); + EXPECT_EQ(ai_real(1.0f), clearcoat_factor); + + ai_real clearcoat_rough_factor(0.0f); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat_rough_factor)); + EXPECT_EQ(ai_real(0.03f), clearcoat_rough_factor); + + // Should import the texture as diffuse and as base color + aiString path; + std::array modes; + static const std::array exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_CLEARCOAT_TEXTURE, &path, nullptr, nullptr, + nullptr, nullptr, modes.data())); + EXPECT_STREQ(path.C_Str(), "PartialCoating.png"); + EXPECT_EQ(exp_modes, modes); + } + } + EXPECT_TRUE(found_partial_coat); +} + +TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_clearcoat) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure); + VerifyClearCoatScene(scene); +} + #ifndef ASSIMP_BUILD_NO_EXPORT +TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_clearcoat) { + { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(nullptr, scene); + // Export + EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb")); + } + + // And re-import + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb", aiProcess_ValidateDataStructure); + VerifyClearCoatScene(scene); +} + TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_pbrSpecularGlossiness) { Assimp::Importer importer; Assimp::Exporter exporter; From 985f3ee6650983582b8612217f236f8c46a274e3 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 15:17:25 +0100 Subject: [PATCH 067/232] Fix glTFv2 texcoord/uv mapping Use the standard property to indicate the UV map index --- code/AssetLib/glTF2/glTF2Exporter.cpp | 21 ++++++----- code/AssetLib/glTF2/glTF2Exporter.h | 2 +- code/AssetLib/glTF2/glTF2Importer.cpp | 3 +- include/assimp/material.h | 4 +-- include/assimp/pbrmaterial.h | 4 +-- test/unit/utglTF2ImportExport.cpp | 50 ++++++++++++++++++++------- 6 files changed, 56 insertions(+), 28 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 3cb3891b0..7e0966aff 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -492,11 +492,14 @@ void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, float& prop, const char mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTex(const aiMaterial& mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, Ref& texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot = 0) { if (mat.GetTextureCount(tt) > 0) { aiString tex; + // Read texcoord (UV map index) + mat.Get(AI_MATKEY_UVWSRC(tt, slot), texCoord); + if (mat.Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { std::string path = tex.C_Str(); @@ -572,21 +575,21 @@ void glTF2Exporter::GetMatTex(const aiMaterial& mat, TextureInfo& prop, aiTextur { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); - if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); - } + //if (texture) { + // GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //} } void glTF2Exporter::GetMatTex(const aiMaterial& mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); GetMatTexProp(mat, prop.scale, "scale", tt, slot); } } @@ -595,10 +598,10 @@ void glTF2Exporter::GetMatTex(const aiMaterial& mat, OcclusionTextureInfo& prop, { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); GetMatTexProp(mat, prop.strength, "strength", tt, slot); } } diff --git a/code/AssetLib/glTF2/glTF2Exporter.h b/code/AssetLib/glTF2/glTF2Exporter.h index edc85d998..f5238297f 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.h +++ b/code/AssetLib/glTF2/glTF2Exporter.h @@ -104,7 +104,7 @@ namespace Assimp void GetTexSampler(const aiMaterial& mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); void GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); void GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTex(const aiMaterial& mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::Ref& texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot); void GetMatTex(const aiMaterial& mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); void GetMatTex(const aiMaterial& mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); void GetMatTex(const aiMaterial& mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index b435f111d..aadc9fb16 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -165,7 +165,8 @@ inline void SetMaterialTextureProperty(std::vector &embeddedTexIdxs, Asset } mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot)); - mat->AddProperty(&prop.texCoord, 1, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(texType, texSlot)); + const int uvIndex = static_cast(prop.texCoord); + mat->AddProperty(&uvIndex, 1, AI_MATKEY_UVWSRC(texType, texSlot)); if (prop.textureTransformSupported) { aiUVTransform transform; diff --git a/include/assimp/material.h b/include/assimp/material.h index f348da369..2024be07f 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -144,9 +144,7 @@ enum aiTextureMapMode { enum aiTextureMapping { /** The mapping coordinates are taken from an UV channel. * - * #AI_MATKEY_UVWSRC property - * - * Specifies from which UV channel + * #AI_MATKEY_UVWSRC property specifies from which UV channel * the texture coordinates are to be taken from (remember, * meshes can have more than one UV channel). */ diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h index c67bcc3b8..93e7e3095 100644 --- a/include/assimp/pbrmaterial.h +++ b/include/assimp/pbrmaterial.h @@ -75,7 +75,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR "$mat.gltf.materialTransmission.transmissionFactor", 0, 0 //#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE aiTextureType_UNKNOWN, 5 -#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" +//#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname" #define _AI_MATKEY_GLTF_MAPPINGID_BASE "$tex.mappingid" #define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE "$tex.mappingfiltermag" @@ -83,7 +83,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define _AI_MATKEY_GLTF_SCALE_BASE "$tex.scale" #define _AI_MATKEY_GLTF_STRENGTH_BASE "$tex.strength" -#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD(type, N) _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N +//#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD(type, N) _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index ee8b1a742..2c000bb37 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -603,32 +603,58 @@ TEST_F(utglTF2ImportExport, sceneMetadata) { } TEST_F(utglTF2ImportExport, texcoords) { + Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", - aiProcess_ValidateDataStructure); + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(scene, nullptr); + ASSERT_TRUE(scene->HasMaterials()); + const aiMaterial *material = scene->mMaterials[0]; + + aiString path; + unsigned int uvIndex = 255; + aiTextureMapMode modes[2]; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 0); + + uvIndex = 255; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 1); +} + +#ifndef ASSIMP_BUILD_NO_EXPORT + +TEST_F(utglTF2ImportExport, texcoords_export) { + { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(scene, nullptr); + ASSERT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf_out.glb")); + } + + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); ASSERT_NE(scene, nullptr); ASSERT_TRUE(scene->HasMaterials()); const aiMaterial *material = scene->mMaterials[0]; aiString path; + unsigned int uvIndex = 255; aiTextureMapMode modes[2]; - EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, - nullptr, nullptr, modes)); + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); EXPECT_STREQ(path.C_Str(), "texture.png"); - - int uvIndex = -1; - EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_DIFFUSE, 0), &uvIndex), aiReturn_SUCCESS); EXPECT_EQ(uvIndex, 0); - // Using manual macro expansion of AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE here. - // The following works with some but not all compilers: - // #define APPLY(X, Y) X(Y) - // ..., APPLY(AI_MATKEY_GLTF_TEXTURE_TEXCOORD, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE), ... - EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_UNKNOWN, 0), &uvIndex), aiReturn_SUCCESS); + uvIndex = 255; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); EXPECT_EQ(uvIndex, 1); } +#endif // ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, recursive_nodes) { Assimp::Importer importer; const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/RecursiveNodes/RecursiveNodes.gltf", aiProcess_ValidateDataStructure); From f412595887647d77cd476576a3c970bfae01c4eb Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 15:25:02 +0100 Subject: [PATCH 068/232] Fix typo Thank you clang! --- code/AssetLib/glTF2/glTF2Exporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 7e0966aff..40ba41c20 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -655,7 +655,7 @@ bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlo // Add any appropriate textures GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - result == result || pbrSG.specularGlossinessTexture.texture; + result = result || pbrSG.specularGlossinessTexture.texture; if (result) { // Likely to always have diffuse From af748755e186faf5a80e597a37122ed55b6d3fcb Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Tue, 15 Jun 2021 13:20:12 -0700 Subject: [PATCH 069/232] Fix issue #2873 --- code/PostProcessing/EmbedTexturesProcess.cpp | 23 +++++++++++++------- code/PostProcessing/EmbedTexturesProcess.h | 3 +++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/code/PostProcessing/EmbedTexturesProcess.cpp b/code/PostProcessing/EmbedTexturesProcess.cpp index 7e435e556..ba978dbde 100644 --- a/code/PostProcessing/EmbedTexturesProcess.cpp +++ b/code/PostProcessing/EmbedTexturesProcess.cpp @@ -41,6 +41,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "EmbedTexturesProcess.h" +#include +#include #include #include "ProcessHelper.h" @@ -62,6 +64,7 @@ bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const { void EmbedTexturesProcess::SetupProperties(const Importer* pImp) { mRootPath = pImp->GetPropertyString("sourceFilePath"); mRootPath = mRootPath.substr(0, mRootPath.find_last_of("\\/") + 1u); + mIOHandler = pImp->GetIOHandler(); } void EmbedTexturesProcess::Execute(aiScene* pScene) { @@ -101,27 +104,31 @@ bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { std::string imagePath = path; // Test path directly - std::ifstream file(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == std::streampos(-1)) { + if (!mIOHandler->Exists(imagePath)) { ASSIMP_LOG_WARN("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder."); // Test path in root path imagePath = mRootPath + path; - file.open(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == std::streampos(-1)) { + if (!mIOHandler->Exists(imagePath)) { // Test path basename in root path imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u); - file.open(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == std::streampos(-1)) { + if (!mIOHandler->Exists(imagePath)) { ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, "."); return false; } } } + IOStream* pFile = mIOHandler->Open(imagePath); + if (pFile == nullptr) { + ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, "."); + return false; + } + imageSize = pFile->FileSize(); aiTexel* imageContent = new aiTexel[ 1ul + static_cast( imageSize ) / sizeof(aiTexel)]; - file.seekg(0, std::ios::beg); - file.read(reinterpret_cast(imageContent), imageSize); + pFile->Seek(0, aiOrigin_SET); + pFile->Read(reinterpret_cast(imageContent), imageSize, 1); + mIOHandler->Close(pFile); // Enlarging the textures table unsigned int textureId = pScene->mNumTextures++; diff --git a/code/PostProcessing/EmbedTexturesProcess.h b/code/PostProcessing/EmbedTexturesProcess.h index 90970937a..5915e0d44 100644 --- a/code/PostProcessing/EmbedTexturesProcess.h +++ b/code/PostProcessing/EmbedTexturesProcess.h @@ -48,6 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. struct aiNode; +class IOSystem; + namespace Assimp { /** @@ -80,6 +82,7 @@ private: private: std::string mRootPath; + IOSystem* mIOHandler = nullptr; }; } // namespace Assimp From 148b8c66a8ac040cc859444a27b5c7f3e061232a Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Tue, 15 Jun 2021 15:18:20 -0700 Subject: [PATCH 070/232] glTF2: zero out extra space created by padding. This makes resulting GLB deterministic. --- code/AssetLib/glTF2/glTF2Asset.inl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index b51ac20c2..c58732a03 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -790,8 +790,10 @@ inline bool Buffer::ReplaceData_joint(const size_t pBufferData_Offset, const siz inline size_t Buffer::AppendData(uint8_t *data, size_t length) { size_t offset = this->byteLength; // Force alignment to 4 bits - Grow((length + 3) & ~3); + size_t paddedLength = (length + 3) & ~3; + Grow(paddedLength); memcpy(mData.get() + offset, data, length); + memset(mData.get() + offset + length, 0, paddedLength - length); return offset; } From a34b9d1c95fc60d6b5a91ef998271097b2d2413d Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 16 Jun 2021 11:21:31 +0200 Subject: [PATCH 071/232] Fix review findings --- code/PostProcessing/EmbedTexturesProcess.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/code/PostProcessing/EmbedTexturesProcess.cpp b/code/PostProcessing/EmbedTexturesProcess.cpp index ba978dbde..cb2853926 100644 --- a/code/PostProcessing/EmbedTexturesProcess.cpp +++ b/code/PostProcessing/EmbedTexturesProcess.cpp @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -50,11 +49,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; -EmbedTexturesProcess::EmbedTexturesProcess() -: BaseProcess() { +EmbedTexturesProcess::EmbedTexturesProcess() : + BaseProcess() { + // empty } EmbedTexturesProcess::~EmbedTexturesProcess() { + // empty } bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const { @@ -68,12 +69,12 @@ void EmbedTexturesProcess::SetupProperties(const Importer* pImp) { } void EmbedTexturesProcess::Execute(aiScene* pScene) { - if (pScene == nullptr || pScene->mRootNode == nullptr) return; + if (pScene == nullptr || pScene->mRootNode == nullptr || mIOHandler == nullptr){ + return; + } aiString path; - uint32_t embeddedTexturesCount = 0u; - for (auto matId = 0u; matId < pScene->mNumMaterials; ++matId) { auto material = pScene->mMaterials[matId]; From e27074594f7444104bbe796feee041b5affa4233 Mon Sep 17 00:00:00 2001 From: Andreas Buhr Date: Wed, 16 Jun 2021 18:58:41 +0200 Subject: [PATCH 072/232] Replace swear words in IFCBoolean.cpp Add a more gentle comment. --- code/AssetLib/IFC/IFCBoolean.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/IFC/IFCBoolean.cpp b/code/AssetLib/IFC/IFCBoolean.cpp index 86cac7f46..afd0ad6eb 100644 --- a/code/AssetLib/IFC/IFCBoolean.cpp +++ b/code/AssetLib/IFC/IFCBoolean.cpp @@ -513,7 +513,7 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const Schema_2x3::IfcPoly } // we got a list of in-out-combinations of intersections. That should be an even number of intersections, or - // we're fucked. + // we are facing a non-recoverable error. if ((intersections.size() & 1) != 0) { IFCImporter::LogWarn("Odd number of intersections, can't work with that. Omitting half space boundary check."); continue; From a8c75c34a1dd297dfed74e660d30b83d4acc9a56 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 17 Jun 2021 21:31:28 +0200 Subject: [PATCH 073/232] Update scene.h Add some more checks against nullptr dereferecnes. --- include/assimp/scene.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/include/assimp/scene.h b/include/assimp/scene.h index fb3b2ef6e..522ddc6dc 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -402,15 +400,23 @@ struct aiScene //! Returns an embedded texture and its index std::pair GetEmbeddedTextureAndIndex(const char* filename) const { + if(nullptr==filename) { + return std::make_pair(nullptr, -1); + } // lookup using texture ID (if referenced like: "*1", "*2", etc.) if ('*' == *filename) { int index = std::atoi(filename + 1); - if (0 > index || mNumTextures <= static_cast(index)) + if (0 > index || mNumTextures <= static_cast(index)) { return std::make_pair(nullptr, -1); + } return std::make_pair(mTextures[index], index); } // lookup using filename const char* shortFilename = GetShortFilename(filename); + if (nullptr == shortFilename) { + return std::make_pair(nullptr, -1); + } + for (unsigned int i = 0; i < mNumTextures; i++) { const char* shortTextureFilename = GetShortFilename(mTextures[i]->mFilename.C_Str()); if (strcmp(shortTextureFilename, shortFilename) == 0) { From 94c3abd841dd08d8c98d1c7cbbf953ffb314bd94 Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Tue, 22 Jun 2021 12:27:15 -0400 Subject: [PATCH 074/232] Apply various performance fixes from clang-tidy --- code/AssetLib/3DS/3DSHelper.h | 76 ++++++++++---------- code/AssetLib/ASE/ASEParser.h | 8 +-- code/AssetLib/B3D/B3DImporter.cpp | 2 +- code/AssetLib/B3D/B3DImporter.h | 2 +- code/AssetLib/Blender/BlenderLoader.cpp | 2 +- code/AssetLib/COB/COBLoader.cpp | 2 +- code/AssetLib/Collada/ColladaParser.cpp | 2 +- code/AssetLib/FBX/FBXConverter.cpp | 2 +- code/AssetLib/FBX/FBXDocument.cpp | 9 ++- code/AssetLib/FBX/FBXExportNode.cpp | 5 +- code/AssetLib/FBX/FBXExportNode.h | 5 +- code/AssetLib/FBX/FBXExporter.cpp | 17 ++--- code/AssetLib/FBX/FBXExporter.h | 15 ++-- code/AssetLib/FBX/FBXProperties.cpp | 9 ++- code/AssetLib/IFC/IFCGeometry.cpp | 2 +- code/AssetLib/IFC/IFCMaterial.cpp | 6 +- code/AssetLib/IFC/IFCUtil.h | 16 ++--- code/AssetLib/LWS/LWSLoader.cpp | 2 +- code/AssetLib/M3D/M3DImporter.cpp | 2 +- code/AssetLib/M3D/M3DImporter.h | 4 +- code/AssetLib/Ogre/OgreMaterial.cpp | 4 +- code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp | 4 +- code/AssetLib/STEPParser/STEPFileReader.cpp | 5 +- code/AssetLib/Step/STEPFile.h | 4 +- code/AssetLib/X/XFileExporter.cpp | 4 +- code/AssetLib/X3D/X3DExporter.hpp | 6 +- code/AssetLib/glTF/glTFAsset.h | 10 ++- code/AssetLib/glTF/glTFAsset.inl | 2 +- code/AssetLib/glTF/glTFExporter.cpp | 3 +- code/AssetLib/glTF2/glTF2Asset.h | 4 +- code/AssetLib/glTF2/glTF2Asset.inl | 2 +- code/AssetLib/glTF2/glTF2Exporter.cpp | 3 +- code/Pbrt/PbrtExporter.cpp | 23 +++--- code/Pbrt/PbrtExporter.h | 4 +- code/PostProcessing/EmbedTexturesProcess.cpp | 2 +- code/PostProcessing/EmbedTexturesProcess.h | 2 +- test/unit/utTypes.cpp | 4 +- 37 files changed, 131 insertions(+), 143 deletions(-) diff --git a/code/AssetLib/3DS/3DSHelper.h b/code/AssetLib/3DS/3DSHelper.h index 1930c0c40..e8efbf949 100644 --- a/code/AssetLib/3DS/3DSHelper.h +++ b/code/AssetLib/3DS/3DSHelper.h @@ -348,16 +348,16 @@ struct Texture { // empty } - Texture(Texture &&other) AI_NO_EXCEPT : mTextureBlend(std::move(other.mTextureBlend)), + Texture(Texture &&other) AI_NO_EXCEPT : mTextureBlend(other.mTextureBlend), mMapName(std::move(other.mMapName)), - mOffsetU(std::move(other.mOffsetU)), - mOffsetV(std::move(other.mOffsetV)), - mScaleU(std::move(other.mScaleU)), - mScaleV(std::move(other.mScaleV)), - mRotation(std::move(other.mRotation)), - mMapMode(std::move(other.mMapMode)), - bPrivate(std::move(other.bPrivate)), - iUVSrc(std::move(other.iUVSrc)) { + mOffsetU(other.mOffsetU), + mOffsetV(other.mOffsetV), + mScaleU(other.mScaleU), + mScaleV(other.mScaleV), + mRotation(other.mRotation), + mMapMode(other.mMapMode), + bPrivate(other.bPrivate), + iUVSrc(other.iUVSrc) { // empty } @@ -366,16 +366,16 @@ struct Texture { return *this; } - mTextureBlend = std::move(other.mTextureBlend); + mTextureBlend = other.mTextureBlend; mMapName = std::move(other.mMapName); - mOffsetU = std::move(other.mOffsetU); - mOffsetV = std::move(other.mOffsetV); - mScaleU = std::move(other.mScaleU); - mScaleV = std::move(other.mScaleV); - mRotation = std::move(other.mRotation); - mMapMode = std::move(other.mMapMode); - bPrivate = std::move(other.bPrivate); - iUVSrc = std::move(other.iUVSrc); + mOffsetU = other.mOffsetU; + mOffsetV = other.mOffsetV; + mScaleU = other.mScaleU; + mScaleV = other.mScaleV; + mRotation = other.mRotation; + mMapMode = other.mMapMode; + bPrivate = other.bPrivate; + iUVSrc = other.iUVSrc; return *this; } @@ -461,13 +461,13 @@ struct Material { //! Move constructor. This is explicitly written because MSVC doesn't support defaulting it Material(Material &&other) AI_NO_EXCEPT : mName(std::move(other.mName)), - mDiffuse(std::move(other.mDiffuse)), - mSpecularExponent(std::move(other.mSpecularExponent)), - mShininessStrength(std::move(other.mShininessStrength)), - mSpecular(std::move(other.mSpecular)), - mAmbient(std::move(other.mAmbient)), - mShading(std::move(other.mShading)), - mTransparency(std::move(other.mTransparency)), + mDiffuse(other.mDiffuse), + mSpecularExponent(other.mSpecularExponent), + mShininessStrength(other.mShininessStrength), + mSpecular(other.mSpecular), + mAmbient(other.mAmbient), + mShading(other.mShading), + mTransparency(other.mTransparency), sTexDiffuse(std::move(other.sTexDiffuse)), sTexOpacity(std::move(other.sTexOpacity)), sTexSpecular(std::move(other.sTexSpecular)), @@ -475,10 +475,10 @@ struct Material { sTexBump(std::move(other.sTexBump)), sTexEmissive(std::move(other.sTexEmissive)), sTexShininess(std::move(other.sTexShininess)), - mBumpHeight(std::move(other.mBumpHeight)), - mEmissive(std::move(other.mEmissive)), + mBumpHeight(other.mBumpHeight), + mEmissive(other.mEmissive), sTexAmbient(std::move(other.sTexAmbient)), - mTwoSided(std::move(other.mTwoSided)) { + mTwoSided(other.mTwoSided) { // empty } @@ -488,13 +488,13 @@ struct Material { } mName = std::move(other.mName); - mDiffuse = std::move(other.mDiffuse); - mSpecularExponent = std::move(other.mSpecularExponent); - mShininessStrength = std::move(other.mShininessStrength), - mSpecular = std::move(other.mSpecular); - mAmbient = std::move(other.mAmbient); - mShading = std::move(other.mShading); - mTransparency = std::move(other.mTransparency); + mDiffuse = other.mDiffuse; + mSpecularExponent = other.mSpecularExponent; + mShininessStrength = other.mShininessStrength, + mSpecular = other.mSpecular; + mAmbient = other.mAmbient; + mShading = other.mShading; + mTransparency = other.mTransparency; sTexDiffuse = std::move(other.sTexDiffuse); sTexOpacity = std::move(other.sTexOpacity); sTexSpecular = std::move(other.sTexSpecular); @@ -502,10 +502,10 @@ struct Material { sTexBump = std::move(other.sTexBump); sTexEmissive = std::move(other.sTexEmissive); sTexShininess = std::move(other.sTexShininess); - mBumpHeight = std::move(other.mBumpHeight); - mEmissive = std::move(other.mEmissive); + mBumpHeight = other.mBumpHeight; + mEmissive = other.mEmissive; sTexAmbient = std::move(other.sTexAmbient); - mTwoSided = std::move(other.mTwoSided); + mTwoSided = other.mTwoSided; return *this; } diff --git a/code/AssetLib/ASE/ASEParser.h b/code/AssetLib/ASE/ASEParser.h index d04fc0662..f49cfc36f 100644 --- a/code/AssetLib/ASE/ASEParser.h +++ b/code/AssetLib/ASE/ASEParser.h @@ -95,8 +95,8 @@ struct Material : public D3DS::Material { Material(Material &&other) AI_NO_EXCEPT : D3DS::Material(std::move(other)), avSubMaterials(std::move(other.avSubMaterials)), - pcInstance(std::move(other.pcInstance)), - bNeed(std::move(other.bNeed)) { + pcInstance(other.pcInstance), + bNeed(other.bNeed) { other.pcInstance = nullptr; } @@ -108,8 +108,8 @@ struct Material : public D3DS::Material { //D3DS::Material::operator=(std::move(other)); avSubMaterials = std::move(other.avSubMaterials); - pcInstance = std::move(other.pcInstance); - bNeed = std::move(other.bNeed); + pcInstance = other.pcInstance; + bNeed = other.bNeed; other.pcInstance = nullptr; diff --git a/code/AssetLib/B3D/B3DImporter.cpp b/code/AssetLib/B3D/B3DImporter.cpp index 3d9a5075a..11f7bcd14 100644 --- a/code/AssetLib/B3D/B3DImporter.cpp +++ b/code/AssetLib/B3D/B3DImporter.cpp @@ -143,7 +143,7 @@ AI_WONT_RETURN void B3DImporter::Oops() { } // ------------------------------------------------------------------------------------------------ -AI_WONT_RETURN void B3DImporter::Fail(string str) { +AI_WONT_RETURN void B3DImporter::Fail(const string &str) { #ifdef DEBUG_B3D ASSIMP_LOG_ERROR("Error in B3D file data: ", str); #endif diff --git a/code/AssetLib/B3D/B3DImporter.h b/code/AssetLib/B3D/B3DImporter.h index e2a75abdf..a7ed65c3b 100644 --- a/code/AssetLib/B3D/B3DImporter.h +++ b/code/AssetLib/B3D/B3DImporter.h @@ -96,7 +96,7 @@ private: }; AI_WONT_RETURN void Oops() AI_WONT_RETURN_SUFFIX; - AI_WONT_RETURN void Fail( std::string str ) AI_WONT_RETURN_SUFFIX; + AI_WONT_RETURN void Fail(const std::string &str) AI_WONT_RETURN_SUFFIX; void ReadTEXS(); void ReadBRUS(); diff --git a/code/AssetLib/Blender/BlenderLoader.cpp b/code/AssetLib/Blender/BlenderLoader.cpp index 7cf4e070e..42a7a1723 100644 --- a/code/AssetLib/Blender/BlenderLoader.cpp +++ b/code/AssetLib/Blender/BlenderLoader.cpp @@ -679,7 +679,7 @@ void BlenderImporter::BuildMaterials(ConversionData &conv_data) { BuildDefaultMaterial(conv_data); - for (std::shared_ptr mat : conv_data.materials_raw) { + for (const std::shared_ptr &mat : conv_data.materials_raw) { // reset per material global counters for (size_t i = 0; i < sizeof(conv_data.next_texture) / sizeof(conv_data.next_texture[0]); ++i) { diff --git a/code/AssetLib/COB/COBLoader.cpp b/code/AssetLib/COB/COBLoader.cpp index 94327c683..822bce16d 100644 --- a/code/AssetLib/COB/COBLoader.cpp +++ b/code/AssetLib/COB/COBLoader.cpp @@ -230,7 +230,7 @@ void COBImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy } // ------------------------------------------------------------------------------------------------ -void ConvertTexture(std::shared_ptr tex, aiMaterial *out, aiTextureType type) { +void ConvertTexture(const std::shared_ptr &tex, aiMaterial *out, aiTextureType type) { const aiString path(tex->path); out->AddProperty(&path, AI_MATKEY_TEXTURE(type, 0)); out->AddProperty(&tex->transform, 1, AI_MATKEY_UVTRANSFORM(type, 0)); diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 5dbf0a567..3166136b2 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -453,7 +453,7 @@ void ColladaParser::PostProcessRootAnimations() { temp.mSubAnims.push_back(clip); - for (std::string animationID : it.second) { + for (const std::string &animationID : it.second) { AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID); if (animation != mAnimationLibrary.end()) { diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index a564b3e9b..a6a3fc566 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -2599,7 +2599,7 @@ void FBXConverter::ConvertAnimationStack(const AnimationStack &st) { anim->mMorphMeshChannels = new aiMeshMorphAnim *[numMorphMeshChannels]; anim->mNumMorphMeshChannels = numMorphMeshChannels; unsigned int i = 0; - for (auto morphAnimIt : morphAnimDatas) { + for (const auto &morphAnimIt : morphAnimDatas) { morphAnimData *animData = morphAnimIt.second; unsigned int numKeys = static_cast(animData->size()); aiMeshMorphAnim *meshMorphAnim = new aiMeshMorphAnim(); diff --git a/code/AssetLib/FBX/FBXDocument.cpp b/code/AssetLib/FBX/FBXDocument.cpp index 7adaadf6c..0c4435348 100644 --- a/code/AssetLib/FBX/FBXDocument.cpp +++ b/code/AssetLib/FBX/FBXDocument.cpp @@ -57,9 +57,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -#include #include #include +#include +#include namespace Assimp { namespace FBX { @@ -248,10 +249,8 @@ Object::~Object() } // ------------------------------------------------------------------------------------------------ -FileGlobalSettings::FileGlobalSettings(const Document& doc, std::shared_ptr props) -: props(props) -, doc(doc) -{ +FileGlobalSettings::FileGlobalSettings(const Document &doc, std::shared_ptr props) : + props(std::move(props)), doc(doc) { // empty } diff --git a/code/AssetLib/FBX/FBXExportNode.cpp b/code/AssetLib/FBX/FBXExportNode.cpp index 91e421420..817308ec8 100644 --- a/code/AssetLib/FBX/FBXExportNode.cpp +++ b/code/AssetLib/FBX/FBXExportNode.cpp @@ -144,9 +144,8 @@ void FBX::Node::AddP70time( // public member functions for writing nodes to stream void FBX::Node::Dump( - std::shared_ptr outfile, - bool binary, int indent -) { + const std::shared_ptr &outfile, + bool binary, int indent) { if (binary) { Assimp::StreamWriterLE outstream(outfile); DumpBinary(outstream); diff --git a/code/AssetLib/FBX/FBXExportNode.h b/code/AssetLib/FBX/FBXExportNode.h index c5f29ef0f..6ef27972d 100644 --- a/code/AssetLib/FBX/FBXExportNode.h +++ b/code/AssetLib/FBX/FBXExportNode.h @@ -157,9 +157,8 @@ public: // member functions for writing data to a file or stream // write the full node to the given file or stream void Dump( - std::shared_ptr outfile, - bool binary, int indent - ); + const std::shared_ptr &outfile, + bool binary, int indent); void Dump(Assimp::StreamWriterLE &s, bool binary, int indent); // these other functions are for writing data piece by piece. diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index 6bdd0b5be..27b65a27f 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -2718,16 +2718,14 @@ void FBXExporter::WriteModelNodes( } } - void FBXExporter::WriteAnimationCurveNode( - StreamWriterLE& outstream, - int64_t uid, - const std::string& name, // "T", "R", or "S" - aiVector3D default_value, - std::string property_name, // "Lcl Translation" etc - int64_t layer_uid, - int64_t node_uid -) { + StreamWriterLE &outstream, + int64_t uid, + const std::string &name, // "T", "R", or "S" + aiVector3D default_value, + const std::string &property_name, // "Lcl Translation" etc + int64_t layer_uid, + int64_t node_uid) { FBX::Node n("AnimationCurveNode"); n.AddProperties(uid, name + FBX::SEPARATOR + "AnimCurveNode", ""); FBX::Node p("Properties70"); @@ -2742,7 +2740,6 @@ void FBXExporter::WriteAnimationCurveNode( this->connections.emplace_back("C", "OP", uid, node_uid, property_name); } - void FBXExporter::WriteAnimationCurve( StreamWriterLE& outstream, double default_value, diff --git a/code/AssetLib/FBX/FBXExporter.h b/code/AssetLib/FBX/FBXExporter.h index 563183268..3f5e8029d 100644 --- a/code/AssetLib/FBX/FBXExporter.h +++ b/code/AssetLib/FBX/FBXExporter.h @@ -156,14 +156,13 @@ namespace Assimp FBX::TransformInheritance ti_type=FBX::TransformInheritance_RSrs ); void WriteAnimationCurveNode( - StreamWriterLE& outstream, - int64_t uid, - const std::string& name, // "T", "R", or "S" - aiVector3D default_value, - std::string property_name, // "Lcl Translation" etc - int64_t animation_layer_uid, - int64_t node_uid - ); + StreamWriterLE &outstream, + int64_t uid, + const std::string &name, // "T", "R", or "S" + aiVector3D default_value, + const std::string &property_name, // "Lcl Translation" etc + int64_t animation_layer_uid, + int64_t node_uid); void WriteAnimationCurve( StreamWriterLE& outstream, double default_value, diff --git a/code/AssetLib/FBX/FBXProperties.cpp b/code/AssetLib/FBX/FBXProperties.cpp index 1a5ebffd1..c3f4de260 100644 --- a/code/AssetLib/FBX/FBXProperties.cpp +++ b/code/AssetLib/FBX/FBXProperties.cpp @@ -52,6 +52,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "FBXDocumentUtil.h" #include "FBXProperties.h" +#include + namespace Assimp { namespace FBX { @@ -172,10 +174,8 @@ PropertyTable::PropertyTable() } // ------------------------------------------------------------------------------------------------ -PropertyTable::PropertyTable(const Element& element, std::shared_ptr templateProps) -: templateProps(templateProps) -, element(&element) -{ +PropertyTable::PropertyTable(const Element &element, std::shared_ptr templateProps) : + templateProps(std::move(templateProps)), element(&element) { const Scope& scope = GetRequiredScope(element); for(const ElementMap::value_type& v : scope.Elements()) { if(v.first != "P") { @@ -199,7 +199,6 @@ PropertyTable::PropertyTable(const Element& element, std::shared_ptr meshtmp = std::make_shared(); if(const Schema_2x3::IfcShellBasedSurfaceModel* shellmod = geo.ToPtr()) { - for(std::shared_ptr shell :shellmod->SbsmBoundary) { + for (const std::shared_ptr &shell : shellmod->SbsmBoundary) { try { const ::Assimp::STEP::EXPRESS::ENTITY& e = shell->To<::Assimp::STEP::EXPRESS::ENTITY>(); const Schema_2x3::IfcConnectedFaceSet& fs = conv.db.MustGetObject(e).To(); diff --git a/code/AssetLib/IFC/IFCMaterial.cpp b/code/AssetLib/IFC/IFCMaterial.cpp index 2a79f0754..c26a3aa0a 100644 --- a/code/AssetLib/IFC/IFCMaterial.cpp +++ b/code/AssetLib/IFC/IFCMaterial.cpp @@ -75,7 +75,7 @@ static void FillMaterial(aiMaterial* mat,const IFC::Schema_2x3::IfcSurfaceStyle* mat->AddProperty(&name,AI_MATKEY_NAME); // now see which kinds of surface information are present - for(std::shared_ptr< const IFC::Schema_2x3::IfcSurfaceStyleElementSelect > sel2 : surf->Styles) { + for (const std::shared_ptr &sel2 : surf->Styles) { if (const IFC::Schema_2x3::IfcSurfaceStyleShading* shade = sel2->ResolveSelectPtr(conv.db)) { aiColor4D col_base,col; @@ -124,7 +124,7 @@ static void FillMaterial(aiMaterial* mat,const IFC::Schema_2x3::IfcSurfaceStyle* } } } - } + } } } @@ -134,7 +134,7 @@ unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionDat for(;range.first != range.second; ++range.first) { if(const IFC::Schema_2x3::IfcStyledItem* const styled = conv.db.GetObject((*range.first).second)->ToPtr()) { for(const IFC::Schema_2x3::IfcPresentationStyleAssignment& as : styled->Styles) { - for(std::shared_ptr sel : as.Styles) { + for (const std::shared_ptr &sel : as.Styles) { if( const IFC::Schema_2x3::IfcSurfaceStyle* const surf = sel->ResolveSelectPtr(conv.db) ) { // try to satisfy from cache diff --git a/code/AssetLib/IFC/IFCUtil.h b/code/AssetLib/IFC/IFCUtil.h index a1190746b..20b9a617c 100644 --- a/code/AssetLib/IFC/IFCUtil.h +++ b/code/AssetLib/IFC/IFCUtil.h @@ -54,6 +54,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include + +#include + struct aiNode; namespace Assimp { @@ -137,14 +141,10 @@ struct TempOpening } // ------------------------------------------------------------------------------ - TempOpening(const IFC::Schema_2x3::IfcSolidModel* solid,IfcVector3 extrusionDir, - std::shared_ptr profileMesh, - std::shared_ptr profileMesh2D) - : solid(solid) - , extrusionDir(extrusionDir) - , profileMesh(profileMesh) - , profileMesh2D(profileMesh2D) - { + TempOpening(const IFC::Schema_2x3::IfcSolidModel *solid, IfcVector3 extrusionDir, + std::shared_ptr profileMesh, + std::shared_ptr profileMesh2D) : + solid(solid), extrusionDir(extrusionDir), profileMesh(std::move(std::move(profileMesh))), profileMesh2D(std::move(std::move(profileMesh2D))) { } // ------------------------------------------------------------------------------ diff --git a/code/AssetLib/LWS/LWSLoader.cpp b/code/AssetLib/LWS/LWSLoader.cpp index d469a1064..01a50b6e4 100644 --- a/code/AssetLib/LWS/LWSLoader.cpp +++ b/code/AssetLib/LWS/LWSLoader.cpp @@ -318,7 +318,7 @@ void LWSImporter::SetupNodeName(aiNode *nd, LWS::NodeDesc &src) { } else { ++s; } - std::string::size_type t = src.path.substr(s).find_last_of("."); + std::string::size_type t = src.path.substr(s).find_last_of('.'); nd->mName.length = ::ai_snprintf(nd->mName.data, MAXLEN, "%s_(%08X)", src.path.substr(s).substr(0, t).c_str(), combined); return; diff --git a/code/AssetLib/M3D/M3DImporter.cpp b/code/AssetLib/M3D/M3DImporter.cpp index 38bbd1d4a..4b2f9bd77 100644 --- a/code/AssetLib/M3D/M3DImporter.cpp +++ b/code/AssetLib/M3D/M3DImporter.cpp @@ -655,7 +655,7 @@ void M3DImporter::convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned in // ------------------------------------------------------------------------------------------------ // find a node by name -aiNode *M3DImporter::findNode(aiNode *pNode, aiString name) { +aiNode *M3DImporter::findNode(aiNode *pNode, const aiString &name) { ai_assert(pNode != nullptr); ai_assert(mScene != nullptr); diff --git a/code/AssetLib/M3D/M3DImporter.h b/code/AssetLib/M3D/M3DImporter.h index 7a2a9fbd3..05e8ced7b 100644 --- a/code/AssetLib/M3D/M3DImporter.h +++ b/code/AssetLib/M3D/M3DImporter.h @@ -89,8 +89,8 @@ private: // helper functions aiColor4D mkColor(uint32_t c); void convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid); - aiNode *findNode(aiNode *pNode, aiString name); - void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m); + aiNode *findNode(aiNode *pNode, const aiString &name); + void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m); void populateMesh(const M3DWrapper &m3d, aiMesh *pMesh, std::vector *faces, std::vector *verteces, std::vector *normals, std::vector *texcoords, std::vector *colors, std::vector *vertexids); diff --git a/code/AssetLib/Ogre/OgreMaterial.cpp b/code/AssetLib/Ogre/OgreMaterial.cpp index 295dedde6..6da82f89c 100644 --- a/code/AssetLib/Ogre/OgreMaterial.cpp +++ b/code/AssetLib/Ogre/OgreMaterial.cpp @@ -415,8 +415,8 @@ bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstr // User defined Assimp config property to detect texture type from filename. if (m_detectTextureTypeFromFilename) { - size_t posSuffix = textureRef.find_last_of("."); - size_t posUnderscore = textureRef.find_last_of("_"); + size_t posSuffix = textureRef.find_last_of('.'); + size_t posUnderscore = textureRef.find_last_of('_'); if (posSuffix != string::npos && posUnderscore != string::npos && posSuffix > posUnderscore) { string identifier = ai_tolower(textureRef.substr(posUnderscore, posSuffix - posUnderscore)); diff --git a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp index becfa41fc..a1da8fd74 100644 --- a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp +++ b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp @@ -99,7 +99,7 @@ static void extractIds(const std::string &key, int &id1, int &id2) { return; } - const std::string::size_type pos = key.find("."); + const std::string::size_type pos = key.find('.'); if (std::string::npos == pos) { return; } @@ -208,7 +208,7 @@ void Q3BSPFileImporter::separateMapName(const std::string &importName, std::stri return; } - const std::string::size_type pos = importName.rfind(","); + const std::string::size_type pos = importName.rfind(','); if (std::string::npos == pos) { archiveName = importName; return; diff --git a/code/AssetLib/STEPParser/STEPFileReader.cpp b/code/AssetLib/STEPParser/STEPFileReader.cpp index e97ea1e28..ac6d83672 100644 --- a/code/AssetLib/STEPParser/STEPFileReader.cpp +++ b/code/AssetLib/STEPParser/STEPFileReader.cpp @@ -49,8 +49,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "STEPFileEncoding.h" #include #include -#include #include +#include +#include using namespace Assimp; @@ -87,7 +88,7 @@ static const char *ISO_Token = "ISO-10303-21;"; static const char *FILE_SCHEMA_Token = "FILE_SCHEMA"; // ------------------------------------------------------------------------------------------------ STEP::DB* STEP::ReadFileHeader(std::shared_ptr stream) { - std::shared_ptr reader = std::shared_ptr(new StreamReaderLE(stream)); + std::shared_ptr reader = std::shared_ptr(new StreamReaderLE(std::move(stream))); std::unique_ptr db = std::unique_ptr(new STEP::DB(reader)); LineSplitter &splitter = db->GetSplitter(); diff --git a/code/AssetLib/Step/STEPFile.h b/code/AssetLib/Step/STEPFile.h index 90eaef5f3..e09faad98 100644 --- a/code/AssetLib/Step/STEPFile.h +++ b/code/AssetLib/Step/STEPFile.h @@ -634,7 +634,7 @@ private: }; template -inline bool operator==(std::shared_ptr lo, T whatever) { +inline bool operator==(const std::shared_ptr &lo, T whatever) { return *lo == whatever; // XXX use std::forward if we have 0x } @@ -816,7 +816,7 @@ public: typedef std::pair RefMapRange; private: - DB(std::shared_ptr reader) : + DB(const std::shared_ptr &reader) : reader(reader), splitter(*reader, true, true), evaluated_count(), schema(nullptr) {} public: diff --git a/code/AssetLib/X/XFileExporter.cpp b/code/AssetLib/X/XFileExporter.cpp index da20b935a..bd997a3c5 100644 --- a/code/AssetLib/X/XFileExporter.cpp +++ b/code/AssetLib/X/XFileExporter.cpp @@ -530,8 +530,8 @@ void XFileExporter::writePath(const aiString &path) while( str.find( "\\\\") != std::string::npos) str.replace( str.find( "\\\\"), 2, "\\"); - while( str.find( "\\") != std::string::npos) - str.replace( str.find( "\\"), 1, "/"); + while (str.find('\\') != std::string::npos) + str.replace(str.find('\\'), 1, "/"); mOutput << str; diff --git a/code/AssetLib/X3D/X3DExporter.hpp b/code/AssetLib/X3D/X3DExporter.hpp index 00115e0b6..fc8f9a921 100644 --- a/code/AssetLib/X3D/X3DExporter.hpp +++ b/code/AssetLib/X3D/X3DExporter.hpp @@ -63,9 +63,9 @@ class X3DExporter { // empty } - SAttribute(SAttribute && rhs) : - Name(std::move(rhs.Name)), - Value(std::move(rhs.Value)) { + SAttribute(SAttribute &&rhs) noexcept : + Name(rhs.Name), + Value(rhs.Value) { // empty } }; diff --git a/code/AssetLib/glTF/glTFAsset.h b/code/AssetLib/glTF/glTFAsset.h index da49a1737..4cef646d2 100644 --- a/code/AssetLib/glTF/glTFAsset.h +++ b/code/AssetLib/glTF/glTFAsset.h @@ -456,11 +456,10 @@ namespace glTF /// \param [in] pDecodedData - pointer to decoded data array. /// \param [in] pDecodedData_Length - size of encoded region, in bytes. /// \param [in] pID - ID of the region. - SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string pID) - : Offset(pOffset), EncodedData_Length(pEncodedData_Length), DecodedData(pDecodedData), DecodedData_Length(pDecodedData_Length), ID(pID) - {} + SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID) : + Offset(pOffset), EncodedData_Length(pEncodedData_Length), DecodedData(pDecodedData), DecodedData_Length(pDecodedData_Length), ID(pID) {} - /// \fn ~SEncodedRegion() + /// \fn ~SEncodedRegion() /// Destructor. ~SEncodedRegion() { delete [] DecodedData; } }; @@ -1149,8 +1148,7 @@ namespace glTF void ReadExtensionsUsed(Document& doc); - - IOStream* OpenFile(std::string path, const char* mode, bool absolute = false); + IOStream *OpenFile(const std::string &path, const char *mode, bool absolute = false); }; } diff --git a/code/AssetLib/glTF/glTFAsset.inl b/code/AssetLib/glTF/glTFAsset.inl index 6e1e60846..e915a3aee 100644 --- a/code/AssetLib/glTF/glTFAsset.inl +++ b/code/AssetLib/glTF/glTFAsset.inl @@ -1377,7 +1377,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) { #undef CHECK_EXT } -inline IOStream *Asset::OpenFile(std::string path, const char *mode, bool absolute) { +inline IOStream *Asset::OpenFile(const std::string& path, const char *mode, bool absolute) { #ifdef ASSIMP_API (void)absolute; return mIOSystem->Open(path, mode); diff --git a/code/AssetLib/glTF/glTFExporter.cpp b/code/AssetLib/glTF/glTFExporter.cpp index 5e1992319..810263f52 100644 --- a/code/AssetLib/glTF/glTFExporter.cpp +++ b/code/AssetLib/glTF/glTFExporter.cpp @@ -405,8 +405,7 @@ void glTFExporter::ExportMaterials() * Search through node hierarchy and find the node containing the given meshID. * Returns true on success, and false otherwise. */ -bool FindMeshNode(Ref& nodeIn, Ref& meshNode, std::string meshID) -{ +bool FindMeshNode(Ref &nodeIn, Ref &meshNode, const std::string &meshID) { for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) { if (meshID.compare(nodeIn->meshes[i]->id) == 0) { meshNode = nodeIn; diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 6aa0f92ed..eaacf8564 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -408,7 +408,7 @@ public: /// \param [in] pDecodedData - pointer to decoded data array. /// \param [in] pDecodedData_Length - size of encoded region, in bytes. /// \param [in] pID - ID of the region. - SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string pID) : + SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID) : Offset(pOffset), EncodedData_Length(pEncodedData_Length), DecodedData(pDecodedData), @@ -1188,7 +1188,7 @@ private: void ReadExtensionsUsed(Document &doc); void ReadExtensionsRequired(Document &doc); - IOStream *OpenFile(std::string path, const char *mode, bool absolute = false); + IOStream *OpenFile(const std::string &path, const char *mode, bool absolute = false); }; inline std::string getContextForErrorMessages(const std::string &id, const std::string &name) { diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index b51ac20c2..ab81e3519 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -2132,7 +2132,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) { #undef CHECK_EXT } -inline IOStream *Asset::OpenFile(std::string path, const char *mode, bool /*absolute*/) { +inline IOStream *Asset::OpenFile(const std::string& path, const char *mode, bool /*absolute*/) { #ifdef ASSIMP_API return mIOSystem->Open(path, mode); #else diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 83356f7c2..0e7251b1e 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -798,8 +798,7 @@ void glTF2Exporter::ExportMaterials() * Search through node hierarchy and find the node containing the given meshID. * Returns true on success, and false otherwise. */ -bool FindMeshNode(Ref& nodeIn, Ref& meshNode, std::string meshID) -{ +bool FindMeshNode(Ref &nodeIn, Ref &meshNode, const std::string &meshID) { for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) { if (meshID.compare(nodeIn->meshes[i]->id) == 0) { meshNode = nodeIn; diff --git a/code/Pbrt/PbrtExporter.cpp b/code/Pbrt/PbrtExporter.cpp index b793c37f9..0be244600 100644 --- a/code/Pbrt/PbrtExporter.cpp +++ b/code/Pbrt/PbrtExporter.cpp @@ -106,14 +106,13 @@ void ExportScenePbrt ( } // end of namespace Assimp // Constructor -PbrtExporter::PbrtExporter ( - const aiScene* pScene, IOSystem* pIOSystem, - const std::string path, const std::string file) -: mScene(pScene), - mIOSystem(pIOSystem), - mPath(path), - mFile(file) -{ +PbrtExporter::PbrtExporter( + const aiScene *pScene, IOSystem *pIOSystem, + const std::string &path, const std::string &file) : + mScene(pScene), + mIOSystem(pIOSystem), + mPath(path), + mFile(file) { // Export embedded textures. if (mScene->mNumTextures > 0) if (!mIOSystem->CreateDirectory("textures")) @@ -210,12 +209,12 @@ void PbrtExporter::WriteMetaData() { aiString* value = static_cast(pMetaData->mValues[i].mData); std::string svalue = value->C_Str(); - std::size_t found = svalue.find_first_of("\n"); + std::size_t found = svalue.find_first_of('\n'); mOutput << "\n"; while (found != std::string::npos) { mOutput << "# " << svalue.substr(0, found) << "\n"; svalue = svalue.substr(found + 1); - found = svalue.find_first_of("\n"); + found = svalue.find_first_of('\n'); } mOutput << "# " << svalue << "\n"; break; @@ -596,8 +595,8 @@ void PbrtExporter::WriteMaterial(int m) { } mOutput << "\n"; - auto White = [](aiColor3D c) { return c.r == 1 && c.g == 1 && c.b == 1; }; - auto Black = [](aiColor3D c) { return c.r == 0 && c.g == 0 && c.b == 0; }; + auto White = [](const aiColor3D &c) { return c.r == 1 && c.g == 1 && c.b == 1; }; + auto Black = [](const aiColor3D &c) { return c.r == 0 && c.g == 0 && c.b == 0; }; aiColor3D diffuse, specular, transparency; bool constantDiffuse = (material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse) == AI_SUCCESS && diff --git a/code/Pbrt/PbrtExporter.h b/code/Pbrt/PbrtExporter.h index 167f318fc..e8ff03ccb 100644 --- a/code/Pbrt/PbrtExporter.h +++ b/code/Pbrt/PbrtExporter.h @@ -74,8 +74,8 @@ class PbrtExporter { public: /// Constructor for a specific scene to export - PbrtExporter(const aiScene* pScene, IOSystem* pIOSystem, - const std::string path, const std::string file); + PbrtExporter(const aiScene *pScene, IOSystem *pIOSystem, + const std::string &path, const std::string &file); /// Destructor virtual ~PbrtExporter(); diff --git a/code/PostProcessing/EmbedTexturesProcess.cpp b/code/PostProcessing/EmbedTexturesProcess.cpp index cb2853926..500032c39 100644 --- a/code/PostProcessing/EmbedTexturesProcess.cpp +++ b/code/PostProcessing/EmbedTexturesProcess.cpp @@ -100,7 +100,7 @@ void EmbedTexturesProcess::Execute(aiScene* pScene) { ASSIMP_LOG_INFO("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." ); } -bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { +bool EmbedTexturesProcess::addTexture(aiScene *pScene, const std::string &path) const { std::streampos imageSize = 0; std::string imagePath = path; diff --git a/code/PostProcessing/EmbedTexturesProcess.h b/code/PostProcessing/EmbedTexturesProcess.h index 5915e0d44..b33968850 100644 --- a/code/PostProcessing/EmbedTexturesProcess.h +++ b/code/PostProcessing/EmbedTexturesProcess.h @@ -78,7 +78,7 @@ public: private: // Resolve the path and add the file content to the scene as a texture. - bool addTexture(aiScene* pScene, std::string path) const; + bool addTexture(aiScene *pScene, const std::string &path) const; private: std::string mRootPath; diff --git a/test/unit/utTypes.cpp b/test/unit/utTypes.cpp index cc354eb3d..1ac9a1d5e 100644 --- a/test/unit/utTypes.cpp +++ b/test/unit/utTypes.cpp @@ -53,8 +53,8 @@ class utTypes : public ::testing::Test { TEST_F( utTypes, Color3dCpmpareOpTest ) { aiColor3D col1( 1, 2, 3 ); aiColor3D col2( 4, 5, 6 ); - aiColor3D col3( col1 ); - + const aiColor3D &col3(col1); + EXPECT_FALSE( col1 == col2 ); EXPECT_FALSE( col2 == col3 ); EXPECT_TRUE( col1 == col3 ); From 16508687015f383b423d710a2a1b13523c2fd9f9 Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Tue, 22 Jun 2021 12:32:58 -0400 Subject: [PATCH 075/232] Remove redundant include --- code/AssetLib/IFC/IFCUtil.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/AssetLib/IFC/IFCUtil.h b/code/AssetLib/IFC/IFCUtil.h index 20b9a617c..e1dd064e1 100644 --- a/code/AssetLib/IFC/IFCUtil.h +++ b/code/AssetLib/IFC/IFCUtil.h @@ -56,8 +56,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -#include - struct aiNode; namespace Assimp { From b17c2f29e9472e0d345a22f831562fc23ba703fd Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Tue, 22 Jun 2021 12:44:36 -0400 Subject: [PATCH 076/232] Replace noexcept with proper macro --- code/AssetLib/X3D/X3DExporter.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/X3D/X3DExporter.hpp b/code/AssetLib/X3D/X3DExporter.hpp index fc8f9a921..fefaba9f3 100644 --- a/code/AssetLib/X3D/X3DExporter.hpp +++ b/code/AssetLib/X3D/X3DExporter.hpp @@ -63,7 +63,7 @@ class X3DExporter { // empty } - SAttribute(SAttribute &&rhs) noexcept : + SAttribute(SAttribute &&rhs) AI_NO_EXCEPT : Name(rhs.Name), Value(rhs.Value) { // empty From 36815b014b7c55e4a5a9a6677a7565dc392aa4ca Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 22 Jun 2021 20:05:16 +0200 Subject: [PATCH 077/232] Update FBXExporter.h --- code/AssetLib/FBX/FBXExporter.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/code/AssetLib/FBX/FBXExporter.h b/code/AssetLib/FBX/FBXExporter.h index 563183268..2b751ea26 100644 --- a/code/AssetLib/FBX/FBXExporter.h +++ b/code/AssetLib/FBX/FBXExporter.h @@ -64,10 +64,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. struct aiScene; struct aiNode; struct aiLight; -//struct aiMaterial; -namespace Assimp -{ +namespace Assimp { class IOSystem; class IOStream; class ExportProperties; From 544148a6263d6141acf157fb23d603583bb6b2ea Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Tue, 22 Jun 2021 14:28:46 -0400 Subject: [PATCH 078/232] Fix code insertion duplication --- code/AssetLib/IFC/IFCUtil.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/IFC/IFCUtil.h b/code/AssetLib/IFC/IFCUtil.h index e1dd064e1..b18f35052 100644 --- a/code/AssetLib/IFC/IFCUtil.h +++ b/code/AssetLib/IFC/IFCUtil.h @@ -142,7 +142,7 @@ struct TempOpening TempOpening(const IFC::Schema_2x3::IfcSolidModel *solid, IfcVector3 extrusionDir, std::shared_ptr profileMesh, std::shared_ptr profileMesh2D) : - solid(solid), extrusionDir(extrusionDir), profileMesh(std::move(std::move(profileMesh))), profileMesh2D(std::move(std::move(profileMesh2D))) { + solid(solid), extrusionDir(extrusionDir), profileMesh(std::move(profileMesh)), profileMesh2D(std::move(profileMesh2D)) { } // ------------------------------------------------------------------------------ From 6170c49155b0e9f7678ab1ceb3cb96c720b7536c Mon Sep 17 00:00:00 2001 From: Pankaj Tyagi Date: Wed, 23 Jun 2021 16:49:09 +0530 Subject: [PATCH 079/232] Fixed: 1. FBX import is unable to read the texture UV rotation angle. 2. FBX export is unable to write the texture UV rotation angle. --- code/AssetLib/FBX/FBXConverter.cpp | 3 +++ code/AssetLib/FBX/FBXDocument.h | 5 +++++ code/AssetLib/FBX/FBXExporter.cpp | 7 +++++++ code/AssetLib/FBX/FBXMaterial.cpp | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index a564b3e9b..066b79871 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -1766,6 +1766,7 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const TextureMap // XXX handle all kinds of UV transformations uvTrafo.mScaling = tex->UVScaling(); uvTrafo.mTranslation = tex->UVTranslation(); + uvTrafo.mRotation = tex->UVRotation(); out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, 0); const PropertyTable &props = tex->Props(); @@ -1885,6 +1886,7 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const LayeredTex // XXX handle all kinds of UV transformations uvTrafo.mScaling = tex->UVScaling(); uvTrafo.mTranslation = tex->UVTranslation(); + uvTrafo.mRotation = tex->UVRotation(); out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, texIndex); const PropertyTable &props = tex->Props(); @@ -2324,6 +2326,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial *out_mat, const PropertyTa // XXX handle all kinds of UV transformations uvTrafo.mScaling = tex->UVScaling(); uvTrafo.mTranslation = tex->UVTranslation(); + uvTrafo.mRotation = tex->UVRotation(); out_mat->AddProperty(&uvTrafo, 1, (name + "|uvtrafo").c_str(), aiTextureType_UNKNOWN, 0); int uvIndex = 0; diff --git a/code/AssetLib/FBX/FBXDocument.h b/code/AssetLib/FBX/FBXDocument.h index 69cda1c1a..1ee526368 100644 --- a/code/AssetLib/FBX/FBXDocument.h +++ b/code/AssetLib/FBX/FBXDocument.h @@ -500,6 +500,10 @@ public: return uvScaling; } + const ai_real &UVRotation() const { + return uvRotation; + } + const PropertyTable& Props() const { ai_assert(props.get()); return *props.get(); @@ -517,6 +521,7 @@ public: private: aiVector2D uvTrans; aiVector2D uvScaling; + ai_real uvRotation; std::string type; std::string relativeFileName; diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index 6bdd0b5be..486e08da9 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -1688,6 +1688,10 @@ void FBXExporter::WriteObjects () // link the image data to the texture connections.emplace_back("C", "OO", image_uid, texture_uid); + aiUVTransform trafo; + unsigned int max = sizeof(aiUVTransform); + aiGetMaterialFloatArray(mat, AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE, 0), (float *)&trafo, &max); + // now write the actual texture node FBX::Node tnode("Texture"); // TODO: some way to determine texture name? @@ -1698,6 +1702,9 @@ void FBXExporter::WriteObjects () tnode.AddChild("Version", int32_t(202)); tnode.AddChild("TextureName", texture_name); FBX::Node p("Properties70"); + p.AddP70vectorA("Translation", trafo.mTranslation[0], trafo.mTranslation[1], 0.0); + p.AddP70vectorA("Rotation", 0, 0, trafo.mRotation); + p.AddP70vectorA("Scaling", trafo.mScaling[0], trafo.mScaling[1], 0.0); p.AddP70enum("CurrentTextureBlendMode", 0); // TODO: verify //p.AddP70string("UVSet", ""); // TODO: how should this work? p.AddP70bool("UseMaterial", 1); diff --git a/code/AssetLib/FBX/FBXMaterial.cpp b/code/AssetLib/FBX/FBXMaterial.cpp index 6ada9630b..aaa043c12 100644 --- a/code/AssetLib/FBX/FBXMaterial.cpp +++ b/code/AssetLib/FBX/FBXMaterial.cpp @@ -210,6 +210,11 @@ Texture::Texture(uint64_t id, const Element& element, const Document& doc, const uvTrans.y = trans.y; } + const aiVector3D &rotation = PropertyGet(*props, "Rotation", ok); + if (ok) { + uvRotation = rotation.z; + } + // resolve video links if(doc.Settings().readTextures) { const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID()); From 170063643cfaf86514b2d28d0c2d1ceae7df4791 Mon Sep 17 00:00:00 2001 From: Jerome St-Louis Date: Wed, 23 Jun 2021 18:11:52 -0400 Subject: [PATCH 080/232] include/material.h: Fixed broken C support - The aiGetMaterialFloat() and aiGetMaterialInteger() C preprocessor definitions were broken because: - They had a space before the opening parenthesis - Using material key definitions expanding 1 to argument into 3 breaks the invocation of macros expecting 5 parameters --- include/assimp/material.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/assimp/material.h b/include/assimp/material.h index 2024be07f..9b0c92b0d 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -1499,7 +1499,7 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialFloatArray( ai_real *pOut, unsigned int *pMax); -#ifdef __cplusplus +#if 1 //def __cplusplus // --------------------------------------------------------------------------- /** @brief Retrieve a single float property with a specific key from the material. @@ -1520,7 +1520,7 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialFloatArray( * @return Specifies whether the key has been found. If not, the output * float remains unmodified.*/ // --------------------------------------------------------------------------- -inline aiReturn aiGetMaterialFloat(const aiMaterial *pMat, +inline aiReturn aiGetMaterialFloat(const C_STRUCT aiMaterial *pMat, const char *pKey, unsigned int type, unsigned int index, @@ -1530,8 +1530,8 @@ inline aiReturn aiGetMaterialFloat(const aiMaterial *pMat, #else -// Use our friend, the C preprocessor -#define aiGetMaterialFloat (pMat, type, index, pKey, pOut) \ +// Use our friend, the C preprocessor // The macro does not work with e.g. AI_MATKEY_OPACITY expanding into 3 args +#define aiGetMaterialFloat(pMat, type, index, pKey, pOut) \ aiGetMaterialFloatArray(pMat, type, index, pKey, pOut, NULL) #endif //!__cplusplus @@ -1548,7 +1548,7 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialIntegerArray(const C_STRUCT aiMaterial * int *pOut, unsigned int *pMax); -#ifdef __cplusplus +#if 1 //def __cplusplus // --------------------------------------------------------------------------- /** @brief Retrieve an integer property with a specific key from a material @@ -1566,7 +1566,7 @@ inline aiReturn aiGetMaterialInteger(const C_STRUCT aiMaterial *pMat, #else // use our friend, the C preprocessor -#define aiGetMaterialInteger (pMat, type, index, pKey, pOut) \ +#define aiGetMaterialInteger(pMat, type, index, pKey, pOut) \ aiGetMaterialIntegerArray(pMat, type, index, pKey, pOut, NULL) #endif //!__cplusplus From 00bf7576883d7cf3edfffe51390855d293848a4e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 24 Jun 2021 13:28:49 +0200 Subject: [PATCH 081/232] Update material.h --- include/assimp/material.h | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/include/assimp/material.h b/include/assimp/material.h index 9b0c92b0d..f0207c6de 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -1499,8 +1499,6 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialFloatArray( ai_real *pOut, unsigned int *pMax); -#if 1 //def __cplusplus - // --------------------------------------------------------------------------- /** @brief Retrieve a single float property with a specific key from the material. * @@ -1528,14 +1526,6 @@ inline aiReturn aiGetMaterialFloat(const C_STRUCT aiMaterial *pMat, return aiGetMaterialFloatArray(pMat, pKey, type, index, pOut, (unsigned int *)0x0); } -#else - -// Use our friend, the C preprocessor // The macro does not work with e.g. AI_MATKEY_OPACITY expanding into 3 args -#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 @@ -1548,8 +1538,6 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialIntegerArray(const C_STRUCT aiMaterial * int *pOut, unsigned int *pMax); -#if 1 //def __cplusplus - // --------------------------------------------------------------------------- /** @brief Retrieve an integer property with a specific key from a material * @@ -1563,14 +1551,6 @@ inline aiReturn aiGetMaterialInteger(const C_STRUCT aiMaterial *pMat, 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 * From d18d83881265714f0ef3d859449e0e5a44450b43 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 24 Jun 2021 16:18:11 +0200 Subject: [PATCH 082/232] Fix formatting --- code/AssetLib/glTF2/glTF2Asset.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 25e917712..605cda332 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -388,16 +388,18 @@ struct CustomExtension { } CustomExtension() = default; - - CustomExtension(const CustomExtension &other) - : name(other.name) - , mStringValue(other.mStringValue) - , mDoubleValue(other.mDoubleValue) - , mUint64Value(other.mUint64Value) - , mInt64Value(other.mInt64Value) - , mBoolValue(other.mBoolValue) - , mValues(other.mValues) - { + + ~CustomExtension() = default; + + CustomExtension(const CustomExtension &other) : + name(other.name), + mStringValue(other.mStringValue), + mDoubleValue(other.mDoubleValue), + mUint64Value(other.mUint64Value), + mInt64Value(other.mInt64Value), + mBoolValue(other.mBoolValue), + mValues(other.mValues) { + // empty } }; From b38b65ff49bf9bb061b2dcec79f18266bc680f4d Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 24 Jun 2021 17:03:00 +0200 Subject: [PATCH 083/232] Add const --- code/AssetLib/glTF2/glTF2Asset.inl | 49 ++++++++++++++++-------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 06a8b5c58..4bcfea5f7 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -777,12 +776,13 @@ inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncod } inline void Buffer::EncodedRegion_SetCurrent(const std::string &pID) { - if ((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) return; + if ((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) { + return; + } for (SEncodedRegion *reg : EncodedRegion_List) { if (reg->ID == pID) { EncodedRegion_Current = reg; - return; } } @@ -832,9 +832,10 @@ inline bool Buffer::ReplaceData_joint(const size_t pBufferData_Offset, const siz } inline size_t Buffer::AppendData(uint8_t *data, size_t length) { - size_t offset = this->byteLength; + const size_t offset = this->byteLength; + // Force alignment to 4 bits - size_t paddedLength = (length + 3) & ~3; + const size_t paddedLength = (length + 3) & ~3; Grow(paddedLength); memcpy(mData.get() + offset, data, length); memset(mData.get() + offset + length, 0, paddedLength - length); @@ -866,9 +867,7 @@ inline void Buffer::Grow(size_t amount) { // // struct BufferView // - inline void BufferView::Read(Value &obj, Asset &r) { - if (Value *bufferVal = FindUInt(obj, "buffer")) { buffer = r.buffers.Retrieve(bufferVal->GetUint()); } @@ -888,16 +887,21 @@ inline void BufferView::Read(Value &obj, Asset &r) { } inline uint8_t *BufferView::GetPointer(size_t accOffset) { - if (!buffer) return nullptr; + if (!buffer) { + return nullptr; + } uint8_t *basePtr = buffer->GetPointer(); - if (!basePtr) return nullptr; + if (!basePtr) { + return nullptr; + } size_t offset = accOffset + byteOffset; if (buffer->EncodedRegion_Current != nullptr) { const size_t begin = buffer->EncodedRegion_Current->Offset; const size_t end = begin + buffer->EncodedRegion_Current->DecodedData_Length; - if ((offset >= begin) && (offset < end)) + if ((offset >= begin) && (offset < end)) { return &buffer->EncodedRegion_Current->DecodedData[offset - begin]; + } } return basePtr + offset; @@ -923,18 +927,18 @@ inline void Accessor::Sparse::PatchData(unsigned int elementSize) { while (pIndices != indicesEnd) { size_t offset; switch (indicesType) { - case ComponentType_UNSIGNED_BYTE: - offset = *pIndices; - break; - case ComponentType_UNSIGNED_SHORT: - offset = *reinterpret_cast(pIndices); - break; - case ComponentType_UNSIGNED_INT: - offset = *reinterpret_cast(pIndices); - break; - default: - // have fun with float and negative values from signed types as indices. - throw DeadlyImportError("Unsupported component type in index."); + case ComponentType_UNSIGNED_BYTE: + offset = *pIndices; + break; + case ComponentType_UNSIGNED_SHORT: + offset = *reinterpret_cast(pIndices); + break; + case ComponentType_UNSIGNED_INT: + offset = *reinterpret_cast(pIndices); + break; + default: + // have fun with float and negative values from signed types as indices. + throw DeadlyImportError("Unsupported component type in index."); } offset *= elementSize; @@ -946,7 +950,6 @@ inline void Accessor::Sparse::PatchData(unsigned int elementSize) { } inline void Accessor::Read(Value &obj, Asset &r) { - if (Value *bufferViewVal = FindUInt(obj, "bufferView")) { bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint()); } From 0e41efb05008c94ed256c887e73b9f15a0abb872 Mon Sep 17 00:00:00 2001 From: Jerome St-Louis Date: Thu, 24 Jun 2021 13:25:44 -0400 Subject: [PATCH 084/232] glTF2: Improved support for AI_MATKEY_OPACITY - Exporter: Writing opacity to pbrMetallicRoughness.baseColorFactor[3] even when alphaMode is set - Importer: Setting AI_MATKEY_OPACITY from pbrMetallicRoughness.baseColorFactor[3] --- code/AssetLib/glTF2/glTF2Exporter.cpp | 16 +++++++--------- code/AssetLib/glTF2/glTF2Importer.cpp | 1 + 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 3ed27a035..d45e04a5d 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -778,19 +778,17 @@ void glTF2Exporter::ExportMaterials() mat.Get(AI_MATKEY_TWOSIDED, m->doubleSided); mat.Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); + float opacity; aiString alphaMode; + if (mat.Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { + if (opacity < 1) { + m->alphaMode = "BLEND"; + m->pbrMetallicRoughness.baseColorFactor[3] *= opacity; + } + } if (mat.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { m->alphaMode = alphaMode.C_Str(); - } else { - float opacity; - - if (mat.Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { - if (opacity < 1) { - m->alphaMode = "BLEND"; - m->pbrMetallicRoughness.baseColorFactor[3] *= opacity; - } - } } { diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index d2fb1e9b8..1a04ed8b0 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -267,6 +267,7 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M SetMaterialColorProperty(r, mat.emissiveFactor, aimat, AI_MATKEY_COLOR_EMISSIVE); aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED); + aimat->AddProperty(&mat.pbrMetallicRoughness.baseColorFactor[3], 1, AI_MATKEY_OPACITY); aiString alphaMode(mat.alphaMode); aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE); From 7c822f23bd075d3621d2e2f33188e1659f657b31 Mon Sep 17 00:00:00 2001 From: Promit Roy Date: Sun, 27 Jun 2021 00:53:40 -0400 Subject: [PATCH 085/232] Added support for custom properties ("extras") in glTF2 --- code/AssetLib/glTF2/glTF2Asset.h | 2 ++ code/AssetLib/glTF2/glTF2Asset.inl | 13 ++++++++++--- code/AssetLib/glTF2/glTF2Importer.cpp | 17 +++++++++++++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 605cda332..9824be149 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -411,6 +411,7 @@ struct Object { std::string name; //!< The user-defined name of this object CustomExtension customExtensions; + CustomExtension extras; //! Objects marked as special are not exported (used to emulate the binary body buffer) virtual bool IsSpecial() const { return false; } @@ -428,6 +429,7 @@ struct Object { inline Value *FindExtension(Value &val, const char *extensionId); inline void ReadExtensions(Value &val); + inline void ReadExtras(Value &val); }; // diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 4bcfea5f7..7bfaa81f5 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -372,6 +372,12 @@ inline void Object::ReadExtensions(Value &val) { } } +inline void Object::ReadExtras(Value &val) { + if (Value *curExtras = FindObject(val, "extras")) { + this->extras = glTF2::ReadExtensions("extras", *curExtras); + } +} + #ifdef ASSIMP_ENABLE_DRACO template @@ -612,6 +618,7 @@ Ref LazyDict::Retrieve(unsigned int i) { ReadMember(obj, "name", inst->name); inst->Read(obj, mAsset); inst->ReadExtensions(obj); + inst->ReadExtras(obj); Ref result = Add(inst.release()); mRecursiveReferenceCheck.erase(i); @@ -1661,9 +1668,9 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) { } } - Value *extras = FindObject(pJSON_Object, "extras"); - if (nullptr != extras) { - if (Value *curTargetNames = FindArray(*extras, "targetNames")) { + Value *curExtras = FindObject(pJSON_Object, "extras"); + if (nullptr != curExtras) { + if (Value *curTargetNames = FindArray(*curExtras, "targetNames")) { this->targetNames.resize(curTargetNames->Size()); for (unsigned int i = 0; i < curTargetNames->Size(); ++i) { Value &targetNameValue = (*curTargetNames)[i]; diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index d2fb1e9b8..4827a9060 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -998,6 +998,14 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) { } } +void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) { + if (extension.mValues.isPresent) { + for (size_t i = 0; i < extension.mValues.value.size(); ++i) { + ParseExtensions(metadata, extension.mValues.value[i]); + } + } +} + aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector &meshOffsets, glTF2::Ref &ptr) { Node &node = *ptr; @@ -1016,9 +1024,14 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & } } - if (node.customExtensions) { + if (node.customExtensions || node.extras) { ainode->mMetaData = new aiMetadata; - ParseExtensions(ainode->mMetaData, node.customExtensions); + if (node.customExtensions) { + ParseExtensions(ainode->mMetaData, node.customExtensions); + } + if (node.extras) { + ParseExtras(ainode->mMetaData, node.extras); + } } GetNodeTransform(ainode->mTransformation, node); From 0a48a35382236dd46f7a1decd8c57480edbd92e5 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 29 Jun 2021 21:22:22 +0200 Subject: [PATCH 086/232] closes https://github.com/assimp/assimp/issues/3971: fix wrong dependency --- tools/assimp_view/AnimEvaluator.cpp | 5 +++-- tools/assimp_view/AnimEvaluator.h | 24 +++++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/tools/assimp_view/AnimEvaluator.cpp b/tools/assimp_view/AnimEvaluator.cpp index df5167923..5a2ddc182 100644 --- a/tools/assimp_view/AnimEvaluator.cpp +++ b/tools/assimp_view/AnimEvaluator.cpp @@ -39,9 +39,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -#include "assimp_view.h" +#include "AnimEvaluator.h" -#include +#include +#include using namespace AssimpView; diff --git a/tools/assimp_view/AnimEvaluator.h b/tools/assimp_view/AnimEvaluator.h index 950763081..566247604 100644 --- a/tools/assimp_view/AnimEvaluator.h +++ b/tools/assimp_view/AnimEvaluator.h @@ -1,4 +1,3 @@ -/** Calculates a pose for a given time of an animation */ /* --------------------------------------------------------------------------- Open Asset Import Library (assimp) @@ -40,11 +39,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ +#pragma once #ifndef AV_ANIMEVALUATOR_H_INCLUDED #define AV_ANIMEVALUATOR_H_INCLUDED +/** Calculates a pose for a given time of an animation */ + #include #include +#include + +struct aiAnimation; namespace AssimpView { @@ -63,18 +68,19 @@ public: /// @brief The class destructor. ~AnimEvaluator(); - /** Evaluates the animation tracks for a given time stamp. The calculated pose can be retrieved as a - * array of transformation matrices afterwards by calling GetTransformations(). - * @param pTime The time for which you want to evaluate the animation, in seconds. Will be mapped into the animation cycle, so - * it can be an arbitrary value. Best use with ever-increasing time stamps. - */ + /// @brief Evaluates the animation tracks for a given time stamp. + /// The calculated pose can be retrieved as an array of transformation + /// matrices afterwards by calling GetTransformations(). + /// @param pTime The time for which you want to evaluate the animation, in seconds. + /// Will be mapped into the animation cycle, so it can get an arbitrary + /// value. Best use with ever-increasing time stamps. void Evaluate(double pTime); - /** Returns the transform matrices calculated at the last Evaluate() call. The array matches the mChannels array of - * the aiAnimation. */ + /// @brief Returns the transform matrices calculated at the last Evaluate() call. + /// The array matches the mChannels array of the aiAnimation. const std::vector &GetTransformations() const { return mTransforms; } -protected: +private: const aiAnimation *mAnim; double mLastTime; std::vector> mLastPositions; From 391080d2e61025910b879967876b6340ff443aec Mon Sep 17 00:00:00 2001 From: "DESKTOP-HEFITKI\\Iraj" <1mohtashamiraj@gmail.com> Date: Thu, 1 Jul 2021 15:01:39 +0430 Subject: [PATCH 087/232] Update Gitignore exclude x64 folder generate by build --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0a999d3aa..7849cab65 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,7 @@ CMakeSettings.json # Output bin/ lib/ - +x64/ # QtCreator CMakeLists.txt.user From 3cbd31900c9ce9d893747e25112d212cab938a70 Mon Sep 17 00:00:00 2001 From: arkeon Date: Thu, 1 Jul 2021 17:23:21 +0200 Subject: [PATCH 088/232] Manage /R/N lines ends correctly on binary files, tested with solidworks PLY export --- code/AssetLib/Ply/PlyParser.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/Ply/PlyParser.cpp b/code/AssetLib/Ply/PlyParser.cpp index 59cb6b976..8af0edfdc 100644 --- a/code/AssetLib/Ply/PlyParser.cpp +++ b/code/AssetLib/Ply/PlyParser.cpp @@ -419,8 +419,7 @@ bool PLY::DOM::ParseHeader(IOStreamBuffer &streamBuffer, std::vector if (PLY::Element::ParseElement(streamBuffer, buffer, &out)) { // add the element to the list of elements alElements.push_back(out); - } else if ( TokenMatch(buffer, "end_header\r", 11) || //checks for header end with /r/n ending - TokenMatch(buffer, "end_header", 10)) { //checks for /n ending, if it doesn't end with /r/n + } else if (TokenMatch(buffer, "end_header", 10)) { //checks for /n ending, if it doesn't end with /r/n // we have reached the end of the header break; } else { @@ -501,6 +500,11 @@ bool PLY::DOM::ParseInstanceBinary(IOStreamBuffer &streamBuffer, DOM *p_pc } streamBuffer.getNextBlock(buffer); + + // remove first char if it's /n in case of file with /r/n + if (((char *)&buffer[0])[0] == '\n') + buffer.erase(buffer.begin(), buffer.begin() + 1); + unsigned int bufferSize = static_cast(buffer.size()); const char *pCur = (char *)&buffer[0]; if (!p_pcOut->ParseElementInstanceListsBinary(streamBuffer, buffer, pCur, bufferSize, loader, p_bBE)) { From 1b37b74f9e95844a4603623335378c5e87bb23e7 Mon Sep 17 00:00:00 2001 From: Rahul Sheth Date: Wed, 7 Jul 2021 17:01:19 -0400 Subject: [PATCH 089/232] Hunter fixes for stb_image --- CMakeLists.txt | 5 ++--- code/AssetLib/M3D/M3DWrapper.h | 2 +- code/CMakeLists.txt | 19 ++++++++++++++++--- code/Common/Assimp.cpp | 2 +- code/Pbrt/PbrtExporter.cpp | 2 +- contrib/{stb_image => stb}/stb_image.h | 0 6 files changed, 21 insertions(+), 9 deletions(-) rename contrib/{stb_image => stb}/stb_image.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index fc1366a1a..2dfe592bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,8 +46,8 @@ option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF) IF(ASSIMP_HUNTER_ENABLED) include("cmake/HunterGate.cmake") HunterGate( - URL "https://github.com/cpp-pm/hunter/archive/v0.23.293.tar.gz" - SHA1 "e8e5470652db77149d9b38656db2a6c0b7642693" + URL "https://github.com/cpp-pm/hunter/archive/v0.23.311.tar.gz" + SHA1 "1a82b9b73055879181cb1466b2ab5d48ee8ae410" ) add_definitions(-DASSIMP_USE_HUNTER) @@ -227,7 +227,6 @@ INCLUDE_DIRECTORIES( BEFORE include ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/include - contrib/ ) LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules" ) diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index b5d1785b2..54d7a2eec 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -57,7 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Share stb_image's PNG loader with other importers/exporters instead of bringing our own copy. #define STBI_ONLY_PNG -#include +#include #include "m3d.h" diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index fe4abc06f..fd92d41e3 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -873,6 +873,7 @@ ELSE() ../contrib/pugixml/src/pugiconfig.hpp ../contrib/pugixml/src/pugixml.hpp ) + INCLUDE_DIRECTORIES("../contrib/pugixml/src") SOURCE_GROUP( Contrib\\Pugixml FILES ${Pugixml_SRCS}) ENDIF() @@ -1034,8 +1035,6 @@ IF(ASSIMP_HUNTER_ENABLED) find_package(RapidJSON CONFIG REQUIRED) ELSE() INCLUDE_DIRECTORIES("../contrib/rapidjson/include") - INCLUDE_DIRECTORIES( "../contrib" ) - INCLUDE_DIRECTORIES("../contrib/pugixml/src") ADD_DEFINITIONS( -DRAPIDJSON_HAS_STDSTRING=1) option( ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR "Suppress rapidjson warning on MSVC (NOTE: breaks android build)" ON ) if(ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR) @@ -1043,6 +1042,18 @@ ELSE() endif() ENDIF() +# stb +IF(ASSIMP_HUNTER_ENABLED) + hunter_add_package(stb) + find_package(stb CONFIG REQUIRED) +ELSE() + SET( stb_SRCS + ../contrib/stb/stb_image.h + ) + INCLUDE_DIRECTORIES("../contrib") + SOURCE_GROUP( Contrib\\stb FILES ${stb_SRCS}) +ENDIF() + # VC2010 fixes if(MSVC10) option( VC10_STDINT_FIX "Fix for VC10 Compiler regarding pstdint.h redefinition errors" OFF ) @@ -1101,6 +1112,7 @@ SET( assimp_src ${open3dgc_SRCS} ${ziplib_SRCS} ${Pugixml_SRCS} + ${stb_SRCS} # Necessary to show the headers in the project when using the VC++ generator: ${PUBLIC_HEADERS} @@ -1158,8 +1170,9 @@ IF(ASSIMP_HUNTER_ENABLED) utf8cpp zip::zip pugixml + stb::stb ) - + if (ASSIMP_BUILD_DRACO) target_link_libraries(assimp PUBLIC ${draco_LIBRARIES}) endif() diff --git a/code/Common/Assimp.cpp b/code/Common/Assimp.cpp index a6c539bca..ca0912979 100644 --- a/code/Common/Assimp.cpp +++ b/code/Common/Assimp.cpp @@ -1277,7 +1277,7 @@ ASSIMP_API void aiQuaternionInterpolate( # endif # define STB_IMAGE_IMPLEMENTATION -# include "stb_image/stb_image.h" +# include "stb/stb_image.h" # if _MSC_VER # pragma warning(pop) diff --git a/code/Pbrt/PbrtExporter.cpp b/code/Pbrt/PbrtExporter.cpp index 260d90322..1c7024c28 100644 --- a/code/Pbrt/PbrtExporter.cpp +++ b/code/Pbrt/PbrtExporter.cpp @@ -83,7 +83,7 @@ Other: #include #include -#include "../contrib/stb_image/stb_image.h" +#include "stb/stb_image.h" using namespace Assimp; diff --git a/contrib/stb_image/stb_image.h b/contrib/stb/stb_image.h similarity index 100% rename from contrib/stb_image/stb_image.h rename to contrib/stb/stb_image.h From e49ee6cfb25ac0bd02f51e3a9f587647d1b9a30e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20L=C3=B3pez=20Antequera?= Date: Thu, 8 Jul 2021 09:51:05 +0200 Subject: [PATCH 090/232] PyAssimp fix: don't always search anaconda paths The helper was erroneously adding the Anaconda paths to the search. --- port/PyAssimp/pyassimp/helper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/port/PyAssimp/pyassimp/helper.py b/port/PyAssimp/pyassimp/helper.py index 7a4b2bdcb..7c14f6097 100644 --- a/port/PyAssimp/pyassimp/helper.py +++ b/port/PyAssimp/pyassimp/helper.py @@ -34,7 +34,8 @@ if os.name=='posix': additional_dirs.extend([item for item in os.environ['LD_LIBRARY_PATH'].split(':') if item]) # check if running from anaconda. - if "conda" or "continuum" in sys.version.lower(): + anaconda_keywords = ("conda", "continuum") + if any(k in sys.version.lower() for k in anaconda_keywords): cur_path = get_python_lib() pattern = re.compile('.*\/lib\/') conda_lib = pattern.match(cur_path).group() From 206b2436d4d76c974695f8e05c618c183f728a24 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Tue, 13 Jul 2021 16:47:31 +0100 Subject: [PATCH 091/232] Apply clangformat --- code/AssetLib/Collada/ColladaParser.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 3166136b2..2e77035ca 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -170,10 +170,10 @@ ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) : // ------------------------------------------------------------------------------------------------ // Destructor, private as well ColladaParser::~ColladaParser() { - for (auto & it : mNodeLibrary) { + for (auto &it : mNodeLibrary) { delete it.second; } - for (auto & it : mMeshLibrary) { + for (auto &it : mMeshLibrary) { delete it.second; } } @@ -396,7 +396,7 @@ void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) { std::string animName; if (!XmlParser::getStdStrAttribute(node, "name", animName)) { - if (!XmlParser::getStdStrAttribute( node, "id", animName )) { + if (!XmlParser::getStdStrAttribute(node, "id", animName)) { animName = std::string("animation_") + ai_to_string(mAnimationClipLibrary.size()); } } @@ -420,7 +420,7 @@ void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) { void ColladaParser::PostProcessControllers() { std::string meshId; - for (auto & it : mControllerLibrary) { + for (auto &it : mControllerLibrary) { meshId = it.second.mMeshId; if (meshId.empty()) { continue; @@ -445,7 +445,7 @@ void ColladaParser::PostProcessRootAnimations() { } Animation temp; - for (auto & it : mAnimationClipLibrary) { + for (auto &it : mAnimationClipLibrary) { std::string clipName = it.first; Animation *clip = new Animation(); @@ -552,7 +552,7 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) { pParent->mSubAnims.push_back(anim); } - for (const auto & channel : channels) { + for (const auto &channel : channels) { anim->mChannels.push_back(channel.second); } @@ -644,7 +644,7 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle } else if (currentName == "skin") { std::string id; if (XmlParser::getStdStrAttribute(currentNode, "source", id)) { - controller.mMeshId = id.substr(1, id.size()-1); + controller.mMeshId = id.substr(1, id.size() - 1); } } else if (currentName == "bind_shape_matrix") { std::string v; @@ -698,7 +698,7 @@ void ColladaParser::ReadControllerJoints(XmlNode &node, Collada::Controller &pCo } else if (strcmp(attrSemantic, "INV_BIND_MATRIX") == 0) { pController.mJointOffsetMatrixSource = attrSource; } else { - throw DeadlyImportError("Unknown semantic \"" , attrSemantic , "\" in data element"); + throw DeadlyImportError("Unknown semantic \"", attrSemantic, "\" in data element"); } } } @@ -708,7 +708,7 @@ void ColladaParser::ReadControllerJoints(XmlNode &node, Collada::Controller &pCo // Reads the joint weights for the given controller void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pController) { // Read vertex count from attributes and resize the array accordingly - int vertexCount=0; + int vertexCount = 0; XmlParser::getIntAttribute(node, "count", vertexCount); pController.mWeightCounts.resize(vertexCount); @@ -723,7 +723,7 @@ void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pC // local URLS always start with a '#'. We don't support global URLs if (attrSource[0] != '#') { - throw DeadlyImportError( "Unsupported URL format in \"", attrSource, "\" in source attribute of data element"); + throw DeadlyImportError("Unsupported URL format in \"", attrSource, "\" in source attribute of data element"); } channel.mAccessor = attrSource + 1; @@ -777,7 +777,7 @@ void ColladaParser::ReadImageLibrary(XmlNode &node) { const std::string ¤tName = currentNode.name(); if (currentName == "image") { std::string id; - if (XmlParser::getStdStrAttribute( currentNode, "id", id )) { + if (XmlParser::getStdStrAttribute(currentNode, "id", id)) { mImageLibrary[id] = Image(); // read on from there ReadImage(currentNode, mImageLibrary[id]); @@ -1727,7 +1727,7 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector 0) { + if (pNumPrimitives > 0) { std::string v; XmlParser::getValueAsString(node, v); const char *content = v.c_str(); From bff1d012bd3791453bb513320ec88388f5acc0e4 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Tue, 13 Jul 2021 16:50:09 +0100 Subject: [PATCH 092/232] Read matrix and input (shared) correctly may have leading and trailing whitespace set attribute is unsigned. It is also optional, default 0 --- code/AssetLib/Collada/ColladaParser.cpp | 199 ++++++++++++------------ test/models/Collada/human.zae | Bin 0 -> 1093924 bytes test/unit/utColladaImportExport.cpp | 22 +++ 3 files changed, 120 insertions(+), 101 deletions(-) create mode 100644 test/models/Collada/human.zae diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 2e77035ca..a58cc6003 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -529,7 +529,7 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) { // have it read into a channel ChannelMap::iterator newChannel = channels.insert(std::make_pair(id, AnimationChannel())).first; ReadAnimationSampler(currentNode, newChannel->second); - } + } } else if (currentName == "channel") { std::string source_name, target; XmlParser::getStdStrAttribute(currentNode, "source", source_name); @@ -627,7 +627,7 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle XmlNode currentNode; while (xmlIt.getNext(currentNode)) { - //for (XmlNode ¤tNode : node.children()) { + //for (XmlNode ¤tNode : node.children()) { const std::string ¤tName = currentNode.name(); if (currentName == "morph") { controller.mType = Morph; @@ -907,7 +907,7 @@ void ColladaParser::ReadCameraLibrary(XmlNode &node) { if (!name.empty()) { cam.mName = name; } - ReadCamera(currentNode, cam); + ReadCamera(currentNode, cam); } } } @@ -1361,8 +1361,8 @@ void ColladaParser::ReadMesh(XmlNode &node, Mesh &pMesh) { } else if (currentName == "vertices") { ReadVertexData(currentNode, pMesh); } else if (currentName == "triangles" || currentName == "lines" || currentName == "linestrips" || - currentName == "polygons" || currentName == "polylist" || currentName == "trifans" || - currentName == "tristrips") { + currentName == "polygons" || currentName == "polylist" || currentName == "trifans" || + currentName == "tristrips") { ReadIndexData(currentNode, pMesh); } } @@ -1674,12 +1674,9 @@ void ColladaParser::ReadInputChannel(XmlNode &node, std::vector &p // read set if texture coordinates if (channel.mType == IT_Texcoord || channel.mType == IT_Color) { - int attrSet = -1; - if (XmlParser::hasAttribute(node, "set")) { - XmlParser::getIntAttribute(node, "set", attrSet); - } - - channel.mIndex = attrSet; + unsigned int attrSet = 0; + if (XmlParser::getUIntAttribute(node, "set", attrSet)) + channel.mIndex = attrSet; } // store, if valid type @@ -1704,20 +1701,20 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vectormSize; ++i) { + result[static_cast(i)] = obj[pInput.mResolved->mSubOffset[i]]; } + pMesh.mColors[pInput.mIndex].push_back(result); + } else { + ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping."); + } - // ignore all bitangent streams except 0 - there can be only one bitangent - if (pInput.mIndex == 0) { - pMesh.mBitangents.push_back(aiVector3D(obj[0], obj[1], obj[2])); - } else { - ASSIMP_LOG_ERROR("Collada: just one vertex bitangent stream supported"); - } - break; - case IT_Texcoord: - // up to 4 texture coord sets are fine, ignore the others - if (pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS) { - // pad to current vertex count if necessary - if (pMesh.mTexCoords[pInput.mIndex].size() < pMesh.mPositions.size() - 1) - pMesh.mTexCoords[pInput.mIndex].insert(pMesh.mTexCoords[pInput.mIndex].end(), - pMesh.mPositions.size() - pMesh.mTexCoords[pInput.mIndex].size() - 1, aiVector3D(0, 0, 0)); - - pMesh.mTexCoords[pInput.mIndex].push_back(aiVector3D(obj[0], obj[1], obj[2])); - if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) { - pMesh.mNumUVComponents[pInput.mIndex] = 3; - } - } else { - ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping."); - } - break; - case IT_Color: - // up to 4 color sets are fine, ignore the others - if (pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS) { - // pad to current vertex count if necessary - if (pMesh.mColors[pInput.mIndex].size() < pMesh.mPositions.size() - 1) - pMesh.mColors[pInput.mIndex].insert(pMesh.mColors[pInput.mIndex].end(), - pMesh.mPositions.size() - pMesh.mColors[pInput.mIndex].size() - 1, aiColor4D(0, 0, 0, 1)); - - aiColor4D result(0, 0, 0, 1); - for (size_t i = 0; i < pInput.mResolved->mSize; ++i) { - result[static_cast(i)] = obj[pInput.mResolved->mSubOffset[i]]; - } - pMesh.mColors[pInput.mIndex].push_back(result); - } else { - ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping."); - } - - break; - default: - // IT_Invalid and IT_Vertex - ai_assert(false && "shouldn't ever get here"); + break; + default: + // IT_Invalid and IT_Vertex + ai_assert(false && "shouldn't ever get here"); } } @@ -2170,10 +2167,10 @@ void ColladaParser::ReadNodeTransformation(XmlNode &node, Node *pNode, Transform // read as many parameters and store in the transformation for (unsigned int a = 0; a < sNumParameters[pType]; a++) { + // skip whitespace before the number + SkipSpacesAndLineEnd(&content); // read a number content = fast_atoreal_move(content, tf.f[a]); - // skip whitespace after it - SkipSpacesAndLineEnd(&content); } // place the transformation at the queue of the node diff --git a/test/models/Collada/human.zae b/test/models/Collada/human.zae new file mode 100644 index 0000000000000000000000000000000000000000..691b09f83f6ac2baf2d6bb55026da9b95470c536 GIT binary patch literal 1093924 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

uuGR&r@9o{lX?*e|=%DZ$(yMTN$V&+tmT zq9}jKxQt?H1AQ&@QpiIJe~Z6E_mBbUN3Z$q--Ur?nl%z%Kmp+r^-OY(m+q%YFX>e# zfamFei0isY`GikyaAdrnPkr?gioP5Eyv0j$d4mkrg>`7SIq2PCTM!F5=m9_G3m`X} z+r331kGDPQ9!xTE$FDNNDNgM4&KL;V3)Rx<8un+PS69wJGciITvV^9+#1D((dpT;W zq+;~6=?GHoNwPC_iNhBP@IXegoSjZ{$`XBwf?p&boOit_O)VO4H^{#Q-{D$fN8bJ| zLOtW+MIvOwA+^AGZkm8M-S-<0aFMy(kaetM4(a`~a5)Y=cZV2HH}n*dIh6D8mktfI z!t=}>>P|X6d7DQeklt7PARf!M_X9oX-MhXGR));w9^%2?{kEAe{DN(aPVsV!>WmS_ zUB+YChpa6LuuW^WF2{zU@}Qx&c7+gX2U|!ADr>FTeMyo{dP=|Npmdg1OOPvDEex&d zM))={!X6?;S*0M@pB7%YaH6UgdO}3&0y}v?J}k0n8!sr|q%mhL5vFWES)&cEWRob^ zh0NMn<9+PD>qtnG$% zVX@QyR<-g*7)*8y@bRKxxb=d7UrZ)GEOuqm61?C7o~-pUYK78yi)e)jV5I2+QU34z(hgos(cc(LhC()+dA zsSG-+{pgK~Y{ctY_9DImv`CyC!j`o}`o0@2QizA9z$FHm2xU=9*<`vWS@*rnOA&e< z@xq)&;y;1On?Dz&t_LV8Voz)Hl&P_|7wq!b8sUa&Uf74lOM^2m>UsQmN`!}SAZHr? zI`2*yVj*A~y01yqa_d9G60q5T$$#!8ehDX0H``#jD2jMbIp~Ce@DOaWDyq>L00fzu z8DPP?!rB`#Y?wwiCD+}xD|UrZt?of9qtDDhieLw+EwXrsb-+?;nTh7j7fh&7bTTIN zTbDhW2*aIrP2ma5RL9>ong;FqFBpBjSc;xYJUhkNMHFer%9{4JB+7d4zQlZy-!)p| zM(W7)lG+!NUT3ecf8B0XyOF&5(~Hs4YgxGR?gKE7P74bJWMxU3|v-*^hU9BV+`aMP(>G5Md z?ew(U!wy5c(~ZE2JLa#jxAwf&@4lz++P?X#6d)n2ZuVXKV27{Yv;+Oj@2}9wsyei@ z&9`-rn5>m&`F~gWruLR~v6GzWbENy8_rv-&yUDOCE&ZbHdv*TEu6^ve;_m0qXH_f8 znklX6f%{6GJ>c-A|33TESEp{RW&!B4F>3ui=bv{Nf1cWvuutE!;&0lS&mN_($DSXn z_+rSjg34L@K1==X*lz{L_gS*KzOCQf-~0V}mLg+;zw)Px*I$XWrXyU9GXC68$>Q%k ze?i6jet%CtXDrXjliBq~g{o>TiaX|-sWtw)a+b&fMp!Xmi&I>PnU@^2pjxI3oK0`- zsY3@5O?;wCW9lSdmv0jBIZ|ZGjDg28x4d`g5d08%!KeI#`14j?E#u z{bHae`Ik!C$NGCEC7ZXk^I0hnUqF0R%?Ofuta6!n9^`zu0yRAEXbs9U^LqECPF4!k zZs;D*5gdGxCH7C=A3{qRekWmk8`yIg_t}gGud6-vz=&DTcclZ-T z$rVPo?7N7G3=C8&G6sUQxwVOk(f{{kN}Y23WXXOfrdc`S(LAQZV`jiRto0E-xj7tvMRqBkjnOL`{!Bc`>bS2Cy6s>KSc`U8gR~COXa8Wo9?Ek@%;**UI#SI z>m-Hf`IVl+&v{92n@@v{gcDlzH@oaVmtU4&#}}@gGL|u8Y#+6`l@h3K{Zp z1>|Tt@6bFs$b?r}Gk(+4*wk*y5_cHiz4eWL`|jS>&sDq&*IK6K`sWI3 zf3zgTqV$pq8T;9zu3l0?TzT%kl~(TlM?-s3Gr^7X9>~~HK0O^umVc*3@D4sFSHtI= zpZi^9cY6Af9!~gAdeooy`z(1sbvaL_yvR|myv&ac_}LX#9*#O$7jTU#jzsi)^&G`c zD}&s@daCk?7ZmAn-;n}YGxT(#QL5T|f9~G&9GAQK@v|yHT25*-7&1YJ%ugo9_lz~Y zCuBbR;2Z$YX4ArQ0^qg_wn@qI4v|lFkAJ84#2Za@GArTUM(^^r&2Orqm5J~9lb6H0 zxAwIbHM@xv5Gh8VRo9GPq1qXV)OW+riq;tAb9bElpYBn6w?C4s2J@-a!pfk&aAe*! z8-K3&Ous-Y;~l&^r^

Ao$|V)IS!XrjDofR+cp% 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

*MMruzAc9`aeV4mOVXe!g)p!=rmE|XzS3l&x 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 z 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 |%F(BJFHCq3pF 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

YBvu9UVk?n=SJ=$+2ziatfpq&?O74_ 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

Qf((sXQc*MjpgC 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≫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�{S+3fZ-hMfu{#Z(yVPGkh*hYRfc)(cg0-9cef(Gw5tG+NE2Qh^& ztn7IO9SF_lhQ#^Xu-4lA@w?1C*fm)?-Ih3ADgxl+02u&d5~R(=gJvn-eXBY|0#nL|?fEGq~nPBe0rc`&HSHokHof$WLnlDMjPa#5ehL_B8LV08NZr_N3B&PWXOT zw3X1Xd?+5>uv_$}SqaF?hW8_oK=r>_9Ax@4DX5U(e5~&-N?Dn{;Q`p$0^TUY#f&)$M0k18G&xvcFlzz!Te5_JRgsx&DG9es5B))aLSM zY+}Tn@M@1ArC8+B*AM5Knh6^hm(rW2Vld8%J&W?Fhf(jDY4|&L?=6ToG3YC&@1D?Q zlEmjDM6Xbbmq7FhezYvE)P3y7i;32~SpRIm_64h9(v3v!ro)2fE?Lj&J}BMp8(;0Fxz=oe zF;kA`r|B*+;-hm_KDbUC*vD!_)QKet?9ddMabk*e2e*zyZXafUA+z0LsAYxY)RH5? zpCZ_gngRlS<4m5z2|1syb9YBCs2tl(m*R&~o%*JV3(mOIab?eHO@8FT$-o%I z$sh(*%mCI_3pA`GwW6f7D79EWsVKiZFFv(0Rj(i~J;0ll4WyI_2&;kgmNumMeshes, 2u); + EXPECT_EQ(scene->mNumMaterials, 2u); + EXPECT_EQ(scene->mNumAnimations, 0u); + EXPECT_EQ(scene->mNumTextures, 2u); + EXPECT_EQ(scene->mNumLights, 0u); + EXPECT_EQ(scene->mNumCameras, 0u); + + // Expected common metadata + aiString value; + EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, value)) << "No importer format metadata"; + EXPECT_STREQ("Collada Importer", value.C_Str()); + + EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, value)) << "No format version metadata"; + EXPECT_STREQ("1.4.1", value.C_Str()); +} From 6e863b2435b59857f2d9d41262b6b110698f638b Mon Sep 17 00:00:00 2001 From: Rahul Sheth Date: Wed, 14 Jul 2021 08:27:04 -0400 Subject: [PATCH 093/232] Find stb for Assimp --- cmake/assimp-hunter-config.cmake.in | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/assimp-hunter-config.cmake.in b/cmake/assimp-hunter-config.cmake.in index 91efcbf24..5c5aeedfd 100644 --- a/cmake/assimp-hunter-config.cmake.in +++ b/cmake/assimp-hunter-config.cmake.in @@ -9,6 +9,7 @@ find_package(poly2tri CONFIG REQUIRED) find_package(polyclipping CONFIG REQUIRED) find_package(zip CONFIG REQUIRED) find_package(pugixml CONFIG REQUIRED) +find_package(stb CONFIG REQUIRED) if(@ASSIMP_BUILD_DRACO@) find_package(draco CONFIG REQUIRED) From 632b2db97c57d2147bf6e8393ba8e35d2b2f4bad Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Wed, 14 Jul 2021 13:39:41 +0100 Subject: [PATCH 094/232] Ensure glTFv2 scene name is unique Use the provided scene name if extant Fixes issue #3978 --- code/AssetLib/glTF2/glTF2Exporter.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 02c3233f3..47780606f 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -88,15 +88,13 @@ namespace Assimp { } // end of namespace Assimp glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const aiScene* pScene, - const ExportProperties* pProperties, bool isBinary) + const ExportProperties* pProperties, bool isBinary) : mFilename(filename) , mIOSystem(pIOSystem) + , mScene(pScene) , mProperties(pProperties) + , mAsset(new Asset(pIOSystem)) { - mScene = pScene; - - mAsset.reset( new Asset( pIOSystem ) ); - // Always on as our triangulation process is aware of this type of encoding mAsset->extensionsUsed.FB_ngon_encoding = true; @@ -1338,8 +1336,11 @@ unsigned int glTF2Exporter::ExportNode(const aiNode* n, Ref& parent) void glTF2Exporter::ExportScene() { - const char* sceneName = "defaultScene"; - Ref scene = mAsset->scenes.Create(sceneName); + // Use the name of the scene if specified + const std::string sceneName = (mScene->mName.length > 0) ? mScene->mName.C_Str() : "defaultScene"; + + // Ensure unique + Ref scene = mAsset->scenes.Create(mAsset->FindUniqueID(sceneName, "")); // root node will be the first one exported (idx 0) if (mAsset->nodes.Size() > 0) { From aa883eda195fd144a27789f69059a8742ad93103 Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Thu, 15 Jul 2021 11:54:55 -0700 Subject: [PATCH 095/232] FBX: fix double precision build. --- code/AssetLib/FBX/FBXExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index ccff15c15..c19c593dd 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -1690,7 +1690,7 @@ void FBXExporter::WriteObjects () aiUVTransform trafo; unsigned int max = sizeof(aiUVTransform); - aiGetMaterialFloatArray(mat, AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE, 0), (float *)&trafo, &max); + aiGetMaterialFloatArray(mat, AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE, 0), (ai_real *)&trafo, &max); // now write the actual texture node FBX::Node tnode("Texture"); From 5171aa52d1cba2fee4ab456bc4c18a2b98c8e883 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 16 Jul 2021 11:47:38 +0200 Subject: [PATCH 096/232] Remove dead code --- code/AssetLib/Collada/ColladaParser.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index a58cc6003..37c7274f4 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -626,8 +626,6 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); XmlNode currentNode; while (xmlIt.getNext(currentNode)) { - - //for (XmlNode ¤tNode : node.children()) { const std::string ¤tName = currentNode.name(); if (currentName == "morph") { controller.mType = Morph; @@ -1439,9 +1437,8 @@ void ColladaParser::ReadDataArray(XmlNode &node) { throw DeadlyImportError("Expected more values while reading float_array contents."); } - ai_real value; // read a number - //SkipSpacesAndLineEnd(&content); + ai_real value; content = fast_atoreal_move(content, value); data.mValues.push_back(value); // skip whitespace after it @@ -1489,11 +1486,10 @@ void ColladaParser::ReadAccessor(XmlNode &node, const std::string &pID) { std::string name; if (XmlParser::hasAttribute(currentNode, "name")) { XmlParser::getStdStrAttribute(currentNode, "name", name); - //name = mReader->getAttributeValue(attrName); // analyse for common type components and store it's sub-offset in the corresponding field - /* Cartesian coordinates */ + // Cartesian coordinates if (name == "X") acc.mSubOffset[0] = acc.mParams.size(); else if (name == "Y") From 5285736ba008044969a5701a62fff927e3f37a86 Mon Sep 17 00:00:00 2001 From: Adrian Perez Date: Fri, 16 Jul 2021 14:21:32 -0700 Subject: [PATCH 097/232] Fix issues encountered during integration atempt --- code/AssetLib/glTF/glTFCommon.h | 7 +++---- code/AssetLib/glTF2/glTF2Asset.inl | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/glTF/glTFCommon.h b/code/AssetLib/glTF/glTFCommon.h index d99ffbe86..500298d80 100644 --- a/code/AssetLib/glTF/glTFCommon.h +++ b/code/AssetLib/glTF/glTFCommon.h @@ -194,12 +194,11 @@ inline void CopyValue(const glTFCommon::mat4 &v, aiMatrix4x4 &o) { inline std::string getCurrentAssetDir(const std::string &pFile) { std::string path = pFile; - int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\'))); - if (pos != int(std::string::npos)) { - path = pFile.substr(0, pos + 1); + if (pos == int(std::string::npos)) { + return ""; } - return path; + return pFile.substr(0, pos + 1); } #if _MSC_VER # pragma warning(pop) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index fd509bada..58095c7bd 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -965,9 +965,9 @@ inline void Accessor::Read(Value &obj, Asset &r) { componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); { const Value* countValue = FindUInt(obj, "count"); - if (!countValue || countValue->GetUint() < 1) + if (!countValue) { - throw DeadlyImportError("A strictly positive count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); + throw DeadlyImportError("A count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); } count = countValue->GetUint(); } From 30d342534a1ea5d71f87dd5f1f255f9d6146c86e Mon Sep 17 00:00:00 2001 From: Adrian Perez Date: Fri, 16 Jul 2021 14:21:32 -0700 Subject: [PATCH 098/232] Fix issues encountered during integration atempt --- code/AssetLib/glTF/glTFCommon.h | 6 +++--- code/AssetLib/glTF2/glTF2Asset.inl | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/AssetLib/glTF/glTFCommon.h b/code/AssetLib/glTF/glTFCommon.h index d99ffbe86..0506ad056 100644 --- a/code/AssetLib/glTF/glTFCommon.h +++ b/code/AssetLib/glTF/glTFCommon.h @@ -195,11 +195,11 @@ inline void CopyValue(const glTFCommon::mat4 &v, aiMatrix4x4 &o) { inline std::string getCurrentAssetDir(const std::string &pFile) { std::string path = pFile; int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\'))); - if (pos != int(std::string::npos)) { - path = pFile.substr(0, pos + 1); + if (pos == int(std::string::npos)) { + return ""; } - return path; + return pFile.substr(0, pos + 1); } #if _MSC_VER # pragma warning(pop) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index fd509bada..58095c7bd 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -965,9 +965,9 @@ inline void Accessor::Read(Value &obj, Asset &r) { componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); { const Value* countValue = FindUInt(obj, "count"); - if (!countValue || countValue->GetUint() < 1) + if (!countValue) { - throw DeadlyImportError("A strictly positive count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); + throw DeadlyImportError("A count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); } count = countValue->GetUint(); } From e13ce4b624728bdcf4475fe511fdbbeab7fc1095 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 21 Jul 2021 09:46:23 +0200 Subject: [PATCH 099/232] Fix version, remove deprecated doc files, fix some path errors --- doc/Doxyfile.in | 36 ++++++------------------------------ 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index ffe39f9f7..8b1e802d2 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -32,7 +32,7 @@ PROJECT_NAME = Assimp # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = "v4.1. (December 2018)" +PROJECT_NUMBER = "v5.0.1. (December 2020)" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer @@ -197,7 +197,7 @@ SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. -TAB_SIZE = 8 +TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". @@ -663,11 +663,7 @@ WARN_LOGFILE = # with spaces. INPUT = @doxy_main_page@ \ - @PROJECT_SOURCE_DIR@ \ - @PROJECT_BINARY_DIR@ \ - @PROJECT_SOURCE_DIR@/include/ \ - @PROJECT_SOURCE_DIR@/doc/dox.h \ - @PROJECT_SOURCE_DIR@/code/BaseImporter.h + @PROJECT_SOURCE_DIR@/include/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is @@ -690,28 +686,9 @@ FILE_PATTERNS = *.c \ *.cxx \ *.cpp \ *.c++ \ - *.d \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ *.inl \ *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.idl \ - *.odl \ - *.cs \ - *.php \ - *.php3 \ - *.inc \ - *.m \ - *.mm \ - *.dox \ - *.py + *.dox # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. @@ -725,7 +702,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = irrXML.h +EXCLUDE = contrib/* # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -739,8 +716,7 @@ EXCLUDE_SYMLINKS = NO # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* -EXCLUDE_PATTERNS = */.svn/* \ - */.svn +EXCLUDE_PATTERNS = */.git/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the From aa8628abef7b766cf75908d0d91f1f11b138c6a1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 21 Jul 2021 20:38:58 +0200 Subject: [PATCH 100/232] Disable html and enable xml --- doc/Doxyfile.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 8b1e802d2..e499f09ea 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -636,7 +636,7 @@ WARN_IF_DOC_ERROR = YES # wrong or incomplete parameter documentation, but not about the absence of # documentation. -WARN_NO_PARAMDOC = NO +WARN_NO_PARAMDOC = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text @@ -877,7 +877,7 @@ IGNORE_PREFIX = # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. -GENERATE_HTML = YES +GENERATE_HTML = NO # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be @@ -1460,7 +1460,7 @@ MAN_LINKS = NO # generate an XML file that captures the structure of # the code including all documentation. -GENERATE_XML = NO +GENERATE_XML = YES # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be From d554b4950d991caf110819966f852eb712425d08 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 22 Jul 2021 20:48:25 +0200 Subject: [PATCH 101/232] Add hpp to dxygen filter --- doc/Doxyfile.in | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index e499f09ea..54273b90c 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -688,6 +688,7 @@ FILE_PATTERNS = *.c \ *.c++ \ *.inl \ *.h \ + *.hpp \ *.dox # The RECURSIVE tag can be used to turn specify whether or not subdirectories From b7f88f30636659ced8cddfdce12309b8f78c631b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 26 Jul 2021 10:51:16 +0200 Subject: [PATCH 102/232] closes https://github.com/assimp/assimp/issues/3957: checkj for empty positions. --- code/AssetLib/Collada/ColladaLoader.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index 492e6971f..ee8f97203 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -619,6 +619,10 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc dstMesh->mName = pSrcMesh->mId; } + if (pSrcMesh->mPositions.empty()) { + return dstMesh.release(); + } + // count the vertices addressed by its faces const size_t numVertices = std::accumulate(pSrcMesh->mFaceSize.begin() + pStartFace, pSrcMesh->mFaceSize.begin() + pStartFace + pSubMesh.mNumFaces, size_t(0)); From 16c3f82222211f7708b8607e4946b0fd25141ed7 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 26 Jul 2021 11:24:18 +0200 Subject: [PATCH 103/232] closes https://github.com/assimp/assimp/issues/3975: Use latest version of OpenDDL-Parser --- contrib/openddlparser/code/OpenDDLParser.cpp | 30 ++++++++++++++----- contrib/openddlparser/code/Value.cpp | 9 +++--- .../include/openddlparser/OpenDDLParser.h | 13 ++++++-- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index 6a9f802ec..0c9e0bd98 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -132,6 +132,24 @@ OpenDDLParser::~OpenDDLParser() { clear(); } +void OpenDDLParser::logToStream(FILE *f, LogSeverity severity, const std::string &message) { + if (f) { + const char *tag = "none"; + switch (severity) { + case ddl_debug_msg: tag = "debug"; break; + case ddl_info_msg: tag = "info"; break; + case ddl_warn_msg: tag = "warn"; break; + case ddl_error_msg: tag = "error"; break; + } + fprintf(f, "OpenDDLParser: (%5s) %s\n", tag, message.c_str()); + } +} + +OpenDDLParser::logCallback OpenDDLParser::StdLogCallback (FILE *destination) { + using namespace std::placeholders; + return std::bind(logToStream, destination ? destination : stderr, _1, _2); +} + void OpenDDLParser::setLogCallback(logCallback callback) { // install user-specific log callback; null = no log callback m_logCallback = callback; @@ -171,12 +189,8 @@ size_t OpenDDLParser::getBufferSize() const { void OpenDDLParser::clear() { m_buffer.resize(0); - if (nullptr != m_context) { - delete m_context; - m_context = nullptr; - } - - // DDLNode::releaseNodes(); + delete m_context; + m_context = nullptr; } bool OpenDDLParser::validate() { @@ -506,7 +520,9 @@ char *OpenDDLParser::parseName(char *in, char *end, Name **name) { in = parseIdentifier(in, end, &id); if (id) { currentName = new Name(ntype, id); - *name = currentName; + if (currentName) { + *name = currentName; + } } return in; diff --git a/contrib/openddlparser/code/Value.cpp b/contrib/openddlparser/code/Value.cpp index 708a6878f..5a8aa39be 100644 --- a/contrib/openddlparser/code/Value.cpp +++ b/contrib/openddlparser/code/Value.cpp @@ -113,13 +113,14 @@ Value::~Value() { if (m_data != nullptr) { if (m_type == ValueType::ddl_ref) { Reference *tmp = (Reference *)m_data; - if (tmp != nullptr) + if (tmp != nullptr) { delete tmp; - } else + } + } else { delete[] m_data; + } } - if (m_next != nullptr) - delete m_next; + delete m_next; } void Value::setBool(bool value) { diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h index 5794add90..3fbb4b6af 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h @@ -29,6 +29,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include BEGIN_ODDLPARSER_NS @@ -97,8 +98,8 @@ DLL_ODDLPARSER_EXPORT const char *getTypeToken(Value::ValueType type); //------------------------------------------------------------------------------------------------- class DLL_ODDLPARSER_EXPORT OpenDDLParser { public: - /// @brief The log callback function pointer. - typedef void (*logCallback)(LogSeverity severity, const std::string &msg); + /// @brief The log callback function. + typedef std::function logCallback; public: /// @brief The default class constructor. @@ -120,6 +121,11 @@ public: /// @return The current log callback. logCallback getLogCallback() const; + /// @brief A default log callback that writes to a FILE. + /// @param destination [in] Output stream. NULL will use stderr. + /// @return A callback that you can pass to setLogCallback. + static logCallback StdLogCallback(FILE *destination = nullptr); + /// @brief Assigns a new buffer to parse. /// @param buffer [in] The buffer /// @param len [in] Size of the buffer @@ -192,6 +198,9 @@ private: typedef std::vector DDLNodeStack; DDLNodeStack m_stack; Context *m_context; + + /// @brief Callback for StdLogCallback(). Not meant to be called directly. + static void logToStream (FILE *, LogSeverity, const std::string &); }; END_ODDLPARSER_NS From df2e7208fbca1b609f20e994d5829a419d17a924 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 26 Jul 2021 11:56:26 +0200 Subject: [PATCH 104/232] Fix fuzzer issue in m3d-importer - closes https://github.com/assimp/assimp/issues/3974 - Check for nullptr before dereferencing name in m3d-data-instance. --- code/AssetLib/M3D/M3DWrapper.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index 54d7a2eec..d5fc9eaa5 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -83,7 +83,11 @@ public: // Name inline std::string Name() const { - if (m3d_) return std::string(m3d_->name); + if (nullptr != m3d_) { + if (nullptr!0m3d_->name) { + return std::string(m3d_->name); + } + } return std::string(); } From 291c0a4faa37581e056b43d882e92556cbdc98f1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 26 Jul 2021 13:13:21 +0200 Subject: [PATCH 105/232] Fix build failure - Fix the failure - Put inlined stuff out of declaration - Add some docu --- code/AssetLib/M3D/M3DWrapper.h | 68 +++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index d5fc9eaa5..96db9c8dd 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef AI_M3DWRAPPER_H_INC #define AI_M3DWRAPPER_H_INC + #if !(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER #include @@ -62,45 +63,68 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "m3d.h" namespace Assimp { + class IOSystem; +/// brief The M3D-Wrapper, provudes c++ access to the data. class M3DWrapper { - m3d_t *m3d_ = nullptr; - unsigned char *saved_output_ = nullptr; - public: - // Construct an empty M3D model + /// Construct an empty M3D model explicit M3DWrapper(); - // Construct an M3D model from provided buffer - // NOTE: The m3d.h SDK function does not mark the data as const. Have assumed it does not write. - // BUG: SECURITY: The m3d.h SDK cannot be informed of the buffer size. BUFFER OVERFLOW IS CERTAIN + /// Construct an M3D model from provided buffer + /// @note The m3d.h SDK function does not mark the data as const. Have assumed it does not write. + /// BUG: SECURITY: The m3d.h SDK cannot be informed of the buffer size. BUFFER OVERFLOW IS CERTAIN explicit M3DWrapper(IOSystem *pIOHandler, const std::vector &buffer); - ~M3DWrapper(); + /// Theclasss destructor. + ~M3DWrapper(); - void reset(); + /// Will reset the wrapper, all data will become nullptr. + void reset(); - // Name - inline std::string Name() const { - if (nullptr != m3d_) { - if (nullptr!0m3d_->name) { - return std::string(m3d_->name); - } - } - return std::string(); - } + // The Name access, empty string returned when no m3d instance. + std::string Name() const; - // Execute a save + /// Executes a save. unsigned char *Save(int quality, int flags, unsigned int &size); + + /// Clearer void ClearSave(); - inline explicit operator bool() const { return m3d_ != nullptr; } + /// True for m3d instance exists. + explicit operator bool() const; // Allow direct access to M3D API - inline m3d_t *operator->() const { return m3d_; } - inline m3d_t *M3D() const { return m3d_; } + m3d_t *operator->(); + m3d_t *M3D() const; + +private: + m3d_t *m3d_ = nullptr; + unsigned char *saved_output_ = nullptr; }; + +inline std::string M3DWrapper::Name() const { + if (nullptr != m3d_) { + if (nullptr != m3d_->name) { + return std::string(m3d_->name); + } + } + return std::string(); +} + +inline explicit operator M3DWrapper::bool() const { + return m3d_ != nullptr; +} + +inline m3d_t *M3DWrapper::operator->() const { + return m3d_; +} + +inline m3d_t *M3DWrapper::M3D() const { + return m3d_; +} + } // namespace Assimp #endif From e8e720d584813ad41703e9e96f1798f7ad11b360 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 26 Jul 2021 13:41:54 +0200 Subject: [PATCH 106/232] Update M3DWrapper.h --- code/AssetLib/M3D/M3DWrapper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index 96db9c8dd..83bbdfb85 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -113,7 +113,7 @@ inline std::string M3DWrapper::Name() const { return std::string(); } -inline explicit operator M3DWrapper::bool() const { +inline M3DWrapper::operator bool() const { return m3d_ != nullptr; } From aeae2cf242046bfe16a0e4c15e52e7651da45f62 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 26 Jul 2021 14:44:26 +0200 Subject: [PATCH 107/232] Update M3DWrapper.h --- code/AssetLib/M3D/M3DWrapper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index 83bbdfb85..ba838d71d 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -96,7 +96,7 @@ public: explicit operator bool() const; // Allow direct access to M3D API - m3d_t *operator->(); + m3d_t *operator->() const; m3d_t *M3D() const; private: From 92af44f0925c190c1226dc9d4bff962001ff3d55 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 26 Jul 2021 15:47:19 +0200 Subject: [PATCH 108/232] Fix euler angles --- include/assimp/quaternion.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/assimp/quaternion.h b/include/assimp/quaternion.h index 7c096fe00..6941fbbc3 100644 --- a/include/assimp/quaternion.h +++ b/include/assimp/quaternion.h @@ -73,7 +73,7 @@ public: explicit aiQuaterniont( const aiMatrix3x3t& pRotMatrix); /** Construct from euler angles */ - aiQuaterniont( TReal rotx, TReal roty, TReal rotz); + aiQuaterniont( TReal roty, TReal rotz, TReal rotx); /** Construct from an axis-angle pair */ aiQuaterniont( aiVector3t axis, TReal angle); From 8ee2c721d91522db582301c2f0ce2d8a403f910c Mon Sep 17 00:00:00 2001 From: xiaohunqupo Date: Wed, 28 Jul 2021 16:32:27 +0800 Subject: [PATCH 109/232] StepExporter support polygon mesh StepExporter support polygon mesh --- code/AssetLib/Step/StepExporter.cpp | 120 ++++++++++++++++++---------- 1 file changed, 76 insertions(+), 44 deletions(-) diff --git a/code/AssetLib/Step/StepExporter.cpp b/code/AssetLib/Step/StepExporter.cpp index dfe5bab67..1228c72ea 100644 --- a/code/AssetLib/Step/StepExporter.cpp +++ b/code/AssetLib/Step/StepExporter.cpp @@ -175,12 +175,11 @@ void StepExporter::WriteFile() fColor.b = 0.8f; int ind = 100; // the start index to be used - int faceEntryLen = 30; // number of entries for a triangle/face + std::vector faceEntryLen; // numbers of entries for a triangle/face // prepare unique (count triangles and vertices) VectorIndexUMap uniqueVerts; // use a map to reduce find complexity to log(n) VectorIndexUMap::iterator it; - int countFace = 0; for (unsigned int i=0; imNumMeshes; ++i) { @@ -189,7 +188,7 @@ void StepExporter::WriteFile() { aiFace* face = &(mesh->mFaces[j]); - if (face->mNumIndices == 3) countFace++; + if (face->mNumIndices >= 3) faceEntryLen.push_back(15 + 5 * face->mNumIndices); } for (unsigned int j=0; jmNumVertices; ++j) { @@ -218,10 +217,13 @@ void StepExporter::WriteFile() // write the top of data mOutput << "DATA" << endstr; mOutput << "#1=MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION(' ',("; - for (int i=0; imFaces[j]); - if (face->mNumIndices != 3) continue; + const int numIndices = face->mNumIndices; + if (numIndices < 3) continue; - aiVector3D* v1 = &(mesh->mVertices[face->mIndices[0]]); - aiVector3D* v2 = &(mesh->mVertices[face->mIndices[1]]); - aiVector3D* v3 = &(mesh->mVertices[face->mIndices[2]]); - aiVector3D dv12 = *v2 - *v1; - aiVector3D dv23 = *v3 - *v2; - aiVector3D dv31 = *v1 - *v3; - aiVector3D dv13 = *v3 - *v1; - dv12.Normalize(); - dv23.Normalize(); - dv31.Normalize(); - dv13.Normalize(); + std::vector pidArray(numIndices, -1); // vertex id + std::vector dvArray(numIndices); // edge dir + for (int k = 0; k < numIndices; ++k) + { + aiVector3D *v1 = &(mesh->mVertices[face->mIndices[k]]); + pidArray[k] = uniqueVerts.find(v1)->second; - aiVector3D dvY = dv12; - aiVector3D dvX = dvY ^ dv13; + aiVector3D *v2 = nullptr; + if (k + 1 == numIndices) + v2 = &(mesh->mVertices[face->mIndices[0]]); + else + v2 = &(mesh->mVertices[face->mIndices[k + 1]]); + dvArray[k] = *v2 - *v1; + dvArray[k].Normalize(); + } + + aiVector3D dvY = dvArray[1]; + aiVector3D dvX = dvY ^ dvArray[0]; dvX.Normalize(); - int pid1 = uniqueVerts.find(v1)->second; - int pid2 = uniqueVerts.find(v2)->second; - int pid3 = uniqueVerts.find(v3)->second; - // mean vertex color for the face if available if (mesh->HasVertexColors(0)) { @@ -339,35 +344,62 @@ void StepExporter::WriteFile() /* 2 directions of the plane */ mOutput << "#" << sid+9 << "=PLANE('',#" << sid+10 << ")" << endstr; - mOutput << "#" << sid+10 << "=AXIS2_PLACEMENT_3D('',#" << pid1 << ", #" << sid+11 << ",#" << sid+12 << ")" << endstr; + mOutput << "#" << sid+10 << "=AXIS2_PLACEMENT_3D('',#" << pidArray[0] << ",#" << sid+11 << ",#" << sid+12 << ")" << endstr; mOutput << "#" << sid + 11 << "=DIRECTION('',(" << dvX.x << "," << dvX.y << "," << dvX.z << "))" << endstr; mOutput << "#" << sid + 12 << "=DIRECTION('',(" << dvY.x << "," << dvY.y << "," << dvY.z << "))" << endstr; mOutput << "#" << sid+13 << "=FACE_BOUND('',#" << sid+14 << ",.T.)" << endstr; - mOutput << "#" << sid+14 << "=EDGE_LOOP('',(#" << sid+15 << ",#" << sid+16 << ",#" << sid+17 << "))" << endstr; + mOutput << "#" << sid+14 << "=EDGE_LOOP('',("; + int edgeLoopStart = sid + 15; + for (int k = 0; k < numIndices; ++k) + { + if (k == 0) + mOutput << "#"; + else + mOutput << ",#"; + mOutput << edgeLoopStart + k; + } + mOutput << "))" << endstr; /* edge loop */ - mOutput << "#" << sid+15 << "=ORIENTED_EDGE('',*,*,#" << sid+18 << ",.T.)" << endstr; - mOutput << "#" << sid+16 << "=ORIENTED_EDGE('',*,*,#" << sid+19 << ",.T.)" << endstr; - mOutput << "#" << sid+17 << "=ORIENTED_EDGE('',*,*,#" << sid+20 << ",.T.)" << endstr; + int orientedEdgesStart = edgeLoopStart + numIndices; + for (int k=0; k < numIndices; k++) + { + mOutput << "#" << edgeLoopStart+k << "=ORIENTED_EDGE('',*,*,#" << orientedEdgesStart + k << ",.T.)" << endstr; + } /* oriented edges */ - mOutput << "#" << sid+18 << "=EDGE_CURVE('',#" << pid1+1 << ",#" << pid2+1 << ",#" << sid+21 << ",.F.)" << endstr; - mOutput << "#" << sid+19 << "=EDGE_CURVE('',#" << pid2+1 << ",#" << pid3+1 << ",#" << sid+22 << ",.T.)" << endstr; - mOutput << "#" << sid+20 << "=EDGE_CURVE('',#" << pid3+1 << ",#" << pid1+1 << ",#" << sid+23 << ",.T.)" << endstr; + int lineStart = orientedEdgesStart + numIndices; + for (int k=0; k < numIndices; ++k) + { + if (k == 0) + mOutput << "#" << orientedEdgesStart+k << "=EDGE_CURVE('',#" << pidArray[k]+1 << ",#" << pidArray[k+1]+1 << ",#" << lineStart+k << ",.F.)" << endstr; + else if (k+1 == numIndices) + mOutput << "#" << orientedEdgesStart+k << "=EDGE_CURVE('',#" << pidArray[k]+1 << ",#" << pidArray[0]+1 << ",#" << lineStart+k << ",.T.)" << endstr; + else + mOutput << "#" << orientedEdgesStart+k << "=EDGE_CURVE('',#" << pidArray[k]+1 << ",#" << pidArray[k+1]+1 << ",#" << lineStart+k << ",.T.)" << endstr; + } - /* 3 lines and 3 vectors for the lines for the 3 edge curves */ - mOutput << "#" << sid+21 << "=LINE('',#" << pid1 << ",#" << sid+24 << ")" << endstr; - mOutput << "#" << sid+22 << "=LINE('',#" << pid2 << ",#" << sid+25 << ")" << endstr; - mOutput << "#" << sid+23 << "=LINE('',#" << pid3 << ",#" << sid+26 << ")" << endstr; - mOutput << "#" << sid+24 << "=VECTOR('',#" << sid+27 << ",1.0)" << endstr; - mOutput << "#" << sid+25 << "=VECTOR('',#" << sid+28 << ",1.0)" << endstr; - mOutput << "#" << sid+26 << "=VECTOR('',#" << sid+29 << ",1.0)" << endstr; - mOutput << "#" << sid+27 << "=DIRECTION('',(" << dv12.x << "," << dv12.y << "," << dv12.z << "))" << endstr; - mOutput << "#" << sid+28 << "=DIRECTION('',(" << dv23.x << "," << dv23.y << "," << dv23.z << "))" << endstr; - mOutput << "#" << sid+29 << "=DIRECTION('',(" << dv31.x << "," << dv31.y << "," << dv31.z << "))" << endstr; - ind += faceEntryLen; // increase counter + /* n lines and n vectors for the lines for the n edge curves */ + int vectorStart = lineStart + numIndices; + for (int k=0; k < numIndices; ++k) + { + mOutput << "#" << lineStart+k << "=LINE('',#" << pidArray[k] << ",#" << vectorStart+k << ")" << endstr; + } + + int directionStart = vectorStart + numIndices; + for (int k=0; k < numIndices; ++k) + { + mOutput << "#" << vectorStart+k << "=VECTOR('',#" << directionStart+k << ",1.0)" << endstr; + } + + for (int k=0; k < numIndices; ++k) + { + const aiVector3D &dv = dvArray[k]; + mOutput << "#" << directionStart + k << "=DIRECTION('',(" << dv.x << "," << dv.y << "," << dv.z << "))" << endstr; + } + ind += 15 + 5*numIndices; // increase counter } } From f87550fdbcb567a08a06f28d10310da35ee5f2d4 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 28 Jul 2021 10:39:39 +0200 Subject: [PATCH 110/232] Fix Issue3760 - Convert left-handed coordinate system to right-handed coordinate system - Rescale model by 0.01 - closes https://github.com/assimp/assimp/issues/3760 --- code/AssetLib/C4D/C4DImporter.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/C4D/C4DImporter.cpp b/code/AssetLib/C4D/C4DImporter.cpp index 434d1429e..5408daa68 100644 --- a/code/AssetLib/C4D/C4DImporter.cpp +++ b/code/AssetLib/C4D/C4DImporter.cpp @@ -146,8 +146,14 @@ void C4DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS ThrowException("failed to read document " + pFile); } + // Generate the root-node pScene->mRootNode = new aiNode(""); - + + // convert left-handed to right-handed + pScene->mRootNode->mTransformation.a1 = 0.01f; + pScene->mRootNode->mTransformation.b2 = 0.01f; + pScene->mRootNode->mTransformation.c3 = -0.01f; + // first convert all materials ReadMaterials(doc->GetFirstMaterial()); From 738c31c3eabb2b35af5d6eeb69b368f1128971e2 Mon Sep 17 00:00:00 2001 From: Krishty Date: Wed, 28 Jul 2021 16:44:46 +0200 Subject: [PATCH 111/232] removed useless code Found while reviewing #3880 --- code/AssetLib/Collada/ColladaParser.cpp | 4 ---- code/AssetLib/X/XFileImporter.cpp | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 37c7274f4..7aa37a112 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -231,11 +231,7 @@ void ColladaParser::UriDecodePath(aiString &ss) { // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes... // I need to filter it without destroying linux paths starting with "/somewhere" -#if defined(_MSC_VER) if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') { -#else - if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') { -#endif --ss.length; ::memmove(ss.data, ss.data + 1, ss.length); ss.data[ss.length] = 0; diff --git a/code/AssetLib/X/XFileImporter.cpp b/code/AssetLib/X/XFileImporter.cpp index df1aba331..4c8c54551 100644 --- a/code/AssetLib/X/XFileImporter.cpp +++ b/code/AssetLib/X/XFileImporter.cpp @@ -667,9 +667,7 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, std::vector Date: Thu, 29 Jul 2021 13:28:51 +0200 Subject: [PATCH 112/232] removed trailing spaces and tabs from source and text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit ignores the “contrib” folder in order to prevent merge conflicts in dependencies, should these be updated via git. --- code/AssetLib/3DS/3DSExporter.cpp | 2 +- code/AssetLib/3MF/D3MFImporter.cpp | 2 +- code/AssetLib/AMF/AMFImporter.cpp | 2 +- code/AssetLib/AMF/AMFImporter_Geometry.cpp | 6 +- code/AssetLib/AMF/AMFImporter_Postprocess.cpp | 6 +- code/AssetLib/Assjson/mesh_splitter.cpp | 8 +- code/AssetLib/Assjson/mesh_splitter.h | 6 +- code/AssetLib/Assxml/AssxmlExporter.cpp | 2 +- code/AssetLib/Blender/BlenderModifier.h | 18 +- code/AssetLib/C4D/C4DImporter.cpp | 6 +- code/AssetLib/COB/COBLoader.cpp | 2 +- code/AssetLib/COB/COBLoader.h | 2 +- code/AssetLib/Collada/ColladaLoader.cpp | 2 +- code/AssetLib/DXF/DXFLoader.cpp | 2 +- code/AssetLib/DXF/DXFLoader.h | 2 +- code/AssetLib/FBX/FBXConverter.cpp | 4 +- code/AssetLib/FBX/FBXConverter.h | 4 +- code/AssetLib/FBX/FBXDocument.cpp | 2 +- code/AssetLib/FBX/FBXExportNode.h | 2 +- code/AssetLib/FBX/FBXExporter.cpp | 12 +- code/AssetLib/FBX/FBXMaterial.cpp | 8 +- code/AssetLib/FBX/FBXMeshGeometry.cpp | 2 +- code/AssetLib/FBX/FBXMeshGeometry.h | 8 +- code/AssetLib/FBX/FBXProperties.h | 6 +- code/AssetLib/FBX/FBXUtil.cpp | 2 +- code/AssetLib/HMP/HMPLoader.cpp | 4 +- code/AssetLib/IFC/IFCCurve.cpp | 2 +- code/AssetLib/IFC/IFCOpenings.cpp | 8 +- code/AssetLib/IFC/IFCReaderGen1_2x3.cpp | 232 +++++------ code/AssetLib/IFC/IFCReaderGen2_2x3.cpp | 162 ++++---- code/AssetLib/IFC/IFCReaderGen_4.cpp | 392 +++++++++--------- code/AssetLib/IFC/IFCReaderGen_4.h | 26 +- code/AssetLib/Irr/IRRLoader.h | 2 +- code/AssetLib/LWO/LWOAnimation.h | 2 +- code/AssetLib/LWS/LWSLoader.cpp | 4 +- code/AssetLib/M3D/M3DWrapper.h | 12 +- code/AssetLib/M3D/m3d.h | 6 +- code/AssetLib/MD5/MD5Loader.cpp | 2 +- code/AssetLib/MDC/MDCFileData.h | 4 +- code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp | 4 +- code/AssetLib/MMD/MMDPmxParser.cpp | 2 +- code/AssetLib/OFF/OFFLoader.cpp | 10 +- code/AssetLib/Obj/ObjExporter.h | 18 +- code/AssetLib/Obj/ObjFileMtlImporter.cpp | 2 +- code/AssetLib/SMD/SMDLoader.cpp | 2 +- code/AssetLib/STL/STLExporter.cpp | 8 +- code/AssetLib/STL/STLLoader.cpp | 4 +- code/AssetLib/X/XFileExporter.cpp | 2 +- code/AssetLib/X/XFileExporter.h | 6 +- code/AssetLib/X3D/X3DImporter.cpp | 2 +- code/AssetLib/XGL/XGLLoader.cpp | 2 +- code/AssetLib/glTF2/glTF2Asset.h | 4 +- code/AssetLib/glTF2/glTF2Asset.inl | 32 +- code/AssetLib/glTF2/glTF2AssetWriter.inl | 16 +- code/Common/DefaultIOStream.cpp | 6 +- code/Common/DefaultIOSystem.cpp | 6 +- code/Common/Exporter.cpp | 4 +- code/Common/FileSystemFilter.h | 4 +- code/Common/Importer.cpp | 80 ++-- code/Common/Importer.h | 6 +- code/Common/ImporterRegistry.cpp | 4 +- code/Common/Win32DebugLogStream.h | 8 +- code/PostProcessing/ArmaturePopulate.h | 2 +- .../PostProcessing/DropFaceNormalsProcess.cpp | 2 +- code/PostProcessing/EmbedTexturesProcess.cpp | 2 +- code/PostProcessing/FindDegenerates.cpp | 2 +- code/PostProcessing/FindInstancesProcess.cpp | 2 +- code/PostProcessing/MakeVerboseFormat.h | 2 +- .../RemoveRedundantMaterials.cpp | 2 +- code/PostProcessing/ScaleProcess.cpp | 38 +- code/PostProcessing/ScaleProcess.h | 4 +- .../SplitByBoneCountProcess.cpp | 12 +- code/PostProcessing/SplitLargeMeshes.cpp | 4 +- code/PostProcessing/TextureTransform.cpp | 2 +- code/PostProcessing/TriangulateProcess.cpp | 12 +- doc/dox.h | 18 +- doc/dox_cmd.h | 38 +- fuzz/assimp_fuzzer.cc | 2 +- include/assimp/BaseImporter.h | 2 +- include/assimp/Compiler/poppack1.h | 4 +- include/assimp/Compiler/pushpack1.h | 4 +- include/assimp/Exceptional.h | 2 +- include/assimp/Exporter.hpp | 2 +- include/assimp/IOStreamBuffer.h | 6 +- include/assimp/IOSystem.hpp | 8 +- include/assimp/Logger.hpp | 2 +- include/assimp/MemoryIOWrapper.h | 6 +- include/assimp/SmallVector.h | 8 +- include/assimp/SmoothingGroups.inl | 22 +- include/assimp/XmlParser.h | 10 +- include/assimp/ai_assert.h | 2 +- include/assimp/anim.h | 6 +- include/assimp/cimport.h | 12 +- include/assimp/defs.h | 2 +- include/assimp/light.h | 2 +- include/assimp/material.h | 8 +- include/assimp/matrix4x4.h | 2 +- include/assimp/matrix4x4.inl | 2 +- include/assimp/mesh.h | 10 +- include/assimp/postprocess.h | 14 +- include/assimp/scene.h | 26 +- include/assimp/vector2.inl | 14 +- .../windows-innosetup/readme_installer.txt | 2 +- .../readme_installer_vieweronly.txt | 2 +- packaging/windows-mkzip/bin_readme.txt | 2 +- port/AndroidJNI/CMakeLists.txt | 4 +- port/AssimpDelphi/Readme.txt | 2 +- port/jassimp/jassimp-native/src/jassimp.cpp | 164 ++++---- .../ModelLoaderHelperClasses.h | 26 +- samples/SimpleAssimpViewX/MyDocument.h | 18 +- samples/SimpleOpenGL/Sample_SimpleOpenGL.c | 4 +- .../SimpleTexturedDirectx11/CMakeLists.txt | 6 +- .../SimpleTexturedDirectx11/ModelLoader.cpp | 2 +- .../SimpleTexturedDirectx11/TextureLoader.cpp | 52 +-- .../SimpleTexturedDirectx11/main.cpp | 10 +- .../src/model_loading.cpp | 4 +- test/models-nonbsd/3D/mar_rifle.source.txt | 6 +- test/models-nonbsd/3DS/cart_wheel.source.txt | 6 +- test/models-nonbsd/3DS/mar_rifle.source.txt | 6 +- test/models-nonbsd/3DS/mp5_sil.source.txt | 6 +- test/models-nonbsd/ASE/Rifle.source.txt | 6 +- test/models-nonbsd/ASE/Rifle2.source.txt | 6 +- .../BLEND/fleurOptonl.source.txt | 16 +- test/models-nonbsd/DXF/rifle.source.txt | 6 +- .../FBX/2013_ASCII/cart_wheel.source.txt | 6 +- .../kwxport_test_vcolors.fbx.source.txt | 6 +- .../FBX/2013_ASCII/mar_rifle.source.txt | 6 +- .../FBX/2013_ASCII/mp5_sil.source.txt | 6 +- .../FBX/2013_BINARY/cart_wheel.source.txt | 6 +- .../kwxport_test_vcolors.fbx.source.txt | 6 +- .../FBX/2013_BINARY/mar_rifle.source.txt | 6 +- .../FBX/2013_BINARY/mp5_sil.source.txt | 6 +- .../LWO2/LWSReferences/QuickDraw.source.txt | 10 +- test/models-nonbsd/LWO/LWO2/rifle.source.txt | 6 +- test/models-nonbsd/MD2/source.txt | 6 +- test/models-nonbsd/MD5/BoarMan.source.txt | 4 +- .../MDL/IDPO (Quake1)/gijoe-readme.txt | 14 +- test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt | 8 +- .../MDL/IDPO (Quake1)/tekmechbot.txt | 6 +- test/models-nonbsd/NFF/NFFSense8/credits.txt | 2 +- test/models-nonbsd/OBJ/rifle.source.txt | 6 +- test/models/3DS/UVTransformTest/note.txt | 4 +- test/models/ASE/MotionCaptureROM.source.txt | 2 +- .../kwxport_test_vcolors.dae.source.txt | 6 +- .../IRR/warn_dwarf_scaling_is_intended.txt | 2 +- test/models/MD2/faerie-source.txt | 2 +- test/models/MD2/sidney-source.txt | 2 +- test/models/Q3D/E-AT-AT.source.txt | 2 +- test/models/Q3D/earth.source.txt | 2 +- test/models/WRL/credits.txt | 2 +- test/models/X/anim_test.txt | 2 +- .../X/kwxport_test_cubewithvcolors.source.txt | 6 +- test/models/X/test.txt | 2 +- test/models/invalid/readme.txt | 8 +- test/regression/README.txt | 6 +- test/unit/AbstractImportExportBase.h | 2 +- test/unit/Common/utStandardShapes.cpp | 2 +- .../MDL/utMDLImporter_HL1_Nodes.cpp | 4 +- test/unit/RandomNumberGeneration.h | 4 +- test/unit/SceneDiffer.cpp | 2 +- test/unit/SceneDiffer.h | 2 +- test/unit/TestIOSystem.h | 2 +- test/unit/utDefaultIOStream.cpp | 2 +- test/unit/utFBXImporterExporter.cpp | 4 +- test/unit/utFindDegenerates.cpp | 4 +- test/unit/utIOStreamBuffer.cpp | 6 +- test/unit/utIOSystem.cpp | 10 +- test/unit/utIssues.cpp | 2 +- test/unit/utVersion.cpp | 2 +- test/unit/utglTF2ImportExport.cpp | 2 +- tools/assimp_cmd/CMakeLists.txt | 2 +- tools/assimp_cmd/Export.cpp | 18 +- tools/assimp_cmd/ImageExtractor.cpp | 20 +- tools/assimp_cmd/Info.cpp | 2 +- tools/assimp_cmd/Main.cpp | 62 +-- tools/assimp_cmd/Main.h | 40 +- tools/assimp_cmd/WriteDump.cpp | 18 +- tools/assimp_cmd/resource.h | 2 +- tools/assimp_view/AnimEvaluator.h | 12 +- tools/assimp_view/CMakeLists.txt | 2 +- tools/assimp_view/Display.cpp | 6 +- tools/assimp_view/MessageProc.cpp | 2 +- tools/assimp_view/Shaders.cpp | 18 +- tools/assimp_view/assimp_view.cpp | 18 +- tools/assimp_view/resource.h | 2 +- 185 files changed, 1158 insertions(+), 1158 deletions(-) diff --git a/code/AssetLib/3DS/3DSExporter.cpp b/code/AssetLib/3DS/3DSExporter.cpp index 92a6d5aa7..0beecd563 100644 --- a/code/AssetLib/3DS/3DSExporter.cpp +++ b/code/AssetLib/3DS/3DSExporter.cpp @@ -291,7 +291,7 @@ void Discreet3DSExporter::WriteMaterials() { ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_SPECULAR); WriteColor(color); } - + if (mat.Get(AI_MATKEY_COLOR_AMBIENT, color) == AI_SUCCESS) { ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_AMBIENT); WriteColor(color); diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index 747af7cfc..a56b82d63 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -274,7 +274,7 @@ private: if (ret) { value = std::atoi(strValue.c_str()); return true; - } + } return false; } diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp index 615882b6a..88a38b827 100644 --- a/code/AssetLib/AMF/AMFImporter.cpp +++ b/code/AssetLib/AMF/AMFImporter.cpp @@ -303,7 +303,7 @@ void AMFImporter::ParseNode_Root() { } XmlNode node = *root; mUnit = ai_tolower(std::string(node.attribute("unit").as_string())); - + mVersion = node.attribute("version").as_string(); // Read attributes for node . diff --git a/code/AssetLib/AMF/AMFImporter_Geometry.cpp b/code/AssetLib/AMF/AMFImporter_Geometry.cpp index 1fd2c49a4..1d2a1f5b4 100644 --- a/code/AssetLib/AMF/AMFImporter_Geometry.cpp +++ b/code/AssetLib/AMF/AMFImporter_Geometry.cpp @@ -75,7 +75,7 @@ void AMFImporter::ParseNode_Mesh(XmlNode &node) { found_volumes = true; } ParseHelper_Node_Exit(); - } + } if (!found_verts && !found_volumes) { mNodeElement_Cur->Child.push_back(ne); @@ -199,9 +199,9 @@ void AMFImporter::ParseNode_Volume(XmlNode &node) { // Read attributes for node . // and assign read data - + ((AMFVolume *)ne)->MaterialID = node.attribute("materialid").as_string(); - + ((AMFVolume *)ne)->Type = type; // Check for child nodes bool col_read = false; diff --git a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp index 43d0de52f..d56d6681d 100644 --- a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp +++ b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp @@ -69,7 +69,7 @@ aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /* } tcol = Color->Color; - + // Check if default color must be used if ((tcol.r == 0) && (tcol.g == 0) && (tcol.b == 0) && (tcol.a == 0)) { tcol.r = 0.5f; @@ -99,10 +99,10 @@ void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &nodeEleme } // all coordinates stored as child and we need to reserve space for future push_back's. - vertexCoordinateArray.reserve(vn->Child.size()); + vertexCoordinateArray.reserve(vn->Child.size()); // colors count equal vertices count. - pVertexColorArray.resize(vn->Child.size()); + pVertexColorArray.resize(vn->Child.size()); col_idx = 0; // Inside vertices collect all data and place to arrays diff --git a/code/AssetLib/Assjson/mesh_splitter.cpp b/code/AssetLib/Assjson/mesh_splitter.cpp index 24385f9a0..9301cc27e 100644 --- a/code/AssetLib/Assjson/mesh_splitter.cpp +++ b/code/AssetLib/Assjson/mesh_splitter.cpp @@ -110,7 +110,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormNumVertices / LIMIT) + 1; - // create a std::vector to remember which vertices have already + // create a std::vector to remember which vertices have already // been copied and to which position (i.e. output index) std::vector was_copied_to; was_copied_to.resize(in_mesh->mNumVertices,WAS_NOT_COPIED); @@ -125,7 +125,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormNumVertices = 0; out_mesh->mMaterialIndex = in_mesh->mMaterialIndex; @@ -179,7 +179,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormNumVertices + iNeed > out_vertex_index) { @@ -240,7 +240,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormTextureCoords[c][out_mesh->mNumVertices] = in_mesh->mTextureCoords[c][index]; } } - // vertex colors + // vertex colors for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { if (in_mesh->HasVertexColors( c)) { out_mesh->mColors[c][out_mesh->mNumVertices] = in_mesh->mColors[c][index]; diff --git a/code/AssetLib/Assjson/mesh_splitter.h b/code/AssetLib/Assjson/mesh_splitter.h index 326f73b41..3bb26118a 100644 --- a/code/AssetLib/Assjson/mesh_splitter.h +++ b/code/AssetLib/Assjson/mesh_splitter.h @@ -22,13 +22,13 @@ struct aiNode; // --------------------------------------------------------------------------- /** Splits meshes of unique vertices into meshes with no more vertices than - * a given, configurable threshold value. + * a given, configurable threshold value. */ -class MeshSplitter +class MeshSplitter { public: - + void SetLimit(unsigned int l) { LIMIT = l; } diff --git a/code/AssetLib/Assxml/AssxmlExporter.cpp b/code/AssetLib/Assxml/AssxmlExporter.cpp index 847ba0d7e..d244813b1 100644 --- a/code/AssetLib/Assxml/AssxmlExporter.cpp +++ b/code/AssetLib/Assxml/AssxmlExporter.cpp @@ -50,7 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -namespace Assimp { +namespace Assimp { void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) { diff --git a/code/AssetLib/Blender/BlenderModifier.h b/code/AssetLib/Blender/BlenderModifier.h index daf120087..d2fea43c1 100644 --- a/code/AssetLib/Blender/BlenderModifier.h +++ b/code/AssetLib/Blender/BlenderModifier.h @@ -52,9 +52,9 @@ namespace Assimp { namespace Blender { // ------------------------------------------------------------------------------------------- -/** +/** * Dummy base class for all blender modifiers. Modifiers are reused between imports, so - * they should be stateless and not try to cache model data. + * they should be stateless and not try to cache model data. */ // ------------------------------------------------------------------------------------------- class BlenderModifier { @@ -67,7 +67,7 @@ public: } // -------------------- - /** + /** * Check if *this* modifier is active, given a ModifierData& block. */ virtual bool IsActive( const ModifierData& /*modin*/) { @@ -75,10 +75,10 @@ public: } // -------------------- - /** + /** * Apply the modifier to a given output node. The original data used * to construct the node is given as well. Not called unless IsActive() - * was called and gave positive response. + * was called and gave positive response. */ virtual void DoIt(aiNode& /*out*/, ConversionData& /*conv_data*/, @@ -92,8 +92,8 @@ public: }; // ------------------------------------------------------------------------------------------- -/** - * Manage all known modifiers and instance and apply them if necessary +/** + * Manage all known modifiers and instance and apply them if necessary */ // ------------------------------------------------------------------------------------------- class BlenderModifierShowcase { @@ -113,8 +113,8 @@ private: // MODIFIERS ///////////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------------------------- -/** - * Mirror modifier. Status: implemented. +/** + * Mirror modifier. Status: implemented. */ // ------------------------------------------------------------------------------------------- class BlenderModifier_Mirror : public BlenderModifier { diff --git a/code/AssetLib/C4D/C4DImporter.cpp b/code/AssetLib/C4D/C4DImporter.cpp index 5408daa68..14a94958b 100644 --- a/code/AssetLib/C4D/C4DImporter.cpp +++ b/code/AssetLib/C4D/C4DImporter.cpp @@ -148,12 +148,12 @@ void C4DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS // Generate the root-node pScene->mRootNode = new aiNode(""); - + // convert left-handed to right-handed pScene->mRootNode->mTransformation.a1 = 0.01f; pScene->mRootNode->mTransformation.b2 = 0.01f; - pScene->mRootNode->mTransformation.c3 = -0.01f; - + pScene->mRootNode->mTransformation.c3 = -0.01f; + // first convert all materials ReadMaterials(doc->GetFirstMaterial()); diff --git a/code/AssetLib/COB/COBLoader.cpp b/code/AssetLib/COB/COBLoader.cpp index 822bce16d..3bef260e5 100644 --- a/code/AssetLib/COB/COBLoader.cpp +++ b/code/AssetLib/COB/COBLoader.cpp @@ -884,7 +884,7 @@ void COBImporter::ReadBinaryFile(Scene &out, StreamReaderLE *reader) { std::string type; type += reader->GetI1(); type += reader->GetI1(); - type += reader->GetI1(); + type += reader->GetI1(); type += reader->GetI1(); ChunkInfo nfo; diff --git a/code/AssetLib/COB/COBLoader.h b/code/AssetLib/COB/COBLoader.h index 2317d094e..e4d41e500 100644 --- a/code/AssetLib/COB/COBLoader.h +++ b/code/AssetLib/COB/COBLoader.h @@ -77,7 +77,7 @@ class COBImporter : public BaseImporter public: COBImporter(); ~COBImporter(); - + // -------------------- bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index ee8f97203..ccb2bb336 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -1675,7 +1675,7 @@ void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/) const Material &material = matIt->second; // a material is only a reference to an effect ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find(material.mEffect); - if (effIt == pParser.mEffectLibrary.end()) + if (effIt == pParser.mEffectLibrary.end()) continue; Effect &effect = effIt->second; diff --git a/code/AssetLib/DXF/DXFLoader.cpp b/code/AssetLib/DXF/DXFLoader.cpp index 49d572b0b..2e38ed976 100644 --- a/code/AssetLib/DXF/DXFLoader.cpp +++ b/code/AssetLib/DXF/DXFLoader.cpp @@ -547,7 +547,7 @@ void DXFImporter::ParseEntities(DXF::LineReader& reader, DXF::FileData& output) ++reader; } - ASSIMP_LOG_VERBOSE_DEBUG( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), + ASSIMP_LOG_VERBOSE_DEBUG( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), " inserted blocks in ENTITIES" ); } diff --git a/code/AssetLib/DXF/DXFLoader.h b/code/AssetLib/DXF/DXFLoader.h index 5319d2528..6649deb34 100644 --- a/code/AssetLib/DXF/DXFLoader.h +++ b/code/AssetLib/DXF/DXFLoader.h @@ -63,7 +63,7 @@ namespace DXF { } // --------------------------------------------------------------------------- -/** +/** * @brief DXF importer implementation. */ class DXFImporter : public BaseImporter { diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index f489e37a4..e0da78583 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -862,7 +862,7 @@ bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std output_nodes.push_back(std::move(nd)); return false; } - + void FBXConverter::SetupNodeMetadata(const Model &model, aiNode &nd) { const PropertyTable &props = model.Props(); DirectPropertyMap unparsedProperties = props.GetUnparsedProperties(); @@ -3572,7 +3572,7 @@ void FBXConverter::ConvertOrphanedEmbeddedTextures() { if (texture->Media() && texture->Media()->ContentLength() > 0) { realTexture = texture; } - } + } } } catch (...) { // do nothing diff --git a/code/AssetLib/FBX/FBXConverter.h b/code/AssetLib/FBX/FBXConverter.h index d208ab429..b9a494695 100644 --- a/code/AssetLib/FBX/FBXConverter.h +++ b/code/AssetLib/FBX/FBXConverter.h @@ -76,7 +76,7 @@ namespace Assimp { namespace FBX { class Document; -/** +/** * Convert a FBX #Document to #aiScene * @param out Empty scene to be populated * @param doc Parsed FBX document @@ -182,7 +182,7 @@ private: // ------------------------------------------------------------------------------------------------ void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform); - + // ------------------------------------------------------------------------------------------------ // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed std::vector diff --git a/code/AssetLib/FBX/FBXDocument.cpp b/code/AssetLib/FBX/FBXDocument.cpp index 0c4435348..8e0439e18 100644 --- a/code/AssetLib/FBX/FBXDocument.cpp +++ b/code/AssetLib/FBX/FBXDocument.cpp @@ -635,7 +635,7 @@ std::vector Document::GetConnectionsBySourceSequenced(uint64_ } // ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsBySourceSequenced(uint64_t source, +std::vector Document::GetConnectionsBySourceSequenced(uint64_t source, const char* const* classnames, size_t count) const { return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count); diff --git a/code/AssetLib/FBX/FBXExportNode.h b/code/AssetLib/FBX/FBXExportNode.h index 6ef27972d..3aca98939 100644 --- a/code/AssetLib/FBX/FBXExportNode.h +++ b/code/AssetLib/FBX/FBXExportNode.h @@ -60,7 +60,7 @@ namespace FBX { } class FBX::Node { -public: +public: // TODO: accessors std::string name; // node name std::vector properties; // node properties diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index c19c593dd..84a77e18d 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -498,7 +498,7 @@ void FBXExporter::WriteDocuments () if (!binary) { WriteAsciiSectionHeader("Documents Description"); } - + // not sure what the use of multiple documents would be, // or whether any end-application supports it FBX::Node docs("Documents"); @@ -1258,7 +1258,7 @@ void FBXExporter::WriteObjects () indent = 2; vertexcolors.End(outstream, binary, indent, true); } - + // uvs, if any for (size_t uvi = 0; uvi < m->GetNumUVChannels(); ++uvi) { if (m->mNumUVComponents[uvi] > 2) { @@ -1751,7 +1751,7 @@ void FBXExporter::WriteObjects () bsnode.AddProperty(blendshape_uid); bsnode.AddProperty(blendshape_name + FBX::SEPARATOR + "Blendshape"); bsnode.AddProperty("Shape"); - bsnode.AddChild("Version", int32_t(100)); + bsnode.AddChild("Version", int32_t(100)); bsnode.Begin(outstream, binary, indent); bsnode.DumpProperties(outstream, binary, indent); bsnode.EndProperties(outstream, binary, indent); @@ -1877,7 +1877,7 @@ void FBXExporter::WriteObjects () // at the same time we can build a list of all the skeleton nodes, // which will be used later to mark them as type "limbNode". std::unordered_set limbnodes; - + //actual bone nodes in fbx, without parenting-up std::unordered_set setAllBoneNamesInScene; for(unsigned int m = 0; m < mScene->mNumMeshes; ++ m) @@ -1887,7 +1887,7 @@ void FBXExporter::WriteObjects () setAllBoneNamesInScene.insert(pMesh->mBones[b]->mName.data); } aiMatrix4x4 mxTransIdentity; - + // and a map of nodes by bone name, as finding them is annoying. std::map node_by_bone; for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { @@ -1956,7 +1956,7 @@ void FBXExporter::WriteObjects () } if (end) { break; } } - + // if it was the skeleton root we can finish here if (end) { break; } } diff --git a/code/AssetLib/FBX/FBXMaterial.cpp b/code/AssetLib/FBX/FBXMaterial.cpp index aaa043c12..7eb047177 100644 --- a/code/AssetLib/FBX/FBXMaterial.cpp +++ b/code/AssetLib/FBX/FBXMaterial.cpp @@ -142,8 +142,8 @@ Material::~Material() { // ------------------------------------------------------------------------------------------------ Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) : - Object(id,element,name), - uvScaling(1.0f,1.0f), + Object(id,element,name), + uvScaling(1.0f,1.0f), media(0) { const Scope& sc = GetRequiredScope(element); @@ -278,8 +278,8 @@ void LayeredTexture::fillTexture(const Document& doc) { // ------------------------------------------------------------------------------------------------ Video::Video(uint64_t id, const Element& element, const Document& doc, const std::string& name) : - Object(id,element,name), - contentLength(0), + Object(id,element,name), + contentLength(0), content(0) { const Scope& sc = GetRequiredScope(element); diff --git a/code/AssetLib/FBX/FBXMeshGeometry.cpp b/code/AssetLib/FBX/FBXMeshGeometry.cpp index 5aecb61b5..6aeebcbe3 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.cpp +++ b/code/AssetLib/FBX/FBXMeshGeometry.cpp @@ -633,7 +633,7 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector& materials_out, cons { return; } - + // materials are handled separately. First of all, they are assigned per-face // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect // has a slightly different meaning for materials. diff --git a/code/AssetLib/FBX/FBXMeshGeometry.h b/code/AssetLib/FBX/FBXMeshGeometry.h index ae17860e3..862693b4b 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.h +++ b/code/AssetLib/FBX/FBXMeshGeometry.h @@ -52,8 +52,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { namespace FBX { -/** - * DOM base class for all kinds of FBX geometry +/** + * DOM base class for all kinds of FBX geometry */ class Geometry : public Object { @@ -76,7 +76,7 @@ private: typedef std::vector MatIndexArray; -/** +/** * DOM class for FBX geometry of type "Mesh" */ class MeshGeometry : public Geometry @@ -84,7 +84,7 @@ class MeshGeometry : public Geometry public: /** The class constructor */ MeshGeometry( uint64_t id, const Element& element, const std::string& name, const Document& doc ); - + /** The class destructor */ virtual ~MeshGeometry(); diff --git a/code/AssetLib/FBX/FBXProperties.h b/code/AssetLib/FBX/FBXProperties.h index 9ab784fa7..d1d6b87ab 100644 --- a/code/AssetLib/FBX/FBXProperties.h +++ b/code/AssetLib/FBX/FBXProperties.h @@ -98,7 +98,7 @@ typedef std::fbx_unordered_map > DirectPro typedef std::fbx_unordered_map PropertyMap; typedef std::fbx_unordered_map LazyPropertyMap; -/** +/** * Represents a property table as can be found in the newer FBX files (Properties60, Properties70) */ class PropertyTable { @@ -130,7 +130,7 @@ private: // ------------------------------------------------------------------------------------------------ template -inline +inline T PropertyGet(const PropertyTable& in, const std::string& name, const T& defaultValue) { const Property* const prop = in.Get(name); if( nullptr == prop) { @@ -148,7 +148,7 @@ T PropertyGet(const PropertyTable& in, const std::string& name, const T& default // ------------------------------------------------------------------------------------------------ template -inline +inline T PropertyGet(const PropertyTable& in, const std::string& name, bool& result, bool useTemplate=false ) { const Property* prop = in.Get(name); if( nullptr == prop) { diff --git a/code/AssetLib/FBX/FBXUtil.cpp b/code/AssetLib/FBX/FBXUtil.cpp index 66abf0565..3fe791b97 100644 --- a/code/AssetLib/FBX/FBXUtil.cpp +++ b/code/AssetLib/FBX/FBXUtil.cpp @@ -101,7 +101,7 @@ std::string GetLineAndColumnText(unsigned int line, unsigned int column) std::string GetTokenText(const Token* tok) { if(tok->IsBinary()) { - return static_cast( Formatter::format() << + return static_cast( Formatter::format() << " (" << TokenTypeString(tok->Type()) << ", offset 0x" << std::hex << tok->Offset() << ") " ); } diff --git a/code/AssetLib/HMP/HMPLoader.cpp b/code/AssetLib/HMP/HMPLoader.cpp index cd14cb9c3..97c1858fb 100644 --- a/code/AssetLib/HMP/HMPLoader.cpp +++ b/code/AssetLib/HMP/HMPLoader.cpp @@ -153,10 +153,10 @@ void HMPImporter::InternReadFile(const std::string &pFile, } else { // Print the magic word to the logger std::string szBuffer = ai_str_toprintable((const char *)&iMagic, sizeof(iMagic)); - + delete[] mBuffer; mBuffer = nullptr; - + // We're definitely unable to load this file throw DeadlyImportError("Unknown HMP subformat ", pFile, ". Magic word (", szBuffer, ") is not known"); diff --git a/code/AssetLib/IFC/IFCCurve.cpp b/code/AssetLib/IFC/IFCCurve.cpp index 28cd9690c..3ded43bc0 100644 --- a/code/AssetLib/IFC/IFCCurve.cpp +++ b/code/AssetLib/IFC/IFCCurve.cpp @@ -514,7 +514,7 @@ IfcFloat Curve::GetParametricRangeDelta() const { // ------------------------------------------------------------------------------------------------ size_t Curve::EstimateSampleCount(IfcFloat a, IfcFloat b) const { - (void)(a); (void)(b); + (void)(a); (void)(b); ai_assert( InRange( a ) ); ai_assert( InRange( b ) ); diff --git a/code/AssetLib/IFC/IFCOpenings.cpp b/code/AssetLib/IFC/IFCOpenings.cpp index b7665582c..d6671b885 100644 --- a/code/AssetLib/IFC/IFCOpenings.cpp +++ b/code/AssetLib/IFC/IFCOpenings.cpp @@ -911,14 +911,14 @@ size_t CloseWindows(ContourVector& contours, // compare base poly normal and contour normal to detect if we need to reverse the face winding if(curmesh.mVertcnt.size() > 0) { IfcVector3 basePolyNormal = TempMesh::ComputePolygonNormal(curmesh.mVerts.data(), curmesh.mVertcnt.front()); - + std::vector worldSpaceContourVtx(it->contour.size()); - + for(size_t a = 0; a < it->contour.size(); ++a) worldSpaceContourVtx[a] = minv * IfcVector3(it->contour[a].x, it->contour[a].y, 0.0); - + IfcVector3 contourNormal = TempMesh::ComputePolygonNormal(worldSpaceContourVtx.data(), worldSpaceContourVtx.size()); - + reverseCountourFaces = (contourNormal * basePolyNormal) > 0.0; } diff --git a/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp b/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp index 2cfa22530..a6f7ae3eb 100644 --- a/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp +++ b/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -1063,27 +1063,27 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcRoo if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRoot"); } do { // convert the 'GlobalId' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->GlobalId, arg, db ); break; } + try { GenericConvert( in->GlobalId, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRoot to be a `IfcGloballyUniqueId`")); } } while(0); do { // convert the 'OwnerHistory' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->OwnerHistory, arg, db ); break; } + try { GenericConvert( in->OwnerHistory, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRoot to be a `IfcOwnerHistory`")); } } while(0); do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRoot to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRoot to be a `IfcText`")); } } while(0); return base; @@ -1150,27 +1150,27 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRepresentation"); } do { // convert the 'ContextOfItems' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ContextOfItems, arg, db ); break; } + try { GenericConvert( in->ContextOfItems, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentation to be a `IfcRepresentationContext`")); } } while(0); do { // convert the 'RepresentationIdentifier' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } + try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationType, arg, db ); break; } + try { GenericConvert( in->RepresentationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Items' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->Items, arg, db ); break; } + try { GenericConvert( in->Items, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRepresentation to be a `SET [1:?] OF IfcRepresentationItem`")); } } while(0); return base; @@ -1237,7 +1237,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcO std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectType, arg, db ); break; } + try { GenericConvert( in->ObjectType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcObject to be a `IfcLabel`")); } } while(0); return base; @@ -1290,20 +1290,20 @@ template <> size_t GenericFill(const DB& db, const LIS std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProductRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProductRepresentation to be a `IfcText`")); } } while(0); do { // convert the 'Representations' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->Representations, arg, db ); break; } + try { GenericConvert( in->Representations, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcProductRepresentation to be a `LIST [1:?] OF IfcRepresentation`")); } } while(0); return base; @@ -1316,14 +1316,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectPlacement, arg, db ); break; } + try { GenericConvert( in->ObjectPlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcProduct to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'Representation' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Representation, arg, db ); break; } + try { GenericConvert( in->Representation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcProduct to be a `IfcProductRepresentation`")); } } while(0); return base; @@ -1336,7 +1336,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Tag, arg, db ); break; } + try { GenericConvert( in->Tag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcElement to be a `IfcIdentifier`")); } } while(0); return base; @@ -1374,13 +1374,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCompositeCurve"); } do { // convert the 'Segments' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Segments, arg, db ); break; } + try { GenericConvert( in->Segments, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurve to be a `LIST [1:?] OF IfcCompositeCurveSegment`")); } } while(0); do { // convert the 'SelfIntersect' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurve to be a `LOGICAL`")); } } while(0); return base; @@ -1400,27 +1400,27 @@ template <> size_t GenericFill(const DB& db, std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis1, arg, db ); break; } + try { GenericConvert( in->Axis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'Axis2' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis2, arg, db ); break; } + try { GenericConvert( in->Axis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'LocalOrigin' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->LocalOrigin, arg, db ); break; } + try { GenericConvert( in->LocalOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCartesianTransformationOperator to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Scale' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale, arg, db ); break; } + try { GenericConvert( in->Scale, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCartesianTransformationOperator to be a `REAL`")); } } while(0); return base; @@ -1433,7 +1433,7 @@ template <> size_t GenericFill(const DB& d std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis3, arg, db ); break; } + try { GenericConvert( in->Axis3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCartesianTransformationOperator3D to be a `IfcDirection`")); } } while(0); return base; @@ -1445,14 +1445,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProperty"); } do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'Description' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProperty to be a `IfcText`")); } } while(0); return base; @@ -1497,7 +1497,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcElementarySurface"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcElementarySurface to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -1515,19 +1515,19 @@ template <> size_t GenericFill(const DB& db, const LIST& param if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcBooleanResult"); } do { // convert the 'Operator' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Operator, arg, db ); break; } + try { GenericConvert( in->Operator, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBooleanResult to be a `IfcBooleanOperator`")); } } while(0); do { // convert the 'FirstOperand' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->FirstOperand, arg, db ); break; } + try { GenericConvert( in->FirstOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); do { // convert the 'SecondOperand' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->SecondOperand, arg, db ); break; } + try { GenericConvert( in->SecondOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); return base; @@ -1551,7 +1551,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcManifoldSolidBrep"); } do { // convert the 'Outer' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Outer, arg, db ); break; } + try { GenericConvert( in->Outer, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcManifoldSolidBrep to be a `IfcClosedShell`")); } } while(0); return base; @@ -1630,12 +1630,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelFillsElement"); } do { // convert the 'RelatingOpeningElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelFillsElement to be a `IfcOpeningElement`")); } } while(0); do { // convert the 'RelatedBuildingElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelFillsElement to be a `IfcElement`")); } } while(0); return base; @@ -1681,12 +1681,12 @@ template <> size_t GenericFill(const DB& db, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelContainedInSpatialStructure"); } do { // convert the 'RelatedElements' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedElements, arg, db ); break; } + try { GenericConvert( in->RelatedElements, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelContainedInSpatialStructure to be a `SET [1:?] OF IfcProduct`")); } } while(0); do { // convert the 'RelatingStructure' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingStructure, arg, db ); break; } + try { GenericConvert( in->RelatingStructure, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelContainedInSpatialStructure to be a `IfcSpatialStructureElement`")); } } while(0); return base; @@ -1772,7 +1772,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcDirection"); } do { // convert the 'DirectionRatios' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->DirectionRatios, arg, db ); break; } + try { GenericConvert( in->DirectionRatios, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcDirection to be a `LIST [2:3] OF REAL`")); } } while(0); return base; @@ -1784,14 +1784,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProfileDef"); } do { // convert the 'ProfileType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ProfileType, arg, db ); break; } + try { GenericConvert( in->ProfileType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProfileDef to be a `IfcProfileTypeEnum`")); } } while(0); do { // convert the 'ProfileName' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ProfileName, arg, db ); break; } + try { GenericConvert( in->ProfileName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProfileDef to be a `IfcLabel`")); } } while(0); return base; @@ -1803,7 +1803,7 @@ template <> size_t GenericFill(const DB& db, const L if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcParameterizedProfileDef"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcParameterizedProfileDef to be a `IfcAxis2Placement2D`")); } } while(0); return base; @@ -1910,7 +1910,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcCircleProfileDef"); } do { // convert the 'Radius' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCircleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1921,7 +1921,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcCircleHollowProfileDef"); } do { // convert the 'WallThickness' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->WallThickness, arg, db ); break; } + try { GenericConvert( in->WallThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCircleHollowProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1933,7 +1933,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPlacement"); } do { // convert the 'Location' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Location, arg, db ); break; } + try { GenericConvert( in->Location, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPlacement to be a `IfcCartesianPoint`")); } } while(0); return base; @@ -1945,13 +1945,13 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcAxis2Placement3D"); } do { // convert the 'Axis' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); do { // convert the 'RefDirection' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); return base; @@ -1964,7 +1964,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyle to be a `IfcLabel`")); } } while(0); return base; @@ -1982,17 +1982,17 @@ template <> size_t GenericFill(const DB& db, const LIS size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcCompositeCurveSegment"); } do { // convert the 'Transition' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Transition, arg, db ); break; } + try { GenericConvert( in->Transition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurveSegment to be a `IfcTransitionCode`")); } } while(0); do { // convert the 'SameSense' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SameSense, arg, db ); break; } + try { GenericConvert( in->SameSense, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurveSegment to be a `BOOLEAN`")); } } while(0); do { // convert the 'ParentCurve' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ParentCurve, arg, db ); break; } + try { GenericConvert( in->ParentCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCompositeCurveSegment to be a `IfcCurve`")); } } while(0); return base; @@ -2004,13 +2004,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcRectangleProfileDef"); } do { // convert the 'XDim' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2106,12 +2106,12 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLocalPlacement"); } do { // convert the 'PlacementRelTo' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PlacementRelTo, arg, db ); break; } + try { GenericConvert( in->PlacementRelTo, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLocalPlacement to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'RelativePlacement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelativePlacement, arg, db ); break; } + try { GenericConvert( in->RelativePlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLocalPlacement to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -2123,13 +2123,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcSweptAreaSolid"); } do { // convert the 'SweptArea' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SweptArea, arg, db ); break; } + try { GenericConvert( in->SweptArea, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptAreaSolid to be a `IfcProfileDef`")); } } while(0); do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptAreaSolid to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -2140,12 +2140,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRevolvedAreaSolid"); } do { // convert the 'Axis' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRevolvedAreaSolid to be a `IfcAxis1Placement`")); } } while(0); do { // convert the 'Angle' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Angle, arg, db ); break; } + try { GenericConvert( in->Angle, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRevolvedAreaSolid to be a `IfcPlaneAngleMeasure`")); } } while(0); return base; @@ -2170,28 +2170,28 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcSweptDiskSolid"); } do { // convert the 'Directrix' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Directrix, arg, db ); break; } + try { GenericConvert( in->Directrix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptDiskSolid to be a `IfcCurve`")); } } while(0); do { // convert the 'Radius' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'InnerRadius' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->InnerRadius, arg, db ); break; } + try { GenericConvert( in->InnerRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'StartParam' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->StartParam, arg, db ); break; } + try { GenericConvert( in->StartParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); do { // convert the 'EndParam' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->EndParam, arg, db ); break; } + try { GenericConvert( in->EndParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); return base; @@ -2203,13 +2203,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcHalfSpaceSolid"); } do { // convert the 'BaseSurface' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->BaseSurface, arg, db ); break; } + try { GenericConvert( in->BaseSurface, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcHalfSpaceSolid to be a `IfcSurface`")); } } while(0); do { // convert the 'AgreementFlag' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->AgreementFlag, arg, db ); break; } + try { GenericConvert( in->AgreementFlag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcHalfSpaceSolid to be a `BOOLEAN`")); } } while(0); return base; @@ -2220,12 +2220,12 @@ template <> size_t GenericFill(const DB& db, const size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPolygonalBoundedHalfSpace"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPolygonalBoundedHalfSpace to be a `IfcAxis2Placement3D`")); } } while(0); do { // convert the 'PolygonalBoundary' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } + try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPolygonalBoundedHalfSpace to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -2251,23 +2251,23 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc if (params.GetSize() < 9) { throw STEP::TypeError("expected 9 arguments to IfcProject"); } do { // convert the 'LongName' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcProject to be a `IfcLabel`")); } } while(0); do { // convert the 'Phase' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Phase, arg, db ); break; } + try { GenericConvert( in->Phase, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcProject to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationContexts' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RepresentationContexts, arg, db ); break; } + try { GenericConvert( in->RepresentationContexts, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcProject to be a `SET [1:?] OF IfcRepresentationContext`")); } } while(0); do { // convert the 'UnitsInContext' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->UnitsInContext, arg, db ); break; } + try { GenericConvert( in->UnitsInContext, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcProject to be a `IfcUnitAssignment`")); } } while(0); return base; @@ -2327,27 +2327,27 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcTrimmedCurve"); } do { // convert the 'BasisCurve' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->BasisCurve, arg, db ); break; } + try { GenericConvert( in->BasisCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcTrimmedCurve to be a `IfcCurve`")); } } while(0); do { // convert the 'Trim1' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim1, arg, db ); break; } + try { GenericConvert( in->Trim1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'Trim2' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim2, arg, db ); break; } + try { GenericConvert( in->Trim2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'SenseAgreement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SenseAgreement, arg, db ); break; } + try { GenericConvert( in->SenseAgreement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcTrimmedCurve to be a `BOOLEAN`")); } } while(0); do { // convert the 'MasterRepresentation' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MasterRepresentation, arg, db ); break; } + try { GenericConvert( in->MasterRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcTrimmedCurve to be a `IfcTrimmingPreference`")); } } while(0); return base; @@ -2359,7 +2359,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcRelDefines"); } do { // convert the 'RelatedObjects' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelDefines to be a `SET [1:?] OF IfcObject`")); } } while(0); return base; @@ -2371,7 +2371,7 @@ template <> size_t GenericFill(const DB& db, const LI if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelDefinesByProperties"); } do { // convert the 'RelatingPropertyDefinition' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } + try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelDefinesByProperties to be a `IfcPropertySetDefinition`")); } } while(0); return base; @@ -2404,7 +2404,7 @@ template <> size_t GenericFill(const DB& db, const L if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryOpenProfileDef"); } do { // convert the 'Curve' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Curve, arg, db ); break; } + try { GenericConvert( in->Curve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryOpenProfileDef to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -2570,13 +2570,13 @@ template <> size_t GenericFill(const DB& db, const LIST& param if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelDecomposes"); } do { // convert the 'RelatingObject' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->RelatingObject, arg, db ); break; } + try { GenericConvert( in->RelatingObject, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelDecomposes to be a `IfcObjectDefinition`")); } } while(0); do { // convert the 'RelatedObjects' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelDecomposes to be a `SET [1:?] OF IfcObjectDefinition`")); } } while(0); return base; @@ -2594,7 +2594,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyline"); } do { // convert the 'Points' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Points, arg, db ); break; } + try { GenericConvert( in->Points, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyline to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -2626,12 +2626,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMappedItem"); } do { // convert the 'MappingSource' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingSource, arg, db ); break; } + try { GenericConvert( in->MappingSource, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMappedItem to be a `IfcRepresentationMap`")); } } while(0); do { // convert the 'MappingTarget' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingTarget, arg, db ); break; } + try { GenericConvert( in->MappingTarget, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMappedItem to be a `IfcCartesianTransformationOperator`")); } } while(0); return base; @@ -2658,13 +2658,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Dimensions, arg, db ); break; } + try { GenericConvert( in->Dimensions, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcNamedUnit to be a `IfcDimensionalExponents`")); } } while(0); do { // convert the 'UnitType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->UnitType, arg, db ); break; } + try { GenericConvert( in->UnitType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcNamedUnit to be a `IfcUnitEnum`")); } } while(0); return base; @@ -2719,13 +2719,13 @@ template <> size_t GenericFill(const DB& db, const L std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSpatialStructureElement to be a `IfcLabel`")); } } while(0); do { // convert the 'CompositionType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->CompositionType, arg, db ); break; } + try { GenericConvert( in->CompositionType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSpatialStructureElement to be a `IfcElementCompositionEnum`")); } } while(0); return base; @@ -2737,19 +2737,19 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 12) { throw STEP::TypeError("expected 12 arguments to IfcBuilding"); } do { // convert the 'ElevationOfRefHeight' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } + try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'ElevationOfTerrain' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } + try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'BuildingAddress' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->BuildingAddress, arg, db ); break; } + try { GenericConvert( in->BuildingAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcBuilding to be a `IfcPostalAddress`")); } } while(0); return base; @@ -2761,7 +2761,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConnectedFaceSet"); } do { // convert the 'CfsFaces' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CfsFaces, arg, db ); break; } + try { GenericConvert( in->CfsFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConnectedFaceSet to be a `SET [1:?] OF IfcFace`")); } } while(0); return base; @@ -2787,7 +2787,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcCo if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConic"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConic to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -2834,32 +2834,32 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 8) { throw STEP::TypeError("expected 8 arguments to IfcIShapeProfileDef"); } do { // convert the 'OverallWidth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallDepth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->OverallDepth, arg, db ); break; } + try { GenericConvert( in->OverallDepth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'WebThickness' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->WebThickness, arg, db ); break; } + try { GenericConvert( in->WebThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FlangeThickness' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->FlangeThickness, arg, db ); break; } + try { GenericConvert( in->FlangeThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FilletRadius' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FilletRadius, arg, db ); break; } + try { GenericConvert( in->FilletRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2933,13 +2933,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertyListValue"); } do { // convert the 'ListValues' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ListValues, arg, db ); break; } + try { GenericConvert( in->ListValues, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertyListValue to be a `LIST [1:?] OF IfcValue`")); } } while(0); do { // convert the 'Unit' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertyListValue to be a `IfcUnit`")); } } while(0); return base; @@ -2965,13 +2965,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcDoo if (params.GetSize() < 10) { throw STEP::TypeError("expected 10 arguments to IfcDoor"); } do { // convert the 'OverallHeight' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallHeight, arg, db ); break; } + try { GenericConvert( in->OverallHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallWidth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2984,20 +2984,20 @@ template <> size_t GenericFill(const DB& db, const LIST& params, std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Item, arg, db ); break; } + try { GenericConvert( in->Item, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcStyledItem to be a `IfcRepresentationItem`")); } } while(0); do { // convert the 'Styles' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcStyledItem to be a `SET [1:?] OF IfcPresentationStyleAssignment`")); } } while(0); do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcStyledItem to be a `IfcLabel`")); } } while(0); return base; @@ -3023,7 +3023,7 @@ template <> size_t GenericFill(const DB& db, const if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryClosedProfileDef"); } do { // convert the 'OuterCurve' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->OuterCurve, arg, db ); break; } + try { GenericConvert( in->OuterCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryClosedProfileDef to be a `IfcCurve`")); } } while(0); return base; @@ -3041,12 +3041,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcLin size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLine"); } do { // convert the 'Pnt' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Pnt, arg, db ); break; } + try { GenericConvert( in->Pnt, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLine to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Dir' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Dir, arg, db ); break; } + try { GenericConvert( in->Dir, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLine to be a `IfcVector`")); } } while(0); return base; @@ -3072,13 +3072,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertySingleValue"); } do { // convert the 'NominalValue' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->NominalValue, arg, db ); break; } + try { GenericConvert( in->NominalValue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertySingleValue to be a `IfcValue`")); } } while(0); do { // convert the 'Unit' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertySingleValue to be a `IfcUnit`")); } } while(0); return base; @@ -3111,7 +3111,7 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcSurfaceStyleShading"); } do { // convert the 'SurfaceColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SurfaceColour, arg, db ); break; } + try { GenericConvert( in->SurfaceColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleShading to be a `IfcColourRgb`")); } } while(0); return base; diff --git a/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp b/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp index c58c7c42f..0d7051195 100644 --- a/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp +++ b/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -59,12 +59,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcSurfaceStyle"); } do { // convert the 'Side' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Side, arg, db ); break; } + try { GenericConvert( in->Side, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyle to be a `IfcSurfaceSide`")); } } while(0); do { // convert the 'Styles' argument std::shared_ptr arg = params[ base++ ]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyle to be a `SET [1:5] OF IfcSurfaceStyleElementSelect`")); } } while(0); return base; @@ -118,7 +118,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcFac if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFace"); } do { // convert the 'Bounds' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bounds, arg, db ); break; } + try { GenericConvert( in->Bounds, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFace to be a `SET [1:?] OF IfcFaceBound`")); } } while(0); return base; @@ -173,7 +173,7 @@ template <> size_t GenericFill(const DB& db, const LIST& std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcColourSpecification to be a `IfcLabel`")); } } while(0); return base; @@ -184,12 +184,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcV size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcVector"); } do { // convert the 'Orientation' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcVector to be a `IfcDirection`")); } } while(0); do { // convert the 'Magnitude' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Magnitude, arg, db ); break; } + try { GenericConvert( in->Magnitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcVector to be a `IfcLengthMeasure`")); } } while(0); return base; @@ -207,17 +207,17 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcColourRgb"); } do { // convert the 'Red' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Red, arg, db ); break; } + try { GenericConvert( in->Red, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Green' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Green, arg, db ); break; } + try { GenericConvert( in->Green, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Blue' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Blue, arg, db ); break; } + try { GenericConvert( in->Blue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); return base; @@ -243,31 +243,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSit if (params.GetSize() < 14) { throw STEP::TypeError("expected 14 arguments to IfcSite"); } do { // convert the 'RefLatitude' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLatitude, arg, db ); break; } + try { GenericConvert( in->RefLatitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefLongitude' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLongitude, arg, db ); break; } + try { GenericConvert( in->RefLongitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefElevation' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefElevation, arg, db ); break; } + try { GenericConvert( in->RefElevation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcSite to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'LandTitleNumber' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LandTitleNumber, arg, db ); break; } + try { GenericConvert( in->LandTitleNumber, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 12 to IfcSite to be a `IfcLabel`")); } } while(0); do { // convert the 'SiteAddress' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SiteAddress, arg, db ); break; } + try { GenericConvert( in->SiteAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 13 to IfcSite to be a `IfcPostalAddress`")); } } while(0); return base; @@ -412,31 +412,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcBSplineCurve"); } do { // convert the 'Degree' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Degree, arg, db ); break; } + try { GenericConvert( in->Degree, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBSplineCurve to be a `INTEGER`")); } } while(0); do { // convert the 'ControlPointsList' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->ControlPointsList, arg, db ); break; } + try { GenericConvert( in->ControlPointsList, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBSplineCurve to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); do { // convert the 'CurveForm' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->CurveForm, arg, db ); break; } + try { GenericConvert( in->CurveForm, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBSplineCurve to be a `IfcBSplineCurveForm`")); } } while(0); do { // convert the 'ClosedCurve' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->ClosedCurve, arg, db ); break; } + try { GenericConvert( in->ClosedCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBSplineCurve to be a `LOGICAL`")); } } while(0); do { // convert the 'SelfIntersect' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcBSplineCurve to be a `LOGICAL`")); } } while(0); return base; @@ -474,7 +474,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcShellBasedSurfaceModel"); } do { // convert the 'SbsmBoundary' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SbsmBoundary, arg, db ); break; } + try { GenericConvert( in->SbsmBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcShellBasedSurfaceModel to be a `SET [1:?] OF IfcShell`")); } } while(0); return base; @@ -492,12 +492,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcExtrudedAreaSolid"); } do { // convert the 'ExtrudedDirection' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } + try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcExtrudedAreaSolid to be a `IfcDirection`")); } } while(0); do { // convert the 'Depth' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Depth, arg, db ); break; } + try { GenericConvert( in->Depth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcExtrudedAreaSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -522,12 +522,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelVoidsElement"); } do { // convert the 'RelatingBuildingElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelVoidsElement to be a `IfcElement`")); } } while(0); do { // convert the 'RelatedOpeningElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelVoidsElement to be a `IfcFeatureElementSubtraction`")); } } while(0); return base; @@ -546,13 +546,13 @@ template <> size_t GenericFill(c if (params.GetSize() < 7) { throw STEP::TypeError("expected 7 arguments to IfcCartesianTransformationOperator3DnonUniform"); } do { // convert the 'Scale2' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale2, arg, db ); break; } + try { GenericConvert( in->Scale2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcCartesianTransformationOperator3DnonUniform to be a `REAL`")); } } while(0); do { // convert the 'Scale3' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale3, arg, db ); break; } + try { GenericConvert( in->Scale3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcCartesianTransformationOperator3DnonUniform to be a `REAL`")); } } while(0); return base; @@ -634,7 +634,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis2Placement2D"); } do { // convert the 'RefDirection' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement2D to be a `IfcDirection`")); } } while(0); return base; @@ -658,7 +658,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcCartesianPoint"); } do { // convert the 'Coordinates' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Coordinates, arg, db ); break; } + try { GenericConvert( in->Coordinates, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianPoint to be a `LIST [1:3] OF IfcLengthMeasure`")); } } while(0); return base; @@ -682,7 +682,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyLoop"); } do { // convert the 'Polygon' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Polygon, arg, db ); break; } + try { GenericConvert( in->Polygon, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyLoop to be a `LIST [3:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -716,14 +716,14 @@ template <> size_t GenericFill(const DB& db, const LIS std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextIdentifier, arg, db ); break; } + try { GenericConvert( in->ContextIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); do { // convert the 'ContextType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextType, arg, db ); break; } + try { GenericConvert( in->ContextType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); return base; @@ -735,27 +735,27 @@ template <> size_t GenericFill(const DB& db, if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcGeometricRepresentationContext"); } do { // convert the 'CoordinateSpaceDimension' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } + try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcGeometricRepresentationContext to be a `IfcDimensionCount`")); } } while(0); do { // convert the 'Precision' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Precision, arg, db ); break; } + try { GenericConvert( in->Precision, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcGeometricRepresentationContext to be a `REAL`")); } } while(0); do { // convert the 'WorldCoordinateSystem' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } + try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcGeometricRepresentationContext to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'TrueNorth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TrueNorth, arg, db ); break; } + try { GenericConvert( in->TrueNorth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcGeometricRepresentationContext to be a `IfcDirection`")); } } while(0); return base; @@ -774,12 +774,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcS if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcSIUnit"); } do { // convert the 'Prefix' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Prefix, arg, db ); break; } + try { GenericConvert( in->Prefix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSIUnit to be a `IfcSIPrefix`")); } } while(0); do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSIUnit to be a `IfcSIUnitName`")); } } while(0); return base; @@ -805,7 +805,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis1Placement"); } do { // convert the 'Axis' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis1Placement to be a `IfcDirection`")); } } while(0); return base; @@ -858,12 +858,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcRepresentationMap"); } do { // convert the 'MappingOrigin' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingOrigin, arg, db ); break; } + try { GenericConvert( in->MappingOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationMap to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'MappedRepresentation' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappedRepresentation, arg, db ); break; } + try { GenericConvert( in->MappedRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationMap to be a `IfcRepresentation`")); } } while(0); return base; @@ -1012,12 +1012,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMeasureWithUnit"); } do { // convert the 'ValueComponent' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ValueComponent, arg, db ); break; } + try { GenericConvert( in->ValueComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMeasureWithUnit to be a `IfcValue`")); } } while(0); do { // convert the 'UnitComponent' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->UnitComponent, arg, db ); break; } + try { GenericConvert( in->UnitComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMeasureWithUnit to be a `IfcUnit`")); } } while(0); return base; @@ -1125,7 +1125,7 @@ template <> size_t GenericFill(const DB& db, const LIS size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFaceBasedSurfaceModel"); } do { // convert the 'FbsmFaces' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->FbsmFaces, arg, db ); break; } + try { GenericConvert( in->FbsmFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBasedSurfaceModel to be a `SET [1:?] OF IfcConnectedFaceSet`")); } } while(0); return base; @@ -1172,13 +1172,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcFaceBound"); } do { // convert the 'Bound' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bound, arg, db ); break; } + try { GenericConvert( in->Bound, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBound to be a `IfcLoop`")); } } while(0); do { // convert the 'Orientation' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcFaceBound to be a `BOOLEAN`")); } } while(0); return base; @@ -1216,12 +1216,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcComplexProperty"); } do { // convert the 'UsageName' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->UsageName, arg, db ); break; } + try { GenericConvert( in->UsageName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcComplexProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'HasProperties' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcComplexProperty to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -1274,7 +1274,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcUnitAssignment"); } do { // convert the 'Units' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Units, arg, db ); break; } + try { GenericConvert( in->Units, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcUnitAssignment to be a `SET [1:?] OF IfcUnit`")); } } while(0); return base; @@ -1307,12 +1307,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcElementQuantity"); } do { // convert the 'MethodOfMeasurement' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } + try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcElementQuantity to be a `IfcLabel`")); } } while(0); do { // convert the 'Quantities' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Quantities, arg, db ); break; } + try { GenericConvert( in->Quantities, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcElementQuantity to be a `SET [1:?] OF IfcPhysicalQuantity`")); } } while(0); return base; @@ -1379,7 +1379,7 @@ template <> size_t GenericFill(const DB& db, con size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPresentationStyleAssignment"); } do { // convert the 'Styles' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyleAssignment to be a `SET [1:?] OF IfcPresentationStyleSelect`")); } } while(0); return base; @@ -1418,13 +1418,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSp size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 11) { throw STEP::TypeError("expected 11 arguments to IfcSpace"); } do { // convert the 'InteriorOrExteriorSpace' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->InteriorOrExteriorSpace, arg, db ); break; } + try { GenericConvert( in->InteriorOrExteriorSpace, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSpace to be a `IfcInternalOrExternalEnum`")); } } while(0); do { // convert the 'ElevationWithFlooring' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } + try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSpace to be a `IfcLengthMeasure`")); } } while(0); return base; @@ -1484,7 +1484,7 @@ template <> size_t GenericFill(const DB& db, const size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcSurfaceStyleWithTextures"); } do { // convert the 'Textures' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Textures, arg, db ); break; } + try { GenericConvert( in->Textures, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleWithTextures to be a `LIST [1:?] OF IfcSurfaceTexture`")); } } while(0); return base; @@ -1495,22 +1495,22 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcBoundingBox"); } do { // convert the 'Corner' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Corner, arg, db ); break; } + try { GenericConvert( in->Corner, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBoundingBox to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'XDim' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'ZDim' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ZDim, arg, db ); break; } + try { GenericConvert( in->ZDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1535,7 +1535,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcC size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCircle"); } do { // convert the 'Radius' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCircle to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1623,12 +1623,12 @@ template <> size_t GenericFill(const DB& db, const LIST& size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcConversionBasedUnit"); } do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcConversionBasedUnit to be a `IfcLabel`")); } } while(0); do { // convert the 'ConversionFactor' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ConversionFactor, arg, db ); break; } + try { GenericConvert( in->ConversionFactor, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcConversionBasedUnit to be a `IfcMeasureWithUnit`")); } } while(0); return base; @@ -1744,12 +1744,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcEllipse"); } do { // convert the 'SemiAxis1' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis1, arg, db ); break; } + try { GenericConvert( in->SemiAxis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'SemiAxis2' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis2, arg, db ); break; } + try { GenericConvert( in->SemiAxis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1816,7 +1816,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcPropertySet"); } do { // convert the 'HasProperties' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcPropertySet to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -1828,48 +1828,48 @@ template <> size_t GenericFill(const DB& db, const LIS if (params.GetSize() < 9) { throw STEP::TypeError("expected 9 arguments to IfcSurfaceStyleRendering"); } do { // convert the 'Transparency' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Transparency, arg, db ); break; } + try { GenericConvert( in->Transparency, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyleRendering to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'DiffuseColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseColour, arg, db ); break; } + try { GenericConvert( in->DiffuseColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'TransmissionColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TransmissionColour, arg, db ); break; } + try { GenericConvert( in->TransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'DiffuseTransmissionColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } + try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'ReflectionColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ReflectionColour, arg, db ); break; } + try { GenericConvert( in->ReflectionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularColour, arg, db ); break; } + try { GenericConvert( in->SpecularColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularHighlight' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularHighlight, arg, db ); break; } + try { GenericConvert( in->SpecularHighlight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSurfaceStyleRendering to be a `IfcSpecularHighlightSelect`")); } } while(0); do { // convert the 'ReflectanceMethod' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } + try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSurfaceStyleRendering to be a `IfcReflectanceMethodEnum`")); } } while(0); return base; diff --git a/code/AssetLib/IFC/IFCReaderGen_4.cpp b/code/AssetLib/IFC/IFCReaderGen_4.cpp index 9eb3e2446..179322902 100644 --- a/code/AssetLib/IFC/IFCReaderGen_4.cpp +++ b/code/AssetLib/IFC/IFCReaderGen_4.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -1254,28 +1254,28 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcRoo if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRoot"); } do { // convert the 'GlobalId' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->GlobalId, arg, db ); break; } + try { GenericConvert( in->GlobalId, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRoot to be a `IfcGloballyUniqueId`")); } } while(0); do { // convert the 'OwnerHistory' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OwnerHistory, arg, db ); break; } + try { GenericConvert( in->OwnerHistory, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRoot to be a `IfcOwnerHistory`")); } } while(0); do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRoot to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRoot to be a `IfcText`")); } } while(0); return base; @@ -1294,7 +1294,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcO boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectType, arg, db ); break; } + try { GenericConvert( in->ObjectType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcObject to be a `IfcLabel`")); } } while(0); return base; @@ -1328,14 +1328,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectPlacement, arg, db ); break; } + try { GenericConvert( in->ObjectPlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcProduct to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'Representation' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Representation, arg, db ); break; } + try { GenericConvert( in->Representation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcProduct to be a `IfcProductRepresentation`")); } } while(0); return base; @@ -1348,7 +1348,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Tag, arg, db ); break; } + try { GenericConvert( in->Tag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcElement to be a `IfcIdentifier`")); } } while(0); return base; @@ -1441,7 +1441,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcManifoldSolidBrep"); } do { // convert the 'Outer' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Outer, arg, db ); break; } + try { GenericConvert( in->Outer, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcManifoldSolidBrep to be a `IfcClosedShell`")); } } while(0); return base; @@ -1473,7 +1473,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcFac if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFace"); } do { // convert the 'Bounds' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bounds, arg, db ); break; } + try { GenericConvert( in->Bounds, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFace to be a `SET [1:?] OF IfcFaceBound`")); } } while(0); return base; @@ -1624,14 +1624,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProfileDef"); } do { // convert the 'ProfileType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ProfileType, arg, db ); break; } + try { GenericConvert( in->ProfileType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProfileDef to be a `IfcProfileTypeEnum`")); } } while(0); do { // convert the 'ProfileName' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ProfileName, arg, db ); break; } + try { GenericConvert( in->ProfileName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProfileDef to be a `IfcLabel`")); } } while(0); return base; @@ -1643,7 +1643,7 @@ template <> size_t GenericFill(const DB& db, const if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryClosedProfileDef"); } do { // convert the 'OuterCurve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->OuterCurve, arg, db ); break; } + try { GenericConvert( in->OuterCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryClosedProfileDef to be a `IfcCurve`")); } } while(0); return base; @@ -1655,7 +1655,7 @@ template <> size_t GenericFill(const DB& db, const L if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryOpenProfileDef"); } do { // convert the 'Curve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Curve, arg, db ); break; } + try { GenericConvert( in->Curve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryOpenProfileDef to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -1666,7 +1666,7 @@ template <> size_t GenericFill(const DB& db, co size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcArbitraryProfileDefWithVoids"); } do { // convert the 'InnerCurves' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->InnerCurves, arg, db ); break; } + try { GenericConvert( in->InnerCurves, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcArbitraryProfileDefWithVoids to be a `SET [1:?] OF IfcCurve`")); } } while(0); return base; @@ -1693,7 +1693,7 @@ template <> size_t GenericFill(const DB& db, const L boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcParameterizedProfileDef to be a `IfcAxis2Placement2D`")); } } while(0); return base; @@ -1726,7 +1726,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPlacement"); } do { // convert the 'Location' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Location, arg, db ); break; } + try { GenericConvert( in->Location, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPlacement to be a `IfcCartesianPoint`")); } } while(0); return base; @@ -1738,7 +1738,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis1Placement"); } do { // convert the 'Axis' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis1Placement to be a `IfcDirection`")); } } while(0); return base; @@ -1750,7 +1750,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis2Placement2D"); } do { // convert the 'RefDirection' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement2D to be a `IfcDirection`")); } } while(0); return base; @@ -1762,13 +1762,13 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcAxis2Placement3D"); } do { // convert the 'Axis' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); do { // convert the 'RefDirection' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); return base; @@ -1792,31 +1792,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcBSplineCurve"); } do { // convert the 'Degree' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Degree, arg, db ); break; } + try { GenericConvert( in->Degree, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBSplineCurve to be a `IfcInteger`")); } } while(0); do { // convert the 'ControlPointsList' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->ControlPointsList, arg, db ); break; } + try { GenericConvert( in->ControlPointsList, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBSplineCurve to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); do { // convert the 'CurveForm' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->CurveForm, arg, db ); break; } + try { GenericConvert( in->CurveForm, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBSplineCurve to be a `IfcBSplineCurveForm`")); } } while(0); do { // convert the 'ClosedCurve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->ClosedCurve, arg, db ); break; } + try { GenericConvert( in->ClosedCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBSplineCurve to be a `IfcLogical`")); } } while(0); do { // convert the 'SelfIntersect' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcBSplineCurve to be a `IfcLogical`")); } } while(0); return base; @@ -1930,19 +1930,19 @@ template <> size_t GenericFill(const DB& db, const LIST& param if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcBooleanResult"); } do { // convert the 'Operator' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Operator, arg, db ); break; } + try { GenericConvert( in->Operator, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBooleanResult to be a `IfcBooleanOperator`")); } } while(0); do { // convert the 'FirstOperand' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->FirstOperand, arg, db ); break; } + try { GenericConvert( in->FirstOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); do { // convert the 'SecondOperand' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->SecondOperand, arg, db ); break; } + try { GenericConvert( in->SecondOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); return base; @@ -1960,13 +1960,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCompositeCurve"); } do { // convert the 'Segments' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Segments, arg, db ); break; } + try { GenericConvert( in->Segments, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurve to be a `LIST [1:?] OF IfcCompositeCurveSegment`")); } } while(0); do { // convert the 'SelfIntersect' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurve to be a `IfcLogical`")); } } while(0); return base; @@ -1991,22 +1991,22 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcBoundingBox"); } do { // convert the 'Corner' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Corner, arg, db ); break; } + try { GenericConvert( in->Corner, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBoundingBox to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'XDim' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'ZDim' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->ZDim, arg, db ); break; } + try { GenericConvert( in->ZDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2018,13 +2018,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcHalfSpaceSolid"); } do { // convert the 'BaseSurface' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->BaseSurface, arg, db ); break; } + try { GenericConvert( in->BaseSurface, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcHalfSpaceSolid to be a `IfcSurface`")); } } while(0); do { // convert the 'AgreementFlag' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->AgreementFlag, arg, db ); break; } + try { GenericConvert( in->AgreementFlag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcHalfSpaceSolid to be a `IfcBoolean`")); } } while(0); return base; @@ -2044,7 +2044,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSpatialElement to be a `IfcLabel`")); } } while(0); return base; @@ -2057,7 +2057,7 @@ template <> size_t GenericFill(const DB& db, const L boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->CompositionType, arg, db ); break; } + try { GenericConvert( in->CompositionType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSpatialStructureElement to be a `IfcElementCompositionEnum`")); } } while(0); return base; @@ -2069,19 +2069,19 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 12) { throw STEP::TypeError("expected 12 arguments to IfcBuilding"); } do { // convert the 'ElevationOfRefHeight' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } + try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'ElevationOfTerrain' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } + try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'BuildingAddress' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->BuildingAddress, arg, db ); break; } + try { GenericConvert( in->BuildingAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcBuilding to be a `IfcPostalAddress`")); } } while(0); return base; @@ -2266,7 +2266,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcCartesianPoint"); } do { // convert the 'Coordinates' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Coordinates, arg, db ); break; } + try { GenericConvert( in->Coordinates, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianPoint to be a `LIST [1:3] OF IfcLengthMeasure`")); } } while(0); return base; @@ -2300,27 +2300,27 @@ template <> size_t GenericFill(const DB& db, boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis1, arg, db ); break; } + try { GenericConvert( in->Axis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'Axis2' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis2, arg, db ); break; } + try { GenericConvert( in->Axis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'LocalOrigin' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->LocalOrigin, arg, db ); break; } + try { GenericConvert( in->LocalOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCartesianTransformationOperator to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Scale' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale, arg, db ); break; } + try { GenericConvert( in->Scale, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCartesianTransformationOperator to be a `IfcReal`")); } } while(0); return base; @@ -2347,7 +2347,7 @@ template <> size_t GenericFill(const DB& d boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis3, arg, db ); break; } + try { GenericConvert( in->Axis3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCartesianTransformationOperator3D to be a `IfcDirection`")); } } while(0); return base; @@ -2359,13 +2359,13 @@ template <> size_t GenericFill(c if (params.GetSize() < 7) { throw STEP::TypeError("expected 7 arguments to IfcCartesianTransformationOperator3DnonUniform"); } do { // convert the 'Scale2' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale2, arg, db ); break; } + try { GenericConvert( in->Scale2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcCartesianTransformationOperator3DnonUniform to be a `IfcReal`")); } } while(0); do { // convert the 'Scale3' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale3, arg, db ); break; } + try { GenericConvert( in->Scale3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcCartesianTransformationOperator3DnonUniform to be a `IfcReal`")); } } while(0); return base; @@ -2412,7 +2412,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcCo if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConic"); } do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConic to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -2423,7 +2423,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcC size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCircle"); } do { // convert the 'Radius' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCircle to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2435,7 +2435,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcCircleProfileDef"); } do { // convert the 'Radius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCircleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2446,7 +2446,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcCircleHollowProfileDef"); } do { // convert the 'WallThickness' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->WallThickness, arg, db ); break; } + try { GenericConvert( in->WallThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCircleHollowProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2472,7 +2472,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConnectedFaceSet"); } do { // convert the 'CfsFaces' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CfsFaces, arg, db ); break; } + try { GenericConvert( in->CfsFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConnectedFaceSet to be a `SET [1:?] OF IfcFace`")); } } while(0); return base; @@ -2505,7 +2505,7 @@ template <> size_t GenericFill(const DB& db, const LIST& boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcColourSpecification to be a `IfcLabel`")); } } while(0); return base; @@ -2516,17 +2516,17 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcColourRgb"); } do { // convert the 'Red' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Red, arg, db ); break; } + try { GenericConvert( in->Red, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Green' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Green, arg, db ); break; } + try { GenericConvert( in->Green, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Blue' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Blue, arg, db ); break; } + try { GenericConvert( in->Blue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); return base; @@ -2579,14 +2579,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProperty"); } do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'Description' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProperty to be a `IfcText`")); } } while(0); return base; @@ -2597,12 +2597,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcComplexProperty"); } do { // convert the 'UsageName' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->UsageName, arg, db ); break; } + try { GenericConvert( in->UsageName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcComplexProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'HasProperties' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcComplexProperty to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -2620,19 +2620,19 @@ template <> size_t GenericFill(const DB& db, const LIS if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcCompositeCurveSegment"); } do { // convert the 'Transition' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Transition, arg, db ); break; } + try { GenericConvert( in->Transition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurveSegment to be a `IfcTransitionCode`")); } } while(0); do { // convert the 'SameSense' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->SameSense, arg, db ); break; } + try { GenericConvert( in->SameSense, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurveSegment to be a `IfcBoolean`")); } } while(0); do { // convert the 'ParentCurve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->ParentCurve, arg, db ); break; } + try { GenericConvert( in->ParentCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCompositeCurveSegment to be a `IfcCurve`")); } } while(0); return base; @@ -2764,35 +2764,35 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectType, arg, db ); break; } + try { GenericConvert( in->ObjectType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcContext to be a `IfcLabel`")); } } while(0); do { // convert the 'LongName' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcContext to be a `IfcLabel`")); } } while(0); do { // convert the 'Phase' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Phase, arg, db ); break; } + try { GenericConvert( in->Phase, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcContext to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationContexts' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationContexts, arg, db ); break; } + try { GenericConvert( in->RepresentationContexts, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcContext to be a `SET [1:?] OF IfcRepresentationContext`")); } } while(0); do { // convert the 'UnitsInContext' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->UnitsInContext, arg, db ); break; } + try { GenericConvert( in->UnitsInContext, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcContext to be a `IfcUnitAssignment`")); } } while(0); return base; @@ -2804,13 +2804,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcNamedUnit"); } do { // convert the 'Dimensions' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Dimensions, arg, db ); break; } + try { GenericConvert( in->Dimensions, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcNamedUnit to be a `IfcDimensionalExponents`")); } } while(0); do { // convert the 'UnitType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->UnitType, arg, db ); break; } + try { GenericConvert( in->UnitType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcNamedUnit to be a `IfcUnitEnum`")); } } while(0); return base; @@ -2843,13 +2843,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcConversionBasedUnit"); } do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcConversionBasedUnit to be a `IfcLabel`")); } } while(0); do { // convert the 'ConversionFactor' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->ConversionFactor, arg, db ); break; } + try { GenericConvert( in->ConversionFactor, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcConversionBasedUnit to be a `IfcMeasureWithUnit`")); } } while(0); return base; @@ -2974,7 +2974,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyle to be a `IfcLabel`")); } } while(0); return base; @@ -2986,7 +2986,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcElementarySurface"); } do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcElementarySurface to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -3025,7 +3025,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcDirection"); } do { // convert the 'DirectionRatios' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->DirectionRatios, arg, db ); break; } + try { GenericConvert( in->DirectionRatios, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcDirection to be a `LIST [2:3] OF IfcReal`")); } } while(0); return base; @@ -3094,35 +3094,35 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcDoo boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallHeight, arg, db ); break; } + try { GenericConvert( in->OverallHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallWidth' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'PredefinedType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PredefinedType, arg, db ); break; } + try { GenericConvert( in->PredefinedType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcDoor to be a `IfcDoorTypeEnum`")); } } while(0); do { // convert the 'OperationType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OperationType, arg, db ); break; } + try { GenericConvert( in->OperationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcDoor to be a `IfcDoorTypeOperationEnum`")); } } while(0); do { // convert the 'UserDefinedOperationType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->UserDefinedOperationType, arg, db ); break; } + try { GenericConvert( in->UserDefinedOperationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 12 to IfcDoor to be a `IfcLabel`")); } } while(0); return base; @@ -3362,12 +3362,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcElementQuantity"); } do { // convert the 'MethodOfMeasurement' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } + try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcElementQuantity to be a `IfcLabel`")); } } while(0); do { // convert the 'Quantities' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Quantities, arg, db ); break; } + try { GenericConvert( in->Quantities, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcElementQuantity to be a `SET [1:?] OF IfcPhysicalQuantity`")); } } while(0); return base; @@ -3378,12 +3378,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcEllipse"); } do { // convert the 'SemiAxis1' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis1, arg, db ); break; } + try { GenericConvert( in->SemiAxis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'SemiAxis2' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis2, arg, db ); break; } + try { GenericConvert( in->SemiAxis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -3486,14 +3486,14 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcSweptAreaSolid"); } do { // convert the 'SweptArea' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SweptArea, arg, db ); break; } + try { GenericConvert( in->SweptArea, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptAreaSolid to be a `IfcProfileDef`")); } } while(0); do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptAreaSolid to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -3505,13 +3505,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcExtrudedAreaSolid"); } do { // convert the 'ExtrudedDirection' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } + try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcExtrudedAreaSolid to be a `IfcDirection`")); } } while(0); do { // convert the 'Depth' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Depth, arg, db ); break; } + try { GenericConvert( in->Depth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcExtrudedAreaSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -3529,7 +3529,7 @@ template <> size_t GenericFill(const DB& db, const LIS size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFaceBasedSurfaceModel"); } do { // convert the 'FbsmFaces' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->FbsmFaces, arg, db ); break; } + try { GenericConvert( in->FbsmFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBasedSurfaceModel to be a `SET [1:?] OF IfcConnectedFaceSet`")); } } while(0); return base; @@ -3541,13 +3541,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcFaceBound"); } do { // convert the 'Bound' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bound, arg, db ); break; } + try { GenericConvert( in->Bound, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBound to be a `IfcLoop`")); } } while(0); do { // convert the 'Orientation' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcFaceBound to be a `IfcBoolean`")); } } while(0); return base; @@ -3774,14 +3774,14 @@ template <> size_t GenericFill(const DB& db, const LIS boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextIdentifier, arg, db ); break; } + try { GenericConvert( in->ContextIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); do { // convert the 'ContextType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextType, arg, db ); break; } + try { GenericConvert( in->ContextType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); return base; @@ -3793,27 +3793,27 @@ template <> size_t GenericFill(const DB& db, if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcGeometricRepresentationContext"); } do { // convert the 'CoordinateSpaceDimension' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } + try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcGeometricRepresentationContext to be a `IfcDimensionCount`")); } } while(0); do { // convert the 'Precision' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Precision, arg, db ); break; } + try { GenericConvert( in->Precision, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcGeometricRepresentationContext to be a `IfcReal`")); } } while(0); do { // convert the 'WorldCoordinateSystem' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } + try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcGeometricRepresentationContext to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'TrueNorth' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TrueNorth, arg, db ); break; } + try { GenericConvert( in->TrueNorth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcGeometricRepresentationContext to be a `IfcDirection`")); } } while(0); return base; @@ -3879,40 +3879,40 @@ template <> size_t GenericFill(const DB& db, const LIST& pa size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 10) { throw STEP::TypeError("expected 10 arguments to IfcIShapeProfileDef"); } do { // convert the 'OverallWidth' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallDepth' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->OverallDepth, arg, db ); break; } + try { GenericConvert( in->OverallDepth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'WebThickness' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->WebThickness, arg, db ); break; } + try { GenericConvert( in->WebThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FlangeThickness' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->FlangeThickness, arg, db ); break; } + try { GenericConvert( in->FlangeThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FilletRadius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FilletRadius, arg, db ); break; } + try { GenericConvert( in->FilletRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcIShapeProfileDef to be a `IfcNonNegativeLengthMeasure`")); } } while(0); do { // convert the 'FlangeEdgeRadius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FlangeEdgeRadius, arg, db ); break; } + try { GenericConvert( in->FlangeEdgeRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcIShapeProfileDef to be a `IfcNonNegativeLengthMeasure`")); } } while(0); do { // convert the 'FlangeSlope' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FlangeSlope, arg, db ); break; } + try { GenericConvert( in->FlangeSlope, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcIShapeProfileDef to be a `IfcPlaneAngleMeasure`")); } } while(0); return base; @@ -4091,12 +4091,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcLin size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLine"); } do { // convert the 'Pnt' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Pnt, arg, db ); break; } + try { GenericConvert( in->Pnt, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLine to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Dir' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Dir, arg, db ); break; } + try { GenericConvert( in->Dir, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLine to be a `IfcVector`")); } } while(0); return base; @@ -4108,12 +4108,12 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLocalPlacement"); } do { // convert the 'PlacementRelTo' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PlacementRelTo, arg, db ); break; } + try { GenericConvert( in->PlacementRelTo, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLocalPlacement to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'RelativePlacement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelativePlacement, arg, db ); break; } + try { GenericConvert( in->RelativePlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLocalPlacement to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -4124,12 +4124,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMappedItem"); } do { // convert the 'MappingSource' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingSource, arg, db ); break; } + try { GenericConvert( in->MappingSource, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMappedItem to be a `IfcRepresentationMap`")); } } while(0); do { // convert the 'MappingTarget' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingTarget, arg, db ); break; } + try { GenericConvert( in->MappingTarget, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMappedItem to be a `IfcCartesianTransformationOperator`")); } } while(0); return base; @@ -4142,20 +4142,20 @@ template <> size_t GenericFill(const DB& db, const LIS boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProductRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProductRepresentation to be a `IfcText`")); } } while(0); do { // convert the 'Representations' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->Representations, arg, db ); break; } + try { GenericConvert( in->Representations, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcProductRepresentation to be a `LIST [1:?] OF IfcRepresentation`")); } } while(0); return base; @@ -4173,12 +4173,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMeasureWithUnit"); } do { // convert the 'ValueComponent' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->ValueComponent, arg, db ); break; } + try { GenericConvert( in->ValueComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMeasureWithUnit to be a `IfcValue`")); } } while(0); do { // convert the 'UnitComponent' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->UnitComponent, arg, db ); break; } + try { GenericConvert( in->UnitComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMeasureWithUnit to be a `IfcUnit`")); } } while(0); return base; @@ -4289,7 +4289,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PredefinedType, arg, db ); break; } + try { GenericConvert( in->PredefinedType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcOpeningElement to be a `IfcOpeningElementTypeEnum`")); } } while(0); return base; @@ -4460,7 +4460,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyLoop"); } do { // convert the 'Polygon' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Polygon, arg, db ); break; } + try { GenericConvert( in->Polygon, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyLoop to be a `LIST [3:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -4471,12 +4471,12 @@ template <> size_t GenericFill(const DB& db, const size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPolygonalBoundedHalfSpace"); } do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPolygonalBoundedHalfSpace to be a `IfcAxis2Placement3D`")); } } while(0); do { // convert the 'PolygonalBoundary' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } + try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPolygonalBoundedHalfSpace to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -4501,7 +4501,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyline"); } do { // convert the 'Points' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Points, arg, db ); break; } + try { GenericConvert( in->Points, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyline to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -4512,7 +4512,7 @@ template <> size_t GenericFill(const DB& db, con size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPresentationStyleAssignment"); } do { // convert the 'Styles' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyleAssignment to be a `SET [1:?] OF IfcPresentationStyleSelect`")); } } while(0); return base; @@ -4592,13 +4592,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertyListValue"); } do { // convert the 'ListValues' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ListValues, arg, db ); break; } + try { GenericConvert( in->ListValues, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertyListValue to be a `LIST [1:?] OF IfcValue`")); } } while(0); do { // convert the 'Unit' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertyListValue to be a `IfcUnit`")); } } while(0); return base; @@ -4616,7 +4616,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcPropertySet"); } do { // convert the 'HasProperties' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcPropertySet to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -4628,13 +4628,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertySingleValue"); } do { // convert the 'NominalValue' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->NominalValue, arg, db ); break; } + try { GenericConvert( in->NominalValue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertySingleValue to be a `IfcValue`")); } } while(0); do { // convert the 'Unit' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertySingleValue to be a `IfcUnit`")); } } while(0); return base; @@ -4758,13 +4758,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcRectangleProfileDef"); } do { // convert the 'XDim' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -4850,12 +4850,12 @@ template <> size_t GenericFill(const DB& db, const LIST& param size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelAggregates"); } do { // convert the 'RelatingObject' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingObject, arg, db ); break; } + try { GenericConvert( in->RelatingObject, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelAggregates to be a `IfcObjectDefinition`")); } } while(0); do { // convert the 'RelatedObjects' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelAggregates to be a `SET [1:?] OF IfcObjectDefinition`")); } } while(0); return base; @@ -4872,12 +4872,12 @@ template <> size_t GenericFill(const DB& db, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelContainedInSpatialStructure"); } do { // convert the 'RelatedElements' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedElements, arg, db ); break; } + try { GenericConvert( in->RelatedElements, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelContainedInSpatialStructure to be a `SET [1:?] OF IfcProduct`")); } } while(0); do { // convert the 'RelatingStructure' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingStructure, arg, db ); break; } + try { GenericConvert( in->RelatingStructure, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelContainedInSpatialStructure to be a `IfcSpatialElement`")); } } while(0); return base; @@ -4894,12 +4894,12 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelDefinesByProperties"); } do { // convert the 'RelatedObjects' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelDefinesByProperties to be a `SET [1:?] OF IfcObjectDefinition`")); } } while(0); do { // convert the 'RelatingPropertyDefinition' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } + try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelDefinesByProperties to be a `IfcPropertySetDefinitionSelect`")); } } while(0); return base; @@ -4910,12 +4910,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelFillsElement"); } do { // convert the 'RelatingOpeningElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelFillsElement to be a `IfcOpeningElement`")); } } while(0); do { // convert the 'RelatedBuildingElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelFillsElement to be a `IfcElement`")); } } while(0); return base; @@ -4926,12 +4926,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelVoidsElement"); } do { // convert the 'RelatingBuildingElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelVoidsElement to be a `IfcElement`")); } } while(0); do { // convert the 'RelatedOpeningElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelVoidsElement to be a `IfcFeatureElementSubtraction`")); } } while(0); return base; @@ -4950,27 +4950,27 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRepresentation"); } do { // convert the 'ContextOfItems' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ContextOfItems, arg, db ); break; } + try { GenericConvert( in->ContextOfItems, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentation to be a `IfcRepresentationContext`")); } } while(0); do { // convert the 'RepresentationIdentifier' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } + try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationType, arg, db ); break; } + try { GenericConvert( in->RepresentationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Items' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->Items, arg, db ); break; } + try { GenericConvert( in->Items, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRepresentation to be a `SET [1:?] OF IfcRepresentationItem`")); } } while(0); return base; @@ -4981,12 +4981,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcRepresentationMap"); } do { // convert the 'MappingOrigin' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingOrigin, arg, db ); break; } + try { GenericConvert( in->MappingOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationMap to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'MappedRepresentation' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappedRepresentation, arg, db ); break; } + try { GenericConvert( in->MappedRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationMap to be a `IfcRepresentation`")); } } while(0); return base; @@ -4998,13 +4998,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRevolvedAreaSolid"); } do { // convert the 'Axis' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRevolvedAreaSolid to be a `IfcAxis1Placement`")); } } while(0); do { // convert the 'Angle' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Angle, arg, db ); break; } + try { GenericConvert( in->Angle, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRevolvedAreaSolid to be a `IfcPlaneAngleMeasure`")); } } while(0); return base; @@ -5058,12 +5058,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcS if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcSIUnit"); } do { // convert the 'Prefix' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Prefix, arg, db ); break; } + try { GenericConvert( in->Prefix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSIUnit to be a `IfcSIPrefix`")); } } while(0); do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSIUnit to be a `IfcSIUnitName`")); } } while(0); return base; @@ -5144,7 +5144,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcShellBasedSurfaceModel"); } do { // convert the 'SbsmBoundary' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SbsmBoundary, arg, db ); break; } + try { GenericConvert( in->SbsmBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcShellBasedSurfaceModel to be a `SET [1:?] OF IfcShell`")); } } while(0); return base; @@ -5156,31 +5156,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSit if (params.GetSize() < 14) { throw STEP::TypeError("expected 14 arguments to IfcSite"); } do { // convert the 'RefLatitude' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLatitude, arg, db ); break; } + try { GenericConvert( in->RefLatitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefLongitude' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLongitude, arg, db ); break; } + try { GenericConvert( in->RefLongitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefElevation' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefElevation, arg, db ); break; } + try { GenericConvert( in->RefElevation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcSite to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'LandTitleNumber' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LandTitleNumber, arg, db ); break; } + try { GenericConvert( in->LandTitleNumber, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 12 to IfcSite to be a `IfcLabel`")); } } while(0); do { // convert the 'SiteAddress' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SiteAddress, arg, db ); break; } + try { GenericConvert( in->SiteAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 13 to IfcSite to be a `IfcPostalAddress`")); } } while(0); return base; @@ -5234,13 +5234,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSp if (params.GetSize() < 11) { throw STEP::TypeError("expected 11 arguments to IfcSpace"); } do { // convert the 'PredefinedType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PredefinedType, arg, db ); break; } + try { GenericConvert( in->PredefinedType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSpace to be a `IfcSpaceTypeEnum`")); } } while(0); do { // convert the 'ElevationWithFlooring' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } + try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSpace to be a `IfcLengthMeasure`")); } } while(0); return base; @@ -5539,18 +5539,18 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcStyledItem"); } do { // convert the 'Item' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Item, arg, db ); break; } + try { GenericConvert( in->Item, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcStyledItem to be a `IfcRepresentationItem`")); } } while(0); do { // convert the 'Styles' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcStyledItem to be a `SET [1:?] OF IfcStyleAssignmentSelect`")); } } while(0); do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcStyledItem to be a `IfcLabel`")); } } while(0); return base; @@ -5624,12 +5624,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcSurfaceStyle"); } do { // convert the 'Side' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Side, arg, db ); break; } + try { GenericConvert( in->Side, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyle to be a `IfcSurfaceSide`")); } } while(0); do { // convert the 'Styles' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyle to be a `SET [1:5] OF IfcSurfaceStyleElementSelect`")); } } while(0); return base; @@ -5641,14 +5641,14 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcSurfaceStyleShading"); } do { // convert the 'SurfaceColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SurfaceColour, arg, db ); break; } + try { GenericConvert( in->SurfaceColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleShading to be a `IfcColourRgb`")); } } while(0); do { // convert the 'Transparency' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Transparency, arg, db ); break; } + try { GenericConvert( in->Transparency, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyleShading to be a `IfcNormalisedRatioMeasure`")); } } while(0); return base; @@ -5660,42 +5660,42 @@ template <> size_t GenericFill(const DB& db, const LIS if (params.GetSize() < 9) { throw STEP::TypeError("expected 9 arguments to IfcSurfaceStyleRendering"); } do { // convert the 'DiffuseColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseColour, arg, db ); break; } + try { GenericConvert( in->DiffuseColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'TransmissionColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TransmissionColour, arg, db ); break; } + try { GenericConvert( in->TransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'DiffuseTransmissionColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } + try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'ReflectionColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ReflectionColour, arg, db ); break; } + try { GenericConvert( in->ReflectionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularColour, arg, db ); break; } + try { GenericConvert( in->SpecularColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularHighlight' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularHighlight, arg, db ); break; } + try { GenericConvert( in->SpecularHighlight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSurfaceStyleRendering to be a `IfcSpecularHighlightSelect`")); } } while(0); do { // convert the 'ReflectanceMethod' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } + try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSurfaceStyleRendering to be a `IfcReflectanceMethodEnum`")); } } while(0); return base; @@ -5706,7 +5706,7 @@ template <> size_t GenericFill(const DB& db, const size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcSurfaceStyleWithTextures"); } do { // convert the 'Textures' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Textures, arg, db ); break; } + try { GenericConvert( in->Textures, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleWithTextures to be a `LIST [1:?] OF IfcSurfaceTexture`")); } } while(0); return base; @@ -5718,34 +5718,34 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcSweptDiskSolid"); } do { // convert the 'Directrix' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Directrix, arg, db ); break; } + try { GenericConvert( in->Directrix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptDiskSolid to be a `IfcCurve`")); } } while(0); do { // convert the 'Radius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'InnerRadius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->InnerRadius, arg, db ); break; } + try { GenericConvert( in->InnerRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'StartParam' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->StartParam, arg, db ); break; } + try { GenericConvert( in->StartParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); do { // convert the 'EndParam' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->EndParam, arg, db ); break; } + try { GenericConvert( in->EndParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); return base; @@ -5924,27 +5924,27 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcTrimmedCurve"); } do { // convert the 'BasisCurve' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->BasisCurve, arg, db ); break; } + try { GenericConvert( in->BasisCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcTrimmedCurve to be a `IfcCurve`")); } } while(0); do { // convert the 'Trim1' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim1, arg, db ); break; } + try { GenericConvert( in->Trim1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'Trim2' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim2, arg, db ); break; } + try { GenericConvert( in->Trim2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'SenseAgreement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SenseAgreement, arg, db ); break; } + try { GenericConvert( in->SenseAgreement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcTrimmedCurve to be a `IfcBoolean`")); } } while(0); do { // convert the 'MasterRepresentation' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MasterRepresentation, arg, db ); break; } + try { GenericConvert( in->MasterRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcTrimmedCurve to be a `IfcTrimmingPreference`")); } } while(0); return base; @@ -5976,7 +5976,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcUnitAssignment"); } do { // convert the 'Units' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Units, arg, db ); break; } + try { GenericConvert( in->Units, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcUnitAssignment to be a `SET [1:?] OF IfcUnit`")); } } while(0); return base; @@ -6029,12 +6029,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcV size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcVector"); } do { // convert the 'Orientation' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcVector to be a `IfcDirection`")); } } while(0); do { // convert the 'Magnitude' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Magnitude, arg, db ); break; } + try { GenericConvert( in->Magnitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcVector to be a `IfcLengthMeasure`")); } } while(0); return base; diff --git a/code/AssetLib/IFC/IFCReaderGen_4.h b/code/AssetLib/IFC/IFCReaderGen_4.h index 0f184cd02..abf021911 100644 --- a/code/AssetLib/IFC/IFCReaderGen_4.h +++ b/code/AssetLib/IFC/IFCReaderGen_4.h @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -51,12 +51,12 @@ namespace Schema_4 { using namespace STEP; using namespace STEP::EXPRESS; - - + + struct NotImplemented : public ObjectHelper { - + }; - + // ****************************************************************************** // IFC Custom data types diff --git a/code/AssetLib/Irr/IRRLoader.h b/code/AssetLib/Irr/IRRLoader.h index 535f6481d..da90902ed 100644 --- a/code/AssetLib/Irr/IRRLoader.h +++ b/code/AssetLib/Irr/IRRLoader.h @@ -273,7 +273,7 @@ private: std::vector& anims); private: - /// Configuration option: desired output FPS + /// Configuration option: desired output FPS double fps; /// Configuration option: speed flag was set? diff --git a/code/AssetLib/LWO/LWOAnimation.h b/code/AssetLib/LWO/LWOAnimation.h index 1ed8caf88..64aa5980a 100644 --- a/code/AssetLib/LWO/LWOAnimation.h +++ b/code/AssetLib/LWO/LWOAnimation.h @@ -114,7 +114,7 @@ enum PrePostBehaviour /** \brief Data structure for a LWO animation keyframe */ struct Key { - Key() AI_NO_EXCEPT + Key() AI_NO_EXCEPT : time() , value() , inter(IT_LINE) diff --git a/code/AssetLib/LWS/LWSLoader.cpp b/code/AssetLib/LWS/LWSLoader.cpp index 01a50b6e4..cb07787fa 100644 --- a/code/AssetLib/LWS/LWSLoader.cpp +++ b/code/AssetLib/LWS/LWSLoader.cpp @@ -200,7 +200,7 @@ void LWSImporter::ReadEnvelope(const LWS::Element &dad, LWO::Envelope &fill) { // reserve enough storage std::list::const_iterator it = dad.children.begin(); - + fill.keys.reserve(strtoul10(it->tokens[1].c_str())); for (++it; it != dad.children.end(); ++it) { @@ -466,7 +466,7 @@ std::string LWSImporter::FindLWOFile(const std::string &in) { std::string tmp(in); if (in.length() > 3 && in[1] == ':' && in[2] != '\\' && in[2] != '/') { tmp = in[0] + (std::string(":\\") + in.substr(2)); - } + } if (io->Exists(tmp)) { return in; diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index ba838d71d..dcb82a83a 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -84,11 +84,11 @@ public: void reset(); // The Name access, empty string returned when no m3d instance. - std::string Name() const; + std::string Name() const; /// Executes a save. unsigned char *Save(int quality, int flags, unsigned int &size); - + /// Clearer void ClearSave(); @@ -113,16 +113,16 @@ inline std::string M3DWrapper::Name() const { return std::string(); } -inline M3DWrapper::operator bool() const { - return m3d_ != nullptr; +inline M3DWrapper::operator bool() const { + return m3d_ != nullptr; } inline m3d_t *M3DWrapper::operator->() const { - return m3d_; + return m3d_; } inline m3d_t *M3DWrapper::M3D() const { - return m3d_; + return m3d_; } } // namespace Assimp diff --git a/code/AssetLib/M3D/m3d.h b/code/AssetLib/M3D/m3d.h index 28bf19482..3adcd5bef 100644 --- a/code/AssetLib/M3D/m3d.h +++ b/code/AssetLib/M3D/m3d.h @@ -829,7 +829,7 @@ unsigned char *_m3dstbi_zlib_compress(unsigned char *data, int data_len, int *ou #include #endif -#if !defined(M3D_NOIMPORTER) +#if !defined(M3D_NOIMPORTER) /* helper functions for the ASCII parser */ static char *_m3d_findarg(char *s) { while (s && *s && *s != ' ' && *s != '\t' && *s != '\r' && *s != '\n') @@ -4516,7 +4516,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size } if (length) { uint32_t v = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t *)h + len)); - memcpy( length, &v, sizeof(uint32_t)); + memcpy( length, &v, sizeof(uint32_t)); len += v; } out = NULL; @@ -4548,7 +4548,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size } } uint32_t v = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t *)h + len)); - memcpy( length, &v, sizeof(uint32_t)); + memcpy( length, &v, sizeof(uint32_t)); len += v; out = NULL; } diff --git a/code/AssetLib/MD5/MD5Loader.cpp b/code/AssetLib/MD5/MD5Loader.cpp index 0d9c77b71..9fba60c61 100644 --- a/code/AssetLib/MD5/MD5Loader.cpp +++ b/code/AssetLib/MD5/MD5Loader.cpp @@ -485,7 +485,7 @@ void MD5Importer::LoadMD5MeshFile() { } MD5::WeightDesc &weightDesc = meshSrc.mWeights[w]; - if (weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON) { + if (weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON) { continue; } diff --git a/code/AssetLib/MDC/MDCFileData.h b/code/AssetLib/MDC/MDCFileData.h index 616556f03..a8f3aea43 100644 --- a/code/AssetLib/MDC/MDCFileData.h +++ b/code/AssetLib/MDC/MDCFileData.h @@ -120,13 +120,13 @@ struct Surface { , ulFlags() , ulNumCompFrames() , ulNumBaseFrames() - , ulNumShaders() + , ulNumShaders() , ulNumVertices() , ulNumTriangles() , ulOffsetTriangles() , ulOffsetShaders() , ulOffsetTexCoords() - , ulOffsetBaseVerts() + , ulOffsetBaseVerts() , ulOffsetCompVerts() , ulOffsetFrameBaseFrames() , ulOffsetFrameCompFrames() diff --git a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp index e6576b344..1ff86fe27 100644 --- a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp +++ b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp @@ -629,7 +629,7 @@ void HL1MDLLoader::read_meshes() { +-- bodypart --+-- model -- [mesh index, mesh index, ...] | | | +-- model -- [mesh index, mesh index, ...] - | | + | | | ... | |-- bodypart -- ... @@ -1298,7 +1298,7 @@ void HL1MDLLoader::read_global_info() { * @note The structure of this method is taken from HL2 source code. * Although this is from HL2, it's implementation is almost identical * to code found in HL1 SDK. See HL1 and HL2 SDKs for more info. -* +* * source: * HL1 source code. * file: studio_render.cpp diff --git a/code/AssetLib/MMD/MMDPmxParser.cpp b/code/AssetLib/MMD/MMDPmxParser.cpp index d57dc169a..82cc75ca2 100644 --- a/code/AssetLib/MMD/MMDPmxParser.cpp +++ b/code/AssetLib/MMD/MMDPmxParser.cpp @@ -102,7 +102,7 @@ namespace pmx const unsigned int targetSize = size * 3; // enough to encode char *targetStart = new char[targetSize]; std::memset(targetStart, 0, targetSize * sizeof(char)); - + utf8::utf16to8( sourceStart, sourceStart + size/2, targetStart ); std::string result(targetStart); diff --git a/code/AssetLib/OFF/OFFLoader.cpp b/code/AssetLib/OFF/OFFLoader.cpp index 360ffc51b..fb8f3b424 100644 --- a/code/AssetLib/OFF/OFFLoader.cpp +++ b/code/AssetLib/OFF/OFFLoader.cpp @@ -138,7 +138,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS const char* car = buffer; const char* end = buffer + mBuffer2.size(); NextToken(&car, end); - + if (car < end - 2 && car[0] == 'S' && car[1] == 'T') { hasTexCoord = true; car += 2; } @@ -164,7 +164,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS dimensions = 3; hasHomogenous = false; NextToken(&car, end); - + // at this point the next token should be an integer number if (car >= end - 1 || *car < '0' || *car > '9') { throw DeadlyImportError("OFF: Header is invalid"); @@ -223,7 +223,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS ASSIMP_LOG_ERROR("OFF: The number of verts in the header is incorrect"); break; } - aiVector3D& v = mesh->mVertices[i]; + aiVector3D& v = mesh->mVertices[i]; sz = line; // helper array to write a for loop over possible dimension values @@ -255,7 +255,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS SkipSpaces(&sz); fast_atoreal_move(sz,(ai_real&)n.z); } - + // reading colors is a pain because the specification says it can be // integers or floats, and any number of them between 1 and 4 included, // until the next comment or end of line @@ -321,7 +321,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS ++i; ++faces; } - + // generate the output node graph pScene->mRootNode = new aiNode(); pScene->mRootNode->mName.Set(""); diff --git a/code/AssetLib/Obj/ObjExporter.h b/code/AssetLib/Obj/ObjExporter.h index 3a46da780..a64f38f74 100644 --- a/code/AssetLib/Obj/ObjExporter.h +++ b/code/AssetLib/Obj/ObjExporter.h @@ -67,7 +67,7 @@ public: ~ObjExporter(); std::string GetMaterialLibName(); std::string GetMaterialLibFileName(); - + /// public string-streams to write all output into std::ostringstream mOutput, mOutputMat; @@ -137,13 +137,13 @@ private: } }; - struct aiVectorCompare { - bool operator() (const aiVector3D& a, const aiVector3D& b) const { - if(a.x < b.x) return true; - if(a.x > b.x) return false; - if(a.y < b.y) return true; - if(a.y > b.y) return false; - if(a.z < b.z) return true; + struct aiVectorCompare { + bool operator() (const aiVector3D& a, const aiVector3D& b) const { + if(a.x < b.x) return true; + if(a.x > b.x) return false; + if(a.y < b.y) return true; + if(a.y > b.y) return false; + if(a.z < b.z) return true; return false; } }; @@ -153,7 +153,7 @@ private: int mNextIndex; typedef std::map dataType; dataType vecMap; - + public: indexMap() : mNextIndex(1) { diff --git a/code/AssetLib/Obj/ObjFileMtlImporter.cpp b/code/AssetLib/Obj/ObjFileMtlImporter.cpp index bf1b70c90..94e57c26b 100644 --- a/code/AssetLib/Obj/ObjFileMtlImporter.cpp +++ b/code/AssetLib/Obj/ObjFileMtlImporter.cpp @@ -146,7 +146,7 @@ void ObjFileMtlImporter::load() { ++m_DataIt; ai_real d; getFloatValue(d); - m_pModel->m_pCurrentMaterial->alpha = static_cast(1.0) - d; + m_pModel->m_pCurrentMaterial->alpha = static_cast(1.0) - d; } m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); } break; diff --git a/code/AssetLib/SMD/SMDLoader.cpp b/code/AssetLib/SMD/SMDLoader.cpp index de9c65c4a..90f0b7697 100644 --- a/code/AssetLib/SMD/SMDLoader.cpp +++ b/code/AssetLib/SMD/SMDLoader.cpp @@ -438,7 +438,7 @@ void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent) { pc->mTransformation = bone.sAnim.asKeys[0].matrix; } - if (bone.iParent == static_cast(-1)) { + if (bone.iParent == static_cast(-1)) { bone.mOffsetMatrix = pc->mTransformation; } else { bone.mOffsetMatrix = asBones[bone.iParent].mOffsetMatrix * pc->mTransformation; diff --git a/code/AssetLib/STL/STLExporter.cpp b/code/AssetLib/STL/STLExporter.cpp index bd4d96c71..59c6148ee 100644 --- a/code/AssetLib/STL/STLExporter.cpp +++ b/code/AssetLib/STL/STLExporter.cpp @@ -69,7 +69,7 @@ void ExportSceneSTL(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene if (exporter.mOutput.fail()) { throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); } - + // we're still here - export successfully completed. Write the file. std::unique_ptr outfile (pIOSystem->Open(pFile,"wt")); if (outfile == nullptr) { @@ -88,7 +88,7 @@ void ExportSceneSTLBinary(const char* pFile,IOSystem* pIOSystem, const aiScene* if (exporter.mOutput.fail()) { throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); } - + // we're still here - export successfully completed. Write the file. std::unique_ptr outfile (pIOSystem->Open(pFile,"wb")); if (outfile == nullptr) { @@ -139,9 +139,9 @@ STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool expo if (exportPointClouds) { WritePointCloud("Assimp_Pointcloud", pScene ); return; - } + } - // Export the assimp mesh + // Export the assimp mesh const std::string name = "AssimpScene"; mOutput << SolidToken << " " << name << endl; for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { diff --git a/code/AssetLib/STL/STLLoader.cpp b/code/AssetLib/STL/STLLoader.cpp index 433fb14c7..8cfe63e0d 100644 --- a/code/AssetLib/STL/STLLoader.cpp +++ b/code/AssetLib/STL/STLLoader.cpp @@ -372,7 +372,7 @@ void STLImporter::LoadASCIIFile(aiNode *root) { pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; for (size_t i=0; imNumVertices; ++i ) { pMesh->mVertices[i].x = positionBuffer[i].x; - pMesh->mVertices[i].y = positionBuffer[i].y; + pMesh->mVertices[i].y = positionBuffer[i].y; pMesh->mVertices[i].z = positionBuffer[i].z; } positionBuffer.clear(); @@ -382,7 +382,7 @@ void STLImporter::LoadASCIIFile(aiNode *root) { pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; for (size_t i=0; imNumVertices; ++i ) { pMesh->mNormals[i].x = normalBuffer[i].x; - pMesh->mNormals[i].y = normalBuffer[i].y; + pMesh->mNormals[i].y = normalBuffer[i].y; pMesh->mNormals[i].z = normalBuffer[i].z; } normalBuffer.clear(); diff --git a/code/AssetLib/X/XFileExporter.cpp b/code/AssetLib/X/XFileExporter.cpp index bd997a3c5..b95cb7abf 100644 --- a/code/AssetLib/X/XFileExporter.cpp +++ b/code/AssetLib/X/XFileExporter.cpp @@ -86,7 +86,7 @@ void ExportSceneXFile(const char* pFile,IOSystem* pIOSystem, const aiScene* pSce if (iDoTheExportThing.mOutput.fail()) { throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); } - + // we're still here - export successfully completed. Write result to the given IOSYstem std::unique_ptr outfile (pIOSystem->Open(pFile,"wt")); if (outfile == nullptr) { diff --git a/code/AssetLib/X/XFileExporter.h b/code/AssetLib/X/XFileExporter.h index 32b75b3d1..620d282b6 100644 --- a/code/AssetLib/X/XFileExporter.h +++ b/code/AssetLib/X/XFileExporter.h @@ -94,9 +94,9 @@ protected: void PushTag() { startstr.append( " "); } /// Leaves an element, decreasing the indentation - void PopTag() { - ai_assert( startstr.length() > 1); - startstr.erase( startstr.length() - 2); + void PopTag() { + ai_assert( startstr.length() > 1); + startstr.erase( startstr.length() - 2); } public: diff --git a/code/AssetLib/X3D/X3DImporter.cpp b/code/AssetLib/X3D/X3DImporter.cpp index 121b7490e..71c35c369 100644 --- a/code/AssetLib/X3D/X3DImporter.cpp +++ b/code/AssetLib/X3D/X3DImporter.cpp @@ -180,6 +180,6 @@ const aiImporterDesc *X3DImporter::GetInfo() const { return &Description; } -} +} #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/XGL/XGLLoader.cpp b/code/AssetLib/XGL/XGLLoader.cpp index 3b84d7ba9..20c2c7079 100644 --- a/code/AssetLib/XGL/XGLLoader.cpp +++ b/code/AssetLib/XGL/XGLLoader.cpp @@ -240,7 +240,7 @@ void XGLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy void XGLImporter::ReadWorld(XmlNode &node, TempScope &scope) { for (XmlNode ¤tNode : node.children()) { const std::string &s = ai_stdStrToLower(currentNode.name()); - + // XXX right now we'd skip if it comes after // or if (s == "lighting") { diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index bb97e3732..cd6c1c89d 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -388,9 +388,9 @@ struct CustomExtension { } CustomExtension() = default; - + ~CustomExtension() = default; - + CustomExtension(const CustomExtension &other) : name(other.name), mStringValue(other.mStringValue), diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 58095c7bd..d5f5b5cd0 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -410,14 +410,14 @@ inline void SetDecodedIndexBuffer_Draco(const draco::Mesh &dracoMesh, Mesh::Prim // Not same size, convert switch (componentBytes) { - case sizeof(uint32_t): - CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); + case sizeof(uint32_t): + CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); break; - case sizeof(uint16_t): - CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); + case sizeof(uint16_t): + CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); break; - case sizeof(uint8_t): - CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); + case sizeof(uint8_t): + CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); break; default: ai_assert(false); @@ -460,23 +460,23 @@ inline void SetDecodedAttributeBuffer_Draco(const draco::Mesh &dracoMesh, uint32 decodedAttribBuffer->Grow(dracoMesh.num_points() * pDracoAttribute->num_components() * componentBytes); switch (accessor.componentType) { - case ComponentType_BYTE: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_BYTE: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; case ComponentType_UNSIGNED_BYTE: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; case ComponentType_SHORT: GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; - case ComponentType_UNSIGNED_SHORT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_UNSIGNED_SHORT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; - case ComponentType_UNSIGNED_INT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_UNSIGNED_INT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; - case ComponentType_FLOAT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_FLOAT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; default: ai_assert(false); @@ -840,7 +840,7 @@ inline bool Buffer::ReplaceData_joint(const size_t pBufferData_Offset, const siz inline size_t Buffer::AppendData(uint8_t *data, size_t length) { const size_t offset = this->byteLength; - + // Force alignment to 4 bits const size_t paddedLength = (length + 3) & ~3; Grow(paddedLength); diff --git a/code/AssetLib/glTF2/glTF2AssetWriter.inl b/code/AssetLib/glTF2/glTF2AssetWriter.inl index bf7dbbb2e..115cdf903 100644 --- a/code/AssetLib/glTF2/glTF2AssetWriter.inl +++ b/code/AssetLib/glTF2/glTF2AssetWriter.inl @@ -452,7 +452,7 @@ namespace glTF2 { WriteTex(materialClearcoat, clearcoat.clearcoatTexture, "clearcoatTexture", w.mAl); WriteTex(materialClearcoat, clearcoat.clearcoatRoughnessTexture, "clearcoatRoughnessTexture", w.mAl); WriteTex(materialClearcoat, clearcoat.clearcoatNormalTexture, "clearcoatNormalTexture", w.mAl); - + if (!materialClearcoat.ObjectEmpty()) { exts.AddMember("KHR_materials_clearcoat", materialClearcoat, w.mAl); } @@ -468,7 +468,7 @@ namespace glTF2 { } WriteTex(materialTransmission, transmission.transmissionTexture, "transmissionTexture", w.mAl); - + if (!materialTransmission.ObjectEmpty()) { exts.AddMember("KHR_materials_transmission", materialTransmission, w.mAl); } @@ -613,7 +613,7 @@ namespace glTF2 { if (n.skin) { obj.AddMember("skin", n.skin->index, w.mAl); } - + //gltf2 spec does not support "skeletons" under node if(n.skeletons.size()) { AddRefsVector(obj, "skeletons", n.skeletons, w.mAl); @@ -711,7 +711,7 @@ namespace glTF2 { if (mAsset.scene) { mDoc.AddMember("scene", mAsset.scene->index, mAl); } - + if(mAsset.extras) { mDoc.AddMember("extras", *mAsset.extras, mAl); } @@ -812,7 +812,7 @@ namespace glTF2 { uint32_t binaryChunkLength = 0; if (bodyBuffer->byteLength > 0) { binaryChunkLength = (bodyBuffer->byteLength + 3) & ~3; // Round up to next multiple of 4 - + auto curPaddingLength = binaryChunkLength - bodyBuffer->byteLength; ++GLB_Chunk_count; @@ -881,7 +881,7 @@ namespace glTF2 { if (this->mAsset.extensionsUsed.KHR_materials_sheen) { exts.PushBack(StringRef("KHR_materials_sheen"), mAl); } - + if (this->mAsset.extensionsUsed.KHR_materials_clearcoat) { exts.PushBack(StringRef("KHR_materials_clearcoat"), mAl); } @@ -893,7 +893,7 @@ namespace glTF2 { if (this->mAsset.extensionsUsed.FB_ngon_encoding) { exts.PushBack(StringRef("FB_ngon_encoding"), mAl); } - + if (this->mAsset.extensionsUsed.KHR_texture_basisu) { exts.PushBack(StringRef("KHR_texture_basisu"), mAl); } @@ -901,7 +901,7 @@ namespace glTF2 { if (!exts.Empty()) mDoc.AddMember("extensionsUsed", exts, mAl); - + //basisu extensionRequired Value extsReq; extsReq.SetArray(); diff --git a/code/Common/DefaultIOStream.cpp b/code/Common/DefaultIOStream.cpp index ba9b9d625..557fbc78f 100644 --- a/code/Common/DefaultIOStream.cpp +++ b/code/Common/DefaultIOStream.cpp @@ -62,7 +62,7 @@ inline int select_fseek(FILE *file, int64_t offset, int origin) { } - + #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) template <> inline size_t select_ftell<8>(FILE *file) { @@ -75,7 +75,7 @@ inline int select_fseek<8>(FILE *file, int64_t offset, int origin) { } #endif // #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) - + } // namespace // ---------------------------------------------------------------------------------- @@ -95,7 +95,7 @@ size_t DefaultIOStream::Read(void *pvBuffer, } ai_assert(nullptr != pvBuffer); ai_assert(0 != pSize); - + return (mFile ? ::fread(pvBuffer, pSize, pCount, mFile) : 0); } diff --git a/code/Common/DefaultIOSystem.cpp b/code/Common/DefaultIOSystem.cpp index 98d51a17d..1ed615b84 100644 --- a/code/Common/DefaultIOSystem.cpp +++ b/code/Common/DefaultIOSystem.cpp @@ -71,7 +71,7 @@ static std::wstring Utf8ToWide(const char *in) { // size includes terminating null; std::wstring adds null automatically std::wstring out(static_cast(size) - 1, L'\0'); MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], size); - + return out; } @@ -85,7 +85,7 @@ static std::string WideToUtf8(const wchar_t *in) { // size includes terminating null; std::string adds null automatically std::string out(static_cast(size) - 1, '\0'); WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], size, nullptr, nullptr); - + return out; } #endif @@ -121,7 +121,7 @@ IOStream *DefaultIOSystem::Open(const char *strFile, const char *strMode) { if (name.empty()) { return nullptr; } - + file = ::_wfopen(name.c_str(), Utf8ToWide(strMode).c_str()); #else file = ::fopen(strFile, strMode); diff --git a/code/Common/Exporter.cpp b/code/Common/Exporter.cpp index ebcc955df..512bbf447 100644 --- a/code/Common/Exporter.cpp +++ b/code/Common/Exporter.cpp @@ -83,7 +83,7 @@ namespace Assimp { void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out); // ------------------------------------------------------------------------------------------------ -// Exporter worker function prototypes. Do not use const, because some exporter need to convert +// Exporter worker function prototypes. Do not use const, because some exporter need to convert // the scene temporary #ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER void ExportSceneCollada(const char*,IOSystem*, const aiScene*, const ExportProperties*); @@ -343,7 +343,7 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha delete pimpl->blob; pimpl->blob = nullptr; } - + auto baseName = pProperties ? pProperties->GetPropertyString(AI_CONFIG_EXPORT_BLOB_NAME, AI_BLOBIO_MAGIC) : AI_BLOBIO_MAGIC; std::shared_ptr old = pimpl->mIOSystem; diff --git a/code/Common/FileSystemFilter.h b/code/Common/FileSystemFilter.h index 6585f9df6..6782dd9e5 100644 --- a/code/Common/FileSystemFilter.h +++ b/code/Common/FileSystemFilter.h @@ -101,7 +101,7 @@ public: /** Tests for the existence of a file at the given path. */ bool Exists( const char* pFile) const { ai_assert( nullptr != mWrapped ); - + std::string tmp = pFile; // Currently this IOSystem is also used to open THE ONE FILE. @@ -126,7 +126,7 @@ public: if ( nullptr == pFile || nullptr == pMode ) { return nullptr; } - + ai_assert( nullptr != pFile ); ai_assert( nullptr != pMode ); diff --git a/code/Common/Importer.cpp b/code/Common/Importer.cpp index a2ad041fb..d0ed3c788 100644 --- a/code/Common/Importer.cpp +++ b/code/Common/Importer.cpp @@ -201,7 +201,7 @@ Importer::~Importer() { // Register a custom post-processing step aiReturn Importer::RegisterPPStep(BaseProcess* pImp) { ai_assert( nullptr != pImp ); - + ASSIMP_BEGIN_EXCEPTION_REGION(); pimpl->mPostProcessingSteps.push_back(pImp); @@ -215,7 +215,7 @@ aiReturn Importer::RegisterPPStep(BaseProcess* pImp) { // Register a custom loader plugin aiReturn Importer::RegisterLoader(BaseImporter* pImp) { ai_assert(nullptr != pImp); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // -------------------------------------------------------------------- @@ -242,7 +242,7 @@ aiReturn Importer::RegisterLoader(BaseImporter* pImp) { pimpl->mImporter.push_back(pImp); ASSIMP_LOG_INFO("Registering custom importer for these file extensions: ", baked); ASSIMP_END_EXCEPTION_REGION(aiReturn); - + return AI_SUCCESS; } @@ -296,7 +296,7 @@ aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) { // Supplies a custom IO handler to the importer to open and access files. void Importer::SetIOHandler( IOSystem* pIOHandler) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // If the new handler is zero, allocate a default IO implementation. if (!pIOHandler) { @@ -315,7 +315,7 @@ void Importer::SetIOHandler( IOSystem* pIOHandler) { // Get the currently set IO handler IOSystem* Importer::GetIOHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mIOHandler; } @@ -323,7 +323,7 @@ IOSystem* Importer::GetIOHandler() const { // Check whether a custom IO handler is currently set bool Importer::IsDefaultIOHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mIsDefaultHandler; } @@ -331,9 +331,9 @@ bool Importer::IsDefaultIOHandler() const { // Supplies a custom progress handler to get regular callbacks during importing void Importer::SetProgressHandler ( ProgressHandler* pHandler ) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); - + // If the new handler is zero, allocate a default implementation. if (!pHandler) { // Release pointer in the possession of the caller @@ -351,7 +351,7 @@ void Importer::SetProgressHandler ( ProgressHandler* pHandler ) { // Get the currently set progress handler ProgressHandler* Importer::GetProgressHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mProgressHandler; } @@ -359,7 +359,7 @@ ProgressHandler* Importer::GetProgressHandler() const { // Check whether a custom progress handler is currently set bool Importer::IsDefaultProgressHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mIsDefaultProgressHandler; } @@ -381,7 +381,7 @@ bool _ValidateFlags(unsigned int pFlags) { // Free the current scene void Importer::FreeScene( ) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); delete pimpl->mScene; @@ -396,14 +396,14 @@ void Importer::FreeScene( ) { // Get the current error string, if any const char* Importer::GetErrorString() const { ai_assert(nullptr != pimpl); - + // Must remain valid as long as ReadFile() or FreeFile() are not called return pimpl->mErrorString.c_str(); } const std::exception_ptr& Importer::GetException() const { ai_assert(nullptr != pimpl); - + // Must remain valid as long as ReadFile() or FreeFile() are not called return pimpl->mException; } @@ -412,7 +412,7 @@ const std::exception_ptr& Importer::GetException() const { // Enable extra-verbose mode void Importer::SetExtraVerbose(bool bDo) { ai_assert(nullptr != pimpl); - + pimpl->bExtraVerbose = bDo; } @@ -420,7 +420,7 @@ void Importer::SetExtraVerbose(bool bDo) { // Get the current scene const aiScene* Importer::GetScene() const { ai_assert(nullptr != pimpl); - + return pimpl->mScene; } @@ -428,7 +428,7 @@ const aiScene* Importer::GetScene() const { // Orphan the current scene and return it. aiScene* Importer::GetOrphanedScene() { ai_assert(nullptr != pimpl); - + aiScene* s = pimpl->mScene; ASSIMP_BEGIN_EXCEPTION_REGION(); @@ -437,7 +437,7 @@ aiScene* Importer::GetOrphanedScene() { pimpl->mErrorString = std::string(); pimpl->mException = std::exception_ptr(); ASSIMP_END_EXCEPTION_REGION(aiScene*); - + return s; } @@ -487,7 +487,7 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, unsigned int pFlags, const char* pHint /*= ""*/) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); if (!pHint) { pHint = ""; @@ -518,7 +518,7 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, // ------------------------------------------------------------------------------------------------ void WriteLogOpening(const std::string& file) { - + ASSIMP_LOG_INFO("Load ", file); // print a full version dump. This is nice because we don't @@ -580,7 +580,7 @@ void WriteLogOpening(const std::string& file) { // Reads the given file and returns its contents if successful. const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); const std::string pFile(_pFile); @@ -745,7 +745,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { // either successful or failure - the pointer expresses it anyways ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString, pimpl->mException); - + return pimpl->mScene; } @@ -754,7 +754,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { // Apply post-processing to the currently bound scene const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // Return immediately if no scene is active if (!pimpl->mScene) { @@ -832,7 +832,7 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) { } #endif // ! DEBUG } - pimpl->mProgressHandler->UpdatePostProcess( static_cast(pimpl->mPostProcessingSteps.size()), + pimpl->mProgressHandler->UpdatePostProcess( static_cast(pimpl->mPostProcessingSteps.size()), static_cast(pimpl->mPostProcessingSteps.size()) ); // update private scene flags @@ -845,14 +845,14 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) { ASSIMP_LOG_INFO("Leaving post processing pipeline"); ASSIMP_END_EXCEPTION_REGION(const aiScene*); - + return pimpl->mScene; } // ------------------------------------------------------------------------------------------------ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // Return immediately if no scene is active @@ -934,14 +934,14 @@ bool Importer::IsExtensionSupported(const char* szExtension) const { // ------------------------------------------------------------------------------------------------ size_t Importer::GetImporterCount() const { ai_assert(nullptr != pimpl); - + return pimpl->mImporter.size(); } // ------------------------------------------------------------------------------------------------ const aiImporterDesc* Importer::GetImporterInfo(size_t index) const { ai_assert(nullptr != pimpl); - + if (index >= pimpl->mImporter.size()) { return nullptr; } @@ -952,7 +952,7 @@ const aiImporterDesc* Importer::GetImporterInfo(size_t index) const { // ------------------------------------------------------------------------------------------------ BaseImporter* Importer::GetImporter (size_t index) const { ai_assert(nullptr != pimpl); - + if (index >= pimpl->mImporter.size()) { return nullptr; } @@ -963,7 +963,7 @@ BaseImporter* Importer::GetImporter (size_t index) const { // Find a loader plugin for a given file extension BaseImporter* Importer::GetImporter (const char* szExtension) const { ai_assert(nullptr != pimpl); - + return GetImporter(GetImporterIndex(szExtension)); } @@ -1002,7 +1002,7 @@ size_t Importer::GetImporterIndex (const char* szExtension) const { // Helper function to build a list of all file extensions supported by ASSIMP void Importer::GetExtensionList(aiString& szOut) const { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); std::set str; for (std::vector::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { @@ -1028,7 +1028,7 @@ void Importer::GetExtensionList(aiString& szOut) const { // Set a configuration property bool Importer::SetPropertyInteger(const char* szName, int iValue) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mIntProperties, szName,iValue); @@ -1040,7 +1040,7 @@ bool Importer::SetPropertyInteger(const char* szName, int iValue) { // Set a configuration property bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mFloatProperties, szName,iValue); @@ -1052,7 +1052,7 @@ bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) { // Set a configuration property bool Importer::SetPropertyString(const char* szName, const std::string& value) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mStringProperties, szName,value); @@ -1064,7 +1064,7 @@ bool Importer::SetPropertyString(const char* szName, const std::string& value) { // Set a configuration property bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mMatrixProperties, szName,value); @@ -1076,7 +1076,7 @@ bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) { // Get a configuration property int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mIntProperties,szName,iErrorReturn); } @@ -1084,7 +1084,7 @@ int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffff // Get a configuration property ai_real Importer::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mFloatProperties,szName,iErrorReturn); } @@ -1092,7 +1092,7 @@ ai_real Importer::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= // Get a configuration property std::string Importer::GetPropertyString(const char* szName, const std::string& iErrorReturn /*= ""*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mStringProperties,szName,iErrorReturn); } @@ -1100,13 +1100,13 @@ std::string Importer::GetPropertyString(const char* szName, const std::string& i // Get a configuration property aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mMatrixProperties,szName,iErrorReturn); } // ------------------------------------------------------------------------------------------------ // Get the memory requirements of a single node -inline +inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) { if ( nullptr == pcNode ) { return; @@ -1124,7 +1124,7 @@ void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) { // Get the memory requirements of the scene void Importer::GetMemoryRequirements(aiMemoryInfo& in) const { ai_assert(nullptr != pimpl); - + in = aiMemoryInfo(); aiScene* mScene = pimpl->mScene; diff --git a/code/Common/Importer.h b/code/Common/Importer.h index e7da7f439..0e04f9452 100644 --- a/code/Common/Importer.h +++ b/code/Common/Importer.h @@ -183,7 +183,7 @@ public: // ------------------------------------------------------------------- /** Construct a batch loader from a given IO system to be used - * to access external files + * to access external files */ explicit BatchLoader(IOSystem* pIO, bool validate = false ); @@ -197,13 +197,13 @@ public: * @param enable True for validation. */ void setValidation( bool enabled ); - + // ------------------------------------------------------------------- /** Returns the current validation step. * @return The current validation step. */ bool getValidation() const; - + // ------------------------------------------------------------------- /** Add a new file to the list of files to be loaded. * @param file File to be loaded diff --git a/code/Common/ImporterRegistry.cpp b/code/Common/ImporterRegistry.cpp index ddfcf6798..5df096166 100644 --- a/code/Common/ImporterRegistry.cpp +++ b/code/Common/ImporterRegistry.cpp @@ -205,8 +205,8 @@ corresponding preprocessor flag to selectively disable formats. namespace Assimp { // ------------------------------------------------------------------------------------------------ -void GetImporterInstanceList(std::vector &out) { - +void GetImporterInstanceList(std::vector &out) { + // Some importers may be unimplemented or otherwise unsuitable for general use // in their current state. Devs can set ASSIMP_ENABLE_DEV_IMPORTERS in their // local environment to enable them, otherwise they're left out of the registry. diff --git a/code/Common/Win32DebugLogStream.h b/code/Common/Win32DebugLogStream.h index 3385aa8ce..51f1ab178 100644 --- a/code/Common/Win32DebugLogStream.h +++ b/code/Common/Win32DebugLogStream.h @@ -71,19 +71,19 @@ public: }; // --------------------------------------------------------------------------- -inline -Win32DebugLogStream::Win32DebugLogStream(){ +inline +Win32DebugLogStream::Win32DebugLogStream(){ // empty } // --------------------------------------------------------------------------- -inline +inline Win32DebugLogStream::~Win32DebugLogStream(){ // empty } // --------------------------------------------------------------------------- -inline +inline void Win32DebugLogStream::write(const char* message) { ::OutputDebugStringA( message); } diff --git a/code/PostProcessing/ArmaturePopulate.h b/code/PostProcessing/ArmaturePopulate.h index 08a72bd66..ded8b4886 100644 --- a/code/PostProcessing/ArmaturePopulate.h +++ b/code/PostProcessing/ArmaturePopulate.h @@ -97,7 +97,7 @@ public: static void BuildBoneList(aiNode *current_node, const aiNode *root_node, const aiScene *scene, - std::vector &bones); + std::vector &bones); static void BuildBoneStack(aiNode *current_node, const aiNode *root_node, const aiScene *scene, diff --git a/code/PostProcessing/DropFaceNormalsProcess.cpp b/code/PostProcessing/DropFaceNormalsProcess.cpp index c01ac35e5..21abf9693 100644 --- a/code/PostProcessing/DropFaceNormalsProcess.cpp +++ b/code/PostProcessing/DropFaceNormalsProcess.cpp @@ -104,7 +104,7 @@ bool DropFaceNormalsProcess::DropMeshFaceNormals (aiMesh* mesh) { if (nullptr == mesh->mNormals) { return false; } - + delete[] mesh->mNormals; mesh->mNormals = nullptr; return true; diff --git a/code/PostProcessing/EmbedTexturesProcess.cpp b/code/PostProcessing/EmbedTexturesProcess.cpp index 500032c39..d7720de98 100644 --- a/code/PostProcessing/EmbedTexturesProcess.cpp +++ b/code/PostProcessing/EmbedTexturesProcess.cpp @@ -137,7 +137,7 @@ bool EmbedTexturesProcess::addTexture(aiScene *pScene, const std::string &path) pScene->mTextures = new aiTexture*[pScene->mNumTextures]; ::memmove(pScene->mTextures, oldTextures, sizeof(aiTexture*) * (pScene->mNumTextures - 1u)); delete [] oldTextures; - + // Add the new texture auto pTexture = new aiTexture; pTexture->mHeight = 0; // Means that this is still compressed diff --git a/code/PostProcessing/FindDegenerates.cpp b/code/PostProcessing/FindDegenerates.cpp index eae91ff02..3809abf50 100644 --- a/code/PostProcessing/FindDegenerates.cpp +++ b/code/PostProcessing/FindDegenerates.cpp @@ -90,7 +90,7 @@ void FindDegeneratesProcess::Execute( aiScene* pScene) { if ( nullptr == pScene) { return; } - + std::unordered_map meshMap; meshMap.reserve(pScene->mNumMeshes); diff --git a/code/PostProcessing/FindInstancesProcess.cpp b/code/PostProcessing/FindInstancesProcess.cpp index ab5f52b78..d46afc201 100644 --- a/code/PostProcessing/FindInstancesProcess.cpp +++ b/code/PostProcessing/FindInstancesProcess.cpp @@ -137,7 +137,7 @@ void FindInstancesProcess::Execute( aiScene* pScene) aiMesh* inst = pScene->mMeshes[i]; hashes[i] = GetMeshHash(inst); - // Find an appropriate epsilon + // Find an appropriate epsilon // to compare position differences against float epsilon = ComputePositionEpsilon(inst); epsilon *= epsilon; diff --git a/code/PostProcessing/MakeVerboseFormat.h b/code/PostProcessing/MakeVerboseFormat.h index c1db76dca..4304a3afa 100644 --- a/code/PostProcessing/MakeVerboseFormat.h +++ b/code/PostProcessing/MakeVerboseFormat.h @@ -98,7 +98,7 @@ public: // ------------------------------------------------------------------- /** Checks whether the scene is already in verbose format. - * @param pScene The data to check. + * @param pScene The data to check. * @return true if the scene is already in verbose format. */ static bool IsVerboseFormat(const aiScene* pScene); diff --git a/code/PostProcessing/RemoveRedundantMaterials.cpp b/code/PostProcessing/RemoveRedundantMaterials.cpp index c252f37a5..36745fb1d 100644 --- a/code/PostProcessing/RemoveRedundantMaterials.cpp +++ b/code/PostProcessing/RemoveRedundantMaterials.cpp @@ -215,7 +215,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene) } else { - ASSIMP_LOG_INFO("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ", + ASSIMP_LOG_INFO("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ", unreferencedRemoved, " unused materials."); } } diff --git a/code/PostProcessing/ScaleProcess.cpp b/code/PostProcessing/ScaleProcess.cpp index 0a3e29c42..63dd0443d 100644 --- a/code/PostProcessing/ScaleProcess.cpp +++ b/code/PostProcessing/ScaleProcess.cpp @@ -75,7 +75,7 @@ void ScaleProcess::SetupProperties( const Importer* pImp ) { // File scaling * Application Scaling float importerScale = pImp->GetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, 1.0f ); - // apply scale to the scale + // apply scale to the scale // helps prevent bugs with backward compatibility for anyone using normal scaling. mScale *= importerScale; } @@ -84,7 +84,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { if(mScale == 1.0f) { return; // nothing to scale } - + ai_assert( mScale != 0 ); ai_assert( nullptr != pScene ); ai_assert( nullptr != pScene->mRootNode ); @@ -96,7 +96,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { if ( nullptr == pScene->mRootNode ) { return; } - + // Process animations and update position transform to new unit system for( unsigned int animationID = 0; animationID < pScene->mNumAnimations; animationID++ ) { @@ -105,7 +105,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { for( unsigned int animationChannel = 0; animationChannel < animation->mNumChannels; animationChannel++) { aiNodeAnim* anim = animation->mChannels[animationChannel]; - + for( unsigned int posKey = 0; posKey < anim->mNumPositionKeys; posKey++) { aiVectorKey& vectorKey = anim->mPositionKeys[posKey]; @@ -116,8 +116,8 @@ void ScaleProcess::Execute( aiScene* pScene ) { for( unsigned int meshID = 0; meshID < pScene->mNumMeshes; meshID++) { - aiMesh *mesh = pScene->mMeshes[meshID]; - + aiMesh *mesh = pScene->mMeshes[meshID]; + // Reconstruct mesh vertexes to the new unit system for( unsigned int vertexID = 0; vertexID < mesh->mNumVertices; vertexID++) { @@ -129,9 +129,9 @@ void ScaleProcess::Execute( aiScene* pScene ) { // bone placement / scaling for( unsigned int boneID = 0; boneID < mesh->mNumBones; boneID++) { - // Reconstruct matrix by transform rather than by scale + // Reconstruct matrix by transform rather than by scale // This prevent scale values being changed which can - // be meaningful in some cases + // be meaningful in some cases // like when you want the modeller to see 1:1 compatibility. aiBone* bone = mesh->mBones[boneID]; @@ -139,10 +139,10 @@ void ScaleProcess::Execute( aiScene* pScene ) { aiQuaternion rotation; bone->mOffsetMatrix.Decompose( scale, rotation, pos); - + aiMatrix4x4 translation; aiMatrix4x4::Translation( pos * mScale, translation ); - + aiMatrix4x4 scaling; aiMatrix4x4::Scaling( aiVector3D(scale), scaling ); @@ -157,7 +157,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { for( unsigned int animMeshID = 0; animMeshID < mesh->mNumAnimMeshes; animMeshID++) { aiAnimMesh * animMesh = mesh->mAnimMeshes[animMeshID]; - + for( unsigned int vertexID = 0; vertexID < animMesh->mNumVertices; vertexID++) { aiVector3D& vertex = animMesh->mVertices[vertexID]; @@ -169,31 +169,31 @@ void ScaleProcess::Execute( aiScene* pScene ) { traverseNodes( pScene->mRootNode ); } -void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) { +void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) { applyScaling( node ); for( size_t i = 0; i < node->mNumChildren; i++) { // recurse into the tree until we are done! - traverseNodes( node->mChildren[i], nested_node_id+1 ); + traverseNodes( node->mChildren[i], nested_node_id+1 ); } } void ScaleProcess::applyScaling( aiNode *currentNode ) { if ( nullptr != currentNode ) { - // Reconstruct matrix by transform rather than by scale + // Reconstruct matrix by transform rather than by scale // This prevent scale values being changed which can - // be meaningful in some cases - // like when you want the modeller to + // be meaningful in some cases + // like when you want the modeller to // see 1:1 compatibility. - + aiVector3D pos, scale; aiQuaternion rotation; currentNode->mTransformation.Decompose( scale, rotation, pos); - + aiMatrix4x4 translation; aiMatrix4x4::Translation( pos * mScale, translation ); - + aiMatrix4x4 scaling; // note: we do not use mScale here, this is on purpose. diff --git a/code/PostProcessing/ScaleProcess.h b/code/PostProcessing/ScaleProcess.h index d88490b1c..4dc7e3559 100644 --- a/code/PostProcessing/ScaleProcess.h +++ b/code/PostProcessing/ScaleProcess.h @@ -55,8 +55,8 @@ namespace Assimp { // --------------------------------------------------------------------------- /** ScaleProcess: Class to rescale the whole model. * Now rescales animations, bones, and blend shapes properly. - * Please note this will not write to 'scale' transform it will rewrite mesh - * and matrixes so that your scale values + * Please note this will not write to 'scale' transform it will rewrite mesh + * and matrixes so that your scale values * from your model package are preserved, so this is completely intentional * bugs should be reported as soon as they are found. */ diff --git a/code/PostProcessing/SplitByBoneCountProcess.cpp b/code/PostProcessing/SplitByBoneCountProcess.cpp index 2613d8561..ed5b9411e 100644 --- a/code/PostProcessing/SplitByBoneCountProcess.cpp +++ b/code/PostProcessing/SplitByBoneCountProcess.cpp @@ -209,7 +209,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector newBonesAtCurrentFace; - + const aiFace& face = pMesh->mFaces[a]; // check every vertex if its bones would still fit into the current submesh for( unsigned int b = 0; b < face.mNumIndices; ++b ) @@ -221,7 +221,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vectormNumAnimMeshes > 0) { newMesh->mNumAnimMeshes = pMesh->mNumAnimMeshes; newMesh->mAnimMeshes = new aiAnimMesh*[newMesh->mNumAnimMeshes]; - + for (unsigned int morphIdx = 0; morphIdx < newMesh->mNumAnimMeshes; ++morphIdx) { aiAnimMesh* origTarget = pMesh->mAnimMeshes[morphIdx]; aiAnimMesh* newTarget = new aiAnimMesh; @@ -421,16 +421,16 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vectormNumVertices = numSubMeshVertices; newTarget->mVertices = new aiVector3D[numSubMeshVertices]; newMesh->mAnimMeshes[morphIdx] = newTarget; - + if (origTarget->HasNormals()) { newTarget->mNormals = new aiVector3D[numSubMeshVertices]; } - + if (origTarget->HasTangentsAndBitangents()) { newTarget->mTangents = new aiVector3D[numSubMeshVertices]; newTarget->mBitangents = new aiVector3D[numSubMeshVertices]; } - + for( unsigned int vi = 0; vi < numSubMeshVertices; ++vi) { // find the source vertex for it in the source mesh unsigned int previousIndex = previousVertexIndices[vi]; diff --git a/code/PostProcessing/SplitLargeMeshes.cpp b/code/PostProcessing/SplitLargeMeshes.cpp index ed680cf7f..cb614edaa 100644 --- a/code/PostProcessing/SplitLargeMeshes.cpp +++ b/code/PostProcessing/SplitLargeMeshes.cpp @@ -40,7 +40,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/** +/** * @file Implementation of the SplitLargeMeshes postprocessing step */ @@ -353,7 +353,7 @@ void SplitLargeMeshesProcess_Vertex::Execute( aiScene* pScene) { std::vector > avList; - //Check for point cloud first, + //Check for point cloud first, //Do not process point cloud, splitMesh works only with faces data for (unsigned int a = 0; a < pScene->mNumMeshes; a++) { if ( pScene->mMeshes[a]->mPrimitiveTypes == aiPrimitiveType_POINT ) { diff --git a/code/PostProcessing/TextureTransform.cpp b/code/PostProcessing/TextureTransform.cpp index 681b047c0..74b00d92c 100644 --- a/code/PostProcessing/TextureTransform.cpp +++ b/code/PostProcessing/TextureTransform.cpp @@ -448,7 +448,7 @@ void TextureTransformStep::Execute( aiScene* pScene) if (size > AI_MAX_NUMBER_OF_TEXTURECOORDS) { if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_ERROR(static_cast(trafo.size()), " UV channels required but just ", + ASSIMP_LOG_ERROR(static_cast(trafo.size()), " UV channels required but just ", AI_MAX_NUMBER_OF_TEXTURECOORDS, " available"); } size = AI_MAX_NUMBER_OF_TEXTURECOORDS; diff --git a/code/PostProcessing/TriangulateProcess.cpp b/code/PostProcessing/TriangulateProcess.cpp index 0f71320b8..b7928ee59 100644 --- a/code/PostProcessing/TriangulateProcess.cpp +++ b/code/PostProcessing/TriangulateProcess.cpp @@ -86,11 +86,11 @@ namespace { /** * @brief Encode the current triangle, and make sure it is recognized as a triangle. - * + * * This method will rotate indices in tri if needed in order to avoid tri to be considered * part of the previous ngon. This method is to be used whenever you want to emit a real triangle, * and make sure it is seen as a triangle. - * + * * @param tri Triangle to encode. */ void ngonEncodeTriangle(aiFace * tri) { @@ -108,10 +108,10 @@ namespace { /** * @brief Encode a quad (2 triangles) in ngon encoding, and make sure they are seen as a single ngon. - * + * * @param tri1 First quad triangle * @param tri2 Second quad triangle - * + * * @pre Triangles must be properly fanned from the most appropriate vertex. */ void ngonEncodeQuad(aiFace *tri1, aiFace *tri2) { @@ -140,7 +140,7 @@ namespace { /** * @brief Check whether this triangle would be considered part of the lastly emitted ngon or not. - * + * * @param tri Current triangle. * @return true If used as is, this triangle will be part of last ngon. * @return false If used as is, this triangle is not considered part of the last ngon. @@ -512,7 +512,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) num = 0; break; - /*curOut -= (max-num); // undo all previous work + /*curOut -= (max-num); // undo all previous work for (tmp = 0; tmp < max-2; ++tmp) { aiFace& nface = *curOut++; diff --git a/doc/dox.h b/doc/dox.h index a4516dc7a..409e775d4 100644 --- a/doc/dox.h +++ b/doc/dox.h @@ -90,8 +90,8 @@ but not all of them are *open-source*. If there's an accompagning '\source @section main_install Installation assimp can be used in two ways: linking against the pre-built libraries or building the library on your own. The former -option is the easiest, but the assimp distribution contains pre-built libraries only for Visual C++ 2013, 2015 and 2017. -For other compilers you'll have to build assimp for yourself. Which is hopefully as hassle-free as the other way, but +option is the easiest, but the assimp distribution contains pre-built libraries only for Visual C++ 2013, 2015 and 2017. +For other compilers you'll have to build assimp for yourself. Which is hopefully as hassle-free as the other way, but needs a bit more work. Both ways are described at the @link install Installation page. @endlink If you want to use assimp on Ubuntu you can install it via the following command: @@ -145,7 +145,7 @@ to your include paths (Menu->Extras->Options->Projects and Solutions-&g and the assimp/lib/<Compiler> path to your linker paths (Menu->Extras->Options->Projects and Solutions->VC++ Directories->Library files). This is necessary only once to setup all paths inside you IDE. -To use the library in your C++ project you can simply generate a project file via cmake. One way is to add the assimp-folder +To use the library in your C++ project you can simply generate a project file via cmake. One way is to add the assimp-folder as a subdirectory via the cmake-command @code @@ -158,7 +158,7 @@ Now just add the assimp-dependency to your application: TARGET_LINK_LIBRARIES(my_game assimp) @endcode -If done correctly you should now be able to compile, link, run and use the application. +If done correctly you should now be able to compile, link, run and use the application. @section install_own Building the library from scratch @@ -170,7 +170,7 @@ to build the library just open a command-prompt / bash, navigate into the repo-f cmake CMakeLists.txt @endcode -A project-file of your default make-system ( like gnu-make on linux or Visual-Studio on Windows ) will be generated. +A project-file of your default make-system ( like gnu-make on linux or Visual-Studio on Windows ) will be generated. Run the build and you are done. You can find the libs at assimp/lib and the dll's / so's at bin. @section assimp_dll Windows DLL Build @@ -496,10 +496,10 @@ X3 Y3 Z3 T3 @endcode with (X1, X2, X3) being the local X base vector, (Y1, Y2, Y3) being the local Y base vector, (Z1, Z2, Z3) being the local Z base vector and (T1, T2, T3) being the -offset of the local origin (the translational part). +offset of the local origin (the translational part). All matrices in the library use row-major storage order. That means that the matrix elements are -stored row-by-row, i.e. they end up like this in memory: -[X1, Y1, Z1, T1, X2, Y2, Z2, T2, X3, Y3, Z3, T3, 0, 0, 0, 1]. +stored row-by-row, i.e. they end up like this in memory: +[X1, Y1, Z1, T1, X2, Y2, Z2, T2, X3, Y3, Z3, T3, 0, 0, 0, 1]. Note that this is neither the OpenGL format nor the DirectX format, because both of them specify the matrix layout such that the translational part occupies three consecutive addresses in memory (so those @@ -1498,7 +1498,7 @@ Just copy'n'paste the template from Appendix A and adapt it for your needs. with DefaultLogger::get()->[error, warn, debug, info].
  • -Make sure that your loader compiles against all build configurations on all supported platforms. You can use our CI-build to check several platforms +Make sure that your loader compiles against all build configurations on all supported platforms. You can use our CI-build to check several platforms like Windows and Linux ( 32 bit and 64 bit ).
  • diff --git a/doc/dox_cmd.h b/doc/dox_cmd.h index 78e755d56..79ea3a861 100644 --- a/doc/dox_cmd.h +++ b/doc/dox_cmd.h @@ -12,7 +12,7 @@ @section intro Introduction -This document describes the usage of assimp's command line tools. +This document describes the usage of assimp's command line tools. This is *not* the SDK reference and programming-related stuff is not covered here.

    NOTE: For simplicity, the following sections are written with Windows in mind. However @@ -29,7 +29,7 @@ assimp [command] [parameters] The following commands are available: - + @@ -184,7 +184,7 @@ Generate a text or binary dump of a model. This is the core component of Assimp' regression test suite but it could also be useful for other developers to quickly examine the contents of a model. Note that text dumps are not intended to be used as intermediate format, Assimp is not able to read them again, nor is the file format -stable or well-defined. It may change with every revision without notice. +stable or well-defined. It may change with every revision without notice. Binary dumps (*.assbin) are backwards- and forwards-compatible.

    Syntax:

    @@ -199,7 +199,7 @@ assimp dump [] [-b] [-s] [common parameters]

    model

    -Required. Relative or absolute path to the input model. +Required. Relative or absolute path to the input model.

    @@ -220,7 +220,7 @@ The long form of this parameter is --binary.
    Optional. If this switch is specified, the dump is shortened to include only min/max values for all vertex components and animation channels. The resulting -file is much smaller, but the original model can't be reconstructed from it. This is +file is much smaller, but the original model can't be reconstructed from it. This is used by Assimp's regression test suite, comparing those minidumps provides a fast way to verify whether a loader works correctly or not. The long form of this parameter is --short. @@ -229,7 +229,7 @@ The long form of this parameter is --short.

    common parameters

    -Optional. Import configuration & postprocessing. +Optional. Import configuration & postprocessing. See the @link common common parameters page @endlink for more information.

    @@ -248,7 +248,7 @@ The log output is included with the dump. @code assimp dump files\*.* -assimp dump files\*.* +assimp dump files\*.* @endcode Dumps all loadable model files in the 'files' subdir. The output dumps are named @@ -275,14 +275,14 @@ assimp extract [] [-t] [-f] [-ba] [-s] [common parameters]

    model

    -Required. Relative or absolute path to the input model. +Required. Relative or absolute path to the input model.

    out

    Optional. Relative or absolute path to write the output images to. If the file name is omitted the output images are named
    -The suffix _img<n> is appended to the file name if the -s switch is not specified +The suffix _img<n> is appended to the file name if the -s switch is not specified (where <n> is the zero-based index of the texture in the model file).
    The output file format is determined from the given file extension. Supported @@ -296,7 +296,7 @@ written in their native file format (e.g. jpg).

    -t<n>

    -Optional. Specifies the (zero-based) index of the embedded texture to be extracted from +Optional. Specifies the (zero-based) index of the embedded texture to be extracted from the model. If this option is *not* specified all textures found are exported. The long form of this parameter is --texture=<n>.

    @@ -348,8 +348,8 @@ imported data structure and writes it to test_img0.bmp. @code -assimp extract files\*.mdl *.bmp -assimp extract files\*.mdl *.bmp +assimp extract files\*.mdl *.bmp +assimp extract files\*.mdl *.bmp @endcode Extracts all embedded textures from all loadable .mdl files in the 'files' subdirectory @@ -361,10 +361,10 @@ and writes them to bitmaps which are named _img.bmp /** @page common Common parameters -The parameters described on this page are commonly used by almost every assimp command. They +The parameters described on this page are commonly used by almost every assimp command. They specify how the library will postprocess the imported data. This is done by several configurable pipeline stages, called 'post processing steps'. Below you can find a list -of all supported steps along with short descriptions of what they're doing.
    Programmers: +of all supported steps along with short descriptions of what they're doing.
    Programmers: more information can be found in the aiPostProcess.h header.
    @link version version @endlink Retrieve the current version of assimp
    @@ -376,7 +376,7 @@ more information can be found in the aiPostProcess.h header. - @@ -428,7 +428,7 @@ more information can be found in the aiPostProcess.h header. - @@ -515,7 +515,7 @@ For convenience some default postprocessing configurations are provided. The corresponding command line parameter is -c<name> (or --config=<name>).
    -ptv --pretransform-verticesMove all vertices into worldspace and collapse the scene graph. Animation data is lost. + Move all vertices into worldspace and collapse the scene graph. Animation data is lost. This is intended for applications which don't support scenegraph-oriented rendering.
    -icl --improve-cache-localityImprove the cache locality of the vertex buffer by reordering the index buffer + Improve the cache locality of the vertex buffer by reordering the index buffer to achieve a lower ACMR (average post-transform vertex cache miss ratio)
    - + @@ -543,7 +543,7 @@ The corresponding command line parameter is -c<name> (or --co There are also some common flags to customize Assimp's logging behaviour:
    Name Description
    - + @@ -558,7 +558,7 @@ There are also some common flags to customize Assimp's logging behaviour: -
    Name Description
    -v or --verboseEnables verbose logging. Debug messages will be produced too. This might + Enables verbose logging. Debug messages will be produced too. This might decrease loading performance and result in *very* long logs ... use with caution if you experience strange issues.
    diff --git a/fuzz/assimp_fuzzer.cc b/fuzz/assimp_fuzzer.cc index b65ee0236..33748c10f 100644 --- a/fuzz/assimp_fuzzer.cc +++ b/fuzz/assimp_fuzzer.cc @@ -54,6 +54,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) { aiProcessPreset_TargetRealtime_Quality, nullptr ); aiDetachLogStream(&stream); - + return 0; } diff --git a/include/assimp/BaseImporter.h b/include/assimp/BaseImporter.h index 38bec1afd..54b5daac1 100644 --- a/include/assimp/BaseImporter.h +++ b/include/assimp/BaseImporter.h @@ -154,7 +154,7 @@ public: /** Returns the exception of the last exception that occurred. * Note: Exceptions are not the only source of error details, so GetErrorText * should be consulted too. - * @return The last exception that occurred. + * @return The last exception that occurred. */ const std::exception_ptr& GetException() const { return m_Exception; diff --git a/include/assimp/Compiler/poppack1.h b/include/assimp/Compiler/poppack1.h index a8e9a3c7e..ff501bc0c 100644 --- a/include/assimp/Compiler/poppack1.h +++ b/include/assimp/Compiler/poppack1.h @@ -1,7 +1,7 @@ // =============================================================================== -// May be included multiple times - resets structure packing to the defaults -// for all supported compilers. Reverts the changes made by #include +// 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 diff --git a/include/assimp/Compiler/pushpack1.h b/include/assimp/Compiler/pushpack1.h index 2a5e2dfe6..b32ed172c 100644 --- a/include/assimp/Compiler/pushpack1.h +++ b/include/assimp/Compiler/pushpack1.h @@ -1,7 +1,7 @@ // =============================================================================== -// May be included multiple times - sets structure packing to 1 +// 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: @@ -37,7 +37,7 @@ #if defined(_MSC_VER) // C4103: Packing was changed after the inclusion of the header, probably missing #pragma pop -# pragma warning (disable : 4103) +# pragma warning (disable : 4103) #endif #define AI_PUSHPACK_IS_DEFINED diff --git a/include/assimp/Exceptional.h b/include/assimp/Exceptional.h index 98e2a3321..1bf399cbc 100644 --- a/include/assimp/Exceptional.h +++ b/include/assimp/Exceptional.h @@ -59,7 +59,7 @@ using std::runtime_error; class ASSIMP_API DeadlyErrorBase : public runtime_error { protected: DeadlyErrorBase(Assimp::Formatter::format f); - + template DeadlyErrorBase(Assimp::Formatter::format f, U&& u, T&&... args) : DeadlyErrorBase(std::move(f << std::forward(u)), std::forward(args)...) {} diff --git a/include/assimp/Exporter.hpp b/include/assimp/Exporter.hpp index 8e78ac954..6ab35a8f0 100644 --- a/include/assimp/Exporter.hpp +++ b/include/assimp/Exporter.hpp @@ -390,7 +390,7 @@ public: * @see SetPropertyInteger() */ bool SetPropertyMatrix(const char *szName, const aiMatrix4x4 &sValue); - + bool SetPropertyCallback(const char *szName, const std::function &f); // ------------------------------------------------------------------- diff --git a/include/assimp/IOStreamBuffer.h b/include/assimp/IOStreamBuffer.h index ee4ac0953..c3b7327ff 100644 --- a/include/assimp/IOStreamBuffer.h +++ b/include/assimp/IOStreamBuffer.h @@ -81,7 +81,7 @@ public: /// @brief Returns the file-size. /// @return The file-size. size_t size() const; - + /// @brief Returns the cache size. /// @return The cache size. size_t cacheSize() const; @@ -278,7 +278,7 @@ bool IOStreamBuffer::getNextDataLine( std::vector &buffer, T continuationT } } } - + buffer[ i ] = '\n'; ++m_cachePos; @@ -334,7 +334,7 @@ template AI_FORCE_INLINE bool IOStreamBuffer::getNextBlock( std::vector &buffer) { // Return the last block-value if getNextLine was used before - if ( 0 != m_cachePos ) { + if ( 0 != m_cachePos ) { buffer = std::vector( m_cache.begin() + m_cachePos, m_cache.end() ); m_cachePos = 0; } else { diff --git a/include/assimp/IOSystem.hpp b/include/assimp/IOSystem.hpp index 76f876440..7be373cf1 100644 --- a/include/assimp/IOSystem.hpp +++ b/include/assimp/IOSystem.hpp @@ -62,9 +62,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "types.h" #ifdef _WIN32 -# include -# include -# include +# include +# include +# include #else # include # include @@ -84,7 +84,7 @@ namespace Assimp { * to the importer library. If you implement this interface, you also want to * supply a custom implementation for IOStream. * - * @see Importer::SetIOHandler() + * @see Importer::SetIOHandler() */ class ASSIMP_API IOSystem #ifndef SWIG diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index 3ca4a6cb2..85204c88d 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -112,7 +112,7 @@ public: /** @brief Writes a debug message * @param message Debug message*/ void verboseDebug(const char* message); - + template void verboseDebug(T&&... args) { verboseDebug(formatMessage(std::forward(args)...).c_str()); diff --git a/include/assimp/MemoryIOWrapper.h b/include/assimp/MemoryIOWrapper.h index 86dc5ea60..24f6a85d1 100644 --- a/include/assimp/MemoryIOWrapper.h +++ b/include/assimp/MemoryIOWrapper.h @@ -57,7 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include namespace Assimp { - + #define AI_MEMORYIO_MAGIC_FILENAME "$$$___magic___$$$" #define AI_MEMORYIO_MAGIC_FILENAME_LENGTH 17 @@ -85,7 +85,7 @@ public: size_t Read(void* pvBuffer, size_t pSize, size_t pCount) { ai_assert(nullptr != pvBuffer); ai_assert(0 != pSize); - + const size_t cnt = std::min( pCount, (length-pos) / pSize); const size_t ofs = pSize * cnt; @@ -209,7 +209,7 @@ public: return existing_io ? existing_io->ComparePaths(one, second) : false; } - bool PushDirectory( const std::string &path ) override { + bool PushDirectory( const std::string &path ) override { return existing_io ? existing_io->PushDirectory(path) : false; } diff --git a/include/assimp/SmallVector.h b/include/assimp/SmallVector.h index bcb8482a9..fb78f5a97 100644 --- a/include/assimp/SmallVector.h +++ b/include/assimp/SmallVector.h @@ -53,17 +53,17 @@ Based on CppCon 2016: Chandler Carruth "High Performance Code 201: Hybrid Data S namespace Assimp { // -------------------------------------------------------------------------------------------- -/// @brief Small vector with inplace storage. +/// @brief Small vector with inplace storage. /// /// Reduces heap allocations when list is shorter. It uses a small array for a dedicated size. -/// When the growing gets bigger than this small cache a dynamic growing algorithm will be +/// When the growing gets bigger than this small cache a dynamic growing algorithm will be /// used. // -------------------------------------------------------------------------------------------- template class SmallVector { public: /// @brief The default class constructor. - SmallVector() : + SmallVector() : mStorage(mInplaceStorage), mSize(0), mCapacity(Capacity) { @@ -84,7 +84,7 @@ public: mStorage[mSize++] = item; return; } - + push_back_and_grow(item); } diff --git a/include/assimp/SmoothingGroups.inl b/include/assimp/SmoothingGroups.inl index a32626e00..08c2dfa53 100644 --- a/include/assimp/SmoothingGroups.inl +++ b/include/assimp/SmoothingGroups.inl @@ -7,8 +7,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -77,7 +77,7 @@ void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& sMesh) sMesh.mNormals[face.mIndices[c]] = vNor; } - // calculate the position bounds so we have a reliable epsilon to check position differences against + // calculate the position bounds so we have a reliable epsilon to check position differences against aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f); for( unsigned int a = 0; a < sMesh.mPositions.size(); a++) { @@ -91,7 +91,7 @@ void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& sMesh) const float posEpsilon = (maxVec - minVec).Length() * 1e-5f; std::vector avNormals; avNormals.resize(sMesh.mNormals.size()); - + // now generate the spatial sort tree SGSpatialSort sSort; for( typename std::vector::iterator i = sMesh.mFaces.begin(); diff --git a/include/assimp/XmlParser.h b/include/assimp/XmlParser.h index 18d48f337..9c2dc419e 100644 --- a/include/assimp/XmlParser.h +++ b/include/assimp/XmlParser.h @@ -94,7 +94,7 @@ using XmlAttribute = pugi::xml_attribute; /// } /// } /// @endcode -/// @tparam TNodeType +/// @tparam TNodeType template class TXmlParser { public: @@ -123,7 +123,7 @@ public: /// @brief Will search for a child-node by its name /// @param name [in] The name of the child-node. - /// @return The node instance or nullptr, if nothing was found. + /// @return The node instance or nullptr, if nothing was found. TNodeType *findNode(const std::string &name) { if (name.empty()) { return nullptr; @@ -162,12 +162,12 @@ public: mData.resize(len + 1); memset(&mData[0], '\0', len + 1); stream->Read(&mData[0], 1, len); - + mDoc = new pugi::xml_document(); pugi::xml_parse_result parse_result = mDoc->load_string(&mData[0], pugi::parse_full); if (parse_result.status == pugi::status_ok) { return true; - } + } ASSIMP_LOG_DEBUG("Error while parse xml.", std::string(parse_result.description()), " @ ", parse_result.offset); @@ -457,7 +457,7 @@ public: } private: - XmlNode &mParent; + XmlNode &mParent; std::vector mNodes; size_t mIndex; }; diff --git a/include/assimp/ai_assert.h b/include/assimp/ai_assert.h index 6736b24c9..b377b6e8b 100644 --- a/include/assimp/ai_assert.h +++ b/include/assimp/ai_assert.h @@ -57,7 +57,7 @@ namespace Assimp #else # define ai_assert(expression) -# define ai_assert_entry() +# define ai_assert_entry() #endif // ASSIMP_BUILD_DEBUG #endif // AI_ASSERT_H_INC diff --git a/include/assimp/anim.h b/include/assimp/anim.h index 79841a537..dcd054d1e 100644 --- a/include/assimp/anim.h +++ b/include/assimp/anim.h @@ -39,7 +39,7 @@ 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. @@ -478,11 +478,11 @@ struct aiAnimation { 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. + * types of the arguments. */ template struct Interpolator { diff --git a/include/assimp/cimport.h b/include/assimp/cimport.h index b4a172742..d660eebb1 100644 --- a/include/assimp/cimport.h +++ b/include/assimp/cimport.h @@ -894,7 +894,7 @@ ASSIMP_API float aiMatrix3Determinant( // -------------------------------------------------------------------------------- /** Get a 3x3 rotation matrix around the Z axis. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param angle Rotation angle, in radians */ ASSIMP_API void aiMatrix3RotationZ( @@ -903,7 +903,7 @@ ASSIMP_API void aiMatrix3RotationZ( // -------------------------------------------------------------------------------- /** Returns a 3x3 rotation matrix for a rotation around an arbitrary axis. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param axis Rotation axis, should be a normalized vector * @param angle Rotation angle, in radians */ @@ -914,7 +914,7 @@ ASSIMP_API void aiMatrix3FromRotationAroundAxis( // -------------------------------------------------------------------------------- /** Get a 3x3 translation matrix. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param translation The translation vector */ ASSIMP_API void aiMatrix3Translation( @@ -923,7 +923,7 @@ ASSIMP_API void aiMatrix3Translation( // -------------------------------------------------------------------------------- /** Create a 3x3 matrix that rotates one vector to another vector. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param from Vector to rotate from * @param to Vector to rotate to */ @@ -1059,7 +1059,7 @@ ASSIMP_API void aiMatrix4DecomposeNoScaling( // -------------------------------------------------------------------------------- /** Creates a 4x4 matrix from a set of euler angles. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @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 @@ -1137,7 +1137,7 @@ ASSIMP_API void aiMatrix4FromTo( // -------------------------------------------------------------------------------- /** Create a Quaternion from euler angles. - * @param q Receives the output quaternion + * @param q Receives the output quaternion * @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 diff --git a/include/assimp/defs.h b/include/assimp/defs.h index d61fd7901..8d1011a28 100644 --- a/include/assimp/defs.h +++ b/include/assimp/defs.h @@ -161,7 +161,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma warning(disable : 4251) #endif /* Force the compiler to inline a function, if possible */ - #define AI_FORCE_INLINE inline + #define AI_FORCE_INLINE inline /* Tells the compiler that a function never returns. Used in code analysis * to skip dead paths (e.g. after an assertion evaluated to false). */ diff --git a/include/assimp/light.h b/include/assimp/light.h index bc37de43a..48efdaebd 100644 --- a/include/assimp/light.h +++ b/include/assimp/light.h @@ -257,7 +257,7 @@ struct aiLight #ifdef __cplusplus } -#endif +#endif #endif // !! AI_LIGHT_H_INC diff --git a/include/assimp/material.h b/include/assimp/material.h index f0207c6de..250ad90d5 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -194,8 +194,8 @@ enum aiTextureType { */ aiTextureType_NONE = 0, - /** LEGACY API MATERIALS - * Legacy refers to materials which + /** LEGACY API MATERIALS + * Legacy refers to materials which * Were originally implemented in the specifications around 2000. * These must never be removed, as most engines support them. */ @@ -339,9 +339,9 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); // --------------------------------------------------------------------------- /** @brief Defines all shading models supported by the library - * + * * Property: #AI_MATKEY_SHADING_MODEL - * + * * 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 diff --git a/include/assimp/matrix4x4.h b/include/assimp/matrix4x4.h index c929ef2a9..6caf7686c 100644 --- a/include/assimp/matrix4x4.h +++ b/include/assimp/matrix4x4.h @@ -230,7 +230,7 @@ public: * @param out Receives the output matrix * @return Reference to the output matrix */ - static aiMatrix4x4t& Translation( const aiVector3t& v, + static aiMatrix4x4t& Translation( const aiVector3t& v, aiMatrix4x4t& out); // ------------------------------------------------------------------- diff --git a/include/assimp/matrix4x4.inl b/include/assimp/matrix4x4.inl index 88315ac28..c1dd87b65 100644 --- a/include/assimp/matrix4x4.inl +++ b/include/assimp/matrix4x4.inl @@ -421,7 +421,7 @@ void aiMatrix4x4t::Decompose(aiVector3t& pScaling, aiVector3tmArmature and aiBone->mNode generically * The point of these is it saves you later having to calculate these elements * This is useful when handling rest information or skin information - * If you have multiple armatures on your models we strongly recommend enabling this - * Instead of writing your own multi-root, multi-armature lookups we have done the + * If you have multiple armatures on your models we strongly recommend enabling this + * Instead of writing your own multi-root, multi-armature lookups we have done the * hard work for you :) */ aiProcess_PopulateArmatureData = 0x4000, @@ -579,7 +579,7 @@ enum aiPostProcessSteps * of the imported model. And if so, it uses that. */ aiProcess_EmbedTextures = 0x10000000, - + // aiProcess_GenEntityMeshes = 0x100000, // aiProcess_OptimizeAnimations = 0x200000 // aiProcess_FixTexturePaths = 0x200000 diff --git a/include/assimp/scene.h b/include/assimp/scene.h index 522ddc6dc..25858ac38 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -70,7 +70,7 @@ extern "C" { #endif // ------------------------------------------------------------------------------- -/** +/** * A node in the imported hierarchy. * * Each node has name, a parent node (except for the root node), @@ -149,12 +149,12 @@ struct ASSIMP_API aiNode * @param name Name to search for * @return nullptr or a valid Node if the search was successful. */ - inline + inline const aiNode* FindNode(const aiString& name) const { return FindNode(name.data); } - inline + inline aiNode* FindNode(const aiString& name) { return FindNode(name.data); } @@ -353,34 +353,34 @@ struct 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 != nullptr && mNumMeshes > 0; + inline bool HasMeshes() const { + return mMeshes != nullptr && 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 != nullptr && mNumMaterials > 0; + inline bool HasMaterials() const { + return mMaterials != nullptr && mNumMaterials > 0; } //! Check whether the scene contains lights - inline bool HasLights() const { - return mLights != nullptr && mNumLights > 0; + inline bool HasLights() const { + return mLights != nullptr && mNumLights > 0; } //! Check whether the scene contains textures inline bool HasTextures() const { - return mTextures != nullptr && mNumTextures > 0; + return mTextures != nullptr && mNumTextures > 0; } //! Check whether the scene contains cameras inline bool HasCameras() const { - return mCameras != nullptr && mNumCameras > 0; + return mCameras != nullptr && mNumCameras > 0; } //! Check whether the scene contains animations - inline bool HasAnimations() const { - return mAnimations != nullptr && mNumAnimations > 0; + inline bool HasAnimations() const { + return mAnimations != nullptr && mNumAnimations > 0; } //! Returns a short filename from a full path diff --git a/include/assimp/vector2.inl b/include/assimp/vector2.inl index 0ca440e72..b51dd0ec2 100644 --- a/include/assimp/vector2.inl +++ b/include/assimp/vector2.inl @@ -190,7 +190,7 @@ aiVector2t operator + (const aiVector2t& v1, const aiVector2t -inline +inline aiVector2t operator - (const aiVector2t& v1, const aiVector2t& v2) { return aiVector2t( v1.x - v2.x, v1.y - v2.y); } @@ -198,7 +198,7 @@ aiVector2t operator - (const aiVector2t& v1, const aiVector2t -inline +inline TReal operator * (const aiVector2t& v1, const aiVector2t& v2) { return v1.x*v2.x + v1.y*v2.y; } @@ -206,7 +206,7 @@ TReal operator * (const aiVector2t& v1, const aiVector2t& v2) { // ------------------------------------------------------------------------------------------------ // scalar multiplication template -inline +inline aiVector2t operator * ( TReal f, const aiVector2t& v) { return aiVector2t( f*v.x, f*v.y); } @@ -214,7 +214,7 @@ aiVector2t operator * ( TReal f, const aiVector2t& v) { // ------------------------------------------------------------------------------------------------ // and the other way around template -inline +inline aiVector2t operator * ( const aiVector2t& v, TReal f) { return aiVector2t( f*v.x, f*v.y); } @@ -222,7 +222,7 @@ aiVector2t operator * ( const aiVector2t& v, TReal f) { // ------------------------------------------------------------------------------------------------ // scalar division template -inline +inline aiVector2t operator / ( const aiVector2t& v, TReal f) { return v * (1/f); } @@ -230,7 +230,7 @@ aiVector2t operator / ( const aiVector2t& v, TReal f) { // ------------------------------------------------------------------------------------------------ // vector division template -inline +inline aiVector2t operator / ( const aiVector2t& v, const aiVector2t& v2) { return aiVector2t(v.x / v2.x,v.y / v2.y); } @@ -238,7 +238,7 @@ aiVector2t operator / ( const aiVector2t& v, const aiVector2t -inline +inline aiVector2t operator - ( const aiVector2t& v) { return aiVector2t( -v.x, -v.y); } diff --git a/packaging/windows-innosetup/readme_installer.txt b/packaging/windows-innosetup/readme_installer.txt index 6ea969dc1..252c396af 100644 --- a/packaging/windows-innosetup/readme_installer.txt +++ b/packaging/windows-innosetup/readme_installer.txt @@ -10,7 +10,7 @@ http://assimp.sf.net Troubleshooting =============== -1. Missing d3dx9_(some-number).dll? +1. Missing d3dx9_(some-number).dll? Install the latest DirectX runtime or grab the file from somewhere (that's evil but mostly fine). 2. Application configuration not correct / missing msvcr***.dll? diff --git a/packaging/windows-innosetup/readme_installer_vieweronly.txt b/packaging/windows-innosetup/readme_installer_vieweronly.txt index 1e84c577d..0acd6ef52 100644 --- a/packaging/windows-innosetup/readme_installer_vieweronly.txt +++ b/packaging/windows-innosetup/readme_installer_vieweronly.txt @@ -19,7 +19,7 @@ Viewer Troubleshooting =============== -1. Missing d3dx9_(number).dll? +1. Missing d3dx9_(number).dll? Install the latest DirectX runtime or grab the file from somewhere (that's evil but mostly fine). 2. Application configuration not correct / missing msvcr***.dll? diff --git a/packaging/windows-mkzip/bin_readme.txt b/packaging/windows-mkzip/bin_readme.txt index 5cff1f30e..f4402d6bf 100644 --- a/packaging/windows-mkzip/bin_readme.txt +++ b/packaging/windows-mkzip/bin_readme.txt @@ -19,7 +19,7 @@ Viewer Troubleshooting =============== -1. Missing d3dx9_42.dll? +1. Missing d3dx9_42.dll? Install the latest DirectX runtime or grab the file from somewhere (that's evil but mostly fine). 2. Application configuration not correct / missing msv*** DLLs? diff --git a/port/AndroidJNI/CMakeLists.txt b/port/AndroidJNI/CMakeLists.txt index 43e842848..8c821c72a 100644 --- a/port/AndroidJNI/CMakeLists.txt +++ b/port/AndroidJNI/CMakeLists.txt @@ -3,10 +3,10 @@ cmake_minimum_required(VERSION 3.10) include_directories(./) include_directories(./../../) add_library( # Defines the name of the library. - android_jniiosystem + android_jniiosystem # Implements a static library. - STATIC + STATIC # relative path to source file(s). AndroidJNIIOSystem.cpp diff --git a/port/AssimpDelphi/Readme.txt b/port/AssimpDelphi/Readme.txt index 07d6935ae..1ec6d2109 100644 --- a/port/AssimpDelphi/Readme.txt +++ b/port/AssimpDelphi/Readme.txt @@ -1,6 +1,6 @@ This is a set of Delphi units for using the Assimp C DLL. This was created for use with Delphi 7, but should be usable as-is or with minimal modifications with later Delphi versions. -This set of headers is enough to load and display a model with external textures. Since I'm not familiar with animated models and some of the other functionality of the assimp library, I did not convert the headers for those features. +This set of headers is enough to load and display a model with external textures. Since I'm not familiar with animated models and some of the other functionality of the assimp library, I did not convert the headers for those features. See http://sourceforge.net/tracker/?func=detail&aid=3212646&group_id=226462&atid=1067634 for the original patch diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index 0cf01b1e3..6661ce9c4 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -15,7 +15,7 @@ #define lprintf(...) printf (__VA_ARGS__) #endif /* ANDROID */ #else -#define lprintf +#define lprintf #endif static std::string gLastErrorString; @@ -63,7 +63,7 @@ static bool createInstance(JNIEnv *env, const char* className, jobject& newInsta newInstance = env->NewObject(clazz, ctr_id); - if (NULL == newInstance) + if (NULL == newInstance) { lprintf("error calling no-arg constructor for class %s\n", className); return false; @@ -94,7 +94,7 @@ static bool createInstance(JNIEnv *env, const char* className, const char* signa newInstance = env->NewObjectA(clazz, ctr_id, params); - if (NULL == newInstance) + if (NULL == newInstance) { lprintf("error calling constructor for class %s, signature %s\n", className, signature); return false; @@ -229,7 +229,7 @@ static bool getStaticField(JNIEnv *env, const char* className, const char* field } -static bool call(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static bool call(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature,/* const*/ jvalue* params) { jclass clazz = env->FindClass(typeName); @@ -275,7 +275,7 @@ static bool callv(JNIEnv *env, jobject object, const char* typeName, return true; } -static jobject callo(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static jobject callo(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature,/* const*/ jvalue* params) { jclass clazz = env->FindClass(typeName); @@ -300,7 +300,7 @@ static jobject callo(JNIEnv *env, jobject object, const char* typeName, const ch return jReturnValue; } -static int calli(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static int calli(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature) { jclass clazz = env->FindClass(typeName); @@ -325,7 +325,7 @@ static int calli(JNIEnv *env, jobject object, const char* typeName, const char* return (int) jReturnValue; } -static int callc(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static int callc(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature) { jclass clazz = env->FindClass(typeName); @@ -351,7 +351,7 @@ static int callc(JNIEnv *env, jobject object, const char* typeName, const char* } -static bool callStaticObject(JNIEnv *env, const char* typeName, const char* methodName, +static bool callStaticObject(JNIEnv *env, const char* typeName, const char* methodName, const char* signature,/* const*/ jvalue* params, jobject& returnValue) { jclass clazz = env->FindClass(typeName); @@ -441,13 +441,13 @@ static bool copyBufferArray(JNIEnv *env, jobject jMesh, const char* jBufferName, class JavaIOStream : public Assimp::IOStream { -private: +private: size_t pos; size_t size; char* buffer; jobject jIOStream; - + public: JavaIOStream(size_t size, char* buffer, jobject jIOStream) : pos(0), @@ -455,28 +455,28 @@ public: buffer(buffer), jIOStream(jIOStream) {}; - - - ~JavaIOStream(void) + + + ~JavaIOStream(void) { free(buffer); - }; + }; size_t Read(void* pvBuffer, size_t pSize, size_t pCount) { const size_t cnt = std::min(pCount,(size - pos)/pSize); const size_t ofs = pSize*cnt; - + memcpy(pvBuffer, buffer + pos, ofs); pos += ofs; - + return cnt; }; - size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) + size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) { return 0; }; - + aiReturn Seek(size_t pOffset, aiOrigin pOrigin) { if (aiOrigin_SET == pOrigin) { @@ -499,40 +499,40 @@ public: } return AI_SUCCESS; }; - + size_t Tell(void) const { return pos; }; - + size_t FileSize() const { return size; }; - + void Flush() {}; - - + + jobject javaObject() { return jIOStream; }; - - + + }; - + class JavaIOSystem : public Assimp::IOSystem { private: JNIEnv* mJniEnv; jobject& mJavaIOSystem; - + public: JavaIOSystem(JNIEnv* env, jobject& javaIOSystem) : mJniEnv(env), mJavaIOSystem(javaIOSystem) {}; - + bool Exists( const char* pFile) const { jvalue params[1]; @@ -544,27 +544,27 @@ class JavaIOSystem : public Assimp::IOSystem { { return (char) callc(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "getOsSeparator", "()C"); }; - + Assimp::IOStream* Open(const char* pFile,const char* pMode = "rb") { jvalue params[2]; params[0].l = mJniEnv->NewStringUTF(pFile); params[1].l = mJniEnv->NewStringUTF(pMode); - - + + jobject jStream = callo(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "open", "(Ljava/lang/String;Ljava/lang/String;)Ljassimp/AiIOStream;", params); if(NULL == jStream) { lprintf("NULL object from AiIOSystem.open\n"); return NULL; } - + size_t size = calli(mJniEnv, jStream, "jassimp/AiIOStream", "getFileSize", "()I"); lprintf("Model file size is %d\n", size); - + char* buffer = (char*)malloc(size); jobject javaBuffer = mJniEnv->NewDirectByteBuffer(buffer, size); - + jvalue readParams[1]; readParams[0].l = javaBuffer; if(call(mJniEnv, jStream, "jassimp/AiIOStream", "read", "(Ljava/nio/ByteBuffer;)Z", readParams)) @@ -581,28 +581,28 @@ class JavaIOSystem : public Assimp::IOSystem { }; void Close( Assimp::IOStream* pFile) { - + jvalue params[1]; params[0].l = ((JavaIOStream*) pFile)->javaObject(); callv(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "close", "(Ljassimp/AiIOStream;)V", params); delete pFile; }; - - + + }; class JavaProgressHandler : public Assimp::ProgressHandler { private: JNIEnv* mJniEnv; jobject& mJavaProgressHandler; - + public: JavaProgressHandler(JNIEnv* env, jobject& javaProgressHandler) : mJniEnv(env), mJavaProgressHandler(javaProgressHandler) {}; - + bool Update(float percentage) { jvalue params[1]; @@ -623,12 +623,12 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) jobject jMesh = NULL; SmartLocalRef refMesh(env, jMesh); - if (!createInstance(env, "jassimp/AiMesh", jMesh)) + if (!createInstance(env, "jassimp/AiMesh", jMesh)) { return false; } - + /* add mesh to m_meshes java.util.List */ jobject jMeshes = NULL; SmartLocalRef refMeshes(env, jMeshes); @@ -671,7 +671,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) /* determine face buffer size */ bool isPureTriangle = cMesh->mPrimitiveTypes == aiPrimitiveType_TRIANGLE; size_t faceBufferSize; - if (isPureTriangle) + if (isPureTriangle) { faceBufferSize = cMesh->mNumFaces * 3 * sizeof(unsigned int); } @@ -715,7 +715,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) /* push face data to java */ if (cMesh->mNumFaces > 0) { - if (isPureTriangle) + if (isPureTriangle) { char* faceBuffer = (char*) malloc(faceBufferSize); @@ -729,7 +729,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) free(faceBuffer); - if (!res) + if (!res) { lprintf("could not copy face data\n"); return false; @@ -750,7 +750,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) memcpy(faceBuffer + faceBufferPos, cMesh->mFaces[face].mIndices, faceDataSize); faceBufferPos += faceDataSize; } - + if (faceBufferPos != faceBufferSize) { /* this should really not happen */ @@ -766,7 +766,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) free(faceBuffer); free(offsetBuffer); - if (!res) + if (!res) { lprintf("could not copy face data\n"); return false; @@ -933,7 +933,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) jobject jBone; SmartLocalRef refBone(env, jBone); - if (!createInstance(env, "jassimp/AiBone", jBone)) + if (!createInstance(env, "jassimp/AiBone", jBone)) { return false; } @@ -988,7 +988,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) wrapParams[0].l = jMatrixArr; jobject jMatrix; SmartLocalRef refMatrix(env, jMatrix); - + if (!callStaticObject(env, "jassimp/Jassimp", "wrapMatrix", "([F)Ljava/lang/Object;", wrapParams, jMatrix)) { return false; @@ -1012,7 +1012,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) { return false; } - + if (!setFloatField(env, jBoneWeight, "m_weight", cBone->mWeights[w].mWeight)) { return false; @@ -1228,7 +1228,7 @@ static bool loadSceneNode(JNIEnv *env, const aiNode *cNode, jobject parent, jobj wrapNodeParams[3].l = jNodeName; jobject jNode; if (!callStaticObject(env, "jassimp/Jassimp", "wrapSceneNode", - "(Ljava/lang/Object;Ljava/lang/Object;[ILjava/lang/String;)Ljava/lang/Object;", wrapNodeParams, jNode)) + "(Ljava/lang/Object;Ljava/lang/Object;[ILjava/lang/String;)Ljava/lang/Object;", wrapNodeParams, jNode)) { return false; } @@ -1287,7 +1287,7 @@ static bool loadSceneGraph(JNIEnv *env, const aiScene* cScene, jobject& jScene) } -static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) { for (unsigned int m = 0; m < cScene->mNumMaterials; m++) { @@ -1320,7 +1320,7 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } /* set texture numbers */ - for (int ttInd = aiTextureType_DIFFUSE; ttInd < aiTextureType_UNKNOWN; ttInd++) + for (int ttInd = aiTextureType_DIFFUSE; ttInd < aiTextureType_UNKNOWN; ttInd++) { aiTextureType tt = static_cast(ttInd); @@ -1341,7 +1341,7 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) for (unsigned int p = 0; p < cMaterial->mNumProperties; p++) { - //printf("%s - %u - %u\n", cScene->mMaterials[m]->mProperties[p]->mKey.C_Str(), + //printf("%s - %u - %u\n", cScene->mMaterials[m]->mProperties[p]->mKey.C_Str(), // cScene->mMaterials[m]->mProperties[p]->mSemantic, // cScene->mMaterials[m]->mProperties[p]->mDataLength); @@ -1362,9 +1362,9 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) /* special case conversion for color3 */ - if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && + if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && cProperty->mType == aiPTI_Float && - cProperty->mDataLength == 3 * sizeof(float)) + cProperty->mDataLength == 3 * sizeof(float)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1380,16 +1380,16 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } /* special case conversion for color4 */ - else if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && + else if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && cProperty->mType == aiPTI_Float && - cProperty->mDataLength == 4 * sizeof(float)) + cProperty->mDataLength == 4 * sizeof(float)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1406,13 +1406,13 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else if (cProperty->mType == aiPTI_Float && cProperty->mDataLength == sizeof(float)) + else if (cProperty->mType == aiPTI_Float && cProperty->mDataLength == sizeof(float)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1425,13 +1425,13 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else if (cProperty->mType == aiPTI_Integer && cProperty->mDataLength == sizeof(int)) + else if (cProperty->mType == aiPTI_Integer && cProperty->mDataLength == sizeof(int)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1444,26 +1444,26 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else if (cProperty->mType == aiPTI_String) + else if (cProperty->mType == aiPTI_String) { /* skip length prefix */ jobject jData = env->NewStringUTF(cProperty->mData + 4); SmartLocalRef refData(env, jData); constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else + else { constructorParams[4].i = cProperty->mDataLength; @@ -1521,7 +1521,7 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } -static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) { lprintf("converting %d animations ...\n", cScene->mNumAnimations); @@ -1603,19 +1603,19 @@ static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) } /* copy keys */ - if (!copyBuffer(env, jNodeAnim, "m_posKeys", cNodeAnim->mPositionKeys, + if (!copyBuffer(env, jNodeAnim, "m_posKeys", cNodeAnim->mPositionKeys, cNodeAnim->mNumPositionKeys * sizeof(aiVectorKey))) { return false; } - if (!copyBuffer(env, jNodeAnim, "m_rotKeys", cNodeAnim->mRotationKeys, + if (!copyBuffer(env, jNodeAnim, "m_rotKeys", cNodeAnim->mRotationKeys, cNodeAnim->mNumRotationKeys * sizeof(aiQuatKey))) { return false; } - if (!copyBuffer(env, jNodeAnim, "m_scaleKeys", cNodeAnim->mScalingKeys, + if (!copyBuffer(env, jNodeAnim, "m_scaleKeys", cNodeAnim->mScalingKeys, cNodeAnim->mNumScalingKeys * sizeof(aiVectorKey))) { return false; @@ -1629,7 +1629,7 @@ static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) } -static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) { lprintf("converting %d lights ...\n", cScene->mNumLights); @@ -1712,7 +1712,7 @@ static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) params[9].l = jAmbient; params[10].f = cLight->mAngleInnerCone; params[11].f = cLight->mAngleOuterCone; - + if (!createInstance(env, "jassimp/AiLight", "(Ljava/lang/String;ILjava/lang/Object;Ljava/lang/Object;FFFLjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;FF)V", params, jLight)) { @@ -1736,13 +1736,13 @@ static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) } } - lprintf("converting lights finished ...\n"); + lprintf("converting lights finished ...\n"); return true; } -static bool loadCameras(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadCameras(JNIEnv *env, const aiScene* cScene, jobject& jScene) { lprintf("converting %d cameras ...\n", cScene->mNumCameras); @@ -1799,7 +1799,7 @@ static bool loadCameras(JNIEnv *env, const aiScene* cScene, jobject& jScene) params[5].f = cCamera->mClipPlaneNear; params[6].f = cCamera->mClipPlaneFar; params[7].f = cCamera->mAspect; - + if (!createInstance(env, "jassimp/AiCamera", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;FFFF)V", params, jCamera)) { @@ -1901,25 +1901,25 @@ JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile (JNIEnv *env, jclass jClazz, jstring jFilename, jlong postProcess, jobject ioSystem, jobject progressHandler) { - jobject jScene = NULL; + jobject jScene = NULL; /* convert params */ const char* cFilename = env->GetStringUTFChars(jFilename, NULL); - + Assimp::Importer imp; - + if(ioSystem != NULL) { - imp.SetIOHandler(new JavaIOSystem(env, ioSystem)); + imp.SetIOHandler(new JavaIOSystem(env, ioSystem)); lprintf("Created aiFileIO\n"); } - + if(progressHandler != NULL) { imp.SetProgressHandler(new JavaProgressHandler(env, progressHandler)); } - + lprintf("opening file: %s\n", cFilename); /* do import */ @@ -1931,7 +1931,7 @@ JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile goto error; } - if (!createInstance(env, "jassimp/AiScene", jScene)) + if (!createInstance(env, "jassimp/AiScene", jScene)) { goto error; } diff --git a/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h b/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h index 0e6dfab3c..1803ad122 100644 --- a/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h +++ b/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h @@ -15,7 +15,7 @@ /* workflow: - 1) create a new scene wrapper + 1) create a new scene wrapper 2) populate an array of of meshHelpers for each mesh in the original scene 3) (eventually) create an animator instance 4) scale the asset (needed?) @@ -24,16 +24,16 @@ 5b) create a static vertex buffer 5c) create index buffer 5d) populate the index buffer - 5e) (eventually) gather weights + 5e) (eventually) gather weights */ #define BUFFER_OFFSET(i) ((char *)NULL + (i)) -struct Vertex +struct Vertex { aiVector3D vPosition; aiVector3D vNormal; - + aiColor4D dColorDiffuse; aiVector3D vTangent; aiVector3D vBitangent; @@ -46,33 +46,33 @@ struct Vertex // Helper Class to store GPU related resources from a given aiMesh // Modeled after AssimpView asset helper -@interface MeshHelper : NSObject -{ +@interface MeshHelper : NSObject +{ // Display list ID, this one shots *all drawing* of the mesh. Only ever use this to draw. Booya. GLuint displayList; - + // VAO that encapsulates all VBO drawing state GLuint vao; - + // VBOs GLuint vertexBuffer; GLuint indexBuffer; GLuint normalBuffer; GLuint numIndices; - + // texture GLuint textureID; - - // Material + + // Material aiColor4D diffuseColor; aiColor4D specularColor; aiColor4D ambientColor; aiColor4D emissiveColor; - + GLfloat opacity; GLfloat shininess; GLfloat specularStrength; - + BOOL twoSided; } diff --git a/samples/SimpleAssimpViewX/MyDocument.h b/samples/SimpleAssimpViewX/MyDocument.h index 16745dc3c..67675e306 100644 --- a/samples/SimpleAssimpViewX/MyDocument.h +++ b/samples/SimpleAssimpViewX/MyDocument.h @@ -20,27 +20,27 @@ #import -@interface MyDocument : NSPersistentDocument +@interface MyDocument : NSPersistentDocument { CVDisplayLinkRef _displayLink; NSOpenGLContext* _glContext; NSOpenGLPixelFormat* _glPixelFormat; - + NSView* _view; - + // Assimp Stuff aiScene* _scene; aiVector3D scene_min, scene_max, scene_center; - double normalizedScale; - + double normalizedScale; + // Our array of textures. GLuint *textureIds; - + // only used if we use - NSMutableArray* modelMeshes; + NSMutableArray* modelMeshes; BOOL builtBuffers; - - NSMutableDictionary* textureDictionary; // Array of Dicionaries that map image filenames to textureIds + + NSMutableDictionary* textureDictionary; // Array of Dicionaries that map image filenames to textureIds } @property (retain) IBOutlet NSView* _view; diff --git a/samples/SimpleOpenGL/Sample_SimpleOpenGL.c b/samples/SimpleOpenGL/Sample_SimpleOpenGL.c index bcb109564..7aa306ed4 100644 --- a/samples/SimpleOpenGL/Sample_SimpleOpenGL.c +++ b/samples/SimpleOpenGL/Sample_SimpleOpenGL.c @@ -29,7 +29,7 @@ /* ---------------------------------------------------------------------------- */ inline static void print_run_command(const char* command_name) { - printf("Run '%s %s' for more information.\n", + printf("Run '%s %s' for more information.\n", PROJECT_NAME, command_name); } @@ -43,7 +43,7 @@ inline static void print_error(const char* msg) { /* ---------------------------------------------------------------------------- */ inline static void print_usage() { - static const char* usage_format = + static const char* usage_format = "Usage: " PROJECT_NAME " " DOUBLE_NEW_LINE diff --git a/samples/SimpleTexturedDirectx11/CMakeLists.txt b/samples/SimpleTexturedDirectx11/CMakeLists.txt index 82144caa9..007ada3af 100644 --- a/samples/SimpleTexturedDirectx11/CMakeLists.txt +++ b/samples/SimpleTexturedDirectx11/CMakeLists.txt @@ -24,13 +24,13 @@ LINK_DIRECTORIES( ) ADD_EXECUTABLE( assimp_simpletextureddirectx11 WIN32 - SimpleTexturedDirectx11/Mesh.h + SimpleTexturedDirectx11/Mesh.h SimpleTexturedDirectx11/ModelLoader.cpp SimpleTexturedDirectx11/ModelLoader.h #SimpleTexturedDirectx11/PixelShader.hlsl SimpleTexturedDirectx11/TextureLoader.cpp - SimpleTexturedDirectx11/TextureLoader.h - #SimpleTexturedDirectx11/VertexShader.hlsl + SimpleTexturedDirectx11/TextureLoader.h + #SimpleTexturedDirectx11/VertexShader.hlsl SimpleTexturedDirectx11/main.cpp SimpleTexturedDirectx11/SafeRelease.hpp ${SAMPLES_SHARED_CODE_DIR}/UTFConverter.cpp diff --git a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp index 92760d691..18bb10f1e 100644 --- a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp +++ b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp @@ -1,6 +1,6 @@ #include "ModelLoader.h" -ModelLoader::ModelLoader() : +ModelLoader::ModelLoader() : dev_(nullptr), devcon_(nullptr), meshes_(), diff --git a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp index 01ba343e8..a02c53ca6 100644 --- a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp +++ b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp @@ -140,16 +140,16 @@ static WICConvert g_WICConvert[] = { GUID_WICPixelFormatBlackWhite, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM - { GUID_WICPixelFormat1bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat2bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat4bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat8bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat1bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat2bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat4bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat8bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat2bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM - { GUID_WICPixelFormat4bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + { GUID_WICPixelFormat2bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + { GUID_WICPixelFormat4bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM - { GUID_WICPixelFormat16bppGrayFixedPoint, GUID_WICPixelFormat16bppGrayHalf }, // DXGI_FORMAT_R16_FLOAT - { GUID_WICPixelFormat32bppGrayFixedPoint, GUID_WICPixelFormat32bppGrayFloat }, // DXGI_FORMAT_R32_FLOAT + { GUID_WICPixelFormat16bppGrayFixedPoint, GUID_WICPixelFormat16bppGrayHalf }, // DXGI_FORMAT_R16_FLOAT + { GUID_WICPixelFormat32bppGrayFixedPoint, GUID_WICPixelFormat32bppGrayFloat }, // DXGI_FORMAT_R32_FLOAT #ifdef DXGI_1_2_FORMATS @@ -165,10 +165,10 @@ static WICConvert g_WICConvert[] = { GUID_WICPixelFormat32bppBGR101010, GUID_WICPixelFormat32bppRGBA1010102 }, // DXGI_FORMAT_R10G10B10A2_UNORM - { GUID_WICPixelFormat24bppBGR, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat24bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat32bppPBGRA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat32bppPRGBA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat24bppBGR, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat24bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppPBGRA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppPRGBA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM { GUID_WICPixelFormat48bppRGB, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat48bppBGR, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM @@ -176,21 +176,21 @@ static WICConvert g_WICConvert[] = { GUID_WICPixelFormat64bppPRGBA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat64bppPBGRA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM - { GUID_WICPixelFormat48bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat48bppBGRFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppRGBAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppBGRAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat48bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppBGRFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppBGRAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat96bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppPRGBAFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppRGBFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppRGBAFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat96bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppPRGBAFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBAFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat32bppCMYK, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppCMYK, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM { GUID_WICPixelFormat64bppCMYK, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat40bppCMYKAlpha, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat80bppCMYKAlpha, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM @@ -198,7 +198,7 @@ static WICConvert g_WICConvert[] = #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) { GUID_WICPixelFormat32bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM { GUID_WICPixelFormat64bppRGB, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM - { GUID_WICPixelFormat64bppPRGBAHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppPRGBAHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT #endif // We don't support n-channel formats diff --git a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp index 02e2b6088..3f8d15320 100644 --- a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp +++ b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp @@ -128,7 +128,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, int argc; LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc); if (!argv) { - MessageBox(nullptr, + MessageBox(nullptr, TEXT("An error occured while reading command line arguments."), TEXT("Error!"), MB_ICONERROR | MB_OK); @@ -145,8 +145,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, // Ensure that a model file has been specified. if (argc < 2) { - MessageBox(nullptr, - TEXT("No model file specified. The program will now close."), + MessageBox(nullptr, + TEXT("No model file specified. The program will now close."), TEXT("Error!"), MB_ICONERROR | MB_OK); free_command_line_allocated_memory(); @@ -157,7 +157,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, g_ModelPath = UTFConverter(argv[1]).str(); free_command_line_allocated_memory(); - + WNDCLASSEX wc; MSG msg; @@ -573,7 +573,7 @@ void InitGraphics() HRESULT CompileShaderFromFile(LPCWSTR pFileName, const D3D_SHADER_MACRO* pDefines, LPCSTR pEntryPoint, LPCSTR pShaderModel, ID3DBlob** ppBytecodeBlob) { UINT compileFlags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR; - + #ifdef _DEBUG compileFlags |= D3DCOMPILE_DEBUG; #endif diff --git a/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp b/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp index aa2344118..9ceeec62e 100644 --- a/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp +++ b/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp @@ -579,7 +579,7 @@ void KillGLWindow() // Properly Kill The Window if (!DestroyWindow(g_hWnd)) // Are We Able To Destroy The Window MessageBox(nullptr, TEXT("Could Not Release hWnd."), TEXT("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION); g_hWnd = nullptr; - } + } if (g_hInstance) { @@ -727,7 +727,7 @@ BOOL CreateGLWindow(const char* title, int width, int height, int bits, bool ful return FALSE; } - if (nullptr == (hRC=wglCreateContext(hDC))) + if (nullptr == (hRC=wglCreateContext(hDC))) { abortGLInit("Can't Create A GL Rendering Context."); return FALSE; diff --git a/test/models-nonbsd/3D/mar_rifle.source.txt b/test/models-nonbsd/3D/mar_rifle.source.txt index c0cd5fe6d..d7183b7b6 100644 --- a/test/models-nonbsd/3D/mar_rifle.source.txt +++ b/test/models-nonbsd/3D/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/3DS/cart_wheel.source.txt b/test/models-nonbsd/3DS/cart_wheel.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/3DS/cart_wheel.source.txt +++ b/test/models-nonbsd/3DS/cart_wheel.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/3DS/mar_rifle.source.txt b/test/models-nonbsd/3DS/mar_rifle.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/3DS/mar_rifle.source.txt +++ b/test/models-nonbsd/3DS/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/3DS/mp5_sil.source.txt b/test/models-nonbsd/3DS/mp5_sil.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/3DS/mp5_sil.source.txt +++ b/test/models-nonbsd/3DS/mp5_sil.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/ASE/Rifle.source.txt b/test/models-nonbsd/ASE/Rifle.source.txt index 1b96f8564..802c57045 100644 --- a/test/models-nonbsd/ASE/Rifle.source.txt +++ b/test/models-nonbsd/ASE/Rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/ASE/Rifle2.source.txt b/test/models-nonbsd/ASE/Rifle2.source.txt index 3fa628db4..7b50d1645 100644 --- a/test/models-nonbsd/ASE/Rifle2.source.txt +++ b/test/models-nonbsd/ASE/Rifle2.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/BLEND/fleurOptonl.source.txt b/test/models-nonbsd/BLEND/fleurOptonl.source.txt index b9c58b5d9..d4c583620 100644 --- a/test/models-nonbsd/BLEND/fleurOptonl.source.txt +++ b/test/models-nonbsd/BLEND/fleurOptonl.source.txt @@ -10,7 +10,7 @@ Puoi utilizzarlo liberamente, modificarlo e migliorarlo. ************* Ma attenzione!! ********************* Nel modificare e utilizzare i modelli, ricorda comunque sempre che : -"il diritto morale all'integrità dell'opera (diritto dell'autore originale) non ti consente di apportare all'opera deformazioni o modificazioni, od ogni altro atto a danno dell'opera stessa, che possano essere di pregiudizio all'onore o alla reputazione dell'autore (la valutazione della lesione dell'onore o della reputazione avviene sulla base di elementi psicologici soggettivi)" +"il diritto morale all'integrità dell'opera (diritto dell'autore originale) non ti consente di apportare all'opera deformazioni o modificazioni, od ogni altro atto a danno dell'opera stessa, che possano essere di pregiudizio all'onore o alla reputazione dell'autore (la valutazione della lesione dell'onore o della reputazione avviene sulla base di elementi psicologici soggettivi)" (dalle faq di Creative Commons Italia) http://www.creativecommons.it/node/165#27 @@ -26,7 +26,7 @@ In particolare, sara' da me considerata lesione d'onore l'uso e/o l'adattamento Se lo fate rischiate la denuncia e il risarcimento danni. Per qualsiasi chiarimento in proposito potete comunque scrivermi. -Questo e' un diritto garantito per legge a me come ad ogni altro artista. +Questo e' un diritto garantito per legge a me come ad ogni altro artista. L'utilizzo della Creative Commons non influisce su questo diritto. ************************************************ @@ -45,7 +45,7 @@ work, so you are not allowed to do exactely what you want with my models. The author (that is me) has the right to prevent distortion, mutilation, or other modification of his work which would be prejudicial to his or her honor or reputation. Me, i consider to be prejudicial to my honor, the modification and/or the use of my models in projects that are related to: - + Racism and hatred instigation. War promotion. Cruelty toward animals. @@ -69,15 +69,15 @@ Note Questo e' stato il mio secondo modello completo. Per questo motivo potrebbe contenere errori e imprecisioni. - + Al momento, questi sono i difetti che ho notato: - + - Non e' nell'origine degli assi. - lo scheletro ha il centro spostato di lato - Nel texture sheet c'e' spazio sprecato. - - + + ################### Notes @@ -86,7 +86,7 @@ This was my first complete model, so it probably contains something wrong. At the moment, this is what i noticed: - + - She's not in the origin of axis - Armature's center is not in armature's center. - Texture sheet contains wasted space. diff --git a/test/models-nonbsd/DXF/rifle.source.txt b/test/models-nonbsd/DXF/rifle.source.txt index a2585afad..fa25f10db 100644 --- a/test/models-nonbsd/DXF/rifle.source.txt +++ b/test/models-nonbsd/DXF/rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt b/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt b/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt index 94ee48e4a..7c81e612f 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt @@ -1,9 +1,9 @@ From kwxport http://www.kwxport.org/ ->> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt b/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt b/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt b/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt b/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt index 94ee48e4a..7c81e612f 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt @@ -1,9 +1,9 @@ From kwxport http://www.kwxport.org/ ->> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt b/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt b/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt b/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt index aaa244217..80afd30a7 100644 --- a/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt +++ b/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt @@ -1,10 +1,10 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas -"These 3d models are contributed by John Hoffman and are based on -characters from a cartoon show called "Jayce and the wheel warriors" +"These 3d models are contributed by John Hoffman and are based on +characters from a cartoon show called "Jayce and the wheel warriors" (except the marauder) John's site: http://www3.sympatico.ca/john.hoffman" ===================================================================== @@ -15,9 +15,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/LWO/LWO2/rifle.source.txt b/test/models-nonbsd/LWO/LWO2/rifle.source.txt index 5523bbc0c..5774ecc0e 100644 --- a/test/models-nonbsd/LWO/LWO2/rifle.source.txt +++ b/test/models-nonbsd/LWO/LWO2/rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/MD2/source.txt b/test/models-nonbsd/MD2/source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/MD2/source.txt +++ b/test/models-nonbsd/MD2/source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/MD5/BoarMan.source.txt b/test/models-nonbsd/MD5/BoarMan.source.txt index 50b2dfb53..69f0c79a2 100644 --- a/test/models-nonbsd/MD5/BoarMan.source.txt +++ b/test/models-nonbsd/MD5/BoarMan.source.txt @@ -1,8 +1,8 @@ -License: Creative Commons +License: Creative Commons - Remix - Share alike - Attribution Author: zphr (Christian Lenke) - + diff --git a/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt b/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt index 2febb0583..84ce9fd95 100644 --- a/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt +++ b/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt @@ -4,9 +4,9 @@ Version : 1 Date : 11/05/97 Author : Kenneth Whelan Email : JWHELAN@pop.prodigy.net -Credits : id software, Larry Hama, Steven Polge, and Rene Post for making Quake ME - - +Credits : id software, Larry Hama, Steven Polge, and Rene Post for making Quake ME + + Build time: ??? Time??? @@ -14,7 +14,7 @@ Type of Mod ----------- Quake C : no Sound : no -MDL : Yes +MDL : Yes Format of QuakeC (if a Quake C Mod) @@ -29,8 +29,8 @@ Description of the Modification ------------------------------- This is a new player.mdl for quake. It's main use is for bots. The Skins are Snake Eyes v4, Duke v3, Low-Light, -Storm Shadow v2, Shockwave, Repeater, Gung-Ho, Shipwreck, Dusty v3, and -Tunnel Rat v2. +Storm Shadow v2, Shockwave, Repeater, Gung-Ho, Shipwreck, Dusty v3, and +Tunnel Rat v2. @@ -40,7 +40,7 @@ None that I know of. How to Install the Modification ------------------------------- -First back up the current player.mdl(copy player.mdl player.bak). Just put +First back up the current player.mdl(copy player.mdl player.bak). Just put it in the progs dir in the hack your using, if any. Technical Details diff --git a/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt b/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt index c07c2f126..cbddccb7e 100644 --- a/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt +++ b/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt @@ -16,17 +16,17 @@ E-mail: sgalbrai@linknet.kitsap.lib.wa.us WWW: www.oz.net/~simitar -This model can be used or modified for any purpose +This model can be used or modified for any purpose as long as this text document is included with it -and all modellers listed in this document are +and all modellers listed in this document are listed wherever credits are appropriate for that purpose. -Help Wanted: +Help Wanted: The Free Models Project can use help with -models and in other areas, such as legal boilerplate +models and in other areas, such as legal boilerplate for models. WWW: www.oz.net/~simitar/model.html diff --git a/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt b/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt index dc0149b01..3b3dd9042 100644 --- a/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt +++ b/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt @@ -8,7 +8,7 @@ that the FMP is a great concept !! There is some movement in the model. The legs can be animated for walking, stomping, or running -around ! +around ! Ok, it's my first model, so I will work on less polygony in the future ;-) @@ -18,9 +18,9 @@ Contact: ebuy@optelnow.net (E-MAIL) Date Created: 7/07/2000 ====================================================== -This model can be used or modified for any purpose +This model can be used or modified for any purpose as long as this text document is included with it -and all modelers listed in this document are +and all modelers listed in this document are listed wherever credits are appropriate for that purpose. diff --git a/test/models-nonbsd/NFF/NFFSense8/credits.txt b/test/models-nonbsd/NFF/NFFSense8/credits.txt index f3cef4d09..bad7fbf15 100644 --- a/test/models-nonbsd/NFF/NFFSense8/credits.txt +++ b/test/models-nonbsd/NFF/NFFSense8/credits.txt @@ -1,4 +1,4 @@ teapot.nff, home4.nff - http://www.martinreddy.net/ukvrsig/wtk.html -cokecan.nff -www.vrupl.evl.uic.edu/Eng591_Pages/cokecan.nff +cokecan.nff -www.vrupl.evl.uic.edu/Eng591_Pages/cokecan.nff TODO: License status to be confirmed diff --git a/test/models-nonbsd/OBJ/rifle.source.txt b/test/models-nonbsd/OBJ/rifle.source.txt index 1d2cec5cf..f7b93fd0f 100644 --- a/test/models-nonbsd/OBJ/rifle.source.txt +++ b/test/models-nonbsd/OBJ/rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notices found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models/3DS/UVTransformTest/note.txt b/test/models/3DS/UVTransformTest/note.txt index 4c8bfedd2..9a6bab030 100644 --- a/test/models/3DS/UVTransformTest/note.txt +++ b/test/models/3DS/UVTransformTest/note.txt @@ -1,5 +1,5 @@ -All 'mirror' files are not absolutely correct. That's mainly -because it's difficult convert Max' handling of mirroring to +All 'mirror' files are not absolutely correct. That's mainly +because it's difficult convert Max' handling of mirroring to our's. In other words: TO DO, but only if someone REALLY needs it. diff --git a/test/models/ASE/MotionCaptureROM.source.txt b/test/models/ASE/MotionCaptureROM.source.txt index 2b961906a..f34e056ef 100644 --- a/test/models/ASE/MotionCaptureROM.source.txt +++ b/test/models/ASE/MotionCaptureROM.source.txt @@ -1,3 +1,3 @@ -"MotionCaptureROM.ase" - Free for any purpose. +"MotionCaptureROM.ase" - Free for any purpose. NOTE: The errors in the middle of the animation are caused by problems during recording, it's not an importer issue. diff --git a/test/models/Collada/kwxport_test_vcolors.dae.source.txt b/test/models/Collada/kwxport_test_vcolors.dae.source.txt index 94ee48e4a..7c81e612f 100644 --- a/test/models/Collada/kwxport_test_vcolors.dae.source.txt +++ b/test/models/Collada/kwxport_test_vcolors.dae.source.txt @@ -1,9 +1,9 @@ From kwxport http://www.kwxport.org/ ->> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models/IRR/warn_dwarf_scaling_is_intended.txt b/test/models/IRR/warn_dwarf_scaling_is_intended.txt index f651a8643..d0e4ea361 100644 --- a/test/models/IRR/warn_dwarf_scaling_is_intended.txt +++ b/test/models/IRR/warn_dwarf_scaling_is_intended.txt @@ -1,3 +1,3 @@ for dawfInCellar_ChildOfCellar & dawfInCellar_SameHierarchy: -the strange scalings of cellar and dwarf are intended. +the strange scalings of cellar and dwarf are intended. diff --git a/test/models/MD2/faerie-source.txt b/test/models/MD2/faerie-source.txt index 4906ff4d1..afb00a170 100644 --- a/test/models/MD2/faerie-source.txt +++ b/test/models/MD2/faerie-source.txt @@ -1,5 +1,5 @@ -From IRRLICHT/media +From IRRLICHT/media The Irrlicht Engine License diff --git a/test/models/MD2/sidney-source.txt b/test/models/MD2/sidney-source.txt index 4906ff4d1..afb00a170 100644 --- a/test/models/MD2/sidney-source.txt +++ b/test/models/MD2/sidney-source.txt @@ -1,5 +1,5 @@ -From IRRLICHT/media +From IRRLICHT/media The Irrlicht Engine License diff --git a/test/models/Q3D/E-AT-AT.source.txt b/test/models/Q3D/E-AT-AT.source.txt index 2df8826f1..900ee552c 100644 --- a/test/models/Q3D/E-AT-AT.source.txt +++ b/test/models/Q3D/E-AT-AT.source.txt @@ -5,4 +5,4 @@ Downloaded 4th November 08 (Obama ftw!) Copyright notice found on the page: Where do the models in the archive come from? -All 3D files available from the3darchive.com are from the public domain. +All 3D files available from the3darchive.com are from the public domain. diff --git a/test/models/Q3D/earth.source.txt b/test/models/Q3D/earth.source.txt index 2df8826f1..900ee552c 100644 --- a/test/models/Q3D/earth.source.txt +++ b/test/models/Q3D/earth.source.txt @@ -5,4 +5,4 @@ Downloaded 4th November 08 (Obama ftw!) Copyright notice found on the page: Where do the models in the archive come from? -All 3D files available from the3darchive.com are from the public domain. +All 3D files available from the3darchive.com are from the public domain. diff --git a/test/models/WRL/credits.txt b/test/models/WRL/credits.txt index 055f73734..7be7fa192 100644 --- a/test/models/WRL/credits.txt +++ b/test/models/WRL/credits.txt @@ -1,2 +1,2 @@ "MotionCaptureROM.ase" Recorded using ViconIQ. -Converted to VRML with 3DS Max 2008. +Converted to VRML with 3DS Max 2008. diff --git a/test/models/X/anim_test.txt b/test/models/X/anim_test.txt index 3d270dc7d..f2ef6c056 100644 --- a/test/models/X/anim_test.txt +++ b/test/models/X/anim_test.txt @@ -4,6 +4,6 @@ Frame 1 - 10: Zylinder knickt ein, so dass der Knick in Richtung z+ zeigt. Frame 10 - 18: Zylinder-Spitze streckt sich in Richtung z-. Frame 18 - 24: Zylinder-Spitze bewegt sich zu Position in Richtung x+ -Remarks: The exporter failed here for some reasons... although the mesh referres to four bones, only two of them are stored in the corresponding node hierarchy. So you have a mesh with 4 bones, a hirarchy with 2 nodes and a animation that affects only those two nodes. +Remarks: The exporter failed here for some reasons... although the mesh referres to four bones, only two of them are stored in the corresponding node hierarchy. So you have a mesh with 4 bones, a hirarchy with 2 nodes and a animation that affects only those two nodes. There is no timing given for the animation. You have to scale the animation manually. For this file, the timing seems to be 24 ticks per second. diff --git a/test/models/X/kwxport_test_cubewithvcolors.source.txt b/test/models/X/kwxport_test_cubewithvcolors.source.txt index 94ee48e4a..7c81e612f 100644 --- a/test/models/X/kwxport_test_cubewithvcolors.source.txt +++ b/test/models/X/kwxport_test_cubewithvcolors.source.txt @@ -1,9 +1,9 @@ From kwxport http://www.kwxport.org/ ->> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models/X/test.txt b/test/models/X/test.txt index eaf9d9c3c..9452855b3 100644 --- a/test/models/X/test.txt +++ b/test/models/X/test.txt @@ -1,3 +1,3 @@ Simple textured test cube exported from Maya. Has a texture that does label each cube side uniquely, but the sides do not match DirectX coordinate space. -Is not readable using D3DXLoadFrameHierarchy, needs custom text parsing. +Is not readable using D3DXLoadFrameHierarchy, needs custom text parsing. diff --git a/test/models/invalid/readme.txt b/test/models/invalid/readme.txt index cab740a84..6ad8b4380 100644 --- a/test/models/invalid/readme.txt +++ b/test/models/invalid/readme.txt @@ -4,9 +4,9 @@ GENERAL ********************************************************* -The files in this directory are invalid ... some of them are empty, +The files in this directory are invalid ... some of them are empty, others have invalid vertices or faces, others are prepared to make - assimp allocate a few hundreds gigs of memory ... most are + assimp allocate a few hundreds gigs of memory ... most are actually regression tests, i.e. there was once a bugfix that fixed the respective loaders. @@ -18,8 +18,8 @@ crash. FILES ********************************************************* -OutOfMemory.off - the number of faces is invalid. There won't be - enough memory so std::vector::reserve() will most likely fail. +OutOfMemory.off - the number of faces is invalid. There won't be + enough memory so std::vector::reserve() will most likely fail. The exception should be caught in Importer.cpp. empty. - These files are completely empty. The corresponding diff --git a/test/regression/README.txt b/test/regression/README.txt index 3e90a143b..a37da9255 100644 --- a/test/regression/README.txt +++ b/test/regression/README.txt @@ -8,7 +8,7 @@ against a regression database provided with assimp (db.zip). A few failures are totally fine (see sections 7+). You need to worry if a huge majority of all files in a particular format (or post-processing configuration) fails as this might be a sign of a recent regression in assimp's codebase or -gross incompatibility with your system or compiler. +gross incompatibility with your system or compiler. 2) What do I need? --------------------------------------------------------------------------------- @@ -53,8 +53,8 @@ Edit the reg_settings.py file and add the path to your repository to The regression database includes mini dumps of the aiScene data structure, i.e. the scene hierarchy plus the sizes of all data arrays MUST match. Floating-point data buffers, such as vertex positions are handled less strictly: min, max and -average values are stored with low precision. This takes hardware- or -compiler-specific differences in floating-point computations into account. +average values are stored with low precision. This takes hardware- or +compiler-specific differences in floating-point computations into account. Generally, almost all significant regressions will be detected while the number of false positives is relatively low. diff --git a/test/unit/AbstractImportExportBase.h b/test/unit/AbstractImportExportBase.h index 72530aedc..7651d2e52 100644 --- a/test/unit/AbstractImportExportBase.h +++ b/test/unit/AbstractImportExportBase.h @@ -67,7 +67,7 @@ bool AbstractImportExportBase::importerTest() { return true; } -inline +inline bool AbstractImportExportBase::exporterTest() { return true; } diff --git a/test/unit/Common/utStandardShapes.cpp b/test/unit/Common/utStandardShapes.cpp index a5df5d898..e1bb6eef9 100644 --- a/test/unit/Common/utStandardShapes.cpp +++ b/test/unit/Common/utStandardShapes.cpp @@ -51,7 +51,7 @@ TEST_F( utStandardShapes, testMakeMesh ) { // The mNumIndices member of the second face is now incorrect const auto& face = aiMeshPtr->mFaces[0]; - EXPECT_EQ(face.mNumIndices, numIndicesPerPrimitive); + EXPECT_EQ(face.mNumIndices, numIndicesPerPrimitive); delete aiMeshPtr; } diff --git a/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp b/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp index ff3b4930c..6fa92f950 100644 --- a/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp +++ b/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp @@ -73,7 +73,7 @@ public: "Bone_3" | "" <----+ "Bone_2" | - "Bone_5" | + "Bone_5" | "" <----+ "" <----+ */ @@ -139,7 +139,7 @@ public: $body "Bodypart_1" <--+ | $body "Bodypart_2" | | $body "Bodypart1" | | - $body "Bodypart" ---|--+ + $body "Bodypart" ---|--+ $body "Bodypart_1" ---+ | $body "Bodypart2" | $body "Bodypart" ------+ diff --git a/test/unit/RandomNumberGeneration.h b/test/unit/RandomNumberGeneration.h index 81fcfb59c..892e78c06 100644 --- a/test/unit/RandomNumberGeneration.h +++ b/test/unit/RandomNumberGeneration.h @@ -53,11 +53,11 @@ class RandomUniformRealGenerator { public: RandomUniformRealGenerator() : dist_(), - rd_(), + rd_(), re_(rd_()) { // empty } - + RandomUniformRealGenerator(T min, T max) : dist_(min, max), rd_(), diff --git a/test/unit/SceneDiffer.cpp b/test/unit/SceneDiffer.cpp index 6ea28671a..368589bf3 100644 --- a/test/unit/SceneDiffer.cpp +++ b/test/unit/SceneDiffer.cpp @@ -48,7 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { -SceneDiffer::SceneDiffer() +SceneDiffer::SceneDiffer() : m_diffs() { // empty } diff --git a/test/unit/SceneDiffer.h b/test/unit/SceneDiffer.h index 9e04c7210..e0dc6005a 100644 --- a/test/unit/SceneDiffer.h +++ b/test/unit/SceneDiffer.h @@ -72,4 +72,4 @@ private: std::vector m_diffs; }; -} +} diff --git a/test/unit/TestIOSystem.h b/test/unit/TestIOSystem.h index fdc3cc49b..4a42b23f0 100644 --- a/test/unit/TestIOSystem.h +++ b/test/unit/TestIOSystem.h @@ -61,7 +61,7 @@ public: virtual ~TestIOSystem() { // empty } - + virtual bool Exists( const char* ) const { return true; } diff --git a/test/unit/utDefaultIOStream.cpp b/test/unit/utDefaultIOStream.cpp index 800fddbc9..d3e2c8a7e 100644 --- a/test/unit/utDefaultIOStream.cpp +++ b/test/unit/utDefaultIOStream.cpp @@ -66,7 +66,7 @@ TEST_F( utDefaultIOStream, FileSizeTest ) { { auto written = std::fwrite(data, sizeof(*data), dataCount, fs ); EXPECT_NE( 0U, written ); - + auto vflush = std::fflush( fs ); ASSERT_EQ(vflush, 0); diff --git a/test/unit/utFBXImporterExporter.cpp b/test/unit/utFBXImporterExporter.cpp index c78d56b97..4cfc9b152 100644 --- a/test/unit/utFBXImporterExporter.cpp +++ b/test/unit/utFBXImporterExporter.cpp @@ -365,7 +365,7 @@ TEST_F(utFBXImporterExporter, importMaxPbrMaterialsMetalRoughness) { float bumpMapAmt; // Presumably amount. ASSERT_EQ(mat->Get("$raw.3dsMax|main|bump_map_amt", aiTextureType_NONE, 0, bumpMapAmt), aiReturn_SUCCESS); EXPECT_EQ(bumpMapAmt, 0.75f); - + aiColor4D emitColor; ASSERT_EQ(mat->Get("$raw.3dsMax|main|emit_color", aiTextureType_NONE, 0, emitColor), aiReturn_SUCCESS); EXPECT_EQ(emitColor, aiColor4D(1, 1, 0, 1)); @@ -418,7 +418,7 @@ TEST_F(utFBXImporterExporter, importMaxPbrMaterialsSpecularGloss) { float bumpMapAmt; // Presumably amount. ASSERT_EQ(mat->Get("$raw.3dsMax|main|bump_map_amt", aiTextureType_NONE, 0, bumpMapAmt), aiReturn_SUCCESS); EXPECT_EQ(bumpMapAmt, 0.66f); - + aiColor4D emitColor; ASSERT_EQ(mat->Get("$raw.3dsMax|main|emit_color", aiTextureType_NONE, 0, emitColor), aiReturn_SUCCESS); EXPECT_EQ(emitColor, aiColor4D(1, 0, 1, 1)); diff --git a/test/unit/utFindDegenerates.cpp b/test/unit/utFindDegenerates.cpp index 1f8e8e93f..6f2abebfb 100644 --- a/test/unit/utFindDegenerates.cpp +++ b/test/unit/utFindDegenerates.cpp @@ -199,10 +199,10 @@ TEST_F(FindDegeneratesProcessTest, meshRemoval) { scene->mRootNode->mMeshes[3] = 3; scene->mRootNode->mMeshes[4] = 4; - mProcess->Execute(scene.get()); + mProcess->Execute(scene.get()); EXPECT_EQ(scene->mNumMeshes, 1u); EXPECT_EQ(scene->mMeshes[0], meshWhichSurvives); EXPECT_EQ(scene->mRootNode->mNumMeshes, 1u); - EXPECT_EQ(scene->mRootNode->mMeshes[0], 0u); + EXPECT_EQ(scene->mRootNode->mMeshes[0], 0u); } diff --git a/test/unit/utIOStreamBuffer.cpp b/test/unit/utIOStreamBuffer.cpp index 6d0d6a7d7..a0e4660df 100644 --- a/test/unit/utIOStreamBuffer.cpp +++ b/test/unit/utIOStreamBuffer.cpp @@ -81,14 +81,14 @@ TEST_F( IOStreamBufferTest, open_close_Test ) { EXPECT_FALSE( myBuffer.open( nullptr ) ); EXPECT_FALSE( myBuffer.close() ); - + const auto dataSize = sizeof(data); const auto dataCount = dataSize / sizeof(*data); char fname[]={ "octest.XXXXXX" }; auto* fs = MakeTmpFile(fname); ASSERT_NE(nullptr, fs); - + auto written = std::fwrite( data, sizeof(*data), dataCount, fs ); EXPECT_NE( 0U, written ); auto flushResult = std::fflush( fs ); @@ -107,7 +107,7 @@ TEST_F( IOStreamBufferTest, open_close_Test ) { } TEST_F( IOStreamBufferTest, readlineTest ) { - + const auto dataSize = sizeof(data); const auto dataCount = dataSize / sizeof(*data); diff --git a/test/unit/utIOSystem.cpp b/test/unit/utIOSystem.cpp index 767984deb..1e866515e 100644 --- a/test/unit/utIOSystem.cpp +++ b/test/unit/utIOSystem.cpp @@ -50,12 +50,12 @@ using namespace Assimp; class IOSystemTest : public ::testing::Test { public: - virtual void SetUp() { - pImp = new TestIOSystem(); + virtual void SetUp() { + pImp = new TestIOSystem(); } - - virtual void TearDown() { - delete pImp; + + virtual void TearDown() { + delete pImp; } protected: diff --git a/test/unit/utIssues.cpp b/test/unit/utIssues.cpp index cb1adb22c..5eeed6ad8 100644 --- a/test/unit/utIssues.cpp +++ b/test/unit/utIssues.cpp @@ -62,7 +62,7 @@ TEST_F( utIssues, OpacityBugWhenExporting_727 ) { aiScene *scene( TestModelFacttory::createDefaultTestModel( opacity ) ); Assimp::Importer importer; Assimp::Exporter exporter; - + std::string path = "dae"; const aiExportFormatDesc *desc = exporter.GetExportFormatDescription( 0 ); EXPECT_NE( desc, nullptr ); diff --git a/test/unit/utVersion.cpp b/test/unit/utVersion.cpp index 0189cd2a9..0de6ef39c 100644 --- a/test/unit/utVersion.cpp +++ b/test/unit/utVersion.cpp @@ -55,7 +55,7 @@ TEST_F( utVersion, aiGetLegalStringTest ) { TEST_F( utVersion, aiGetVersionMinorTest ) { EXPECT_EQ( aiGetVersionMinor(), 0U ); } - + TEST_F( utVersion, aiGetVersionMajorTest ) { EXPECT_EQ( aiGetVersionMajor(), 5U ); } diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 2c000bb37..90070d63e 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -692,7 +692,7 @@ TEST_F(utglTF2ImportExport, indexOutOfRange) { } }; LogObserver logObserver; - + DefaultLogger::get()->attachStream(&logObserver); const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/IndexOutOfRange/IndexOutOfRange.gltf", aiProcess_ValidateDataStructure); ASSERT_NE(scene, nullptr); diff --git a/tools/assimp_cmd/CMakeLists.txt b/tools/assimp_cmd/CMakeLists.txt index 3a39fa748..5aeac0f7b 100644 --- a/tools/assimp_cmd/CMakeLists.txt +++ b/tools/assimp_cmd/CMakeLists.txt @@ -1,6 +1,6 @@ # Open Asset Import Library (assimp) # ---------------------------------------------------------------------- -# +# # Copyright (c) 2006-2021, assimp team diff --git a/tools/assimp_cmd/Export.cpp b/tools/assimp_cmd/Export.cpp index 1e2f10541..6c3c41de9 100644 --- a/tools/assimp_cmd/Export.cpp +++ b/tools/assimp_cmd/Export.cpp @@ -7,8 +7,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ diff --git a/tools/assimp_cmd/ImageExtractor.cpp b/tools/assimp_cmd/ImageExtractor.cpp index 105c4fe37..23aa9c249 100644 --- a/tools/assimp_cmd/ImageExtractor.cpp +++ b/tools/assimp_cmd/ImageExtractor.cpp @@ -7,8 +7,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -356,6 +356,6 @@ int Assimp_Extract(const char *const *params, unsigned int num) { return m; } } - + return AssimpCmdError::Success; } diff --git a/tools/assimp_cmd/Info.cpp b/tools/assimp_cmd/Info.cpp index e0d511a73..2c35ba227 100644 --- a/tools/assimp_cmd/Info.cpp +++ b/tools/assimp_cmd/Info.cpp @@ -316,7 +316,7 @@ int Assimp_Info (const char* const* params, unsigned int num) { printf("assimp info: Invalid arguments, verbose and silent at the same time are forbitten. "); return AssimpCmdInfoError::InvalidCombinaisonOfArguments; } - + // Parse post-processing flags unless -r was specified ImportData import; if (!raw) { diff --git a/tools/assimp_cmd/Main.cpp b/tools/assimp_cmd/Main.cpp index 2fb7559bb..8d76e1f5e 100644 --- a/tools/assimp_cmd/Main.cpp +++ b/tools/assimp_cmd/Main.cpp @@ -9,8 +9,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -27,16 +27,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -47,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Main.h" -const char* AICMD_MSG_ABOUT = +const char* AICMD_MSG_ABOUT = "------------------------------------------------------ \n" "Open Asset Import Library (\"Assimp\", https://github.com/assimp/assimp) \n" " -- Commandline toolchain --\n" @@ -55,7 +55,7 @@ const char* AICMD_MSG_ABOUT = "Version %i.%i %s%s%s%s%s(GIT commit %x)\n\n"; -const char* AICMD_MSG_HELP = +const char* AICMD_MSG_HELP = "assimp \n\n" " verbs:\n" " \tinfo - Quick file stats\n" @@ -106,7 +106,7 @@ int main (int argc, char* argv[]) } // assimp help - // Display some basic help (--help and -h work as well + // Display some basic help (--help and -h work as well // because people could try them intuitively) if (!strcmp(argv[1], "help") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { printf("%s",AICMD_MSG_HELP); @@ -114,7 +114,7 @@ int main (int argc, char* argv[]) } // assimp cmpdump - // Compare two mini model dumps (regression suite) + // Compare two mini model dumps (regression suite) if (! strcmp(argv[1], "cmpdump")) { return Assimp_CompareDump (&argv[2],argc-2); } @@ -125,7 +125,7 @@ int main (int argc, char* argv[]) globalImporter = &imp; #ifndef ASSIMP_BUILD_NO_EXPORT - // + // Assimp::Exporter exp; globalExporter = &exp; #endif @@ -145,7 +145,7 @@ int main (int argc, char* argv[]) // List all export file formats supported by Assimp (not the file extensions, just the format identifiers!) if (! strcmp(argv[1], "listexport")) { aiString s; - + for(size_t i = 0, end = exp.GetExportFormatCount(); i < end; ++i) { const aiExportFormatDesc* const e = exp.GetExportFormatDescription(i); s.Append( e->id ); @@ -176,7 +176,7 @@ int main (int argc, char* argv[]) return AssimpCmdError::Success; } } - + printf("Unknown file format id: \'%s\'\n",argv[2]); return AssimpCmdError::UnknownFileFormat; } @@ -207,13 +207,13 @@ int main (int argc, char* argv[]) return Assimp_Info ((const char**)&argv[2],argc-2); } - // assimp dump - // Dump a model to a file + // assimp dump + // Dump a model to a file if (! strcmp(argv[1], "dump")) { return Assimp_Dump (&argv[2],argc-2); } - // assimp extract + // assimp extract // Extract an embedded texture from a file if (! strcmp(argv[1], "extract")) { return Assimp_Extract (&argv[2],argc-2); @@ -236,7 +236,7 @@ int main (int argc, char* argv[]) void SetLogStreams(const ImportData& imp) { printf("\nAttaching log stream ... OK\n"); - + unsigned int flags = 0; if (imp.logFile.length()) { flags |= aiDefaultLogStream_FILE; @@ -264,7 +264,7 @@ void PrintHorBar() // ------------------------------------------------------------------------------ // Import a specific file const aiScene* ImportModel( - const ImportData& imp, + const ImportData& imp, const std::string& path) { // Attach log streams @@ -282,7 +282,7 @@ const aiScene* ImportModel( if (imp.showLog) { PrintHorBar(); } - + // do the actual import, measure time const clock_t first = clock(); @@ -302,7 +302,7 @@ const aiScene* ImportModel( printf("Importing file ... OK \n import took approx. %.5f seconds\n" "\n",seconds); - if (imp.log) { + if (imp.log) { FreeLogStreams(); } return scene; @@ -310,8 +310,8 @@ const aiScene* ImportModel( #ifndef ASSIMP_BUILD_NO_EXPORT // ------------------------------------------------------------------------------ -bool ExportModel(const aiScene* pOut, - const ImportData& imp, +bool ExportModel(const aiScene* pOut, + const ImportData& imp, const std::string& path, const char* pID) { @@ -352,7 +352,7 @@ bool ExportModel(const aiScene* pOut, printf("Exporting file ... OK \n export took approx. %.5f seconds\n" "\n",seconds); - if (imp.log) { + if (imp.log) { FreeLogStreams(); } @@ -363,7 +363,7 @@ bool ExportModel(const aiScene* pOut, // ------------------------------------------------------------------------------ // Process standard arguments int ProcessStandardArguments( - ImportData& fill, + ImportData& fill, const char* const * params, unsigned int num) { @@ -396,7 +396,7 @@ int ProcessStandardArguments( // // -c --config-file= - for (unsigned int i = 0; i < num;++i) + for (unsigned int i = 0; i < num;++i) { const char *param = params[ i ]; printf( "param = %s\n", param ); @@ -504,11 +504,11 @@ int ProcessStandardArguments( else if (!strncmp(params[i], "-rx=", 4) || !strncmp(params[i], "--rotation-x=", 13)) { std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4)); fill.rot.x = std::stof(value); - } + } else if (!strncmp(params[i], "-ry=", 4) || !strncmp(params[i], "--rotation-y=", 13)) { std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4)); fill.rot.y = std::stof(value); - } + } else if (!strncmp(params[i], "-rz=", 4) || !strncmp(params[i], "--rotation-z=", 13)) { std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4)); fill.rot.z = std::stof(value); @@ -530,7 +530,7 @@ int ProcessStandardArguments( // ------------------------------------------------------------------------------ int Assimp_TestBatchLoad ( - const char* const* params, + const char* const* params, unsigned int num) { for(unsigned int i = 0; i < num; ++i) { diff --git a/tools/assimp_cmd/Main.h b/tools/assimp_cmd/Main.h index e7fbb6c75..5ac306abd 100644 --- a/tools/assimp_cmd/Main.h +++ b/tools/assimp_cmd/Main.h @@ -7,8 +7,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -144,7 +144,7 @@ enum AssimpCmdError { * @param params Command line parameters to be processed * @param num NUmber of params * @return An #AssimpCmdError value. */ -int ProcessStandardArguments(ImportData& fill, +int ProcessStandardArguments(ImportData& fill, const char* const* params, unsigned int num); @@ -153,7 +153,7 @@ int ProcessStandardArguments(ImportData& fill, * @param imp Import configuration to be used * @param path Path to the file to be read */ const aiScene* ImportModel( - const ImportData& imp, + const ImportData& imp, const std::string& path); #ifndef ASSIMP_BUILD_NO_EXPORT @@ -163,8 +163,8 @@ const aiScene* ImportModel( * @param imp Import configuration to be used * @param path Path to the file to be written * @param format Format id*/ -bool ExportModel(const aiScene* pOut, - const ImportData& imp, +bool ExportModel(const aiScene* pOut, + const ImportData& imp, const std::string& path, const char* pID); @@ -176,7 +176,7 @@ bool ExportModel(const aiScene* pOut, * @param Number of params * @return An #AssimpCmdError value.*/ int Assimp_Dump ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdExportError @@ -186,7 +186,7 @@ enum AssimpCmdExportError { FailedToExportModel, // Add new error codes here... - + LastAssimpCmdExportError, // Must be last. }; @@ -196,7 +196,7 @@ enum AssimpCmdExportError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdExportError value. */ int Assimp_Export ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdExtractError @@ -217,7 +217,7 @@ enum AssimpCmdExtractError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdExtractError value. */ int Assimp_Extract ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdCompareDumpError @@ -238,7 +238,7 @@ enum AssimpCmdCompareDumpError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdCompareDumpError. */ int Assimp_CompareDump ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdInfoError @@ -257,7 +257,7 @@ enum AssimpCmdInfoError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdInfoError value. */ int Assimp_Info ( - const char* const* params, + const char* const* params, unsigned int num); // ------------------------------------------------------------------------------ @@ -266,7 +266,7 @@ int Assimp_Info ( * @param Number of params * @return An #AssimpCmdError value. */ int Assimp_TestBatchLoad ( - const char* const* params, + const char* const* params, unsigned int num); diff --git a/tools/assimp_cmd/WriteDump.cpp b/tools/assimp_cmd/WriteDump.cpp index 5809d4ce6..fd8839a17 100644 --- a/tools/assimp_cmd/WriteDump.cpp +++ b/tools/assimp_cmd/WriteDump.cpp @@ -7,8 +7,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ diff --git a/tools/assimp_cmd/resource.h b/tools/assimp_cmd/resource.h index c516b5e5c..caf3a0a69 100644 --- a/tools/assimp_cmd/resource.h +++ b/tools/assimp_cmd/resource.h @@ -9,7 +9,7 @@ // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 diff --git a/tools/assimp_view/AnimEvaluator.h b/tools/assimp_view/AnimEvaluator.h index 566247604..394ebef4a 100644 --- a/tools/assimp_view/AnimEvaluator.h +++ b/tools/assimp_view/AnimEvaluator.h @@ -53,7 +53,7 @@ struct aiAnimation; namespace AssimpView { -/** +/** * @brief Calculates transformations for a given timestamp from a set of animation tracks. Not directly useful, * better use the AnimPlayer class. */ @@ -68,15 +68,15 @@ public: /// @brief The class destructor. ~AnimEvaluator(); - /// @brief Evaluates the animation tracks for a given time stamp. - /// The calculated pose can be retrieved as an array of transformation + /// @brief Evaluates the animation tracks for a given time stamp. + /// The calculated pose can be retrieved as an array of transformation /// matrices afterwards by calling GetTransformations(). - /// @param pTime The time for which you want to evaluate the animation, in seconds. - /// Will be mapped into the animation cycle, so it can get an arbitrary + /// @param pTime The time for which you want to evaluate the animation, in seconds. + /// Will be mapped into the animation cycle, so it can get an arbitrary /// value. Best use with ever-increasing time stamps. void Evaluate(double pTime); - /// @brief Returns the transform matrices calculated at the last Evaluate() call. + /// @brief Returns the transform matrices calculated at the last Evaluate() call. /// The array matches the mChannels array of the aiAnimation. const std::vector &GetTransformations() const { return mTransforms; } diff --git a/tools/assimp_view/CMakeLists.txt b/tools/assimp_view/CMakeLists.txt index 8ff556f05..0199392fe 100644 --- a/tools/assimp_view/CMakeLists.txt +++ b/tools/assimp_view/CMakeLists.txt @@ -1,6 +1,6 @@ # Open Asset Import Library (assimp) # ---------------------------------------------------------------------- -# +# # Copyright (c) 2006-2021, assimp team diff --git a/tools/assimp_view/Display.cpp b/tools/assimp_view/Display.cpp index 4178ab955..ac9aa5329 100644 --- a/tools/assimp_view/Display.cpp +++ b/tools/assimp_view/Display.cpp @@ -105,7 +105,7 @@ void GetNodeCount(aiNode* pcNode, unsigned int* piCnt) int CDisplay::EnableAnimTools(BOOL hm) { EnableWindow(GetDlgItem(g_hDlg,IDC_PLAY),hm); EnableWindow(GetDlgItem(g_hDlg,IDC_SLIDERANIM),hm); - + return 1; } @@ -171,7 +171,7 @@ int CDisplay::AddNodeToDisplayList( { iIndex += iDepth * 100; } - else + else iIndex += iDepth * 10; ai_snprintf(chTemp, MAXLEN,"Node %u",iIndex); } @@ -1053,7 +1053,7 @@ int CDisplay::OnSetupTextureView(TextureInfo* pcNew) case aiTextureOp_SmoothAdd: szOp = "addsmooth"; break; - default: + default: szOp = "mul"; break; }; diff --git a/tools/assimp_view/MessageProc.cpp b/tools/assimp_view/MessageProc.cpp index 580d35bf1..56831eb2a 100644 --- a/tools/assimp_view/MessageProc.cpp +++ b/tools/assimp_view/MessageProc.cpp @@ -2206,7 +2206,7 @@ int APIENTRY _tWinMain(HINSTANCE hInstance, "ASSIMP ModelViewer",MB_OK); return -4; } - + CLogDisplay::Instance().AddEntry("[OK] Here we go!"); // create the log window diff --git a/tools/assimp_view/Shaders.cpp b/tools/assimp_view/Shaders.cpp index a3404d5bf..b8ee8dbf8 100644 --- a/tools/assimp_view/Shaders.cpp +++ b/tools/assimp_view/Shaders.cpp @@ -7,8 +7,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ diff --git a/tools/assimp_view/assimp_view.cpp b/tools/assimp_view/assimp_view.cpp index 5ab7c53ad..e780e2aaf 100644 --- a/tools/assimp_view/assimp_view.cpp +++ b/tools/assimp_view/assimp_view.cpp @@ -7,8 +7,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -25,16 +25,16 @@ 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ diff --git a/tools/assimp_view/resource.h b/tools/assimp_view/resource.h index 5077f6ccf..754eb69bd 100644 --- a/tools/assimp_view/resource.h +++ b/tools/assimp_view/resource.h @@ -223,7 +223,7 @@ #define IDC_STATIC -1 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 From 4991f728c897a303b9cb0b307e164017fe8b4eca Mon Sep 17 00:00:00 2001 From: Krishty Date: Thu, 29 Jul 2021 14:23:52 +0200 Subject: [PATCH 113/232] =?UTF-8?q?style=20fix=C2=A0=E2=80=93=20initializi?= =?UTF-8?q?ng=20and=20assigning=20empty=20std::string=20properly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit for details, see #3764 --- code/AssetLib/STEPParser/STEPFileReader.cpp | 4 ++-- code/AssetLib/glTF/glTFCommon.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/AssetLib/STEPParser/STEPFileReader.cpp b/code/AssetLib/STEPParser/STEPFileReader.cpp index ac6d83672..360277912 100644 --- a/code/AssetLib/STEPParser/STEPFileReader.cpp +++ b/code/AssetLib/STEPParser/STEPFileReader.cpp @@ -58,13 +58,13 @@ using namespace Assimp; namespace EXPRESS = STEP::EXPRESS; // ------------------------------------------------------------------------------------------------ -std::string AddLineNumber(const std::string& s,uint64_t line /*= LINE_NOT_SPECIFIED*/, const std::string& prefix = "") +std::string AddLineNumber(const std::string& s,uint64_t line /*= LINE_NOT_SPECIFIED*/, const std::string& prefix = std::string()) { return line == STEP::SyntaxError::LINE_NOT_SPECIFIED ? prefix+s : static_cast( (Formatter::format(),prefix,"(line ",line,") ",s) ); } // ------------------------------------------------------------------------------------------------ -std::string AddEntityID(const std::string& s,uint64_t entity /*= ENTITY_NOT_SPECIFIED*/, const std::string& prefix = "") +std::string AddEntityID(const std::string& s,uint64_t entity /*= ENTITY_NOT_SPECIFIED*/, const std::string& prefix = std::string()) { return entity == STEP::TypeError::ENTITY_NOT_SPECIFIED ? prefix+s : static_cast( (Formatter::format(),prefix,"(entity #",entity,") ",s)); } diff --git a/code/AssetLib/glTF/glTFCommon.h b/code/AssetLib/glTF/glTFCommon.h index 0506ad056..6f35e7881 100644 --- a/code/AssetLib/glTF/glTFCommon.h +++ b/code/AssetLib/glTF/glTFCommon.h @@ -196,7 +196,7 @@ inline std::string getCurrentAssetDir(const std::string &pFile) { std::string path = pFile; int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\'))); if (pos == int(std::string::npos)) { - return ""; + return std::string(); } return pFile.substr(0, pos + 1); From 42a7611f8511e5af0e357a56b658b381b14c8454 Mon Sep 17 00:00:00 2001 From: Krishty Date: Thu, 29 Jul 2021 14:39:22 +0200 Subject: [PATCH 114/232] style fix: indentation --- code/AssetLib/3MF/3MFXmlTags.h | 2 +- code/AssetLib/MMD/MMDPmxParser.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/AssetLib/3MF/3MFXmlTags.h b/code/AssetLib/3MF/3MFXmlTags.h index d447556d6..9fa2affd4 100644 --- a/code/AssetLib/3MF/3MFXmlTags.h +++ b/code/AssetLib/3MF/3MFXmlTags.h @@ -103,7 +103,7 @@ namespace XmlTag { const char* const PACKAGE_TEXTURE_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture"; const char* const PACKAGE_CORE_PROPERTIES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; const char* const PACKAGE_THUMBNAIL_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"; - } +} } // Namespace D3MF } // Namespace Assimp diff --git a/code/AssetLib/MMD/MMDPmxParser.cpp b/code/AssetLib/MMD/MMDPmxParser.cpp index d57dc169a..ba8efa8e9 100644 --- a/code/AssetLib/MMD/MMDPmxParser.cpp +++ b/code/AssetLib/MMD/MMDPmxParser.cpp @@ -516,13 +516,13 @@ namespace pmx stream->read((char*) magic, sizeof(char) * 4); if (magic[0] != 0x50 || magic[1] != 0x4d || magic[2] != 0x58 || magic[3] != 0x20) { - throw DeadlyImportError("MMD: Invalid magic number."); - } + throw DeadlyImportError("MMD: Invalid magic number."); + } stream->read((char*) &version, sizeof(float)); if (version != 2.0f && version != 2.1f) { throw DeadlyImportError("MMD: Unsupported version (must be 2.0 or 2.1): ", ai_to_string(version)); - } + } this->setting.Read(stream); this->model_name = ReadString(stream, setting.encoding); From a1eaaaa0e398ce860c0ecfe42d61f98222f57208 Mon Sep 17 00:00:00 2001 From: Krishty Date: Thu, 29 Jul 2021 14:45:39 +0200 Subject: [PATCH 115/232] fix comments fixes some copy-paste errors in logger comments introduced with 89584c167aaf341a6d717083bb9a52a4c1b0ace1 --- include/assimp/Logger.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index 3ca4a6cb2..18e913953 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -99,8 +99,8 @@ public: virtual ~Logger(); // ---------------------------------------------------------------------- - /** @brief Writes a info message - * @param message Info message*/ + /** @brief Writes a debug message + * @param message Debug message*/ void debug(const char* message); template @@ -109,7 +109,7 @@ public: } // ---------------------------------------------------------------------- - /** @brief Writes a debug message + /** @brief Writes a debug message * @param message Debug message*/ void verboseDebug(const char* message); @@ -140,7 +140,7 @@ public: // ---------------------------------------------------------------------- /** @brief Writes an error message - * @param message Info message*/ + * @param message Error message*/ void error(const char* message); template From bb53961fa98c226267813a80ed2b59f940f5ab20 Mon Sep 17 00:00:00 2001 From: Krishty Date: Thu, 29 Jul 2021 14:57:25 +0200 Subject: [PATCH 116/232] more range-based for f6b4370f6ac1bf2db0ef9edeb0ff1ce8c7e61aab and 7c822f23bd075d3621d2e2f33188e1659f657b31 introduced raw loops on data types with heavy nesting; range-based for suits better here --- code/AssetLib/glTF2/glTF2Importer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index d80df97d2..921aaad9e 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -992,8 +992,8 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) { metadata->Add(extension.name.c_str(), extension.mBoolValue.value); } else if (extension.mValues.isPresent) { aiMetadata val; - for (size_t i = 0; i < extension.mValues.value.size(); ++i) { - ParseExtensions(&val, extension.mValues.value[i]); + for (auto const & subExtension : extension.mValues.value) { + ParseExtensions(&val, subExtension); } metadata->Add(extension.name.c_str(), val); } @@ -1001,8 +1001,8 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) { void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) { if (extension.mValues.isPresent) { - for (size_t i = 0; i < extension.mValues.value.size(); ++i) { - ParseExtensions(metadata, extension.mValues.value[i]); + for (auto const & subExtension : extension.mValues.value) { + ParseExtensions(metadata, subExtension); } } } From 6ce524893169285bf523da430fc230f0caa0a151 Mon Sep 17 00:00:00 2001 From: Spectrum76 <49938052+Spectrum76@users.noreply.github.com> Date: Thu, 29 Jul 2021 21:38:23 +0530 Subject: [PATCH 117/232] Update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7849cab65..09d631ee8 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,7 @@ test/gtest/src/gtest-stamp/gtest-gitinfo.txt test/gtest/src/gtest-stamp/gtest-gitclone-lastrun.txt Assimp.opensdf contrib/zlib/CTestTestfile.cmake +contrib/zlib/Debug/zlibstaticd.pdb ipch/assimp_viewer-44bbbcd1/assimp_viewerd-ccc45335.ipch bin64/assimp-vc140-mt.dll bin64/assimp-vc140-mtd.dll @@ -118,4 +119,4 @@ tools/assimp_qt_viewer/moc_mainwindow.cpp_parameters tools/assimp_qt_viewer/ui_mainwindow.h #Generated directory -generated/* \ No newline at end of file +generated/* From 8d6d6b48c3dad5bb244c0bb7bb6097be4ff083eb Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Mon, 2 Aug 2021 11:58:46 -0700 Subject: [PATCH 118/232] Obj: make a predicate more robust. Since we might encounter invalid input it is a good idea to check the actual size of the array. --- code/AssetLib/Obj/ObjFileImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/Obj/ObjFileImporter.cpp b/code/AssetLib/Obj/ObjFileImporter.cpp index d6232be81..5e2b48e24 100644 --- a/code/AssetLib/Obj/ObjFileImporter.cpp +++ b/code/AssetLib/Obj/ObjFileImporter.cpp @@ -468,7 +468,7 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model *pModel, } // Copy all vertex colors - if (!pModel->m_VertexColors.empty()) { + if (vertex < pModel->m_VertexColors.size()) { const aiVector3D &color = pModel->m_VertexColors[vertex]; pMesh->mColors[0][newIndex] = aiColor4D(color.x, color.y, color.z, 1.0); } From 538cb3125c6d83a0a4d76ec589e21a787093a2f0 Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Mon, 2 Aug 2021 11:47:35 -0700 Subject: [PATCH 119/232] Use strlen() rather than fixed length in fast_atof.h This avoids reading past the length of the input string. --- include/assimp/fast_atof.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/assimp/fast_atof.h b/include/assimp/fast_atof.h index aea793f35..43bbbff64 100644 --- a/include/assimp/fast_atof.h +++ b/include/assimp/fast_atof.h @@ -194,7 +194,7 @@ uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_ino if ( *in < '0' || *in > '9' ) { // The string is known to be bad, so don't risk printing the whole thing. - throw ExceptionType("The string \"", ai_str_toprintable(in, 30), "\" cannot be converted into a value." ); + throw ExceptionType("The string \"", ai_str_toprintable(in, (int)strlen(in)), "\" cannot be converted into a value." ); } for ( ;; ) { @@ -294,7 +294,7 @@ const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) if (!(c[0] >= '0' && c[0] <= '9') && !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) { // The string is known to be bad, so don't risk printing the whole thing. - throw ExceptionType("Cannot parse string \"", ai_str_toprintable(c, 30), + throw ExceptionType("Cannot parse string \"", ai_str_toprintable(c, (int)strlen(c)), "\" as a real number: does not start with digit " "or decimal point followed by digit."); } From c3fcbfd2c11d8bb1f62cbabf5654c322a3ed77a7 Mon Sep 17 00:00:00 2001 From: Mykhailo Smoliakov Date: Wed, 11 Aug 2021 08:54:34 +0300 Subject: [PATCH 120/232] Fix issue of incorrect reading of PBR properties such as base and emissive color in FBX --- code/AssetLib/FBX/FBXConverter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index e0da78583..a92745fb6 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -2131,7 +2131,7 @@ void FBXConverter::SetShadingPropertiesCommon(aiMaterial *out_mat, const Propert if (ok) { out_mat->AddProperty(&Emissive, 1, AI_MATKEY_COLOR_EMISSIVE); } else { - const aiColor3D &emissiveColor = GetColorPropertyFromMaterial(props, "Maya|emissive", ok); + const aiColor3D &emissiveColor = GetColorProperty(props, "Maya|emissive", ok); if (ok) { out_mat->AddProperty(&emissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE); } @@ -2218,7 +2218,7 @@ void FBXConverter::SetShadingPropertiesCommon(aiMaterial *out_mat, const Propert } // PBR material information - const aiColor3D &baseColor = GetColorPropertyFromMaterial(props, "Maya|base_color", ok); + const aiColor3D &baseColor = GetColorProperty(props, "Maya|base_color", ok); if (ok) { out_mat->AddProperty(&baseColor, 1, AI_MATKEY_BASE_COLOR); } From 69c152d7c130e96287eada4467611322f8bd64ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Ubi=C3=B1as?= Date: Wed, 11 Aug 2021 18:57:21 -0400 Subject: [PATCH 121/232] Add missing diagnostic push --- code/AssetLib/glTF/glTFExporter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/AssetLib/glTF/glTFExporter.cpp b/code/AssetLib/glTF/glTFExporter.cpp index 810263f52..10943905c 100644 --- a/code/AssetLib/glTF/glTFExporter.cpp +++ b/code/AssetLib/glTF/glTFExporter.cpp @@ -526,6 +526,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref Date: Wed, 11 Aug 2021 19:32:26 -0400 Subject: [PATCH 122/232] Disable diagnostic for LogStream comparator --- code/Common/Assimp.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/code/Common/Assimp.cpp b/code/Common/Assimp.cpp index ca0912979..3a0ec7d60 100644 --- a/code/Common/Assimp.cpp +++ b/code/Common/Assimp.cpp @@ -72,12 +72,25 @@ namespace Assimp { // underlying structure for aiPropertyStore typedef BatchLoader::PropertyMap PropertyMap; +#if defined(__has_warning) +#if __has_warning("-Wordered-compare-function-pointers") +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wordered-compare-function-pointers" +#endif +#endif + /** Stores the LogStream objects for all active C log streams */ struct mpred { bool operator()(const aiLogStream &s0, const aiLogStream &s1) const { return s0.callback < s1.callback && s0.user < s1.user; } }; + +#if defined(__has_warning) +#if __has_warning("-Wordered-compare-function-pointers") +#pragma GCC diagnostic pop +#endif +#endif typedef std::map LogStreamMap; /** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */ From b95df54225c0406290143d0d4ed08d7439420652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Ubi=C3=B1as?= Date: Wed, 11 Aug 2021 19:33:10 -0400 Subject: [PATCH 123/232] Remove unused code --- contrib/openddlparser/code/OpenDDLExport.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contrib/openddlparser/code/OpenDDLExport.cpp b/contrib/openddlparser/code/OpenDDLExport.cpp index 6d6f2458d..d235b553b 100644 --- a/contrib/openddlparser/code/OpenDDLExport.cpp +++ b/contrib/openddlparser/code/OpenDDLExport.cpp @@ -134,10 +134,9 @@ bool OpenDDLExport::writeToStream(const std::string &statement) { } bool OpenDDLExport::writeNode(DDLNode *node, std::string &statement) { - bool success(true); writeNodeHeader(node, statement); if (node->hasProperties()) { - success |= writeProperties(node, statement); + writeProperties(node, statement); } writeLineEnd(statement); From 4b1ff645e3d21c370ffd7447063263dfe1711cd1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 12 Aug 2021 21:13:07 +0200 Subject: [PATCH 124/232] closes https://github.com/assimp/assimp/issues/3398: Add support for embedded textures --- code/AssetLib/3DS/3DSConverter.cpp | 11 +- code/AssetLib/3DS/3DSLoader.h | 9 + code/AssetLib/3MF/3MFTypes.h | 125 +++++ code/AssetLib/3MF/3MFXmlTags.h | 10 +- code/AssetLib/3MF/D3MFImporter.cpp | 524 +-------------------- code/AssetLib/3MF/D3MFOpcPackage.cpp | 60 ++- code/AssetLib/3MF/D3MFOpcPackage.h | 12 +- code/AssetLib/3MF/XmlSerializer.cpp | 590 ++++++++++++++++++++++++ code/AssetLib/3MF/XmlSerializer.h | 104 +++++ code/AssetLib/Collada/ColladaLoader.cpp | 23 +- code/AssetLib/Collada/ColladaParser.cpp | 6 +- code/AssetLib/Obj/ObjFileImporter.cpp | 2 +- code/AssetLib/glTF2/glTF2Importer.cpp | 5 +- code/CMakeLists.txt | 5 +- include/assimp/scene.h | 2 +- include/assimp/vector3.h | 33 +- tools/assimp_view/MaterialManager.h | 73 ++- tools/assimp_view/MeshRenderer.cpp | 9 +- 18 files changed, 1006 insertions(+), 597 deletions(-) create mode 100644 code/AssetLib/3MF/3MFTypes.h create mode 100644 code/AssetLib/3MF/XmlSerializer.cpp create mode 100644 code/AssetLib/3MF/XmlSerializer.h diff --git a/code/AssetLib/3DS/3DSConverter.cpp b/code/AssetLib/3DS/3DSConverter.cpp index aca16b0d6..add1553bc 100644 --- a/code/AssetLib/3DS/3DSConverter.cpp +++ b/code/AssetLib/3DS/3DSConverter.cpp @@ -68,8 +68,8 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() { unsigned int idx(NotSet); for (unsigned int i = 0; i < mScene->mMaterials.size(); ++i) { std::string s = mScene->mMaterials[i].mName; - for (std::string::iterator it = s.begin(); it != s.end(); ++it) { - *it = static_cast(::tolower(static_cast(*it))); + for (char & it : s) { + it = static_cast(::tolower(static_cast(it))); } if (std::string::npos == s.find("default")) continue; @@ -79,12 +79,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() { mScene->mMaterials[i].mDiffuse.r != mScene->mMaterials[i].mDiffuse.b) continue; - if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || - mScene->mMaterials[i].sTexBump.mMapName.length() != 0 || - mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || - mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || - mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || - mScene->mMaterials[i].sTexShininess.mMapName.length() != 0) { + if (ContainsTextures(i)) { continue; } idx = i; diff --git a/code/AssetLib/3DS/3DSLoader.h b/code/AssetLib/3DS/3DSLoader.h index 2091fbeb7..dba20eede 100644 --- a/code/AssetLib/3DS/3DSLoader.h +++ b/code/AssetLib/3DS/3DSLoader.h @@ -208,6 +208,15 @@ protected: */ void ReplaceDefaultMaterial(); + bool ContainsTextures(unsigned int i) const { + return mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || + mScene->mMaterials[i].sTexBump.mMapName.length() != 0 || + mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || + mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || + mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || + mScene->mMaterials[i].sTexShininess.mMapName.length() != 0; + } + // ------------------------------------------------------------------- /** Convert the whole scene */ diff --git a/code/AssetLib/3MF/3MFTypes.h b/code/AssetLib/3MF/3MFTypes.h new file mode 100644 index 000000000..e404bf012 --- /dev/null +++ b/code/AssetLib/3MF/3MFTypes.h @@ -0,0 +1,125 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct aiMaterial; +struct aiMesh; + +namespace Assimp { +namespace D3MF { + +enum class ResourceType { + RT_Object, + RT_BaseMaterials, + RT_EmbeddedTexture2D, + RT_Texture2DGroup, + RT_Unknown +}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...) + +class Resource { +public: + int mId; + + Resource(int id) : + mId(id) { + // empty + } + + virtual ~Resource() { + // empty + } + + virtual ResourceType getType() const { + return ResourceType::RT_Unknown; + } +}; + +class EmbeddedTexture : public Resource { +public: + std::string mPath; + std::string mContentType; + std::string mTilestyleU; + std::string mTilestyleV; + std::vector mBuffer; + + EmbeddedTexture(int id) : + Resource(id), + mPath(), + mContentType(), + mTilestyleU(), + mTilestyleV() { + // empty + } + + ~EmbeddedTexture() = default; + + ResourceType getType() const override { + return ResourceType::RT_EmbeddedTexture2D; + } +}; + +class Texture2DGroup : public Resource { +public: + std::vector mTex2dCoords; + int mTexId; + Texture2DGroup(int id) : + Resource(id), + mTexId(-1) { + // empty + } + + ~Texture2DGroup() = default; + + ResourceType getType() const override { + return ResourceType::RT_Texture2DGroup; + } +}; + +class BaseMaterials : public Resource { +public: + std::vector mMaterialIndex; + + BaseMaterials(int id) : + Resource(id), + mMaterialIndex() { + // empty + } + + ~BaseMaterials() = default; + + ResourceType getType() const override { + return ResourceType::RT_BaseMaterials; + } +}; + +struct Component { + int mObjectId; + aiMatrix4x4 mTransformation; +}; + +class Object : public Resource { +public: + std::vector mMeshes; + std::vector mMeshIndex; + std::vector mComponents; + std::string mName; + + Object(int id) : + Resource(id), + mName(std::string("Object_") + ai_to_string(id)) { + // empty + } + + ~Object() = default; + + ResourceType getType() const override { + return ResourceType::RT_Object; + } +}; + +} // namespace D3MF +} // namespace Assimp diff --git a/code/AssetLib/3MF/3MFXmlTags.h b/code/AssetLib/3MF/3MFXmlTags.h index d447556d6..28b198f5d 100644 --- a/code/AssetLib/3MF/3MFXmlTags.h +++ b/code/AssetLib/3MF/3MFXmlTags.h @@ -80,13 +80,21 @@ namespace XmlTag { const char* const item = "item"; const char* const objectid = "objectid"; const char* const transform = "transform"; + const char *const path = "path"; // Material definitions const char* const basematerials = "basematerials"; - const char* const basematerials_id = "id"; const char* const basematerials_base = "base"; const char* const basematerials_name = "name"; const char* const basematerials_displaycolor = "displaycolor"; + const char* const texture_2d = "m:texture2d"; + const char *const texture_group = "m:texture2dgroup"; + const char *const texture_content_type = "contenttype"; + const char *const texture_tilestyleu = "tilestyleu"; + const char *const texture_tilestylev = "tilestylev"; + const char *const texture_2d_coord = "m:tex2coord"; + const char *const texture_cuurd_u = "u"; + const char *const texture_cuurd_v = "v"; // Meta info tags const char* const CONTENT_TYPES_ARCHIVE = "[Content_Types].xml"; diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index 747af7cfc..fe3c07744 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "D3MFImporter.h" #include "3MFXmlTags.h" #include "D3MFOpcPackage.h" +#include "XmlSerializer.h" #include #include @@ -61,513 +62,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include +#include namespace Assimp { -namespace D3MF { - -enum class ResourceType { - RT_Object, - RT_BaseMaterials, - RT_Unknown -}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...) - -class Resource { -public: - int mId; - - Resource(int id) : - mId(id) { - // empty - } - - virtual ~Resource() { - // empty - } - - virtual ResourceType getType() const { - return ResourceType::RT_Unknown; - } -}; - -class BaseMaterials : public Resource { -public: - std::vector mMaterials; - std::vector mMaterialIndex; - - BaseMaterials(int id) : - Resource(id), - mMaterials(), - mMaterialIndex() { - // empty - } - - ~BaseMaterials() = default; - - ResourceType getType() const override { - return ResourceType::RT_BaseMaterials; - } -}; - -struct Component { - int mObjectId; - aiMatrix4x4 mTransformation; -}; - -class Object : public Resource { -public: - std::vector mMeshes; - std::vector mMeshIndex; - std::vector mComponents; - std::string mName; - - Object(int id) : - Resource(id), - mName(std::string("Object_") + ai_to_string(id)) { - // empty - } - - ~Object() = default; - - ResourceType getType() const override { - return ResourceType::RT_Object; - } -}; - -class XmlSerializer { -public: - XmlSerializer(XmlParser *xmlParser) : - mResourcesDictionnary(), - mMaterialCount(0), - mMeshCount(0), - mXmlParser(xmlParser) { - // empty - } - - ~XmlSerializer() { - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it ) { - delete it->second; - } - } - - void ImportXml(aiScene *scene) { - if (nullptr == scene) { - return; - } - - scene->mRootNode = new aiNode(XmlTag::RootTag); - - XmlNode node = mXmlParser->getRootNode().child(XmlTag::model); - if (node.empty()) { - return; - } - XmlNode resNode = node.child(XmlTag::resources); - for (auto ¤tNode : resNode.children()) { - const std::string currentNodeName = currentNode.name(); - if (currentNodeName == XmlTag::object) { - ReadObject(currentNode); - } else if (currentNodeName == XmlTag::basematerials) { - ReadBaseMaterials(currentNode); - } else if (currentNodeName == XmlTag::meta) { - ReadMetadata(currentNode); - } - } - - XmlNode buildNode = node.child(XmlTag::build); - for (auto ¤tNode : buildNode.children()) { - const std::string currentNodeName = currentNode.name(); - if (currentNodeName == XmlTag::item) { - int objectId = -1; - std::string transformationMatrixStr; - aiMatrix4x4 transformationMatrix; - getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId); - bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transformationMatrixStr); - - auto it = mResourcesDictionnary.find(objectId); - if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { - Object *obj = static_cast(it->second); - if (hasTransform) { - transformationMatrix = parseTransformMatrix(transformationMatrixStr); - } - - addObjectToNode(scene->mRootNode, obj, transformationMatrix); - } - } - } - - // import the metadata - if (!mMetaData.empty()) { - const size_t numMeta = mMetaData.size(); - scene->mMetaData = aiMetadata::Alloc(static_cast(numMeta)); - for (size_t i = 0; i < numMeta; ++i) { - aiString val(mMetaData[i].value); - scene->mMetaData->Set(static_cast(i), mMetaData[i].name, val); - } - } - - // import the meshes - scene->mNumMeshes = static_cast(mMeshCount); - if (scene->mNumMeshes != 0) { - scene->mMeshes = new aiMesh *[scene->mNumMeshes](); - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it) { - if (it->second->getType() == ResourceType::RT_Object) { - Object *obj = static_cast(it->second); - ai_assert(nullptr != obj); - for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) { - scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i]; - } - } - } - } - - // import the materials - scene->mNumMaterials = mMaterialCount; - if (scene->mNumMaterials != 0) { - scene->mMaterials = new aiMaterial *[scene->mNumMaterials]; - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it) { - if (it->second->getType() == ResourceType::RT_BaseMaterials) { - BaseMaterials *baseMaterials = static_cast(it->second); - for (unsigned int i = 0; i < baseMaterials->mMaterials.size(); ++i) { - scene->mMaterials[baseMaterials->mMaterialIndex[i]] = baseMaterials->mMaterials[i]; - } - } - } - } - } - -private: - void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) { - ai_assert(nullptr != obj); - - aiNode *sceneNode = new aiNode(obj->mName); - sceneNode->mNumMeshes = static_cast(obj->mMeshes.size()); - sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes]; - std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes); - - sceneNode->mTransformation = nodeTransform; - if (nullptr != parent) { - parent->addChildren(1, &sceneNode); - } - - for (size_t i = 0; i < obj->mComponents.size(); ++i) { - Component c = obj->mComponents[i]; - auto it = mResourcesDictionnary.find(c.mObjectId); - if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { - addObjectToNode(sceneNode, static_cast(it->second), c.mTransformation); - } - } - } - - bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) { - pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str()); - if (!objectAttribute.empty()) { - value = objectAttribute.as_string(); - return true; - } - - return false; - } - - bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) { - std::string strValue; - bool ret = getNodeAttribute(node, attribute, strValue); - if (ret) { - value = std::atoi(strValue.c_str()); - return true; - } - - return false; - } - - aiMatrix4x4 parseTransformMatrix(std::string matrixStr) { - // split the string - std::vector numbers; - std::string currentNumber; - for (size_t i = 0; i < matrixStr.size(); ++i) { - const char c = matrixStr[i]; - if (c == ' ') { - if (currentNumber.size() > 0) { - float f = std::stof(currentNumber); - numbers.push_back(f); - currentNumber.clear(); - } - } else { - currentNumber.push_back(c); - } - } - if (currentNumber.size() > 0) { - const float f = std::stof(currentNumber); - numbers.push_back(f); - } - - aiMatrix4x4 transformMatrix; - transformMatrix.a1 = numbers[0]; - transformMatrix.b1 = numbers[1]; - transformMatrix.c1 = numbers[2]; - transformMatrix.d1 = 0; - - transformMatrix.a2 = numbers[3]; - transformMatrix.b2 = numbers[4]; - transformMatrix.c2 = numbers[5]; - transformMatrix.d2 = 0; - - transformMatrix.a3 = numbers[6]; - transformMatrix.b3 = numbers[7]; - transformMatrix.c3 = numbers[8]; - transformMatrix.d3 = 0; - - transformMatrix.a4 = numbers[9]; - transformMatrix.b4 = numbers[10]; - transformMatrix.c4 = numbers[11]; - transformMatrix.d4 = 1; - - return transformMatrix; - } - - void ReadObject(XmlNode &node) { - int id = -1, pid = -1, pindex = -1; - bool hasId = getNodeAttribute(node, XmlTag::id, id); - bool hasPid = getNodeAttribute(node, XmlTag::pid, pid); - bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex); - if (!hasId) { - return; - } - - Object *obj = new Object(id); - - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == D3MF::XmlTag::mesh) { - auto mesh = ReadMesh(currentNode); - mesh->mName.Set(ai_to_string(id)); - - if (hasPid) { - auto it = mResourcesDictionnary.find(pid); - if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) { - BaseMaterials *materials = static_cast(it->second); - mesh->mMaterialIndex = materials->mMaterialIndex[pindex]; - } - } - - obj->mMeshes.push_back(mesh); - obj->mMeshIndex.push_back(mMeshCount); - mMeshCount++; - } else if (currentName == D3MF::XmlTag::components) { - for (XmlNode ¤tSubNode : currentNode.children()) { - const std::string subNodeName = currentSubNode.name(); - if (subNodeName == D3MF::XmlTag::component) { - int objectId = -1; - std::string componentTransformStr; - aiMatrix4x4 componentTransform; - if (getNodeAttribute(currentSubNode, D3MF::XmlTag::transform, componentTransformStr)) { - componentTransform = parseTransformMatrix(componentTransformStr); - } - - if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) { - obj->mComponents.push_back({ objectId, componentTransform }); - } - } - } - } - } - - mResourcesDictionnary.insert(std::make_pair(id, obj)); - } - - aiMesh *ReadMesh(XmlNode &node) { - aiMesh *mesh = new aiMesh(); - - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::vertices) { - ImportVertices(currentNode, mesh); - } else if (currentName == XmlTag::triangles) { - ImportTriangles(currentNode, mesh); - } - } - - return mesh; - } - - void ReadMetadata(XmlNode &node) { - pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name); - const std::string name = attribute.as_string(); - const std::string value = node.value(); - if (name.empty()) { - return; - } - - MetaEntry entry; - entry.name = name; - entry.value = value; - mMetaData.push_back(entry); - } - - void ImportVertices(XmlNode &node, aiMesh *mesh) { - std::vector vertices; - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::vertex) { - vertices.push_back(ReadVertex(currentNode)); - } - } - - mesh->mNumVertices = static_cast(vertices.size()); - mesh->mVertices = new aiVector3D[mesh->mNumVertices]; - std::copy(vertices.begin(), vertices.end(), mesh->mVertices); - } - - aiVector3D ReadVertex(XmlNode &node) { - aiVector3D vertex; - vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr); - vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr); - vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr); - - return vertex; - } - - void ImportTriangles(XmlNode &node, aiMesh *mesh) { - std::vector faces; - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::triangle) { - aiFace face = ReadTriangle(currentNode); - faces.push_back(face); - - int pid = 0, p1 = 0; - bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid); - bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1); - - if (hasPid && hasP1) { - auto it = mResourcesDictionnary.find(pid); - if (it != mResourcesDictionnary.end()) { - if (it->second->getType() == ResourceType::RT_BaseMaterials) { - BaseMaterials *baseMaterials = static_cast(it->second); - mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1]; - } - // TODO: manage the separation into several meshes if the triangles of the mesh do not all refer to the same material - } - } - } - } - - mesh->mNumFaces = static_cast(faces.size()); - mesh->mFaces = new aiFace[mesh->mNumFaces]; - mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - - std::copy(faces.begin(), faces.end(), mesh->mFaces); - } - - aiFace ReadTriangle(XmlNode &node) { - aiFace face; - - face.mNumIndices = 3; - face.mIndices = new unsigned int[face.mNumIndices]; - face.mIndices[0] = static_cast(std::atoi(node.attribute(XmlTag::v1).as_string())); - face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); - face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); - - return face; - } - - void ReadBaseMaterials(XmlNode &node) { - int id = -1; - if (getNodeAttribute(node, D3MF::XmlTag::basematerials_id, id)) { - BaseMaterials *baseMaterials = new BaseMaterials(id); - - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::basematerials_base) { - baseMaterials->mMaterialIndex.push_back(mMaterialCount); - baseMaterials->mMaterials.push_back(readMaterialDef(currentNode, id)); - ++mMaterialCount; - } - } - - mResourcesDictionnary.insert(std::make_pair(id, baseMaterials)); - } - } - - bool parseColor(const char *color, aiColor4D &diffuse) { - if (nullptr == color) { - return false; - } - - //format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) - const size_t len = strlen(color); - if (9 != len && 7 != len) { - return false; - } - - const char *buf(color); - if ('#' != buf[0]) { - return false; - } - - char r[3] = { buf[1], buf[2], '\0' }; - diffuse.r = static_cast(strtol(r, nullptr, 16)) / ai_real(255.0); - - char g[3] = { buf[3], buf[4], '\0' }; - diffuse.g = static_cast(strtol(g, nullptr, 16)) / ai_real(255.0); - - char b[3] = { buf[5], buf[6], '\0' }; - diffuse.b = static_cast(strtol(b, nullptr, 16)) / ai_real(255.0); - - if (7 == len) - return true; - - char a[3] = { buf[7], buf[8], '\0' }; - diffuse.a = static_cast(strtol(a, nullptr, 16)) / ai_real(255.0); - - return true; - } - - void assignDiffuseColor(XmlNode &node, aiMaterial *mat) { - const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string(); - aiColor4D diffuse; - if (parseColor(color, diffuse)) { - mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - } - } - - aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId) { - aiMaterial *material = new aiMaterial(); - material->mNumProperties = 0; - std::string name; - bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name); - - std::string stdMaterialName; - const std::string strId(ai_to_string(basematerialsId)); - stdMaterialName += "id"; - stdMaterialName += strId; - stdMaterialName += "_"; - if (hasName) { - stdMaterialName += std::string(name); - } else { - stdMaterialName += "basemat_"; - stdMaterialName += ai_to_string(mMaterialCount - basematerialsId); - } - - aiString assimpMaterialName(stdMaterialName); - material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME); - - assignDiffuseColor(node, material); - - return material; - } - -private: - struct MetaEntry { - std::string name; - std::string value; - }; - std::vector mMetaData; - std::map mResourcesDictionnary; - unsigned int mMaterialCount, mMeshCount; - XmlParser *mXmlParser; -}; - -} //namespace D3MF using namespace D3MF; @@ -597,7 +94,9 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo const std::string extension(GetExtension(filename)); if (extension == desc.mFileExtensions) { return true; - } else if (!extension.length() || checkSig) { + } + + if (!extension.length() || checkSig) { if (nullptr == pIOHandler) { return false; } @@ -611,7 +110,7 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo return false; } -void D3MFImporter::SetupProperties(const Importer * /*pImp*/) { +void D3MFImporter::SetupProperties(const Importer*) { // empty } @@ -624,8 +123,17 @@ void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene, XmlParser xmlParser; if (xmlParser.parse(opcPackage.RootStream())) { - XmlSerializer xmlSerializer(&xmlParser); + XmlSerializer xmlSerializer(&xmlParser, &opcPackage); xmlSerializer.ImportXml(pScene); + + const std::vector &tex = opcPackage.GetEmbeddedTextures(); + if (!tex.empty()) { + pScene->mNumTextures = static_cast(tex.size()); + pScene->mTextures = new aiTexture *[pScene->mNumTextures]; + for (unsigned int i = 0; i < pScene->mNumTextures; ++i) { + pScene->mTextures[i] = tex[i]; + } + } } } diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index dbf4f2e10..d3b667583 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -43,14 +43,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "D3MFOpcPackage.h" #include - #include #include #include #include #include #include - +#include #include "3MFXmlTags.h" #include #include @@ -64,11 +63,12 @@ namespace Assimp { namespace D3MF { // ------------------------------------------------------------------------------------------------ -typedef std::shared_ptr OpcPackageRelationshipPtr; +using OpcPackageRelationshipPtr = std::shared_ptr; class OpcPackageRelationshipReader { public: - OpcPackageRelationshipReader(XmlParser &parser) { + OpcPackageRelationshipReader(XmlParser &parser) : + m_relationShips() { XmlNode root = parser.getRootNode(); ParseRootNode(root); } @@ -91,6 +91,7 @@ public: if (relPtr->id.empty() || relPtr->type.empty() || relPtr->target.empty()) { return false; } + return true; } @@ -100,7 +101,7 @@ public: } for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { - std::string name = currentNode.name(); + const std::string name = currentNode.name(); if (name == "Relationship") { OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship()); relPtr->id = currentNode.attribute(XmlTag::RELS_ATTRIB_ID).as_string(); @@ -116,11 +117,20 @@ public: std::vector m_relationShips; }; +static bool IsEmbeddedTexture( const std::string &filename ) { + const std::string extension = BaseImporter::GetExtension(filename); + + if (extension == "jpg" || extension == "png") { + return true; + } + + return false; +} // ------------------------------------------------------------------------------------------------ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : mRootStream(nullptr), mZipArchive() { - mZipArchive.reset(new ZipArchiveIOSystem(pIOHandler, rFile)); + mZipArchive = std::make_unique(pIOHandler, rFile); if (!mZipArchive->isOpen()) { throw DeadlyImportError("Failed to open file ", rFile, "."); } @@ -141,13 +151,13 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : } std::string rootFile = ReadPackageRootRelationship(fileStream); - if (rootFile.size() > 0 && rootFile[0] == '/') { + if (!rootFile.empty() && rootFile[0] == '/') { rootFile = rootFile.substr(1); if (rootFile[0] == '/') { // deal with zip-bug rootFile = rootFile.substr(1); } - } + } ASSIMP_LOG_VERBOSE_DEBUG(rootFile); @@ -158,9 +168,12 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : if (nullptr == mRootStream) { throw DeadlyImportError("Cannot open root-file in archive : " + rootFile); } - } else if (file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) { ASSIMP_LOG_WARN("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES", file); + } else if (IsEmbeddedTexture(file)) { + IOStream *fileStream = mZipArchive->Open(file.c_str()); + LoadEmbeddedTextures(fileStream, file); + mZipArchive->Close(fileStream); } else { ASSIMP_LOG_WARN("Ignored file of unknown type: ", file); } @@ -169,20 +182,25 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : D3MFOpcPackage::~D3MFOpcPackage() { mZipArchive->Close(mRootStream); + mZipArchive = nullptr; } IOStream *D3MFOpcPackage::RootStream() const { return mRootStream; } -static const std::string ModelRef = "3D/3dmodel.model"; +const std::vector &D3MFOpcPackage::GetEmbeddedTextures() const { + return mEmbeddedTextures; +} + +static const char *const ModelRef = "3D/3dmodel.model"; bool D3MFOpcPackage::validate() { if (nullptr == mRootStream || nullptr == mZipArchive) { return false; } - return mZipArchive->Exists(ModelRef.c_str()); + return mZipArchive->Exists(ModelRef); } std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) { @@ -204,6 +222,26 @@ std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) { return (*itr)->target; } +void D3MFOpcPackage::LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename) { + if (nullptr == fileStream) { + return; + } + + const size_t size = fileStream->FileSize(); + if (0 == size) { + return; + } + + char *data = new char[size]; + fileStream->Read(data, 1, size); + aiTexture *texture = new aiTexture; + texture->mFilename.Set(filename.c_str()); + texture->mWidth = static_cast(size); + texture->mHeight = 0; + texture->pcData = (aiTexel*) data; + mEmbeddedTextures.emplace_back(texture); +} + } // Namespace D3MF } // Namespace Assimp diff --git a/code/AssetLib/3MF/D3MFOpcPackage.h b/code/AssetLib/3MF/D3MFOpcPackage.h index 22b4510d0..93928b9a7 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.h +++ b/code/AssetLib/3MF/D3MFOpcPackage.h @@ -46,8 +46,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +struct aiTexture; + namespace Assimp { - class ZipArchiveIOSystem; + +class ZipArchiveIOSystem; namespace D3MF { @@ -63,16 +66,19 @@ public: ~D3MFOpcPackage(); IOStream* RootStream() const; bool validate(); + const std::vector &GetEmbeddedTextures() const; protected: std::string ReadPackageRootRelationship(IOStream* stream); + void LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename); private: IOStream* mRootStream; std::unique_ptr mZipArchive; + std::vector mEmbeddedTextures; }; -} // Namespace D3MF -} // Namespace Assimp +} // namespace D3MF +} // namespace Assimp #endif // D3MFOPCPACKAGE_H diff --git a/code/AssetLib/3MF/XmlSerializer.cpp b/code/AssetLib/3MF/XmlSerializer.cpp new file mode 100644 index 000000000..d93c7b121 --- /dev/null +++ b/code/AssetLib/3MF/XmlSerializer.cpp @@ -0,0 +1,590 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2021, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#include "XmlSerializer.h" +#include "D3MFOpcPackage.h" +#include "3MFXmlTags.h" +#include "3MFTypes.h" +#include + +namespace Assimp { +namespace D3MF { + +static const int IdNotSet = -1; + +XmlSerializer::XmlSerializer(XmlParser *xmlParser, D3MFOpcPackage *archive) : + mResourcesDictionnary(), + mMeshCount(0), + mXmlParser(xmlParser), + mD3MFOpcPackage(archive) { + ai_assert(nullptr != xmlParser); + ai_assert(nullptr != archive); +} + +XmlSerializer::~XmlSerializer() { + for (auto &it : mResourcesDictionnary) { + delete it.second; + } +} + +void XmlSerializer::ImportXml(aiScene *scene) { + if (nullptr == scene) { + return; + } + + scene->mRootNode = new aiNode(XmlTag::RootTag); + XmlNode node = mXmlParser->getRootNode().child(XmlTag::model); + if (node.empty()) { + return; + } + + XmlNode resNode = node.child(XmlTag::resources); + for (auto ¤tNode : resNode.children()) { + const std::string currentNodeName = currentNode.name(); + if (currentNodeName == XmlTag::texture_2d) { + ReadEmbeddecTexture(currentNode); + } else if (currentNodeName == XmlTag::texture_group) { + ReadTextureGroup(currentNode); + } else if (currentNodeName == XmlTag::object) { + ReadObject(currentNode); + } else if (currentNodeName == XmlTag::basematerials) { + ReadBaseMaterials(currentNode); + } else if (currentNodeName == XmlTag::meta) { + ReadMetadata(currentNode); + } + } + StoreMaterialsInScene(scene); + XmlNode buildNode = node.child(XmlTag::build); + if (buildNode.empty()) { + return; + } + + for (auto ¤tNode : buildNode.children()) { + const std::string currentNodeName = currentNode.name(); + if (currentNodeName == XmlTag::item) { + int objectId = IdNotSet; + std::string transformationMatrixStr; + aiMatrix4x4 transformationMatrix; + getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId); + bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transformationMatrixStr); + + auto it = mResourcesDictionnary.find(objectId); + if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { + Object *obj = static_cast(it->second); + if (hasTransform) { + transformationMatrix = parseTransformMatrix(transformationMatrixStr); + } + + addObjectToNode(scene->mRootNode, obj, transformationMatrix); + } + } + } + + // import the metadata + if (!mMetaData.empty()) { + const size_t numMeta = mMetaData.size(); + scene->mMetaData = aiMetadata::Alloc(static_cast(numMeta)); + for (size_t i = 0; i < numMeta; ++i) { + aiString val(mMetaData[i].value); + scene->mMetaData->Set(static_cast(i), mMetaData[i].name, val); + } + } + + // import the meshes, materials are already stored + scene->mNumMeshes = static_cast(mMeshCount); + if (scene->mNumMeshes != 0) { + scene->mMeshes = new aiMesh *[scene->mNumMeshes](); + for (auto &it : mResourcesDictionnary) { + if (it.second->getType() == ResourceType::RT_Object) { + Object *obj = static_cast(it.second); + ai_assert(nullptr != obj); + for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) { + scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i]; + } + } + } + } +} + +void XmlSerializer::addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) { + ai_assert(nullptr != obj); + + aiNode *sceneNode = new aiNode(obj->mName); + sceneNode->mNumMeshes = static_cast(obj->mMeshes.size()); + sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes]; + std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes); + + sceneNode->mTransformation = nodeTransform; + if (nullptr != parent) { + parent->addChildren(1, &sceneNode); + } + + for (Assimp::D3MF::Component c : obj->mComponents) { + auto it = mResourcesDictionnary.find(c.mObjectId); + if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { + addObjectToNode(sceneNode, static_cast(it->second), c.mTransformation); + } + } +} + +bool XmlSerializer::getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) { + pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str()); + if (!objectAttribute.empty()) { + value = objectAttribute.as_string(); + return true; + } + + return false; +} + +bool XmlSerializer::getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) { + std::string strValue; + bool ret = getNodeAttribute(node, attribute, strValue); + if (ret) { + value = std::atoi(strValue.c_str()); + return true; + } + + return false; +} + +aiMatrix4x4 XmlSerializer::parseTransformMatrix(std::string matrixStr) { + // split the string + std::vector numbers; + std::string currentNumber; + for (char c : matrixStr) { + if (c == ' ') { + if (!currentNumber.empty()) { + float f = std::stof(currentNumber); + numbers.push_back(f); + currentNumber.clear(); + } + } else { + currentNumber.push_back(c); + } + } + if (!currentNumber.empty()) { + const float f = std::stof(currentNumber); + numbers.push_back(f); + } + + aiMatrix4x4 transformMatrix; + transformMatrix.a1 = numbers[0]; + transformMatrix.b1 = numbers[1]; + transformMatrix.c1 = numbers[2]; + transformMatrix.d1 = 0; + + transformMatrix.a2 = numbers[3]; + transformMatrix.b2 = numbers[4]; + transformMatrix.c2 = numbers[5]; + transformMatrix.d2 = 0; + + transformMatrix.a3 = numbers[6]; + transformMatrix.b3 = numbers[7]; + transformMatrix.c3 = numbers[8]; + transformMatrix.d3 = 0; + + transformMatrix.a4 = numbers[9]; + transformMatrix.b4 = numbers[10]; + transformMatrix.c4 = numbers[11]; + transformMatrix.d4 = 1; + + return transformMatrix; +} + +void XmlSerializer::ReadObject(XmlNode &node) { + int id = IdNotSet, pid = IdNotSet, pindex = IdNotSet; + bool hasId = getNodeAttribute(node, XmlTag::id, id); + if (!hasId) { + return; + } + + bool hasPid = getNodeAttribute(node, XmlTag::pid, pid); + bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex); + + Object *obj = new Object(id); + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == D3MF::XmlTag::mesh) { + auto mesh = ReadMesh(currentNode); + mesh->mName.Set(ai_to_string(id)); + + if (hasPid) { + auto it = mResourcesDictionnary.find(pid); + if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) { + BaseMaterials *materials = static_cast(it->second); + mesh->mMaterialIndex = materials->mMaterialIndex[pindex]; + } + } + + obj->mMeshes.push_back(mesh); + obj->mMeshIndex.push_back(mMeshCount); + mMeshCount++; + } else if (currentName == D3MF::XmlTag::components) { + for (XmlNode ¤tSubNode : currentNode.children()) { + const std::string subNodeName = currentSubNode.name(); + if (subNodeName == D3MF::XmlTag::component) { + int objectId = IdNotSet; + std::string componentTransformStr; + aiMatrix4x4 componentTransform; + if (getNodeAttribute(currentSubNode, D3MF::XmlTag::transform, componentTransformStr)) { + componentTransform = parseTransformMatrix(componentTransformStr); + } + + if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) { + obj->mComponents.push_back({ objectId, componentTransform }); + } + } + } + } + } + + mResourcesDictionnary.insert(std::make_pair(id, obj)); +} + +aiMesh *XmlSerializer::ReadMesh(XmlNode &node) { + if (node.empty()) { + return nullptr; + } + + aiMesh *mesh = new aiMesh(); + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::vertices) { + ImportVertices(currentNode, mesh); + } else if (currentName == XmlTag::triangles) { + ImportTriangles(currentNode, mesh); + } + } + + return mesh; +} + +void XmlSerializer::ReadMetadata(XmlNode &node) { + pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name); + const std::string name = attribute.as_string(); + const std::string value = node.value(); + if (name.empty()) { + return; + } + + MetaEntry entry; + entry.name = name; + entry.value = value; + mMetaData.push_back(entry); +} + +void XmlSerializer::ImportVertices(XmlNode &node, aiMesh *mesh) { + ai_assert(nullptr != mesh); + + std::vector vertices; + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::vertex) { + vertices.push_back(ReadVertex(currentNode)); + } + } + + mesh->mNumVertices = static_cast(vertices.size()); + mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + std::copy(vertices.begin(), vertices.end(), mesh->mVertices); +} + +aiVector3D XmlSerializer::ReadVertex(XmlNode &node) { + aiVector3D vertex; + vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr); + vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr); + vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr); + + return vertex; +} + +void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) { + std::vector faces; + + const size_t numTriangles = std::distance(node.children(XmlTag::triangle).begin(), node.children(XmlTag::triangle).end()); + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::triangle) { + int pid = IdNotSet, p1 = IdNotSet; + bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid); + bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1); + + if (hasPid && hasP1) { + auto it = mResourcesDictionnary.find(pid); + if (it != mResourcesDictionnary.end()) { + if (it->second->getType() == ResourceType::RT_BaseMaterials) { + BaseMaterials *baseMaterials = static_cast(it->second); + mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1]; + } else if (it->second->getType() == ResourceType::RT_Texture2DGroup) { + if (mesh->mTextureCoords[0] == nullptr) { + Texture2DGroup *group = static_cast(it->second); + const std::string name = ai_to_string(group->mTexId); + for (size_t i = 0; i < mMaterials.size(); ++i) { + if (name == mMaterials[i]->GetName().C_Str()) { + mesh->mMaterialIndex = static_cast(i); + } + } + mesh->mTextureCoords[0] = new aiVector3D[group->mTex2dCoords.size()]; + for (unsigned int i = 0; i < group->mTex2dCoords.size(); ++i) { + mesh->mTextureCoords[0][i] = aiVector3D(group->mTex2dCoords[i].x, group->mTex2dCoords[i].y, 0); + } + } + } + } + } + + aiFace face = ReadTriangle(currentNode); + faces.push_back(face); + } + } + + mesh->mNumFaces = static_cast(faces.size()); + mesh->mFaces = new aiFace[mesh->mNumFaces]; + mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + std::copy(faces.begin(), faces.end(), mesh->mFaces); +} + +aiFace XmlSerializer::ReadTriangle(XmlNode &node) { + aiFace face; + + face.mNumIndices = 3; + face.mIndices = new unsigned int[face.mNumIndices]; + face.mIndices[0] = static_cast(std::atoi(node.attribute(XmlTag::v1).as_string())); + face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); + face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); + + return face; +} + +void XmlSerializer::ReadBaseMaterials(XmlNode &node) { + int id = IdNotSet; + if (getNodeAttribute(node, D3MF::XmlTag::id, id)) { + BaseMaterials *baseMaterials = new BaseMaterials(id); + + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::basematerials_base) { + baseMaterials->mMaterialIndex.push_back(static_cast(mMaterials.size())); + mMaterials.push_back(readMaterialDef(currentNode, id)); + } + } + + mResourcesDictionnary.insert(std::make_pair(id, baseMaterials)); + } +} + +static const size_t ColRGBA_Len = 9; +static const size_t ColRGB_Len = 7; + +// format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) +static bool validateColorString(const char *color) { + const size_t len = strlen(color); + if (ColRGBA_Len != len && ColRGB_Len != len) { + return false; + } + + return true; +} + +bool XmlSerializer::parseColor(const char *color, aiColor4D &diffuse) { + if (nullptr == color) { + return false; + } + + if (!validateColorString(color)) { + return false; + } + + //const char *buf(color); + if ('#' != color[0]) { + return false; + } + + char r[3] = { color[1], color[2], '\0' }; + diffuse.r = static_cast(strtol(r, nullptr, 16)) / ai_real(255.0); + + char g[3] = { color[3], color[4], '\0' }; + diffuse.g = static_cast(strtol(g, nullptr, 16)) / ai_real(255.0); + + char b[3] = { color[5], color[6], '\0' }; + diffuse.b = static_cast(strtol(b, nullptr, 16)) / ai_real(255.0); + const size_t len = strlen(color); + if (ColRGB_Len == len) { + return true; + } + + char a[3] = { color[7], color[8], '\0' }; + diffuse.a = static_cast(strtol(a, nullptr, 16)) / ai_real(255.0); + + return true; +} + +void XmlSerializer::ReadEmbeddecTexture(XmlNode &node) { + if (node.empty()) { + return; + } + + std::string value; + EmbeddedTexture *tex2D = nullptr; + if (XmlParser::getStdStrAttribute(node, XmlTag::id, value)) { + tex2D = new EmbeddedTexture(atoi(value.c_str())); + } + if (nullptr == tex2D) { + return; + } + + if (XmlParser::getStdStrAttribute(node, XmlTag::path, value)) { + tex2D->mPath = value; + } + if (XmlParser::getStdStrAttribute(node, XmlTag::texture_content_type, value)) { + tex2D->mContentType = value; + } + if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestyleu, value)) { + tex2D->mTilestyleU = value; + } + if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestylev, value)) { + tex2D->mTilestyleV = value; + } + mEmbeddedTextures.emplace_back(tex2D); + StoreEmbeddedTexture(tex2D); +} + +void XmlSerializer::StoreEmbeddedTexture(EmbeddedTexture *tex) { + aiMaterial *mat = new aiMaterial; + aiString s; + s.Set(ai_to_string(tex->mId).c_str()); + mat->AddProperty(&s, AI_MATKEY_NAME); + s.Set(tex->mPath); + mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0)); + + aiColor3D col; + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_DIFFUSE); + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_AMBIENT); + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_EMISSIVE); + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_SPECULAR); + mMaterials.emplace_back(mat); +} + +void XmlSerializer::ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup) { + if (node.empty() || nullptr == tex2DGroup) { + return; + } + int id = IdNotSet; + if (XmlParser::getIntAttribute(node, "texid", id)) { + tex2DGroup->mTexId = id; + } + double value; + for (XmlNode currentNode : node.children()) { + const std::string currentName = currentNode.name(); + aiVector2D texCoord; + if (currentName == XmlTag::texture_2d_coord) { + XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_u, value); + texCoord.x = (ai_real)value; + XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_v, value); + texCoord.y = (ai_real)value; + tex2DGroup->mTex2dCoords.push_back(texCoord); + } + } +} + +void XmlSerializer::ReadTextureGroup(XmlNode &node) { + if (node.empty()) { + return; + } + + int id = IdNotSet; + if (!XmlParser::getIntAttribute(node, XmlTag::id, id)) { + return; + } + + Texture2DGroup *group = new Texture2DGroup(id); + ReadTextureCoords2D(node, group); + mResourcesDictionnary.insert(std::make_pair(id, group)); +} + +void XmlSerializer::assignDiffuseColor(XmlNode &node, aiMaterial *mat) { + const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string(); + aiColor4D diffuse; + if (parseColor(color, diffuse)) { + mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + } +} + +aiMaterial *XmlSerializer::readMaterialDef(XmlNode &node, unsigned int basematerialsId) { + aiMaterial *material = new aiMaterial(); + material->mNumProperties = 0; + std::string name; + bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name); + + std::string stdMaterialName; + const std::string strId(ai_to_string(basematerialsId)); + stdMaterialName += "id"; + stdMaterialName += strId; + stdMaterialName += "_"; + if (hasName) { + stdMaterialName += std::string(name); + } else { + stdMaterialName += "basemat_"; + stdMaterialName += ai_to_string(mMaterials.size()); + } + + aiString assimpMaterialName(stdMaterialName); + material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME); + + assignDiffuseColor(node, material); + + return material; +} + +void XmlSerializer::StoreMaterialsInScene( aiScene *scene ) { + if (nullptr == scene || mMaterials.empty()) { + return; + } + + scene->mNumMaterials = static_cast(mMaterials.size()); + scene->mMaterials = new aiMaterial*[scene->mNumMaterials]; + for (size_t i = 0; i < mMaterials.size(); ++i) { + scene->mMaterials[i] = mMaterials[i]; + } +} +} // namespace D3MF +} // namespace Assimp diff --git a/code/AssetLib/3MF/XmlSerializer.h b/code/AssetLib/3MF/XmlSerializer.h new file mode 100644 index 000000000..40300d70d --- /dev/null +++ b/code/AssetLib/3MF/XmlSerializer.h @@ -0,0 +1,104 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2021, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include +#include +#include +#include + +struct aiNode; +struct aiMesh; +struct aiMaterial; + +namespace Assimp { +namespace D3MF { + +class Resource; +class D3MFOpcPackage; +class Object; +class Texture2DGroup; +class EmbeddedTexture; + +class XmlSerializer { +public: + XmlSerializer(XmlParser *xmlParser, D3MFOpcPackage *archive); + ~XmlSerializer(); + void ImportXml(aiScene *scene); + +private: + void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform); + bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value); + bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value); + aiMatrix4x4 parseTransformMatrix(std::string matrixStr); + void ReadObject(XmlNode &node); + aiMesh *ReadMesh(XmlNode &node); + void ReadMetadata(XmlNode &node); + void ImportVertices(XmlNode &node, aiMesh *mesh); + aiVector3D ReadVertex(XmlNode &node); + void ImportTriangles(XmlNode &node, aiMesh *mesh); + aiFace ReadTriangle(XmlNode &node); + void ReadBaseMaterials(XmlNode &node); + bool parseColor(const char *color, aiColor4D &diffuse); + void ReadEmbeddecTexture(XmlNode &node); + void StoreEmbeddedTexture(EmbeddedTexture *tex); + void ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup); + void ReadTextureGroup(XmlNode &node); + void assignDiffuseColor(XmlNode &node, aiMaterial *mat); + aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId); + void StoreMaterialsInScene(aiScene *scene); + +private: + struct MetaEntry { + std::string name; + std::string value; + }; + std::vector mMetaData; + std::vector mEmbeddedTextures; + std::vector mMaterials; + std::map mResourcesDictionnary; + unsigned int mMeshCount; + XmlParser *mXmlParser; + D3MFOpcPackage *mD3MFOpcPackage; +}; + +} // namespace D3MF +} // namespace Assimp diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index 492e6971f..876a6fd1d 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -135,14 +135,15 @@ bool ColladaLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool // XML - too generic, we need to open the file and search for typical keywords if (extension == "xml" || !extension.length() || checkSig) { - /* If CanRead() is called in order to check whether we - * support a specific file extension in general pIOHandler - * might be nullptr and it's our duty to return true here. - */ - if (!pIOHandler) { + // If CanRead() is called in order to check whether we + // support a specific file extension in general pIOHandler + // might be nullptr and it's our duty to return true here. + if (nullptr == pIOHandler) { return true; } - static const char *tokens[] = { "mNumMeshes = static_cast(newMeshRefs.size()); - if (newMeshRefs.size()) { + if (!newMeshRefs.empty()) { struct UIntTypeConverter { unsigned int operator()(const size_t &v) const { return static_cast(v); @@ -1538,9 +1539,9 @@ void ColladaLoader::AddTexture(aiMaterial &mat, map = sampler.mUVId; } else { map = -1; - for (std::string::const_iterator it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) { - if (IsNumeric(*it)) { - map = strtoul10(&(*it)); + for (char it : sampler.mUVChannel) { + if (IsNumeric(it)) { + map = strtoul10(&it); break; } } @@ -1682,7 +1683,7 @@ void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/) // store the material mMaterialIndexByName[matIt->first] = newMats.size(); - newMats.push_back(std::pair(&effect, mat)); + newMats.emplace_back(&effect, mat); } // ScenePreprocessor generates a default material automatically if none is there. // All further code here in this loader works well without a valid material so diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 37c7274f4..bc5f951ab 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -918,7 +918,7 @@ void ColladaParser::ReadMaterial(XmlNode &node, Collada::Material &pMaterial) { if (currentName == "instance_effect") { std::string url; readUrlAttribute(currentNode, url); - pMaterial.mEffect = url.c_str(); + pMaterial.mEffect = url; } } } @@ -2208,8 +2208,8 @@ void ColladaParser::ReadMaterialVertexInputBinding(XmlNode &node, Collada::Seman void ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive) { // Attempt to load any undefined Collada::Image in ImageLibrary - for (ImageLibrary::iterator it = mImageLibrary.begin(); it != mImageLibrary.end(); ++it) { - Collada::Image &image = (*it).second; + for (auto & it : mImageLibrary) { + Collada::Image &image = it.second; if (image.mImageData.empty()) { std::unique_ptr image_file(zip_archive.Open(image.mFileName.c_str())); diff --git a/code/AssetLib/Obj/ObjFileImporter.cpp b/code/AssetLib/Obj/ObjFileImporter.cpp index d6232be81..e45533c30 100644 --- a/code/AssetLib/Obj/ObjFileImporter.cpp +++ b/code/AssetLib/Obj/ObjFileImporter.cpp @@ -162,7 +162,7 @@ void ObjFileImporter::InternReadFile(const std::string &file, aiScene *pScene, I // ------------------------------------------------------------------------------------------------ // Create the data from parsed obj-file void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene *pScene) { - if (0L == pModel) { + if (nullptr == pModel) { return; } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index d80df97d2..08a1ea0ed 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1470,10 +1470,11 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { } } - if (numEmbeddedTexs == 0) + if (numEmbeddedTexs == 0) { return; + } - ASSIMP_LOG_DEBUG("Importing ", numEmbeddedTexs, " embedded textures"); + ASSIMP_LOG_DEBUG("Importing ", numEmbeddedTexs, " embedded textures"); mScene->mTextures = new aiTexture *[numEmbeddedTexs]; std::fill(mScene->mTextures, mScene->mTextures + numEmbeddedTexs, nullptr); diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index fd92d41e3..130ab8870 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -820,7 +820,10 @@ ADD_ASSIMP_IMPORTER( GLTF AssetLib/glTF2/glTF2Importer.h ) -ADD_ASSIMP_IMPORTER( 3MF +ADD_ASSIMP_IMPORTER(3MF + AssetLib/3MF/3MFTypes.h + AssetLib/3MF/XmlSerializer.h + AssetLib/3MF/XmlSerializer.cpp AssetLib/3MF/D3MFImporter.h AssetLib/3MF/D3MFImporter.cpp AssetLib/3MF/D3MFOpcPackage.h diff --git a/include/assimp/scene.h b/include/assimp/scene.h index 522ddc6dc..b54c29dfa 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -400,7 +400,7 @@ struct aiScene //! Returns an embedded texture and its index std::pair GetEmbeddedTextureAndIndex(const char* filename) const { - if(nullptr==filename) { + if (nullptr==filename) { return std::make_pair(nullptr, -1); } // lookup using texture ID (if referenced like: "*1", "*2", etc.) diff --git a/include/assimp/vector3.h b/include/assimp/vector3.h index b084cf9c3..e3ad0b680 100644 --- a/include/assimp/vector3.h +++ b/include/assimp/vector3.h @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -65,27 +63,49 @@ template class aiMatrix3x3t; template class aiMatrix4x4t; // --------------------------------------------------------------------------- -/** Represents a three-dimensional vector. */ +/// @brief Represents a three-dimensional vector. +// --------------------------------------------------------------------------- template class aiVector3t { public: + /// @brief The default class constructor. aiVector3t() AI_NO_EXCEPT : x(), y(), z() {} + + /// @brief The class constructor with the components. + /// @param _x The x-component for the vector. + /// @param _y The y-component for the vector. + /// @param _z The z-component for the vector. aiVector3t(TReal _x, TReal _y, TReal _z) : x(_x), y(_y), z(_z) {} + + /// @brief The class constructor with a default value. + /// @param _xyz The value for x, y and z. explicit aiVector3t (TReal _xyz ) : x(_xyz), y(_xyz), z(_xyz) {} + + /// @brief The copy constructor. + /// @param o The instance to copy from. aiVector3t( const aiVector3t& o ) = default; - // combined operators + /// @brief combined operators + /// @brief The copy constructor. const aiVector3t& operator += (const aiVector3t& o); + + /// @brief The copy constructor. const aiVector3t& operator -= (const aiVector3t& o); + + /// @brief The copy constructor. const aiVector3t& operator *= (TReal f); + + /// @brief The copy constructor. const aiVector3t& operator /= (TReal f); - // transform vector by matrix + /// @brief Transform vector by matrix aiVector3t& operator *= (const aiMatrix3x3t& mat); aiVector3t& operator *= (const aiMatrix4x4t& mat); - // access a single element + /// @brief access a single element, const. TReal operator[](unsigned int i) const; + + /// @brief access a single element, non-const. TReal& operator[](unsigned int i); // comparison @@ -93,6 +113,7 @@ public: bool operator!= (const aiVector3t& other) const; bool operator < (const aiVector3t& other) const; + /// @brief bool Equal(const aiVector3t& other, TReal epsilon = 1e-6) const; template diff --git a/tools/assimp_view/MaterialManager.h b/tools/assimp_view/MaterialManager.h index 77ca4224f..9aeb5e992 100644 --- a/tools/assimp_view/MaterialManager.h +++ b/tools/assimp_view/MaterialManager.h @@ -52,24 +52,11 @@ namespace AssimpView { */ //------------------------------------------------------------------------------- class CMaterialManager { -private: friend class CDisplay; - // default constructor - CMaterialManager() : - m_iShaderCount(0), sDefaultTexture() {} - - ~CMaterialManager() { - if (sDefaultTexture) { - sDefaultTexture->Release(); - } - Reset(); - } - public: //------------------------------------------------------------------ // Singleton accessors - static CMaterialManager s_cInstance; inline static CMaterialManager &Instance() { return s_cInstance; } @@ -80,24 +67,20 @@ public: // Must be called before CreateMaterial() to prevent memory leaking void DeleteMaterial(AssetHelper::MeshHelper *pcIn); - //------------------------------------------------------------------ - // Create the material for a mesh. - // - // The function checks whether an identical shader is already in use. - // A shader is considered to be identical if it has the same input - // signature and takes the same number of texture channels. - int CreateMaterial(AssetHelper::MeshHelper *pcMesh, - const aiMesh *pcSource); - - //------------------------------------------------------------------ - // Setup the material for a given mesh - // pcMesh Mesh to be rendered - // pcProj Projection matrix - // aiMe Current world matrix - // pcCam Camera matrix - // vPos Position of the camera - // TODO: Extract camera position from matrix ... - // + /// @brief Create the material for a mesh. + /// + /// The function checks whether an identical shader is already in use. + /// A shader is considered to be identical if it has the same input + /// signature and takes the same number of texture channels. + int CreateMaterial(AssetHelper::MeshHelper *pcMesh, const aiMesh *pcSource); + + /// @brief Setup the material for a given mesh. + /// @param pcMesh Mesh to be rendered + /// @param pcProj Projection matrix + /// @param aiMe Current world matrix + /// @param pcCam Camera matrix + /// @param vPos Position of the camera + /// @return 0 if successful. int SetupMaterial(AssetHelper::MeshHelper *pcMesh, const aiMatrix4x4 &pcProj, const aiMatrix4x4 &aiMe, @@ -143,14 +126,29 @@ public: // Reset the state of the class // Called whenever a new asset is loaded inline void Reset() { - this->m_iShaderCount = 0; - for (TextureCache::iterator it = sCachedTextures.begin(); it != sCachedTextures.end(); ++it) { - (*it).second->Release(); + m_iShaderCount = 0; + for (auto & sCachedTexture : sCachedTextures) { + sCachedTexture.second->Release(); } sCachedTextures.clear(); } private: + // The default constructor + CMaterialManager() : + m_iShaderCount(0), + sDefaultTexture() { + // empty + } + + // Destructor, private. + ~CMaterialManager() { + if (sDefaultTexture) { + sDefaultTexture->Release(); + } + Reset(); + } + //------------------------------------------------------------------ // find a valid path to a texture file // @@ -183,15 +181,14 @@ private: bool HasAlphaPixels(IDirect3DTexture9 *piTexture); private: - // + static CMaterialManager s_cInstance; + // Specifies the number of different shaders generated for // the current asset. This number is incremented by CreateMaterial() // each time a shader isn't found in cache and needs to be created - // unsigned int m_iShaderCount; IDirect3DTexture9 *sDefaultTexture; - - typedef std::map TextureCache; + using TextureCache = std::map; TextureCache sCachedTextures; }; diff --git a/tools/assimp_view/MeshRenderer.cpp b/tools/assimp_view/MeshRenderer.cpp index 0ec12e5b1..bc1a5236f 100644 --- a/tools/assimp_view/MeshRenderer.cpp +++ b/tools/assimp_view/MeshRenderer.cpp @@ -61,11 +61,14 @@ int CMeshRenderer::DrawUnsorted(unsigned int iIndex) { D3DPRIMITIVETYPE type = D3DPT_POINTLIST; switch (g_pcAsset->pcScene->mMeshes[iIndex]->mPrimitiveTypes) { case aiPrimitiveType_POINT: - type = D3DPT_POINTLIST;break; + type = D3DPT_POINTLIST; + break; case aiPrimitiveType_LINE: - type = D3DPT_LINELIST;break; + type = D3DPT_LINELIST; + break; case aiPrimitiveType_TRIANGLE: - type = D3DPT_TRIANGLELIST;break; + type = D3DPT_TRIANGLELIST; + break; } // and draw the mesh g_piDevice->DrawIndexedPrimitive(type, From de2f5cf0213ffb575c5b1381e39a2660cde13fba Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Fri, 13 Aug 2021 16:14:28 +0100 Subject: [PATCH 125/232] Crash fixes --- code/AssetLib/FBX/FBXParser.cpp | 24 ++++++++---------------- code/AssetLib/glTF2/glTF2Asset.inl | 2 +- code/AssetLib/glTF2/glTF2Importer.cpp | 6 ++++++ 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/code/AssetLib/FBX/FBXParser.cpp b/code/AssetLib/FBX/FBXParser.cpp index 37cf83cf9..61e53c2d0 100644 --- a/code/AssetLib/FBX/FBXParser.cpp +++ b/code/AssetLib/FBX/FBXParser.cpp @@ -642,8 +642,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -733,8 +732,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -816,8 +814,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -892,8 +889,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 4; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -954,8 +950,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -1019,8 +1014,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 4; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -1088,8 +1082,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 8; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -1150,8 +1143,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 8; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index d5f5b5cd0..9f793d7c0 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1522,7 +1522,7 @@ inline bool GetAttribTargetVector(Mesh::Primitive &p, const int targetIndex, con inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) { Value *curName = FindMember(pJSON_Object, "name"); - if (nullptr != curName) { + if (nullptr != curName && curName->IsString()) { name = curName->GetString(); } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index 921aaad9e..a4f3f0ba1 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1336,6 +1336,12 @@ std::unordered_map GatherSamplers(Animation &an continue; } + auto& animsampler = anim.samplers[channel.sampler]; + if (animsampler.input->count != animsampler.output->count) { + ASSIMP_LOG_WARN("Animation ", anim.name, ": Sampler input size ", animsampler.input->count, " doesn't match output size ", animsampler.output->count); + continue; + } + const unsigned int node_index = channel.target.node.GetIndex(); AnimationSamplers &sampler = samplers[node_index]; From c1e830cf3b62de1f0fcc7ec14dcd374392e2e393 Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Fri, 13 Aug 2021 17:46:10 +0100 Subject: [PATCH 126/232] The GLTF2 specs aren't very specific about the restrictions of the number of keyframes in sampler input and output. It seems logical that they should be the same, but there is an official sample model from Khronos where output has more keyframes. I thus assume that the GLTF2 standard allows more keyframes in output, but not in input. Fixed the check accordingly. --- code/AssetLib/glTF2/glTF2Importer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index a4f3f0ba1..7a781ba04 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1337,8 +1337,8 @@ std::unordered_map GatherSamplers(Animation &an } auto& animsampler = anim.samplers[channel.sampler]; - if (animsampler.input->count != animsampler.output->count) { - ASSIMP_LOG_WARN("Animation ", anim.name, ": Sampler input size ", animsampler.input->count, " doesn't match output size ", animsampler.output->count); + if (animsampler.input->count > animsampler.output->count) { + ASSIMP_LOG_WARN("Animation ", anim.name, ": Number of keyframes in sampler input ", animsampler.input->count, " exceeds number of keyframes in sampler output ", animsampler.output->count); continue; } From d926c5ed61727564b1db0a802a1aae51b0eb6916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E6=B5=A9=E7=84=B6?= Date: Thu, 19 Aug 2021 18:49:43 +0800 Subject: [PATCH 127/232] fix sample build error --- .../SimpleTexturedOpenGL/src/model_loading.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp b/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp index 9ceeec62e..be0e651f1 100644 --- a/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp +++ b/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp @@ -23,7 +23,7 @@ #endif // _MSC_VER #define STB_IMAGE_IMPLEMENTATION -#include "contrib/stb_image/stb_image.h" +#include "contrib/stb/stb_image.h" #ifdef _MSC_VER #pragma warning(default: 4100) // Enable warning 'unreferenced formal parameter' From 468aa50aed40ca3a49b03e9e7766785467869682 Mon Sep 17 00:00:00 2001 From: kovacsv Date: Thu, 19 Aug 2021 13:50:26 +0200 Subject: [PATCH 128/232] mingw build fix --- code/AssetLib/Assbin/AssbinFileWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/Assbin/AssbinFileWriter.cpp b/code/AssetLib/Assbin/AssbinFileWriter.cpp index 95379a303..2519b0b93 100644 --- a/code/AssetLib/Assbin/AssbinFileWriter.cpp +++ b/code/AssetLib/Assbin/AssbinFileWriter.cpp @@ -172,7 +172,7 @@ inline size_t Write(IOStream *stream, const aiQuaternion &v) { t += Write(stream, v.z); ai_assert(t == 16); - return 16; + return t; } // ----------------------------------------------------------------------------------- From c396bc78b1ea5acc5c6f40401951d5994edd0171 Mon Sep 17 00:00:00 2001 From: kimmi Date: Fri, 20 Aug 2021 19:40:04 +0200 Subject: [PATCH 129/232] closes https://github.com/assimp/assimp/issues/3951: Use using directive to define type. --- test/unit/utglTF2ImportExport.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 90070d63e..0b9d4d70d 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -748,7 +748,8 @@ TEST_F(utglTF2ImportExport, import_dracoEncoded) { TEST_F(utglTF2ImportExport, wrongTypes) { // Deliberately broken version of the BoxTextured.gltf asset. - std::vector> wrongTypes = { + using tup_T = std::tuple; + std::vector wrongTypes = { { "/glTF2/wrongTypes/badArray.gltf", "array", "primitives", "meshes[0]" }, { "/glTF2/wrongTypes/badString.gltf", "string", "name", "scenes[0]" }, { "/glTF2/wrongTypes/badUint.gltf", "uint", "index", "materials[0]" }, From 38dcd3583c4026574dc0a61cd0683331064bc63a Mon Sep 17 00:00:00 2001 From: kimmi Date: Fri, 20 Aug 2021 20:20:47 +0200 Subject: [PATCH 130/232] Fix compiler warnings: comparison signed unsigned. --- test/unit/utglTF2ImportExport.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 0b9d4d70d..e29d09145 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -615,12 +615,12 @@ TEST_F(utglTF2ImportExport, texcoords) { aiTextureMapMode modes[2]; EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); EXPECT_STREQ(path.C_Str(), "texture.png"); - EXPECT_EQ(uvIndex, 0); + EXPECT_EQ(uvIndex, 0u); uvIndex = 255; EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); EXPECT_STREQ(path.C_Str(), "texture.png"); - EXPECT_EQ(uvIndex, 1); + EXPECT_EQ(uvIndex, 1u); } #ifndef ASSIMP_BUILD_NO_EXPORT @@ -646,12 +646,12 @@ TEST_F(utglTF2ImportExport, texcoords_export) { aiTextureMapMode modes[2]; EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); EXPECT_STREQ(path.C_Str(), "texture.png"); - EXPECT_EQ(uvIndex, 0); + EXPECT_EQ(uvIndex, 0u); uvIndex = 255; EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); EXPECT_STREQ(path.C_Str(), "texture.png"); - EXPECT_EQ(uvIndex, 1); + EXPECT_EQ(uvIndex, 1u); } #endif // ASSIMP_BUILD_NO_EXPORT From 18f58947a476071c4cb18000cc6fbf976aff1b53 Mon Sep 17 00:00:00 2001 From: kimmi Date: Fri, 20 Aug 2021 20:42:02 +0200 Subject: [PATCH 131/232] CMake: move hunter cmake-scripts into cmake-modules --- CMakeLists.txt | 6 +++--- {cmake => cmake-modules}/HunterGate.cmake | 0 cmake-modules/assimp-hunter-config.cmake.in | 19 +++++++++++++++++++ .../assimp-plain-config.cmake.in | 0 cmake/assimp-hunter-config.cmake.in | 19 ------------------- 5 files changed, 22 insertions(+), 22 deletions(-) rename {cmake => cmake-modules}/HunterGate.cmake (100%) create mode 100644 cmake-modules/assimp-hunter-config.cmake.in rename {cmake => cmake-modules}/assimp-plain-config.cmake.in (100%) delete mode 100644 cmake/assimp-hunter-config.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 2dfe592bc..217d558fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,7 @@ CMAKE_MINIMUM_REQUIRED( VERSION 3.10 ) option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF) IF(ASSIMP_HUNTER_ENABLED) - include("cmake/HunterGate.cmake") + include("cmake-modules/HunterGate.cmake") HunterGate( URL "https://github.com/cpp-pm/hunter/archive/v0.23.311.tar.gz" SHA1 "1a82b9b73055879181cb1466b2ab5d48ee8ae410" @@ -397,14 +397,14 @@ set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") IF(ASSIMP_HUNTER_ENABLED) set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}") - set(CMAKE_CONFIG_TEMPLATE_FILE "cmake/assimp-hunter-config.cmake.in") + set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-mocules/assimp-hunter-config.cmake.in") set(NAMESPACE "${PROJECT_NAME}::") set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") set(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake") set(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}Config.cmake") ELSE() set(CONFIG_INSTALL_DIR "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}") - set(CMAKE_CONFIG_TEMPLATE_FILE "cmake/assimp-plain-config.cmake.in") + set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-modules/assimp-plain-config.cmake.in") string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWERCASE) set(NAMESPACE "${PROJECT_NAME_LOWERCASE}::") set(TARGETS_EXPORT_NAME "${PROJECT_NAME_LOWERCASE}Targets") diff --git a/cmake/HunterGate.cmake b/cmake-modules/HunterGate.cmake similarity index 100% rename from cmake/HunterGate.cmake rename to cmake-modules/HunterGate.cmake diff --git a/cmake-modules/assimp-hunter-config.cmake.in b/cmake-modules/assimp-hunter-config.cmake.in new file mode 100644 index 000000000..1988f7e7d --- /dev/null +++ b/cmake-modules/assimp-hunter-config.cmake.in @@ -0,0 +1,19 @@ +@PACKAGE_INIT@ + +find_package(RapidJSON CONFIG REQUIRED) +find_package(ZLIB CONFIG REQUIRED) +find_package(utf8cpp CONFIG REQUIRED) +find_package(minizip CONFIG REQUIRED) +find_package(openddlparser CONFIG REQUIRED) +find_package(poly2tri CONFIG REQUIRED) +find_package(polyclipping CONFIG REQUIRED) +find_package(zip CONFIG REQUIRED) +find_package(pugixml CONFIG REQUIRED) +find_package(stb CONFIG REQUIRED) + +if(@ASSIMP_BUILD_DRACO@) + find_package(draco CONFIG REQUIRED) +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/cmake/assimp-plain-config.cmake.in b/cmake-modules/assimp-plain-config.cmake.in similarity index 100% rename from cmake/assimp-plain-config.cmake.in rename to cmake-modules/assimp-plain-config.cmake.in diff --git a/cmake/assimp-hunter-config.cmake.in b/cmake/assimp-hunter-config.cmake.in deleted file mode 100644 index 5c5aeedfd..000000000 --- a/cmake/assimp-hunter-config.cmake.in +++ /dev/null @@ -1,19 +0,0 @@ -@PACKAGE_INIT@ - -find_package(RapidJSON CONFIG REQUIRED) -find_package(ZLIB CONFIG REQUIRED) -find_package(utf8cpp CONFIG REQUIRED) -find_package(minizip CONFIG REQUIRED) -find_package(openddlparser CONFIG REQUIRED) -find_package(poly2tri CONFIG REQUIRED) -find_package(polyclipping CONFIG REQUIRED) -find_package(zip CONFIG REQUIRED) -find_package(pugixml CONFIG REQUIRED) -find_package(stb CONFIG REQUIRED) - -if(@ASSIMP_BUILD_DRACO@) - find_package(draco CONFIG REQUIRED) -endif() - -include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") -check_required_components("@PROJECT_NAME@") From f24101546bd0acd19d3fa02cd7e6515c3ff14b57 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 20 Aug 2021 21:02:42 +0200 Subject: [PATCH 132/232] Fix typo in path --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 217d558fa..6a938f0d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -397,7 +397,7 @@ set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") IF(ASSIMP_HUNTER_ENABLED) set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}") - set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-mocules/assimp-hunter-config.cmake.in") + set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-modules/assimp-hunter-config.cmake.in") set(NAMESPACE "${PROJECT_NAME}::") set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") set(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake") From 0590a39159beabd8263e891390997f752478bd25 Mon Sep 17 00:00:00 2001 From: kovacsv Date: Tue, 24 Aug 2021 07:26:20 +0200 Subject: [PATCH 133/232] Fix M3D import crash and memory leak. The same default material pointer was assigned to all the materials in the scene, so poor destructor tried to free the same pointer multiple times. --- code/AssetLib/M3D/M3DImporter.cpp | 14 +++++----- test/unit/utM3DImportExport.cpp | 43 ++++++++++++++----------------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/code/AssetLib/M3D/M3DImporter.cpp b/code/AssetLib/M3D/M3DImporter.cpp index 4b2f9bd77..efa1d5475 100644 --- a/code/AssetLib/M3D/M3DImporter.cpp +++ b/code/AssetLib/M3D/M3DImporter.cpp @@ -233,12 +233,12 @@ void M3DImporter::importMaterials(const M3DWrapper &m3d) { ASSIMP_LOG_DEBUG("M3D: importMaterials ", mScene->mNumMaterials); // add a default material as first - aiMaterial *mat = new aiMaterial; - mat->AddProperty(&name, AI_MATKEY_NAME); + aiMaterial *defaultMat = new aiMaterial; + defaultMat->AddProperty(&name, AI_MATKEY_NAME); c.a = 1.0f; c.b = c.g = c.r = 0.6f; - mat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE); - mScene->mMaterials[0] = mat; + defaultMat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE); + mScene->mMaterials[0] = defaultMat; if (!m3d->nummaterial || !m3d->material) { return; @@ -300,12 +300,12 @@ void M3DImporter::importMaterials(const M3DWrapper &m3d) { m->prop[j].value.textureid < m3d->numtexture && m3d->texture[m->prop[j].value.textureid].name) { name.Set(std::string(std::string(m3d->texture[m->prop[j].value.textureid].name) + ".png")); - mat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index); + newMat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index); n = 0; - mat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index); + newMat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index); } } - mScene->mMaterials[i + 1] = mat; + mScene->mMaterials[i + 1] = newMat; } } diff --git a/test/unit/utM3DImportExport.cpp b/test/unit/utM3DImportExport.cpp index bd9fca168..24359d319 100644 --- a/test/unit/utM3DImportExport.cpp +++ b/test/unit/utM3DImportExport.cpp @@ -50,35 +50,32 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; -class utM3DImportExport : public AbstractImportExportBase { -public: - bool importerTest() override { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); +TEST(utM3DImportExport, import_cube_normals) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); #ifndef ASSIMP_BUILD_NO_M3D_IMPORTER - return nullptr != scene; + ASSERT_NE(nullptr, scene); #else - return nullptr == scene; + ASSERT_EQ(nullptr, scene); #endif // ASSIMP_BUILD_NO_M3D_IMPORTER - } +} -#ifndef ASSIMP_BUILD_NO_EXPORT - bool exporterTest() override { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); - Exporter exporter; - aiReturn ret = exporter.Export(scene, "m3d", ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals_out.m3d"); - return ret == AI_SUCCESS; - } -#endif -}; - -TEST_F(utM3DImportExport, importM3DFromFileTest) { - EXPECT_TRUE(importerTest()); +TEST(utM3DImportExport, import_cube_usemtl) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_usemtl.m3d", aiProcess_ValidateDataStructure); +#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER + ASSERT_NE(nullptr, scene); +#else + ASSERT_EQ(nullptr, scene); +#endif // ASSIMP_BUILD_NO_M3D_IMPORTER } #ifndef ASSIMP_BUILD_NO_EXPORT -TEST_F(utM3DImportExport, exportM3DFromFileTest) { - EXPECT_TRUE(exporterTest()); +TEST(utM3DImportExport, export_cube_normals) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); + Exporter exporter; + aiReturn ret = exporter.Export(scene, "m3d", ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals_out.m3d"); + ASSERT_EQ(AI_SUCCESS, ret); } #endif // ASSIMP_BUILD_NO_EXPORT From 37ba06783957c455d0a4a08e75aca600a78c7bb6 Mon Sep 17 00:00:00 2001 From: kovacsv Date: Tue, 24 Aug 2021 09:09:35 +0200 Subject: [PATCH 134/232] Revert back test, because the new one revealed an undefined behavior error. --- test/unit/utM3DImportExport.cpp | 46 +++++++++++++++++---------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/test/unit/utM3DImportExport.cpp b/test/unit/utM3DImportExport.cpp index 24359d319..7efc24985 100644 --- a/test/unit/utM3DImportExport.cpp +++ b/test/unit/utM3DImportExport.cpp @@ -43,39 +43,41 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include "UnitTestPCH.h" -#include -#include #include +#include #include using namespace Assimp; -TEST(utM3DImportExport, import_cube_normals) { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); +class utM3DImportExport : public AbstractImportExportBase { +public: + bool importerTest() override { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); #ifndef ASSIMP_BUILD_NO_M3D_IMPORTER - ASSERT_NE(nullptr, scene); + return nullptr != scene; #else - ASSERT_EQ(nullptr, scene); + return nullptr == scene; #endif // ASSIMP_BUILD_NO_M3D_IMPORTER -} + } -TEST(utM3DImportExport, import_cube_usemtl) { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_usemtl.m3d", aiProcess_ValidateDataStructure); -#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER - ASSERT_NE(nullptr, scene); -#else - ASSERT_EQ(nullptr, scene); -#endif // ASSIMP_BUILD_NO_M3D_IMPORTER +#ifndef ASSIMP_BUILD_NO_EXPORT + bool exporterTest() override { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); + Exporter exporter; + aiReturn ret = exporter.Export(scene, "m3d", ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals_out.m3d"); + return ret == AI_SUCCESS; + } +#endif +}; + +TEST_F(utM3DImportExport, importM3DFromFileTest) { + EXPECT_TRUE(importerTest()); } #ifndef ASSIMP_BUILD_NO_EXPORT -TEST(utM3DImportExport, export_cube_normals) { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); - Exporter exporter; - aiReturn ret = exporter.Export(scene, "m3d", ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals_out.m3d"); - ASSERT_EQ(AI_SUCCESS, ret); +TEST_F(utM3DImportExport, exportM3DFromFileTest) { + EXPECT_TRUE(exporterTest()); } #endif // ASSIMP_BUILD_NO_EXPORT From cb262dab5e5599d922633600292e3a59cc2a7f98 Mon Sep 17 00:00:00 2001 From: kovacsv Date: Tue, 24 Aug 2021 11:02:09 +0200 Subject: [PATCH 135/232] Revert test back to the exact same version. --- test/unit/utM3DImportExport.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/test/unit/utM3DImportExport.cpp b/test/unit/utM3DImportExport.cpp index 7efc24985..bd9fca168 100644 --- a/test/unit/utM3DImportExport.cpp +++ b/test/unit/utM3DImportExport.cpp @@ -43,19 +43,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include "UnitTestPCH.h" -#include +#include #include +#include #include using namespace Assimp; class utM3DImportExport : public AbstractImportExportBase { public: - bool importerTest() override { + bool importerTest() override { Assimp::Importer importer; const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); #ifndef ASSIMP_BUILD_NO_M3D_IMPORTER - return nullptr != scene; + return nullptr != scene; #else return nullptr == scene; #endif // ASSIMP_BUILD_NO_M3D_IMPORTER @@ -63,11 +64,11 @@ public: #ifndef ASSIMP_BUILD_NO_EXPORT bool exporterTest() override { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); - Exporter exporter; - aiReturn ret = exporter.Export(scene, "m3d", ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals_out.m3d"); - return ret == AI_SUCCESS; + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); + Exporter exporter; + aiReturn ret = exporter.Export(scene, "m3d", ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals_out.m3d"); + return ret == AI_SUCCESS; } #endif }; @@ -78,6 +79,6 @@ TEST_F(utM3DImportExport, importM3DFromFileTest) { #ifndef ASSIMP_BUILD_NO_EXPORT TEST_F(utM3DImportExport, exportM3DFromFileTest) { - EXPECT_TRUE(exporterTest()); + EXPECT_TRUE(exporterTest()); } #endif // ASSIMP_BUILD_NO_EXPORT From a71baaeedf6d471860b638737f1338509ab72fe3 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 24 Aug 2021 19:22:15 +0200 Subject: [PATCH 136/232] Enable Viewer only for VS-Builds --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a938f0d9..bfa30e96a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,11 +135,11 @@ IF ( WIN32 ) # Use subset of Windows.h ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN ) - OPTION ( ASSIMP_BUILD_ASSIMP_VIEW - "If the Assimp view tool is built. (requires DirectX)" - OFF ) - IF(MSVC) + OPTION ( ASSIMP_BUILD_ASSIMP_VIEW + "If the Assimp view tool is built. (requires DirectX)" + OFF ) + OPTION( ASSIMP_INSTALL_PDB "Install MSVC debug files." ON ) From eabfc05bbbfba39a6f78f023ac0fb4d2c9fdbc00 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 27 Aug 2021 10:41:25 +0200 Subject: [PATCH 137/232] Handle empty keys --- code/AssetLib/FBX/FBXParser.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/AssetLib/FBX/FBXParser.cpp b/code/AssetLib/FBX/FBXParser.cpp index 61e53c2d0..582940363 100644 --- a/code/AssetLib/FBX/FBXParser.cpp +++ b/code/AssetLib/FBX/FBXParser.cpp @@ -192,6 +192,10 @@ Scope::Scope(Parser& parser,bool topLevel) } const std::string& str = n->StringContents(); + if (str.empty()) { + ParseError("unexpected content: empty string."); + } + elements.insert(ElementMap::value_type(str,new_Element(*n,parser))); // Element() should stop at the next Key token (or right after a Close token) From a45878c41ad735a1e5f1755fcf0be14788f42021 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 27 Aug 2021 14:04:00 +0200 Subject: [PATCH 138/232] Fix possible overrun - closes https://github.com/assimp/assimp/issues/3425 --- code/Common/RemoveComments.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/code/Common/RemoveComments.cpp b/code/Common/RemoveComments.cpp index d1c2ac391..e1ba99761 100644 --- a/code/Common/RemoveComments.cpp +++ b/code/Common/RemoveComments.cpp @@ -59,13 +59,16 @@ void CommentRemover::RemoveLineComments(const char* szComment, ai_assert(nullptr != szBuffer); ai_assert(*szComment); - const size_t len = strlen(szComment); + size_t len = strlen(szComment); + const size_t lenBuffer = strlen(szBuffer); + if (len > lenBuffer) { + len = lenBuffer; + } while (*szBuffer) { // skip over quotes if (*szBuffer == '\"' || *szBuffer == '\'') while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\''); - if (!strncmp(szBuffer,szComment,len)) { while (!IsLineEnd(*szBuffer)) *szBuffer++ = chReplacement; From 999192489c379814fc108761e6acec814d0b2c9b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 27 Aug 2021 14:52:48 +0200 Subject: [PATCH 139/232] Delete FindIrrXML.cmake - Deprecated --- cmake-modules/FindIrrXML.cmake | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 cmake-modules/FindIrrXML.cmake diff --git a/cmake-modules/FindIrrXML.cmake b/cmake-modules/FindIrrXML.cmake deleted file mode 100644 index 5434e0b86..000000000 --- a/cmake-modules/FindIrrXML.cmake +++ /dev/null @@ -1,17 +0,0 @@ -# Find IrrXMl from irrlicht project -# -# Find LibIrrXML headers and library -# -# IRRXML_FOUND - IrrXML found -# IRRXML_INCLUDE_DIR - Headers location -# IRRXML_LIBRARY - IrrXML main library - -find_path(IRRXML_INCLUDE_DIR irrXML.h - PATH_SUFFIXES include/irrlicht include/irrxml) -find_library(IRRXML_LIBRARY IrrXML) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(IrrXML REQUIRED_VARS IRRXML_INCLUDE_DIR IRRXML_LIBRARY) - - -mark_as_advanced(IRRXML_INCLUDE_DIR IRRXML_LIBRARY) From b39f38b73c5fb55bacce66e9505f166571fa9ac2 Mon Sep 17 00:00:00 2001 From: kovacsv Date: Fri, 27 Aug 2021 18:17:27 +0200 Subject: [PATCH 140/232] Add export property for assimp json exporter to write compressed json (without whitespaces). --- code/AssetLib/Assjson/json_exporter.cpp | 42 ++++++++++++------- .../ImportExport/utAssjsonImportExport.cpp | 13 +++++- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/code/AssetLib/Assjson/json_exporter.cpp b/code/AssetLib/Assjson/json_exporter.cpp index b9099d392..7b2c8ec81 100644 --- a/code/AssetLib/Assjson/json_exporter.cpp +++ b/code/AssetLib/Assjson/json_exporter.cpp @@ -41,12 +41,17 @@ public: enum { Flag_DoNotIndent = 0x1, Flag_WriteSpecialFloats = 0x2, + Flag_SkipWhitespaces = 0x4 }; - + JSONWriter(Assimp::IOStream &out, unsigned int flags = 0u) : - out(out), first(), flags(flags) { + out(out), indent (""), newline("\n"), space(" "), buff (), first(false), flags(flags) { // make sure that all formatting happens using the standard, C locale and not the user's current locale buff.imbue(std::locale("C")); + if (flags & Flag_SkipWhitespaces) { + newline = ""; + space = ""; + } } ~JSONWriter() { @@ -70,7 +75,7 @@ public: void Key(const std::string &name) { AddIndentation(); Delimit(); - buff << '\"' + name + "\": "; + buff << '\"' + name + "\":" << space; } template @@ -78,12 +83,12 @@ public: AddIndentation(); Delimit(); - LiteralToString(buff, name) << '\n'; + LiteralToString(buff, name) << newline; } template void SimpleValue(const Literal &s) { - LiteralToString(buff, s) << '\n'; + LiteralToString(buff, s) << newline; } void SimpleValue(const void *buffer, size_t len) { @@ -102,7 +107,7 @@ public: } } - buff << '\"' << cur_out << "\"\n"; + buff << '\"' << cur_out << "\"" << newline; delete[] cur_out; } @@ -115,7 +120,7 @@ public: } } first = true; - buff << "{\n"; + buff << "{" << newline; PushIndent(); } @@ -123,7 +128,7 @@ public: PopIndent(); AddIndentation(); first = false; - buff << "}\n"; + buff << "}" << newline; } void StartArray(bool is_element = false) { @@ -135,19 +140,19 @@ public: } } first = true; - buff << "[\n"; + buff << "[" << newline; PushIndent(); } void EndArray() { PopIndent(); AddIndentation(); - buff << "]\n"; + buff << "]" << newline; first = false; } void AddIndentation() { - if (!(flags & Flag_DoNotIndent)) { + if (!(flags & Flag_DoNotIndent) && !(flags & Flag_SkipWhitespaces)) { buff << indent; } } @@ -156,7 +161,7 @@ public: if (!first) { buff << ','; } else { - buff << ' '; + buff << space; first = false; } } @@ -227,7 +232,9 @@ private: private: Assimp::IOStream &out; - std::string indent, newline; + std::string indent; + std::string newline; + std::string space; std::stringstream buff; bool first; @@ -765,7 +772,7 @@ void Write(JSONWriter &out, const aiScene &ai) { out.EndObj(); } -void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *scene, const Assimp::ExportProperties *) { +void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *scene, const Assimp::ExportProperties *pProperties) { std::unique_ptr str(io->Open(file, "wt")); if (!str) { throw DeadlyExportError("could not open output file"); @@ -782,7 +789,12 @@ void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *sc splitter.Execute(scenecopy_tmp); // XXX Flag_WriteSpecialFloats is turned on by default, right now we don't have a configuration interface for exporters - JSONWriter s(*str, JSONWriter::Flag_WriteSpecialFloats); + + unsigned int flags = JSONWriter::Flag_WriteSpecialFloats; + if (pProperties->GetPropertyBool("JSON_SKIP_WHITESPACES", false)) { + flags |= JSONWriter::Flag_SkipWhitespaces; + } + JSONWriter s(*str, flags); Write(s, *scenecopy_tmp); } catch (...) { diff --git a/test/unit/ImportExport/utAssjsonImportExport.cpp b/test/unit/ImportExport/utAssjsonImportExport.cpp index 7987804a9..13724f755 100644 --- a/test/unit/ImportExport/utAssjsonImportExport.cpp +++ b/test/unit/ImportExport/utAssjsonImportExport.cpp @@ -58,7 +58,18 @@ public: Exporter exporter; aiReturn res = exporter.Export(scene, "assjson", "./spider_test.json"); - return aiReturn_SUCCESS == res; + if (aiReturn_SUCCESS != res) { + return false; + } + + Assimp::ExportProperties exportProperties; + exportProperties.SetPropertyBool("JSON_SKIP_WHITESPACES", true); + aiReturn resNoWhitespace = exporter.Export(scene, "assjson", "./spider_test_nowhitespace.json", 0u, &exportProperties); + if (aiReturn_SUCCESS != resNoWhitespace) { + return false; + } + + return true; } }; From 3e090b21f5fa6969106c29196231718dcd26b15b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 28 Aug 2021 13:33:25 +0200 Subject: [PATCH 141/232] Fix setup of embedded texture loading --- code/AssetLib/3MF/D3MFOpcPackage.cpp | 16 +++++++++++++--- code/AssetLib/3MF/XmlSerializer.cpp | 3 ++- tools/assimp_view/Material.cpp | 7 ++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index d3b667583..5a8c9a2bf 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -58,6 +58,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include "contrib/stb/stb_image.h" + namespace Assimp { namespace D3MF { @@ -119,8 +121,11 @@ public: static bool IsEmbeddedTexture( const std::string &filename ) { const std::string extension = BaseImporter::GetExtension(filename); - if (extension == "jpg" || extension == "png") { + std::string::size_type pos = filename.find("thumbnail"); + if (pos == std::string::npos) { + return false; + } return true; } @@ -232,12 +237,17 @@ void D3MFOpcPackage::LoadEmbeddedTextures(IOStream *fileStream, const std::strin return; } - char *data = new char[size]; + unsigned char *data = new unsigned char[size]; fileStream->Read(data, 1, size); aiTexture *texture = new aiTexture; - texture->mFilename.Set(filename.c_str()); + std::string embName = "*" + filename; + texture->mFilename.Set(embName.c_str()); texture->mWidth = static_cast(size); texture->mHeight = 0; + texture->achFormatHint[0] = 'p'; + texture->achFormatHint[1] = 'n'; + texture->achFormatHint[2] = 'g'; + texture->achFormatHint[3] = '\0'; texture->pcData = (aiTexel*) data; mEmbeddedTextures.emplace_back(texture); } diff --git a/code/AssetLib/3MF/XmlSerializer.cpp b/code/AssetLib/3MF/XmlSerializer.cpp index d93c7b121..a86f4901b 100644 --- a/code/AssetLib/3MF/XmlSerializer.cpp +++ b/code/AssetLib/3MF/XmlSerializer.cpp @@ -493,7 +493,8 @@ void XmlSerializer::StoreEmbeddedTexture(EmbeddedTexture *tex) { aiString s; s.Set(ai_to_string(tex->mId).c_str()); mat->AddProperty(&s, AI_MATKEY_NAME); - s.Set(tex->mPath); + const std::string name = "*" + tex->mPath; + s.Set(name); mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0)); aiColor3D col; diff --git a/tools/assimp_view/Material.cpp b/tools/assimp_view/Material.cpp index bcc93011e..100074445 100644 --- a/tools/assimp_view/Material.cpp +++ b/tools/assimp_view/Material.cpp @@ -325,9 +325,10 @@ int CMaterialManager::FindValidPath(aiString* p_szString) // first check whether we can directly load the file FILE* pFile = fopen(p_szString->data,"rb"); - if (pFile)fclose(pFile); - else - { + if (pFile) { + fclose(pFile); + } + else { // check whether we can use the directory of the asset as relative base char szTemp[MAX_PATH*2], tmp2[MAX_PATH*2]; strcpy(szTemp, g_szFileName); From a7bc858698a910cf955e13b501208e11ee2061e8 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 28 Aug 2021 13:36:57 +0200 Subject: [PATCH 142/232] Fix review finding. --- code/AssetLib/3DS/3DSLoader.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/3DS/3DSLoader.h b/code/AssetLib/3DS/3DSLoader.h index dba20eede..04dcac237 100644 --- a/code/AssetLib/3DS/3DSLoader.h +++ b/code/AssetLib/3DS/3DSLoader.h @@ -209,12 +209,12 @@ protected: void ReplaceDefaultMaterial(); bool ContainsTextures(unsigned int i) const { - return mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || - mScene->mMaterials[i].sTexBump.mMapName.length() != 0 || - mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || - mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || - mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || - mScene->mMaterials[i].sTexShininess.mMapName.length() != 0; + return !mScene->mMaterials[i].sTexDiffuse.mMapName.empty() || + !mScene->mMaterials[i].sTexBump.mMapName.empty() || + !mScene->mMaterials[i].sTexOpacity.mMapName.empty() || + !mScene->mMaterials[i].sTexEmissive.mMapName.empty() || + !mScene->mMaterials[i].sTexSpecular.mMapName.empty() || + !mScene->mMaterials[i].sTexShininess.mMapName.empty() ; } // ------------------------------------------------------------------- From 69051bbc2c4cc456e916e43ba1b25075aa42e9b5 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 28 Aug 2021 13:46:41 +0200 Subject: [PATCH 143/232] Add missing docu --- code/AssetLib/3MF/3MFTypes.h | 40 ++++++++++++++++++++++++++++++++ code/AssetLib/3MF/D3MFImporter.h | 34 ++++++++++++++++++++++----- 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/3MF/3MFTypes.h b/code/AssetLib/3MF/3MFTypes.h index e404bf012..c4e9e4243 100644 --- a/code/AssetLib/3MF/3MFTypes.h +++ b/code/AssetLib/3MF/3MFTypes.h @@ -1,3 +1,43 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2021, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ #pragma once #include diff --git a/code/AssetLib/3MF/D3MFImporter.h b/code/AssetLib/3MF/D3MFImporter.h index 811c463b6..2b37010d9 100644 --- a/code/AssetLib/3MF/D3MFImporter.h +++ b/code/AssetLib/3MF/D3MFImporter.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -47,17 +46,40 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { +// --------------------------------------------------------------------------- /// @brief The 3MF-importer class. +/// +/// Implements the basic topology import and embedded textures. +// --------------------------------------------------------------------------- class D3MFImporter : public BaseImporter { public: + /// @brief The default class constructor. D3MFImporter(); - ~D3MFImporter(); - bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const; - void SetupProperties(const Importer *pImp); - const aiImporterDesc *GetInfo() const; + + /// @brief The class destructor. + ~D3MFImporter() override; + + /// @brief Performs the data format detection. + /// @param pFile The filename to check. + /// @param pIOHandler The used IO-System. + /// @param checkSig true for signature checking. + /// @return true for can be loaded, false for not. + bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override; + + /// @brief Not used + /// @param pImp Not used + void SetupProperties(const Importer *pImp) override; + + /// @brief The importer description getter. + /// @return The info + const aiImporterDesc *GetInfo() const override; protected: - void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); + /// @brief Internal read function, performs the file parsing. + /// @param pFile The filename + /// @param pScene The scene to load in. + /// @param pIOHandler The io-system + void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override; }; } // Namespace Assimp From c9b76f5255d86fd9936310a99e0729beaaa9bc28 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 28 Aug 2021 14:20:12 +0200 Subject: [PATCH 144/232] Decrease xml-serializer complexity --- code/AssetLib/3MF/XmlSerializer.cpp | 295 ++++++++++++++-------------- code/AssetLib/3MF/XmlSerializer.h | 7 - 2 files changed, 150 insertions(+), 152 deletions(-) diff --git a/code/AssetLib/3MF/XmlSerializer.cpp b/code/AssetLib/3MF/XmlSerializer.cpp index a86f4901b..daef9a8ac 100644 --- a/code/AssetLib/3MF/XmlSerializer.cpp +++ b/code/AssetLib/3MF/XmlSerializer.cpp @@ -49,6 +49,150 @@ namespace D3MF { static const int IdNotSet = -1; +namespace { + +static const size_t ColRGBA_Len = 9; +static const size_t ColRGB_Len = 7; + +// format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) +bool validateColorString(const char *color) { + const size_t len = strlen(color); + if (ColRGBA_Len != len && ColRGB_Len != len) { + return false; + } + + return true; +} + +aiFace ReadTriangle(XmlNode &node) { + aiFace face; + + face.mNumIndices = 3; + face.mIndices = new unsigned int[face.mNumIndices]; + face.mIndices[0] = static_cast(std::atoi(node.attribute(XmlTag::v1).as_string())); + face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); + face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); + + return face; +} + +aiVector3D ReadVertex(XmlNode &node) { + aiVector3D vertex; + vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr); + vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr); + vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr); + + return vertex; +} + +bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) { + pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str()); + if (!objectAttribute.empty()) { + value = objectAttribute.as_string(); + return true; + } + + return false; +} + +bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) { + std::string strValue; + const bool ret = getNodeAttribute(node, attribute, strValue); + if (ret) { + value = std::atoi(strValue.c_str()); + return true; + } + + return false; +} + +aiMatrix4x4 parseTransformMatrix(std::string matrixStr) { + // split the string + std::vector numbers; + std::string currentNumber; + for (char c : matrixStr) { + if (c == ' ') { + if (!currentNumber.empty()) { + float f = std::stof(currentNumber); + numbers.push_back(f); + currentNumber.clear(); + } + } else { + currentNumber.push_back(c); + } + } + if (!currentNumber.empty()) { + const float f = std::stof(currentNumber); + numbers.push_back(f); + } + + aiMatrix4x4 transformMatrix; + transformMatrix.a1 = numbers[0]; + transformMatrix.b1 = numbers[1]; + transformMatrix.c1 = numbers[2]; + transformMatrix.d1 = 0; + + transformMatrix.a2 = numbers[3]; + transformMatrix.b2 = numbers[4]; + transformMatrix.c2 = numbers[5]; + transformMatrix.d2 = 0; + + transformMatrix.a3 = numbers[6]; + transformMatrix.b3 = numbers[7]; + transformMatrix.c3 = numbers[8]; + transformMatrix.d3 = 0; + + transformMatrix.a4 = numbers[9]; + transformMatrix.b4 = numbers[10]; + transformMatrix.c4 = numbers[11]; + transformMatrix.d4 = 1; + + return transformMatrix; +} + +bool parseColor(const char *color, aiColor4D &diffuse) { + if (nullptr == color) { + return false; + } + + if (!validateColorString(color)) { + return false; + } + + //const char *buf(color); + if ('#' != color[0]) { + return false; + } + + char r[3] = { color[1], color[2], '\0' }; + diffuse.r = static_cast(strtol(r, nullptr, 16)) / ai_real(255.0); + + char g[3] = { color[3], color[4], '\0' }; + diffuse.g = static_cast(strtol(g, nullptr, 16)) / ai_real(255.0); + + char b[3] = { color[5], color[6], '\0' }; + diffuse.b = static_cast(strtol(b, nullptr, 16)) / ai_real(255.0); + const size_t len = strlen(color); + if (ColRGB_Len == len) { + return true; + } + + char a[3] = { color[7], color[8], '\0' }; + diffuse.a = static_cast(strtol(a, nullptr, 16)) / ai_real(255.0); + + return true; +} + +void assignDiffuseColor(XmlNode &node, aiMaterial *mat) { + const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string(); + aiColor4D diffuse; + if (parseColor(color, diffuse)) { + mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + } +} + +} // namespace + XmlSerializer::XmlSerializer(XmlParser *xmlParser, D3MFOpcPackage *archive) : mResourcesDictionnary(), mMeshCount(0), @@ -164,71 +308,6 @@ void XmlSerializer::addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nod } } -bool XmlSerializer::getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) { - pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str()); - if (!objectAttribute.empty()) { - value = objectAttribute.as_string(); - return true; - } - - return false; -} - -bool XmlSerializer::getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) { - std::string strValue; - bool ret = getNodeAttribute(node, attribute, strValue); - if (ret) { - value = std::atoi(strValue.c_str()); - return true; - } - - return false; -} - -aiMatrix4x4 XmlSerializer::parseTransformMatrix(std::string matrixStr) { - // split the string - std::vector numbers; - std::string currentNumber; - for (char c : matrixStr) { - if (c == ' ') { - if (!currentNumber.empty()) { - float f = std::stof(currentNumber); - numbers.push_back(f); - currentNumber.clear(); - } - } else { - currentNumber.push_back(c); - } - } - if (!currentNumber.empty()) { - const float f = std::stof(currentNumber); - numbers.push_back(f); - } - - aiMatrix4x4 transformMatrix; - transformMatrix.a1 = numbers[0]; - transformMatrix.b1 = numbers[1]; - transformMatrix.c1 = numbers[2]; - transformMatrix.d1 = 0; - - transformMatrix.a2 = numbers[3]; - transformMatrix.b2 = numbers[4]; - transformMatrix.c2 = numbers[5]; - transformMatrix.d2 = 0; - - transformMatrix.a3 = numbers[6]; - transformMatrix.b3 = numbers[7]; - transformMatrix.c3 = numbers[8]; - transformMatrix.d3 = 0; - - transformMatrix.a4 = numbers[9]; - transformMatrix.b4 = numbers[10]; - transformMatrix.c4 = numbers[11]; - transformMatrix.d4 = 1; - - return transformMatrix; -} - void XmlSerializer::ReadObject(XmlNode &node) { int id = IdNotSet, pid = IdNotSet, pindex = IdNotSet; bool hasId = getNodeAttribute(node, XmlTag::id, id); @@ -327,19 +406,8 @@ void XmlSerializer::ImportVertices(XmlNode &node, aiMesh *mesh) { std::copy(vertices.begin(), vertices.end(), mesh->mVertices); } -aiVector3D XmlSerializer::ReadVertex(XmlNode &node) { - aiVector3D vertex; - vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr); - vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr); - vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr); - - return vertex; -} - void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) { std::vector faces; - - const size_t numTriangles = std::distance(node.children(XmlTag::triangle).begin(), node.children(XmlTag::triangle).end()); for (XmlNode ¤tNode : node.children()) { const std::string currentName = currentNode.name(); if (currentName == XmlTag::triangle) { @@ -383,18 +451,6 @@ void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) { std::copy(faces.begin(), faces.end(), mesh->mFaces); } -aiFace XmlSerializer::ReadTriangle(XmlNode &node) { - aiFace face; - - face.mNumIndices = 3; - face.mIndices = new unsigned int[face.mNumIndices]; - face.mIndices[0] = static_cast(std::atoi(node.attribute(XmlTag::v1).as_string())); - face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); - face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); - - return face; -} - void XmlSerializer::ReadBaseMaterials(XmlNode &node) { int id = IdNotSet; if (getNodeAttribute(node, D3MF::XmlTag::id, id)) { @@ -412,52 +468,6 @@ void XmlSerializer::ReadBaseMaterials(XmlNode &node) { } } -static const size_t ColRGBA_Len = 9; -static const size_t ColRGB_Len = 7; - -// format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) -static bool validateColorString(const char *color) { - const size_t len = strlen(color); - if (ColRGBA_Len != len && ColRGB_Len != len) { - return false; - } - - return true; -} - -bool XmlSerializer::parseColor(const char *color, aiColor4D &diffuse) { - if (nullptr == color) { - return false; - } - - if (!validateColorString(color)) { - return false; - } - - //const char *buf(color); - if ('#' != color[0]) { - return false; - } - - char r[3] = { color[1], color[2], '\0' }; - diffuse.r = static_cast(strtol(r, nullptr, 16)) / ai_real(255.0); - - char g[3] = { color[3], color[4], '\0' }; - diffuse.g = static_cast(strtol(g, nullptr, 16)) / ai_real(255.0); - - char b[3] = { color[5], color[6], '\0' }; - diffuse.b = static_cast(strtol(b, nullptr, 16)) / ai_real(255.0); - const size_t len = strlen(color); - if (ColRGB_Len == len) { - return true; - } - - char a[3] = { color[7], color[8], '\0' }; - diffuse.a = static_cast(strtol(a, nullptr, 16)) / ai_real(255.0); - - return true; -} - void XmlSerializer::ReadEmbeddecTexture(XmlNode &node) { if (node.empty()) { return; @@ -509,11 +519,13 @@ void XmlSerializer::ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGrou if (node.empty() || nullptr == tex2DGroup) { return; } + int id = IdNotSet; if (XmlParser::getIntAttribute(node, "texid", id)) { tex2DGroup->mTexId = id; } - double value; + + double value = 0.0; for (XmlNode currentNode : node.children()) { const std::string currentName = currentNode.name(); aiVector2D texCoord; @@ -542,14 +554,6 @@ void XmlSerializer::ReadTextureGroup(XmlNode &node) { mResourcesDictionnary.insert(std::make_pair(id, group)); } -void XmlSerializer::assignDiffuseColor(XmlNode &node, aiMaterial *mat) { - const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string(); - aiColor4D diffuse; - if (parseColor(color, diffuse)) { - mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - } -} - aiMaterial *XmlSerializer::readMaterialDef(XmlNode &node, unsigned int basematerialsId) { aiMaterial *material = new aiMaterial(); material->mNumProperties = 0; @@ -576,16 +580,17 @@ aiMaterial *XmlSerializer::readMaterialDef(XmlNode &node, unsigned int basemater return material; } -void XmlSerializer::StoreMaterialsInScene( aiScene *scene ) { +void XmlSerializer::StoreMaterialsInScene(aiScene *scene) { if (nullptr == scene || mMaterials.empty()) { return; } scene->mNumMaterials = static_cast(mMaterials.size()); - scene->mMaterials = new aiMaterial*[scene->mNumMaterials]; + scene->mMaterials = new aiMaterial *[scene->mNumMaterials]; for (size_t i = 0; i < mMaterials.size(); ++i) { scene->mMaterials[i] = mMaterials[i]; } } + } // namespace D3MF } // namespace Assimp diff --git a/code/AssetLib/3MF/XmlSerializer.h b/code/AssetLib/3MF/XmlSerializer.h index 40300d70d..e6714b250 100644 --- a/code/AssetLib/3MF/XmlSerializer.h +++ b/code/AssetLib/3MF/XmlSerializer.h @@ -66,23 +66,16 @@ public: private: void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform); - bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value); - bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value); - aiMatrix4x4 parseTransformMatrix(std::string matrixStr); void ReadObject(XmlNode &node); aiMesh *ReadMesh(XmlNode &node); void ReadMetadata(XmlNode &node); void ImportVertices(XmlNode &node, aiMesh *mesh); - aiVector3D ReadVertex(XmlNode &node); void ImportTriangles(XmlNode &node, aiMesh *mesh); - aiFace ReadTriangle(XmlNode &node); void ReadBaseMaterials(XmlNode &node); - bool parseColor(const char *color, aiColor4D &diffuse); void ReadEmbeddecTexture(XmlNode &node); void StoreEmbeddedTexture(EmbeddedTexture *tex); void ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup); void ReadTextureGroup(XmlNode &node); - void assignDiffuseColor(XmlNode &node, aiMaterial *mat); aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId); void StoreMaterialsInScene(aiScene *scene); From 5ca2cbb7ae0bf43be94683d0f244b44e2a7d3a51 Mon Sep 17 00:00:00 2001 From: kirillsurkov Date: Sat, 28 Aug 2021 20:04:37 +0300 Subject: [PATCH 145/232] Fix MinGW build --- code/AssetLib/FBX/FBXExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index 84a77e18d..f4a3017d8 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -1803,7 +1803,7 @@ void FBXExporter::WriteObjects () blendchannel_uid, blendshape_name + FBX::SEPARATOR + "SubDeformer", "BlendShapeChannel" ); sdnode.AddChild("Version", int32_t(100)); - sdnode.AddChild("DeformPercent", float_t(0.0)); + sdnode.AddChild("DeformPercent", float(0.0)); FBX::Node p("Properties70"); p.AddP70numberA("DeformPercent", 0.0); sdnode.AddChild(p); From ceafa95610cbab9c9ec29b6ca00c16c0d25b229d Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 29 Aug 2021 10:36:02 +0200 Subject: [PATCH 146/232] Remove unused header --- code/AssetLib/3MF/D3MFOpcPackage.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index 5a8c9a2bf..25ed4a368 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -58,8 +58,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include "contrib/stb/stb_image.h" - namespace Assimp { namespace D3MF { From c9d35b6edcc87ca2f85742b23c2a3dae08ef23b1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 29 Aug 2021 18:35:44 +0200 Subject: [PATCH 147/232] Remove C++14 feature. --- code/AssetLib/3MF/D3MFOpcPackage.cpp | 3 ++- code/AssetLib/3MF/D3MFOpcPackage.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index 25ed4a368..c29cec368 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -133,7 +133,7 @@ static bool IsEmbeddedTexture( const std::string &filename ) { D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : mRootStream(nullptr), mZipArchive() { - mZipArchive = std::make_unique(pIOHandler, rFile); + mZipArchive = new ZipArchiveIOSystem(pIOHandler, rFile); if (!mZipArchive->isOpen()) { throw DeadlyImportError("Failed to open file ", rFile, "."); } @@ -185,6 +185,7 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : D3MFOpcPackage::~D3MFOpcPackage() { mZipArchive->Close(mRootStream); + delete mZipArchive; mZipArchive = nullptr; } diff --git a/code/AssetLib/3MF/D3MFOpcPackage.h b/code/AssetLib/3MF/D3MFOpcPackage.h index 93928b9a7..fda74a879 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.h +++ b/code/AssetLib/3MF/D3MFOpcPackage.h @@ -74,7 +74,7 @@ protected: private: IOStream* mRootStream; - std::unique_ptr mZipArchive; + ZipArchiveIOSystem *mZipArchive; std::vector mEmbeddedTextures; }; From e2c2a60c45aeb3cd9cac2db5861e8dc44a523dbd Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 29 Aug 2021 18:41:07 +0200 Subject: [PATCH 148/232] Remove not used attribute --- code/AssetLib/3MF/D3MFImporter.cpp | 2 +- code/AssetLib/3MF/XmlSerializer.cpp | 6 ++---- code/AssetLib/3MF/XmlSerializer.h | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index fe3c07744..58dde9738 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -123,7 +123,7 @@ void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene, XmlParser xmlParser; if (xmlParser.parse(opcPackage.RootStream())) { - XmlSerializer xmlSerializer(&xmlParser, &opcPackage); + XmlSerializer xmlSerializer(&xmlParser); xmlSerializer.ImportXml(pScene); const std::vector &tex = opcPackage.GetEmbeddedTextures(); diff --git a/code/AssetLib/3MF/XmlSerializer.cpp b/code/AssetLib/3MF/XmlSerializer.cpp index daef9a8ac..7a33d08ed 100644 --- a/code/AssetLib/3MF/XmlSerializer.cpp +++ b/code/AssetLib/3MF/XmlSerializer.cpp @@ -193,13 +193,11 @@ void assignDiffuseColor(XmlNode &node, aiMaterial *mat) { } // namespace -XmlSerializer::XmlSerializer(XmlParser *xmlParser, D3MFOpcPackage *archive) : +XmlSerializer::XmlSerializer(XmlParser *xmlParser) : mResourcesDictionnary(), mMeshCount(0), - mXmlParser(xmlParser), - mD3MFOpcPackage(archive) { + mXmlParser(xmlParser) { ai_assert(nullptr != xmlParser); - ai_assert(nullptr != archive); } XmlSerializer::~XmlSerializer() { diff --git a/code/AssetLib/3MF/XmlSerializer.h b/code/AssetLib/3MF/XmlSerializer.h index e6714b250..14da82e99 100644 --- a/code/AssetLib/3MF/XmlSerializer.h +++ b/code/AssetLib/3MF/XmlSerializer.h @@ -60,7 +60,7 @@ class EmbeddedTexture; class XmlSerializer { public: - XmlSerializer(XmlParser *xmlParser, D3MFOpcPackage *archive); + XmlSerializer(XmlParser *xmlParser); ~XmlSerializer(); void ImportXml(aiScene *scene); @@ -90,7 +90,6 @@ private: std::map mResourcesDictionnary; unsigned int mMeshCount; XmlParser *mXmlParser; - D3MFOpcPackage *mD3MFOpcPackage; }; } // namespace D3MF From 38c611a02c3ed8af773710e2d2345b8e42893647 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 30 Aug 2021 08:33:35 +0200 Subject: [PATCH 149/232] Update ColladaLoader.cpp --- code/AssetLib/Collada/ColladaLoader.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index a7b5ef5ef..f7b5f2278 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -1543,9 +1543,9 @@ void ColladaLoader::AddTexture(aiMaterial &mat, map = sampler.mUVId; } else { map = -1; - for (char it : sampler.mUVChannel) { - if (IsNumeric(it)) { - map = strtoul10(&it); + for (std::string::const_iterator it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) { + if (IsNumeric(*it)) { + map = strtoul10(&(*it)); break; } } From 51f294c5876b2e41f72cd49561394d003c7c060a Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Mon, 30 Aug 2021 14:59:17 +0100 Subject: [PATCH 150/232] Fixes issues our internal compliance and code quality tool found: * Adds nullptr checks and asserts to protect certain code paths * Fixes wrong integer type in a printf call * Adds const to const values * Prevents integer overflow with explicit casts --- code/AssetLib/FBX/FBXConverter.cpp | 4 +++- code/Common/DefaultIOSystem.cpp | 2 +- code/Common/ScenePreprocessor.cpp | 3 +++ code/Material/MaterialSystem.cpp | 12 +++++++++--- code/PostProcessing/OptimizeGraph.cpp | 2 +- code/PostProcessing/PretransformVertices.cpp | 2 +- code/PostProcessing/SortByPTypeProcess.cpp | 2 +- 7 files changed, 19 insertions(+), 8 deletions(-) diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index a92745fb6..fa7ee3986 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -917,8 +917,10 @@ void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root } else if (line) { const std::vector &indices = ConvertLine(*line, root_node); std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); - } else { + } else if (geo) { FBXImporter::LogWarn("ignoring unrecognized geometry: ", geo->Name()); + } else { + FBXImporter::LogWarn("skipping null geometry"); } } diff --git a/code/Common/DefaultIOSystem.cpp b/code/Common/DefaultIOSystem.cpp index 1ed615b84..de93909de 100644 --- a/code/Common/DefaultIOSystem.cpp +++ b/code/Common/DefaultIOSystem.cpp @@ -173,7 +173,7 @@ inline static std::string MakeAbsolutePath(const char *in) { free(ret); } #endif - if (!ret) { + else { // preserve the input path, maybe someone else is able to fix // the path before it is accessed (e.g. our file system filter) ASSIMP_LOG_WARN("Invalid path: ", std::string(in)); diff --git a/code/Common/ScenePreprocessor.cpp b/code/Common/ScenePreprocessor.cpp index 132b32df7..2ea17c643 100644 --- a/code/Common/ScenePreprocessor.cpp +++ b/code/Common/ScenePreprocessor.cpp @@ -89,6 +89,9 @@ void ScenePreprocessor::ProcessScene() { ASSIMP_LOG_DEBUG("ScenePreprocessor: Adding default material \'" AI_DEFAULT_MATERIAL_NAME "\'"); for (unsigned int i = 0; i < scene->mNumMeshes; ++i) { + if (nullptr == scene->mMeshes[i]) { + continue; + } scene->mMeshes[i]->mMaterialIndex = scene->mNumMaterials; } diff --git a/code/Material/MaterialSystem.cpp b/code/Material/MaterialSystem.cpp index c35a1aa93..23d198953 100644 --- a/code/Material/MaterialSystem.cpp +++ b/code/Material/MaterialSystem.cpp @@ -555,17 +555,23 @@ uint32_t Assimp::ComputeMaterialHash(const aiMaterial *mat, bool includeMatName } // ------------------------------------------------------------------------------------------------ -void aiMaterial::CopyPropertyList(aiMaterial *pcDest, +void aiMaterial::CopyPropertyList(aiMaterial *const pcDest, const aiMaterial *pcSrc) { ai_assert(nullptr != pcDest); ai_assert(nullptr != pcSrc); + ai_assert(pcDest->mNumProperties <= pcDest->mNumAllocated); + ai_assert(pcSrc->mNumProperties <= pcSrc->mNumAllocated); - unsigned int iOldNum = pcDest->mNumProperties; + const unsigned int iOldNum = pcDest->mNumProperties; pcDest->mNumAllocated += pcSrc->mNumAllocated; pcDest->mNumProperties += pcSrc->mNumProperties; + const unsigned int numAllocated = pcDest->mNumAllocated; aiMaterialProperty **pcOld = pcDest->mProperties; - pcDest->mProperties = new aiMaterialProperty *[pcDest->mNumAllocated]; + pcDest->mProperties = new aiMaterialProperty *[numAllocated]; + + ai_assert(!iOldNum || pcOld); + ai_assert(iOldNum < numAllocated); if (iOldNum && pcOld) { for (unsigned int i = 0; i < iOldNum; ++i) { diff --git a/code/PostProcessing/OptimizeGraph.cpp b/code/PostProcessing/OptimizeGraph.cpp index e33c2ab18..d7bcf3fec 100644 --- a/code/PostProcessing/OptimizeGraph.cpp +++ b/code/PostProcessing/OptimizeGraph.cpp @@ -170,7 +170,7 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode *nd, std::list &n ++it; } if (join_master && !join.empty()) { - join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%i", count_merged++); + join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%u", count_merged++); unsigned int out_meshes = 0; for (std::list::const_iterator it = join.cbegin(); it != join.cend(); ++it) { diff --git a/code/PostProcessing/PretransformVertices.cpp b/code/PostProcessing/PretransformVertices.cpp index e9a3af0d2..fa95319ff 100644 --- a/code/PostProcessing/PretransformVertices.cpp +++ b/code/PostProcessing/PretransformVertices.cpp @@ -481,7 +481,7 @@ void PretransformVertices::Execute(aiScene *pScene) { pScene->mMeshes[i]->mNumBones = 0; } } else { - apcOutMeshes.reserve(pScene->mNumMaterials << 1u); + apcOutMeshes.reserve(static_cast(pScene->mNumMaterials) << 1u); std::list aiVFormats; std::vector s(pScene->mNumMeshes, 0); diff --git a/code/PostProcessing/SortByPTypeProcess.cpp b/code/PostProcessing/SortByPTypeProcess.cpp index 20ab63249..3787be51e 100644 --- a/code/PostProcessing/SortByPTypeProcess.cpp +++ b/code/PostProcessing/SortByPTypeProcess.cpp @@ -127,7 +127,7 @@ void SortByPTypeProcess::Execute(aiScene *pScene) { unsigned int aiNumMeshesPerPType[4] = { 0, 0, 0, 0 }; std::vector outMeshes; - outMeshes.reserve(pScene->mNumMeshes << 1u); + outMeshes.reserve(static_cast(pScene->mNumMeshes) << 1u); bool bAnyChanges = false; From 96f0787f51e91b759132ef5ecf631dd530e749f0 Mon Sep 17 00:00:00 2001 From: Doug Roeper Date: Mon, 30 Aug 2021 18:15:37 -0400 Subject: [PATCH 151/232] Fix the -Werror=unused-but-set-parameter warning by removing the skipFirst variable. --- code/AssetLib/XGL/XGLLoader.cpp | 7 ++----- code/AssetLib/XGL/XGLLoader.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/XGL/XGLLoader.cpp b/code/AssetLib/XGL/XGLLoader.cpp index 20c2c7079..bbfa31829 100644 --- a/code/AssetLib/XGL/XGLLoader.cpp +++ b/code/AssetLib/XGL/XGLLoader.cpp @@ -250,7 +250,7 @@ void XGLImporter::ReadWorld(XmlNode &node, TempScope &scope) { } } - aiNode *const nd = ReadObject(node, scope, true); + aiNode *const nd = ReadObject(node, scope); if (!nd) { ThrowException("failure reading "); } @@ -296,16 +296,13 @@ aiLight *XGLImporter::ReadDirectionalLight(XmlNode &node) { } // ------------------------------------------------------------------------------------------------ -aiNode *XGLImporter::ReadObject(XmlNode &node, TempScope &scope, bool skipFirst/*, const char *closetag */) { +aiNode *XGLImporter::ReadObject(XmlNode &node, TempScope &scope) { aiNode *nd = new aiNode; std::vector children; std::vector meshes; try { for (XmlNode &child : node.children()) { - - skipFirst = false; - const std::string &s = ai_stdStrToLower(child.name()); if (s == "mesh") { const size_t prev = scope.meshes_linear.size(); diff --git a/code/AssetLib/XGL/XGLLoader.h b/code/AssetLib/XGL/XGLLoader.h index f7da4e0a7..a2b224ac9 100644 --- a/code/AssetLib/XGL/XGLLoader.h +++ b/code/AssetLib/XGL/XGLLoader.h @@ -185,7 +185,7 @@ private: void ReadWorld(XmlNode &node, TempScope &scope); void ReadLighting(XmlNode &node, TempScope &scope); aiLight *ReadDirectionalLight(XmlNode &node); - aiNode *ReadObject(XmlNode &node, TempScope &scope, bool skipFirst = false/*, const char *closetag = "object"*/); + aiNode *ReadObject(XmlNode &node, TempScope &scope); bool ReadMesh(XmlNode &node, TempScope &scope); void ReadMaterial(XmlNode &node, TempScope &scope); aiVector2D ReadVec2(XmlNode &node); From 9b535d1c159d8501a97f4399abf78e256afb29bb Mon Sep 17 00:00:00 2001 From: Madrich Date: Tue, 31 Aug 2021 12:59:31 +0200 Subject: [PATCH 152/232] Fix Double Precision errors/warnings --- CMakeLists.txt | 3 ++- code/AssetLib/3DS/3DSLoader.cpp | 2 +- code/PostProcessing/CalcTangentsProcess.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bfa30e96a..9f893646a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -265,8 +265,9 @@ ELSEIF(MSVC) ENDIF() # disable "elements of array '' will be default initialized" warning on MSVC2013 IF(MSVC12) - ADD_COMPILE_OPTIONS(/wd4351) + ADD_COMPILE_OPTIONS(/wd4351) ENDIF() + ADD_COMPILE_OPTIONS(/wd4244) #supress warning for double to float conversion if Double precission is activated SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /Zi /Od") SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi") SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FULL /PDBALTPATH:%_PDB% /OPT:REF /OPT:ICF") diff --git a/code/AssetLib/3DS/3DSLoader.cpp b/code/AssetLib/3DS/3DSLoader.cpp index b5e6f749b..05b5117ba 100644 --- a/code/AssetLib/3DS/3DSLoader.cpp +++ b/code/AssetLib/3DS/3DSLoader.cpp @@ -449,7 +449,7 @@ void Discreet3DSImporter::ParseChunk(const char *name, unsigned int num) { // Read the lense angle camera->mHorizontalFOV = AI_DEG_TO_RAD(stream->GetF4()); if (camera->mHorizontalFOV < 0.001f) { - camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f); + camera->mHorizontalFOV = float(AI_DEG_TO_RAD(45.f)); } // Now check for further subchunks diff --git a/code/PostProcessing/CalcTangentsProcess.cpp b/code/PostProcessing/CalcTangentsProcess.cpp index 46feff4a1..3e6bb0270 100644 --- a/code/PostProcessing/CalcTangentsProcess.cpp +++ b/code/PostProcessing/CalcTangentsProcess.cpp @@ -56,7 +56,7 @@ using namespace Assimp; // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer CalcTangentsProcess::CalcTangentsProcess() : - configMaxAngle(AI_DEG_TO_RAD(45.f)), configSourceUV(0) { + configMaxAngle(float(AI_DEG_TO_RAD(45.f))), configSourceUV(0) { // nothing to do here } From 4c86772091dc5a9b0373adf3faabc4a321b92437 Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Thu, 2 Sep 2021 08:27:03 +0100 Subject: [PATCH 153/232] Added another nullptr safety check --- code/AssetLib/glTF2/glTF2Importer.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index 08573bce7..ada7aa046 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1337,6 +1337,17 @@ std::unordered_map GatherSamplers(Animation &an } auto& animsampler = anim.samplers[channel.sampler]; + + if (!animsampler.input) { + ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping."); + continue; + } + + if (!animsampler.output) { + ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler output. Skipping."); + continue; + } + if (animsampler.input->count > animsampler.output->count) { ASSIMP_LOG_WARN("Animation ", anim.name, ": Number of keyframes in sampler input ", animsampler.input->count, " exceeds number of keyframes in sampler output ", animsampler.output->count); continue; From d710d0700fbfaf7c20153e4d89aa1eccc2aa232e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 2 Sep 2021 10:10:42 +0200 Subject: [PATCH 154/232] Make nullptr test more explicit. --- code/AssetLib/glTF2/glTF2Importer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index ada7aa046..2786614f3 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1338,12 +1338,12 @@ std::unordered_map GatherSamplers(Animation &an auto& animsampler = anim.samplers[channel.sampler]; - if (!animsampler.input) { + if (nullptr == animsampler.input) { ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping."); continue; } - if (!animsampler.output) { + if (nullptr == animsampler.output) { ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler output. Skipping."); continue; } From 72ea80b41f7364ffacd5878ce4c357aaef0dd92d Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Thu, 2 Sep 2021 10:00:56 +0100 Subject: [PATCH 155/232] Revert last change (gltf2::Ref type is not a pointer and has a bool() operator) --- code/AssetLib/glTF2/glTF2Importer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index 2786614f3..aa48b7330 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1338,12 +1338,12 @@ std::unordered_map GatherSamplers(Animation &an auto& animsampler = anim.samplers[channel.sampler]; - if (nullptr == animsampler.input) { + if (animsampler.input) { ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping."); continue; } - if (nullptr == animsampler.output) { + if (animsampler.output) { ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler output. Skipping."); continue; } From bf8e36ae28247bbb36a243215945748366be4f4d Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Thu, 2 Sep 2021 10:07:28 +0100 Subject: [PATCH 156/232] Fixed typo --- code/AssetLib/glTF2/glTF2Importer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index aa48b7330..ada7aa046 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1338,12 +1338,12 @@ std::unordered_map GatherSamplers(Animation &an auto& animsampler = anim.samplers[channel.sampler]; - if (animsampler.input) { + if (!animsampler.input) { ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping."); continue; } - if (animsampler.output) { + if (!animsampler.output) { ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler output. Skipping."); continue; } From 3001d88172dfccbe917afd0815381d5524b3e854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Martin?= Date: Tue, 7 Sep 2021 15:04:08 +0200 Subject: [PATCH 157/232] Merge branch 'master' into x3d_pugi_migration --- .gitignore | 2 +- CMakeLists.txt | 20 +- cmake-modules/FindIrrXML.cmake | 17 - {cmake => cmake-modules}/HunterGate.cmake | 0 cmake-modules/assimp-hunter-config.cmake.in | 19 + .../assimp-plain-config.cmake.in | 0 cmake/assimp-hunter-config.cmake.in | 18 - code/AssetLib/3DS/3DSConverter.cpp | 11 +- code/AssetLib/3DS/3DSExporter.cpp | 2 +- code/AssetLib/3DS/3DSHelper.h | 76 +- code/AssetLib/3DS/3DSLoader.h | 9 + code/AssetLib/3MF/3MFTypes.h | 165 + code/AssetLib/3MF/3MFXmlTags.h | 12 +- code/AssetLib/3MF/D3MFImporter.cpp | 522 +- code/AssetLib/3MF/D3MFImporter.h | 34 +- code/AssetLib/3MF/D3MFOpcPackage.cpp | 69 +- code/AssetLib/3MF/D3MFOpcPackage.h | 14 +- code/AssetLib/3MF/XmlSerializer.cpp | 594 ++ code/AssetLib/3MF/XmlSerializer.h | 96 + code/AssetLib/AMF/AMFImporter.cpp | 2 +- code/AssetLib/AMF/AMFImporter_Geometry.cpp | 6 +- code/AssetLib/AMF/AMFImporter_Postprocess.cpp | 6 +- code/AssetLib/ASE/ASEParser.h | 8 +- code/AssetLib/Assbin/AssbinFileWriter.cpp | 2 +- code/AssetLib/Assjson/json_exporter.cpp | 42 +- code/AssetLib/Assjson/mesh_splitter.cpp | 8 +- code/AssetLib/Assjson/mesh_splitter.h | 6 +- code/AssetLib/Assxml/AssxmlExporter.cpp | 2 +- code/AssetLib/B3D/B3DImporter.cpp | 2 +- code/AssetLib/B3D/B3DImporter.h | 2 +- code/AssetLib/Blender/BlenderLoader.cpp | 2 +- code/AssetLib/Blender/BlenderModifier.h | 18 +- code/AssetLib/C4D/C4DImporter.cpp | 6 + code/AssetLib/COB/COBLoader.cpp | 4 +- code/AssetLib/COB/COBLoader.h | 2 +- code/AssetLib/Collada/ColladaLoader.cpp | 25 +- code/AssetLib/Collada/ColladaParser.cpp | 241 +- code/AssetLib/DXF/DXFLoader.cpp | 4 +- code/AssetLib/DXF/DXFLoader.h | 2 +- code/AssetLib/FBX/FBXConverter.cpp | 17 +- code/AssetLib/FBX/FBXConverter.h | 4 +- code/AssetLib/FBX/FBXDocument.cpp | 11 +- code/AssetLib/FBX/FBXDocument.h | 5 + code/AssetLib/FBX/FBXExportNode.cpp | 5 +- code/AssetLib/FBX/FBXExportNode.h | 7 +- code/AssetLib/FBX/FBXExporter.cpp | 145 +- code/AssetLib/FBX/FBXExporter.h | 21 +- code/AssetLib/FBX/FBXMaterial.cpp | 13 +- code/AssetLib/FBX/FBXMeshGeometry.cpp | 2 +- code/AssetLib/FBX/FBXMeshGeometry.h | 8 +- code/AssetLib/FBX/FBXParser.cpp | 28 +- code/AssetLib/FBX/FBXProperties.cpp | 9 +- code/AssetLib/FBX/FBXProperties.h | 6 +- code/AssetLib/FBX/FBXUtil.cpp | 2 +- code/AssetLib/HMP/HMPLoader.cpp | 4 +- code/AssetLib/IFC/IFCBoolean.cpp | 2 +- code/AssetLib/IFC/IFCCurve.cpp | 2 +- code/AssetLib/IFC/IFCGeometry.cpp | 2 +- code/AssetLib/IFC/IFCMaterial.cpp | 6 +- code/AssetLib/IFC/IFCOpenings.cpp | 8 +- code/AssetLib/IFC/IFCReaderGen1_2x3.cpp | 232 +- code/AssetLib/IFC/IFCReaderGen2_2x3.cpp | 162 +- code/AssetLib/IFC/IFCReaderGen_4.cpp | 392 +- code/AssetLib/IFC/IFCReaderGen_4.h | 26 +- code/AssetLib/IFC/IFCUtil.h | 14 +- code/AssetLib/Irr/IRRLoader.h | 2 +- code/AssetLib/LWO/LWOAnimation.h | 2 +- code/AssetLib/LWS/LWSLoader.cpp | 6 +- code/AssetLib/M3D/M3DImporter.cpp | 16 +- code/AssetLib/M3D/M3DImporter.h | 4 +- code/AssetLib/M3D/M3DWrapper.h | 68 +- code/AssetLib/M3D/m3d.h | 1230 +-- code/AssetLib/MD5/MD5Loader.cpp | 2 +- code/AssetLib/MDC/MDCFileData.h | 4 +- code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp | 4 +- code/AssetLib/MMD/MMDPmxParser.cpp | 8 +- code/AssetLib/OFF/OFFLoader.cpp | 10 +- code/AssetLib/Obj/ObjExporter.h | 18 +- code/AssetLib/Obj/ObjFileImporter.cpp | 4 +- code/AssetLib/Obj/ObjFileMtlImporter.cpp | 2 +- code/AssetLib/Ogre/OgreMaterial.cpp | 4 +- code/AssetLib/Ply/PlyParser.cpp | 8 +- code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp | 4 +- code/AssetLib/SMD/SMDLoader.cpp | 2 +- code/AssetLib/STEPParser/STEPFileReader.cpp | 9 +- code/AssetLib/STL/STLExporter.cpp | 8 +- code/AssetLib/STL/STLLoader.cpp | 4 +- code/AssetLib/Step/STEPFile.h | 4 +- code/AssetLib/Step/StepExporter.cpp | 120 +- code/AssetLib/X/XFileExporter.cpp | 6 +- code/AssetLib/X/XFileExporter.h | 6 +- code/AssetLib/X/XFileImporter.cpp | 4 +- code/AssetLib/X3D/X3DExporter.hpp | 6 +- code/AssetLib/X3D/X3DImporter.cpp | 1 - code/AssetLib/XGL/XGLLoader.cpp | 9 +- code/AssetLib/XGL/XGLLoader.h | 2 +- code/AssetLib/glTF/glTFAsset.h | 10 +- code/AssetLib/glTF/glTFAsset.inl | 2 +- code/AssetLib/glTF/glTFCommon.h | 6 +- code/AssetLib/glTF/glTFExporter.cpp | 4 +- code/AssetLib/glTF2/glTF2Asset.h | 103 +- code/AssetLib/glTF2/glTF2Asset.inl | 184 +- code/AssetLib/glTF2/glTF2AssetWriter.inl | 16 +- code/AssetLib/glTF2/glTF2Exporter.cpp | 314 +- code/AssetLib/glTF2/glTF2Exporter.h | 26 +- code/AssetLib/glTF2/glTF2Importer.cpp | 120 +- code/CMakeLists.txt | 28 +- code/Common/Assimp.cpp | 46 + code/Common/DefaultIOStream.cpp | 6 +- code/Common/DefaultIOSystem.cpp | 8 +- code/Common/Exporter.cpp | 4 +- code/Common/FileSystemFilter.h | 4 +- code/Common/Importer.cpp | 80 +- code/Common/Importer.h | 6 +- code/Common/ImporterRegistry.cpp | 4 +- code/Common/RemoveComments.cpp | 7 +- code/Common/SceneCombiner.cpp | 18 +- code/Common/ScenePreprocessor.cpp | 3 + code/Common/Win32DebugLogStream.h | 8 +- code/Common/material.cpp | 12 +- code/Material/MaterialSystem.cpp | 12 +- code/Pbrt/PbrtExporter.cpp | 26 +- code/Pbrt/PbrtExporter.h | 4 +- code/PostProcessing/ArmaturePopulate.h | 2 +- .../PostProcessing/DropFaceNormalsProcess.cpp | 2 +- code/PostProcessing/EmbedTexturesProcess.cpp | 40 +- code/PostProcessing/EmbedTexturesProcess.h | 5 +- code/PostProcessing/FindDegenerates.cpp | 2 +- code/PostProcessing/FindInstancesProcess.cpp | 2 +- code/PostProcessing/MakeVerboseFormat.h | 2 +- code/PostProcessing/OptimizeGraph.cpp | 2 +- code/PostProcessing/PretransformVertices.cpp | 2 +- .../RemoveRedundantMaterials.cpp | 2 +- code/PostProcessing/ScaleProcess.cpp | 38 +- code/PostProcessing/ScaleProcess.h | 4 +- code/PostProcessing/SortByPTypeProcess.cpp | 2 +- .../SplitByBoneCountProcess.cpp | 12 +- code/PostProcessing/SplitLargeMeshes.cpp | 4 +- code/PostProcessing/TextureTransform.cpp | 2 +- code/PostProcessing/TriangulateProcess.cpp | 12 +- contrib/draco/.ruby-version | 1 - contrib/draco/.travis.yml | 31 - contrib/draco/CMakeLists.txt | 8 +- contrib/draco/README.md | 6 +- .../draco/cmake/draco_build_definitions.cmake | 9 +- contrib/draco/cmake/draco_features.cmake | 63 - contrib/draco/cmake/draco_flags.cmake | 9 + contrib/draco/cmake/draco_install.cmake | 2 +- contrib/draco/cmake/draco_sanitizer.cmake | 20 +- contrib/draco/cmake/draco_targets.cmake | 24 +- contrib/draco/src/draco/core/cycle_timer.cc | 14 +- contrib/draco/src/draco/core/cycle_timer.h | 7 +- contrib/draco/src/draco/io/parser_utils.cc | 3 +- contrib/draco/src/draco/io/ply_reader.cc | 4 +- .../draco/src/draco/io/stdio_file_reader.cc | 7 + contrib/openddlparser/code/OpenDDLExport.cpp | 3 +- contrib/openddlparser/code/OpenDDLParser.cpp | 30 +- contrib/openddlparser/code/Value.cpp | 9 +- .../include/openddlparser/OpenDDLParser.h | 13 +- contrib/poly2tri/poly2tri/sweep/sweep.cc | 2 +- {code/Pbrt => contrib/stb}/stb_image.h | 0 contrib/stb_image/stb_image.h | 7462 ----------------- doc/Doxyfile.in | 41 +- doc/dox.h | 18 +- doc/dox_cmd.h | 38 +- fuzz/assimp_fuzzer.cc | 2 +- include/assimp/BaseImporter.h | 2 +- include/assimp/Compiler/poppack1.h | 4 +- include/assimp/Compiler/pushpack1.h | 4 +- include/assimp/Exceptional.h | 2 +- include/assimp/Exporter.hpp | 2 +- include/assimp/IOStreamBuffer.h | 6 +- include/assimp/IOSystem.hpp | 8 +- include/assimp/Logger.hpp | 10 +- include/assimp/MemoryIOWrapper.h | 6 +- include/assimp/SmallVector.h | 8 +- include/assimp/SmoothingGroups.inl | 22 +- include/assimp/XmlParser.h | 10 +- include/assimp/ai_assert.h | 2 +- include/assimp/anim.h | 6 +- include/assimp/cimport.h | 12 +- include/assimp/defs.h | 2 +- include/assimp/fast_atof.h | 4 +- include/assimp/light.h | 2 +- include/assimp/material.h | 130 +- include/assimp/matrix4x4.h | 2 +- include/assimp/matrix4x4.inl | 2 +- include/assimp/mesh.h | 10 +- include/assimp/metadata.h | 2 +- include/assimp/pbrmaterial.h | 46 +- include/assimp/postprocess.h | 14 +- include/assimp/quaternion.h | 2 +- include/assimp/scene.h | 49 +- include/assimp/vector2.inl | 14 +- include/assimp/vector3.h | 33 +- .../windows-innosetup/readme_installer.txt | 2 +- .../readme_installer_vieweronly.txt | 2 +- packaging/windows-mkzip/bin_readme.txt | 2 +- port/AndroidJNI/CMakeLists.txt | 4 +- port/AssimpDelphi/Readme.txt | 2 +- port/PyAssimp/pyassimp/helper.py | 3 +- port/jassimp/jassimp-native/src/jassimp.cpp | 164 +- .../ModelLoaderHelperClasses.h | 26 +- samples/SimpleAssimpViewX/MyDocument.h | 18 +- samples/SimpleOpenGL/Sample_SimpleOpenGL.c | 4 +- .../SimpleTexturedDirectx11/CMakeLists.txt | 6 +- .../SimpleTexturedDirectx11/ModelLoader.cpp | 2 +- .../SimpleTexturedDirectx11/TextureLoader.cpp | 52 +- .../SimpleTexturedDirectx11/main.cpp | 10 +- .../src/model_loading.cpp | 6 +- test/models-nonbsd/3D/mar_rifle.source.txt | 6 +- test/models-nonbsd/3DS/cart_wheel.source.txt | 6 +- test/models-nonbsd/3DS/mar_rifle.source.txt | 6 +- test/models-nonbsd/3DS/mp5_sil.source.txt | 6 +- test/models-nonbsd/ASE/Rifle.source.txt | 6 +- test/models-nonbsd/ASE/Rifle2.source.txt | 6 +- .../BLEND/fleurOptonl.source.txt | 16 +- test/models-nonbsd/DXF/rifle.source.txt | 6 +- .../FBX/2013_ASCII/cart_wheel.source.txt | 6 +- .../kwxport_test_vcolors.fbx.source.txt | 6 +- .../FBX/2013_ASCII/mar_rifle.source.txt | 6 +- .../FBX/2013_ASCII/mp5_sil.source.txt | 6 +- .../FBX/2013_BINARY/cart_wheel.source.txt | 6 +- .../kwxport_test_vcolors.fbx.source.txt | 6 +- .../FBX/2013_BINARY/mar_rifle.source.txt | 6 +- .../FBX/2013_BINARY/mp5_sil.source.txt | 6 +- .../LWO2/LWSReferences/QuickDraw.source.txt | 10 +- test/models-nonbsd/LWO/LWO2/rifle.source.txt | 6 +- test/models-nonbsd/MD2/source.txt | 6 +- test/models-nonbsd/MD5/BoarMan.source.txt | 4 +- .../MDL/IDPO (Quake1)/gijoe-readme.txt | 14 +- test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt | 8 +- .../MDL/IDPO (Quake1)/tekmechbot.txt | 6 +- test/models-nonbsd/NFF/NFFSense8/credits.txt | 2 +- test/models-nonbsd/OBJ/rifle.source.txt | 6 +- test/models/3DS/UVTransformTest/note.txt | 4 +- test/models/ASE/MotionCaptureROM.source.txt | 2 +- test/models/Collada/human.zae | Bin 0 -> 1093924 bytes .../kwxport_test_vcolors.dae.source.txt | 6 +- .../IRR/warn_dwarf_scaling_is_intended.txt | 2 +- test/models/MD2/faerie-source.txt | 2 +- test/models/MD2/sidney-source.txt | 2 +- test/models/Q3D/E-AT-AT.source.txt | 2 +- test/models/Q3D/earth.source.txt | 2 +- test/models/WRL/credits.txt | 2 +- test/models/X/anim_test.txt | 2 +- .../X/kwxport_test_cubewithvcolors.source.txt | 6 +- test/models/X/test.txt | 2 +- .../glTF2/ClearCoat-glTF/ClearCoatLabels.png | Bin 0 -> 10270 bytes .../glTF2/ClearCoat-glTF/ClearCoatTest.bin | Bin 0 -> 50328 bytes .../glTF2/ClearCoat-glTF/ClearCoatTest.gltf | 1669 ++++ .../glTF2/ClearCoat-glTF/PartialCoating.png | Bin 0 -> 5077 bytes .../ClearCoat-glTF/PartialCoating_Alpha.png | Bin 0 -> 5065 bytes .../ClearCoat-glTF/PlasticWrap_normals.jpg | Bin 0 -> 144210 bytes .../glTF2/ClearCoat-glTF/RibsNormal.png | Bin 0 -> 1605 bytes .../glTF2/ClearCoat-glTF/RoughnessStripes.png | Bin 0 -> 5033 bytes test/models/invalid/readme.txt | 8 +- test/regression/README.txt | 6 +- test/unit/AbstractImportExportBase.h | 2 +- test/unit/Common/utStandardShapes.cpp | 2 +- .../MDL/utMDLImporter_HL1_Nodes.cpp | 4 +- .../ImportExport/utAssjsonImportExport.cpp | 13 +- test/unit/RandomNumberGeneration.h | 4 +- test/unit/SceneDiffer.cpp | 2 +- test/unit/SceneDiffer.h | 2 +- test/unit/TestIOSystem.h | 2 +- test/unit/utColladaImportExport.cpp | 22 + test/unit/utDefaultIOStream.cpp | 2 +- test/unit/utFBXImporterExporter.cpp | 4 +- test/unit/utFindDegenerates.cpp | 4 +- test/unit/utIOStreamBuffer.cpp | 6 +- test/unit/utIOSystem.cpp | 10 +- test/unit/utIssues.cpp | 2 +- test/unit/utTypes.cpp | 4 +- test/unit/utVersion.cpp | 2 +- test/unit/utglTF2ImportExport.cpp | 186 +- tools/assimp_cmd/CMakeLists.txt | 2 +- tools/assimp_cmd/Export.cpp | 18 +- tools/assimp_cmd/ImageExtractor.cpp | 20 +- tools/assimp_cmd/Info.cpp | 2 +- tools/assimp_cmd/Main.cpp | 62 +- tools/assimp_cmd/Main.h | 40 +- tools/assimp_cmd/WriteDump.cpp | 18 +- tools/assimp_cmd/resource.h | 2 +- tools/assimp_view/AnimEvaluator.cpp | 5 +- tools/assimp_view/AnimEvaluator.h | 26 +- tools/assimp_view/CMakeLists.txt | 2 +- tools/assimp_view/Display.cpp | 6 +- tools/assimp_view/Material.cpp | 7 +- tools/assimp_view/MaterialManager.h | 73 +- tools/assimp_view/MeshRenderer.cpp | 9 +- tools/assimp_view/MessageProc.cpp | 2 +- tools/assimp_view/Shaders.cpp | 18 +- tools/assimp_view/assimp_view.cpp | 25 +- tools/assimp_view/resource.h | 2 +- 295 files changed, 5518 insertions(+), 11500 deletions(-) delete mode 100644 cmake-modules/FindIrrXML.cmake rename {cmake => cmake-modules}/HunterGate.cmake (100%) create mode 100644 cmake-modules/assimp-hunter-config.cmake.in rename {cmake => cmake-modules}/assimp-plain-config.cmake.in (100%) delete mode 100644 cmake/assimp-hunter-config.cmake.in create mode 100644 code/AssetLib/3MF/3MFTypes.h create mode 100644 code/AssetLib/3MF/XmlSerializer.cpp create mode 100644 code/AssetLib/3MF/XmlSerializer.h delete mode 100644 contrib/draco/.ruby-version delete mode 100644 contrib/draco/.travis.yml delete mode 100644 contrib/draco/cmake/draco_features.cmake rename {code/Pbrt => contrib/stb}/stb_image.h (100%) delete mode 100644 contrib/stb_image/stb_image.h create mode 100644 test/models/Collada/human.zae create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf create mode 100644 test/models/glTF2/ClearCoat-glTF/PartialCoating.png create mode 100644 test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png create mode 100644 test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg create mode 100644 test/models/glTF2/ClearCoat-glTF/RibsNormal.png create mode 100644 test/models/glTF2/ClearCoat-glTF/RoughnessStripes.png diff --git a/.gitignore b/.gitignore index 0a999d3aa..7849cab65 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,7 @@ CMakeSettings.json # Output bin/ lib/ - +x64/ # QtCreator CMakeLists.txt.user diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b9d6fc55..bfa30e96a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,10 +44,10 @@ CMAKE_MINIMUM_REQUIRED( VERSION 3.10 ) option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF) IF(ASSIMP_HUNTER_ENABLED) - include("cmake/HunterGate.cmake") + include("cmake-modules/HunterGate.cmake") HunterGate( - URL "https://github.com/cpp-pm/hunter/archive/v0.23.293.tar.gz" - SHA1 "e8e5470652db77149d9b38656db2a6c0b7642693" + URL "https://github.com/cpp-pm/hunter/archive/v0.23.311.tar.gz" + SHA1 "1a82b9b73055879181cb1466b2ab5d48ee8ae410" ) add_definitions(-DASSIMP_USE_HUNTER) @@ -135,11 +135,11 @@ IF ( WIN32 ) # Use subset of Windows.h ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN ) - OPTION ( ASSIMP_BUILD_ASSIMP_VIEW - "If the Assimp view tool is built. (requires DirectX)" - OFF ) - IF(MSVC) + OPTION ( ASSIMP_BUILD_ASSIMP_VIEW + "If the Assimp view tool is built. (requires DirectX)" + OFF ) + OPTION( ASSIMP_INSTALL_PDB "Install MSVC debug files." ON ) @@ -268,6 +268,8 @@ ELSEIF(MSVC) ADD_COMPILE_OPTIONS(/wd4351) ENDIF() SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /Zi /Od") + SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi") + SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FULL /PDBALTPATH:%_PDB% /OPT:REF /OPT:ICF") ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) IF(NOT ASSIMP_HUNTER_ENABLED) SET(CMAKE_CXX_STANDARD 11) @@ -395,14 +397,14 @@ set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") IF(ASSIMP_HUNTER_ENABLED) set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}") - set(CMAKE_CONFIG_TEMPLATE_FILE "cmake/assimp-hunter-config.cmake.in") + set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-modules/assimp-hunter-config.cmake.in") set(NAMESPACE "${PROJECT_NAME}::") set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") set(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake") set(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}Config.cmake") ELSE() set(CONFIG_INSTALL_DIR "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}") - set(CMAKE_CONFIG_TEMPLATE_FILE "cmake/assimp-plain-config.cmake.in") + set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-modules/assimp-plain-config.cmake.in") string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWERCASE) set(NAMESPACE "${PROJECT_NAME_LOWERCASE}::") set(TARGETS_EXPORT_NAME "${PROJECT_NAME_LOWERCASE}Targets") diff --git a/cmake-modules/FindIrrXML.cmake b/cmake-modules/FindIrrXML.cmake deleted file mode 100644 index 5434e0b86..000000000 --- a/cmake-modules/FindIrrXML.cmake +++ /dev/null @@ -1,17 +0,0 @@ -# Find IrrXMl from irrlicht project -# -# Find LibIrrXML headers and library -# -# IRRXML_FOUND - IrrXML found -# IRRXML_INCLUDE_DIR - Headers location -# IRRXML_LIBRARY - IrrXML main library - -find_path(IRRXML_INCLUDE_DIR irrXML.h - PATH_SUFFIXES include/irrlicht include/irrxml) -find_library(IRRXML_LIBRARY IrrXML) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(IrrXML REQUIRED_VARS IRRXML_INCLUDE_DIR IRRXML_LIBRARY) - - -mark_as_advanced(IRRXML_INCLUDE_DIR IRRXML_LIBRARY) diff --git a/cmake/HunterGate.cmake b/cmake-modules/HunterGate.cmake similarity index 100% rename from cmake/HunterGate.cmake rename to cmake-modules/HunterGate.cmake diff --git a/cmake-modules/assimp-hunter-config.cmake.in b/cmake-modules/assimp-hunter-config.cmake.in new file mode 100644 index 000000000..1988f7e7d --- /dev/null +++ b/cmake-modules/assimp-hunter-config.cmake.in @@ -0,0 +1,19 @@ +@PACKAGE_INIT@ + +find_package(RapidJSON CONFIG REQUIRED) +find_package(ZLIB CONFIG REQUIRED) +find_package(utf8cpp CONFIG REQUIRED) +find_package(minizip CONFIG REQUIRED) +find_package(openddlparser CONFIG REQUIRED) +find_package(poly2tri CONFIG REQUIRED) +find_package(polyclipping CONFIG REQUIRED) +find_package(zip CONFIG REQUIRED) +find_package(pugixml CONFIG REQUIRED) +find_package(stb CONFIG REQUIRED) + +if(@ASSIMP_BUILD_DRACO@) + find_package(draco CONFIG REQUIRED) +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/cmake/assimp-plain-config.cmake.in b/cmake-modules/assimp-plain-config.cmake.in similarity index 100% rename from cmake/assimp-plain-config.cmake.in rename to cmake-modules/assimp-plain-config.cmake.in diff --git a/cmake/assimp-hunter-config.cmake.in b/cmake/assimp-hunter-config.cmake.in deleted file mode 100644 index 91efcbf24..000000000 --- a/cmake/assimp-hunter-config.cmake.in +++ /dev/null @@ -1,18 +0,0 @@ -@PACKAGE_INIT@ - -find_package(RapidJSON CONFIG REQUIRED) -find_package(ZLIB CONFIG REQUIRED) -find_package(utf8cpp CONFIG REQUIRED) -find_package(minizip CONFIG REQUIRED) -find_package(openddlparser CONFIG REQUIRED) -find_package(poly2tri CONFIG REQUIRED) -find_package(polyclipping CONFIG REQUIRED) -find_package(zip CONFIG REQUIRED) -find_package(pugixml CONFIG REQUIRED) - -if(@ASSIMP_BUILD_DRACO@) - find_package(draco CONFIG REQUIRED) -endif() - -include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") -check_required_components("@PROJECT_NAME@") diff --git a/code/AssetLib/3DS/3DSConverter.cpp b/code/AssetLib/3DS/3DSConverter.cpp index aca16b0d6..add1553bc 100644 --- a/code/AssetLib/3DS/3DSConverter.cpp +++ b/code/AssetLib/3DS/3DSConverter.cpp @@ -68,8 +68,8 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() { unsigned int idx(NotSet); for (unsigned int i = 0; i < mScene->mMaterials.size(); ++i) { std::string s = mScene->mMaterials[i].mName; - for (std::string::iterator it = s.begin(); it != s.end(); ++it) { - *it = static_cast(::tolower(static_cast(*it))); + for (char & it : s) { + it = static_cast(::tolower(static_cast(it))); } if (std::string::npos == s.find("default")) continue; @@ -79,12 +79,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() { mScene->mMaterials[i].mDiffuse.r != mScene->mMaterials[i].mDiffuse.b) continue; - if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || - mScene->mMaterials[i].sTexBump.mMapName.length() != 0 || - mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || - mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || - mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || - mScene->mMaterials[i].sTexShininess.mMapName.length() != 0) { + if (ContainsTextures(i)) { continue; } idx = i; diff --git a/code/AssetLib/3DS/3DSExporter.cpp b/code/AssetLib/3DS/3DSExporter.cpp index 92a6d5aa7..0beecd563 100644 --- a/code/AssetLib/3DS/3DSExporter.cpp +++ b/code/AssetLib/3DS/3DSExporter.cpp @@ -291,7 +291,7 @@ void Discreet3DSExporter::WriteMaterials() { ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_SPECULAR); WriteColor(color); } - + if (mat.Get(AI_MATKEY_COLOR_AMBIENT, color) == AI_SUCCESS) { ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_AMBIENT); WriteColor(color); diff --git a/code/AssetLib/3DS/3DSHelper.h b/code/AssetLib/3DS/3DSHelper.h index 1930c0c40..e8efbf949 100644 --- a/code/AssetLib/3DS/3DSHelper.h +++ b/code/AssetLib/3DS/3DSHelper.h @@ -348,16 +348,16 @@ struct Texture { // empty } - Texture(Texture &&other) AI_NO_EXCEPT : mTextureBlend(std::move(other.mTextureBlend)), + Texture(Texture &&other) AI_NO_EXCEPT : mTextureBlend(other.mTextureBlend), mMapName(std::move(other.mMapName)), - mOffsetU(std::move(other.mOffsetU)), - mOffsetV(std::move(other.mOffsetV)), - mScaleU(std::move(other.mScaleU)), - mScaleV(std::move(other.mScaleV)), - mRotation(std::move(other.mRotation)), - mMapMode(std::move(other.mMapMode)), - bPrivate(std::move(other.bPrivate)), - iUVSrc(std::move(other.iUVSrc)) { + mOffsetU(other.mOffsetU), + mOffsetV(other.mOffsetV), + mScaleU(other.mScaleU), + mScaleV(other.mScaleV), + mRotation(other.mRotation), + mMapMode(other.mMapMode), + bPrivate(other.bPrivate), + iUVSrc(other.iUVSrc) { // empty } @@ -366,16 +366,16 @@ struct Texture { return *this; } - mTextureBlend = std::move(other.mTextureBlend); + mTextureBlend = other.mTextureBlend; mMapName = std::move(other.mMapName); - mOffsetU = std::move(other.mOffsetU); - mOffsetV = std::move(other.mOffsetV); - mScaleU = std::move(other.mScaleU); - mScaleV = std::move(other.mScaleV); - mRotation = std::move(other.mRotation); - mMapMode = std::move(other.mMapMode); - bPrivate = std::move(other.bPrivate); - iUVSrc = std::move(other.iUVSrc); + mOffsetU = other.mOffsetU; + mOffsetV = other.mOffsetV; + mScaleU = other.mScaleU; + mScaleV = other.mScaleV; + mRotation = other.mRotation; + mMapMode = other.mMapMode; + bPrivate = other.bPrivate; + iUVSrc = other.iUVSrc; return *this; } @@ -461,13 +461,13 @@ struct Material { //! Move constructor. This is explicitly written because MSVC doesn't support defaulting it Material(Material &&other) AI_NO_EXCEPT : mName(std::move(other.mName)), - mDiffuse(std::move(other.mDiffuse)), - mSpecularExponent(std::move(other.mSpecularExponent)), - mShininessStrength(std::move(other.mShininessStrength)), - mSpecular(std::move(other.mSpecular)), - mAmbient(std::move(other.mAmbient)), - mShading(std::move(other.mShading)), - mTransparency(std::move(other.mTransparency)), + mDiffuse(other.mDiffuse), + mSpecularExponent(other.mSpecularExponent), + mShininessStrength(other.mShininessStrength), + mSpecular(other.mSpecular), + mAmbient(other.mAmbient), + mShading(other.mShading), + mTransparency(other.mTransparency), sTexDiffuse(std::move(other.sTexDiffuse)), sTexOpacity(std::move(other.sTexOpacity)), sTexSpecular(std::move(other.sTexSpecular)), @@ -475,10 +475,10 @@ struct Material { sTexBump(std::move(other.sTexBump)), sTexEmissive(std::move(other.sTexEmissive)), sTexShininess(std::move(other.sTexShininess)), - mBumpHeight(std::move(other.mBumpHeight)), - mEmissive(std::move(other.mEmissive)), + mBumpHeight(other.mBumpHeight), + mEmissive(other.mEmissive), sTexAmbient(std::move(other.sTexAmbient)), - mTwoSided(std::move(other.mTwoSided)) { + mTwoSided(other.mTwoSided) { // empty } @@ -488,13 +488,13 @@ struct Material { } mName = std::move(other.mName); - mDiffuse = std::move(other.mDiffuse); - mSpecularExponent = std::move(other.mSpecularExponent); - mShininessStrength = std::move(other.mShininessStrength), - mSpecular = std::move(other.mSpecular); - mAmbient = std::move(other.mAmbient); - mShading = std::move(other.mShading); - mTransparency = std::move(other.mTransparency); + mDiffuse = other.mDiffuse; + mSpecularExponent = other.mSpecularExponent; + mShininessStrength = other.mShininessStrength, + mSpecular = other.mSpecular; + mAmbient = other.mAmbient; + mShading = other.mShading; + mTransparency = other.mTransparency; sTexDiffuse = std::move(other.sTexDiffuse); sTexOpacity = std::move(other.sTexOpacity); sTexSpecular = std::move(other.sTexSpecular); @@ -502,10 +502,10 @@ struct Material { sTexBump = std::move(other.sTexBump); sTexEmissive = std::move(other.sTexEmissive); sTexShininess = std::move(other.sTexShininess); - mBumpHeight = std::move(other.mBumpHeight); - mEmissive = std::move(other.mEmissive); + mBumpHeight = other.mBumpHeight; + mEmissive = other.mEmissive; sTexAmbient = std::move(other.sTexAmbient); - mTwoSided = std::move(other.mTwoSided); + mTwoSided = other.mTwoSided; return *this; } diff --git a/code/AssetLib/3DS/3DSLoader.h b/code/AssetLib/3DS/3DSLoader.h index 2091fbeb7..04dcac237 100644 --- a/code/AssetLib/3DS/3DSLoader.h +++ b/code/AssetLib/3DS/3DSLoader.h @@ -208,6 +208,15 @@ protected: */ void ReplaceDefaultMaterial(); + bool ContainsTextures(unsigned int i) const { + return !mScene->mMaterials[i].sTexDiffuse.mMapName.empty() || + !mScene->mMaterials[i].sTexBump.mMapName.empty() || + !mScene->mMaterials[i].sTexOpacity.mMapName.empty() || + !mScene->mMaterials[i].sTexEmissive.mMapName.empty() || + !mScene->mMaterials[i].sTexSpecular.mMapName.empty() || + !mScene->mMaterials[i].sTexShininess.mMapName.empty() ; + } + // ------------------------------------------------------------------- /** Convert the whole scene */ diff --git a/code/AssetLib/3MF/3MFTypes.h b/code/AssetLib/3MF/3MFTypes.h new file mode 100644 index 000000000..c4e9e4243 --- /dev/null +++ b/code/AssetLib/3MF/3MFTypes.h @@ -0,0 +1,165 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2021, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include +#include +#include +#include +#include + +struct aiMaterial; +struct aiMesh; + +namespace Assimp { +namespace D3MF { + +enum class ResourceType { + RT_Object, + RT_BaseMaterials, + RT_EmbeddedTexture2D, + RT_Texture2DGroup, + RT_Unknown +}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...) + +class Resource { +public: + int mId; + + Resource(int id) : + mId(id) { + // empty + } + + virtual ~Resource() { + // empty + } + + virtual ResourceType getType() const { + return ResourceType::RT_Unknown; + } +}; + +class EmbeddedTexture : public Resource { +public: + std::string mPath; + std::string mContentType; + std::string mTilestyleU; + std::string mTilestyleV; + std::vector mBuffer; + + EmbeddedTexture(int id) : + Resource(id), + mPath(), + mContentType(), + mTilestyleU(), + mTilestyleV() { + // empty + } + + ~EmbeddedTexture() = default; + + ResourceType getType() const override { + return ResourceType::RT_EmbeddedTexture2D; + } +}; + +class Texture2DGroup : public Resource { +public: + std::vector mTex2dCoords; + int mTexId; + Texture2DGroup(int id) : + Resource(id), + mTexId(-1) { + // empty + } + + ~Texture2DGroup() = default; + + ResourceType getType() const override { + return ResourceType::RT_Texture2DGroup; + } +}; + +class BaseMaterials : public Resource { +public: + std::vector mMaterialIndex; + + BaseMaterials(int id) : + Resource(id), + mMaterialIndex() { + // empty + } + + ~BaseMaterials() = default; + + ResourceType getType() const override { + return ResourceType::RT_BaseMaterials; + } +}; + +struct Component { + int mObjectId; + aiMatrix4x4 mTransformation; +}; + +class Object : public Resource { +public: + std::vector mMeshes; + std::vector mMeshIndex; + std::vector mComponents; + std::string mName; + + Object(int id) : + Resource(id), + mName(std::string("Object_") + ai_to_string(id)) { + // empty + } + + ~Object() = default; + + ResourceType getType() const override { + return ResourceType::RT_Object; + } +}; + +} // namespace D3MF +} // namespace Assimp diff --git a/code/AssetLib/3MF/3MFXmlTags.h b/code/AssetLib/3MF/3MFXmlTags.h index d447556d6..a6e9758c1 100644 --- a/code/AssetLib/3MF/3MFXmlTags.h +++ b/code/AssetLib/3MF/3MFXmlTags.h @@ -80,13 +80,21 @@ namespace XmlTag { const char* const item = "item"; const char* const objectid = "objectid"; const char* const transform = "transform"; + const char *const path = "path"; // Material definitions const char* const basematerials = "basematerials"; - const char* const basematerials_id = "id"; const char* const basematerials_base = "base"; const char* const basematerials_name = "name"; const char* const basematerials_displaycolor = "displaycolor"; + const char* const texture_2d = "m:texture2d"; + const char *const texture_group = "m:texture2dgroup"; + const char *const texture_content_type = "contenttype"; + const char *const texture_tilestyleu = "tilestyleu"; + const char *const texture_tilestylev = "tilestylev"; + const char *const texture_2d_coord = "m:tex2coord"; + const char *const texture_cuurd_u = "u"; + const char *const texture_cuurd_v = "v"; // Meta info tags const char* const CONTENT_TYPES_ARCHIVE = "[Content_Types].xml"; @@ -103,7 +111,7 @@ namespace XmlTag { const char* const PACKAGE_TEXTURE_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture"; const char* const PACKAGE_CORE_PROPERTIES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; const char* const PACKAGE_THUMBNAIL_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"; - } +} } // Namespace D3MF } // Namespace Assimp diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index 747af7cfc..58dde9738 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "D3MFImporter.h" #include "3MFXmlTags.h" #include "D3MFOpcPackage.h" +#include "XmlSerializer.h" #include #include @@ -61,513 +62,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include +#include namespace Assimp { -namespace D3MF { - -enum class ResourceType { - RT_Object, - RT_BaseMaterials, - RT_Unknown -}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...) - -class Resource { -public: - int mId; - - Resource(int id) : - mId(id) { - // empty - } - - virtual ~Resource() { - // empty - } - - virtual ResourceType getType() const { - return ResourceType::RT_Unknown; - } -}; - -class BaseMaterials : public Resource { -public: - std::vector mMaterials; - std::vector mMaterialIndex; - - BaseMaterials(int id) : - Resource(id), - mMaterials(), - mMaterialIndex() { - // empty - } - - ~BaseMaterials() = default; - - ResourceType getType() const override { - return ResourceType::RT_BaseMaterials; - } -}; - -struct Component { - int mObjectId; - aiMatrix4x4 mTransformation; -}; - -class Object : public Resource { -public: - std::vector mMeshes; - std::vector mMeshIndex; - std::vector mComponents; - std::string mName; - - Object(int id) : - Resource(id), - mName(std::string("Object_") + ai_to_string(id)) { - // empty - } - - ~Object() = default; - - ResourceType getType() const override { - return ResourceType::RT_Object; - } -}; - -class XmlSerializer { -public: - XmlSerializer(XmlParser *xmlParser) : - mResourcesDictionnary(), - mMaterialCount(0), - mMeshCount(0), - mXmlParser(xmlParser) { - // empty - } - - ~XmlSerializer() { - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it ) { - delete it->second; - } - } - - void ImportXml(aiScene *scene) { - if (nullptr == scene) { - return; - } - - scene->mRootNode = new aiNode(XmlTag::RootTag); - - XmlNode node = mXmlParser->getRootNode().child(XmlTag::model); - if (node.empty()) { - return; - } - XmlNode resNode = node.child(XmlTag::resources); - for (auto ¤tNode : resNode.children()) { - const std::string currentNodeName = currentNode.name(); - if (currentNodeName == XmlTag::object) { - ReadObject(currentNode); - } else if (currentNodeName == XmlTag::basematerials) { - ReadBaseMaterials(currentNode); - } else if (currentNodeName == XmlTag::meta) { - ReadMetadata(currentNode); - } - } - - XmlNode buildNode = node.child(XmlTag::build); - for (auto ¤tNode : buildNode.children()) { - const std::string currentNodeName = currentNode.name(); - if (currentNodeName == XmlTag::item) { - int objectId = -1; - std::string transformationMatrixStr; - aiMatrix4x4 transformationMatrix; - getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId); - bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transformationMatrixStr); - - auto it = mResourcesDictionnary.find(objectId); - if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { - Object *obj = static_cast(it->second); - if (hasTransform) { - transformationMatrix = parseTransformMatrix(transformationMatrixStr); - } - - addObjectToNode(scene->mRootNode, obj, transformationMatrix); - } - } - } - - // import the metadata - if (!mMetaData.empty()) { - const size_t numMeta = mMetaData.size(); - scene->mMetaData = aiMetadata::Alloc(static_cast(numMeta)); - for (size_t i = 0; i < numMeta; ++i) { - aiString val(mMetaData[i].value); - scene->mMetaData->Set(static_cast(i), mMetaData[i].name, val); - } - } - - // import the meshes - scene->mNumMeshes = static_cast(mMeshCount); - if (scene->mNumMeshes != 0) { - scene->mMeshes = new aiMesh *[scene->mNumMeshes](); - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it) { - if (it->second->getType() == ResourceType::RT_Object) { - Object *obj = static_cast(it->second); - ai_assert(nullptr != obj); - for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) { - scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i]; - } - } - } - } - - // import the materials - scene->mNumMaterials = mMaterialCount; - if (scene->mNumMaterials != 0) { - scene->mMaterials = new aiMaterial *[scene->mNumMaterials]; - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it) { - if (it->second->getType() == ResourceType::RT_BaseMaterials) { - BaseMaterials *baseMaterials = static_cast(it->second); - for (unsigned int i = 0; i < baseMaterials->mMaterials.size(); ++i) { - scene->mMaterials[baseMaterials->mMaterialIndex[i]] = baseMaterials->mMaterials[i]; - } - } - } - } - } - -private: - void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) { - ai_assert(nullptr != obj); - - aiNode *sceneNode = new aiNode(obj->mName); - sceneNode->mNumMeshes = static_cast(obj->mMeshes.size()); - sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes]; - std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes); - - sceneNode->mTransformation = nodeTransform; - if (nullptr != parent) { - parent->addChildren(1, &sceneNode); - } - - for (size_t i = 0; i < obj->mComponents.size(); ++i) { - Component c = obj->mComponents[i]; - auto it = mResourcesDictionnary.find(c.mObjectId); - if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { - addObjectToNode(sceneNode, static_cast(it->second), c.mTransformation); - } - } - } - - bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) { - pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str()); - if (!objectAttribute.empty()) { - value = objectAttribute.as_string(); - return true; - } - - return false; - } - - bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) { - std::string strValue; - bool ret = getNodeAttribute(node, attribute, strValue); - if (ret) { - value = std::atoi(strValue.c_str()); - return true; - } - - return false; - } - - aiMatrix4x4 parseTransformMatrix(std::string matrixStr) { - // split the string - std::vector numbers; - std::string currentNumber; - for (size_t i = 0; i < matrixStr.size(); ++i) { - const char c = matrixStr[i]; - if (c == ' ') { - if (currentNumber.size() > 0) { - float f = std::stof(currentNumber); - numbers.push_back(f); - currentNumber.clear(); - } - } else { - currentNumber.push_back(c); - } - } - if (currentNumber.size() > 0) { - const float f = std::stof(currentNumber); - numbers.push_back(f); - } - - aiMatrix4x4 transformMatrix; - transformMatrix.a1 = numbers[0]; - transformMatrix.b1 = numbers[1]; - transformMatrix.c1 = numbers[2]; - transformMatrix.d1 = 0; - - transformMatrix.a2 = numbers[3]; - transformMatrix.b2 = numbers[4]; - transformMatrix.c2 = numbers[5]; - transformMatrix.d2 = 0; - - transformMatrix.a3 = numbers[6]; - transformMatrix.b3 = numbers[7]; - transformMatrix.c3 = numbers[8]; - transformMatrix.d3 = 0; - - transformMatrix.a4 = numbers[9]; - transformMatrix.b4 = numbers[10]; - transformMatrix.c4 = numbers[11]; - transformMatrix.d4 = 1; - - return transformMatrix; - } - - void ReadObject(XmlNode &node) { - int id = -1, pid = -1, pindex = -1; - bool hasId = getNodeAttribute(node, XmlTag::id, id); - bool hasPid = getNodeAttribute(node, XmlTag::pid, pid); - bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex); - if (!hasId) { - return; - } - - Object *obj = new Object(id); - - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == D3MF::XmlTag::mesh) { - auto mesh = ReadMesh(currentNode); - mesh->mName.Set(ai_to_string(id)); - - if (hasPid) { - auto it = mResourcesDictionnary.find(pid); - if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) { - BaseMaterials *materials = static_cast(it->second); - mesh->mMaterialIndex = materials->mMaterialIndex[pindex]; - } - } - - obj->mMeshes.push_back(mesh); - obj->mMeshIndex.push_back(mMeshCount); - mMeshCount++; - } else if (currentName == D3MF::XmlTag::components) { - for (XmlNode ¤tSubNode : currentNode.children()) { - const std::string subNodeName = currentSubNode.name(); - if (subNodeName == D3MF::XmlTag::component) { - int objectId = -1; - std::string componentTransformStr; - aiMatrix4x4 componentTransform; - if (getNodeAttribute(currentSubNode, D3MF::XmlTag::transform, componentTransformStr)) { - componentTransform = parseTransformMatrix(componentTransformStr); - } - - if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) { - obj->mComponents.push_back({ objectId, componentTransform }); - } - } - } - } - } - - mResourcesDictionnary.insert(std::make_pair(id, obj)); - } - - aiMesh *ReadMesh(XmlNode &node) { - aiMesh *mesh = new aiMesh(); - - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::vertices) { - ImportVertices(currentNode, mesh); - } else if (currentName == XmlTag::triangles) { - ImportTriangles(currentNode, mesh); - } - } - - return mesh; - } - - void ReadMetadata(XmlNode &node) { - pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name); - const std::string name = attribute.as_string(); - const std::string value = node.value(); - if (name.empty()) { - return; - } - - MetaEntry entry; - entry.name = name; - entry.value = value; - mMetaData.push_back(entry); - } - - void ImportVertices(XmlNode &node, aiMesh *mesh) { - std::vector vertices; - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::vertex) { - vertices.push_back(ReadVertex(currentNode)); - } - } - - mesh->mNumVertices = static_cast(vertices.size()); - mesh->mVertices = new aiVector3D[mesh->mNumVertices]; - std::copy(vertices.begin(), vertices.end(), mesh->mVertices); - } - - aiVector3D ReadVertex(XmlNode &node) { - aiVector3D vertex; - vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr); - vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr); - vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr); - - return vertex; - } - - void ImportTriangles(XmlNode &node, aiMesh *mesh) { - std::vector faces; - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::triangle) { - aiFace face = ReadTriangle(currentNode); - faces.push_back(face); - - int pid = 0, p1 = 0; - bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid); - bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1); - - if (hasPid && hasP1) { - auto it = mResourcesDictionnary.find(pid); - if (it != mResourcesDictionnary.end()) { - if (it->second->getType() == ResourceType::RT_BaseMaterials) { - BaseMaterials *baseMaterials = static_cast(it->second); - mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1]; - } - // TODO: manage the separation into several meshes if the triangles of the mesh do not all refer to the same material - } - } - } - } - - mesh->mNumFaces = static_cast(faces.size()); - mesh->mFaces = new aiFace[mesh->mNumFaces]; - mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - - std::copy(faces.begin(), faces.end(), mesh->mFaces); - } - - aiFace ReadTriangle(XmlNode &node) { - aiFace face; - - face.mNumIndices = 3; - face.mIndices = new unsigned int[face.mNumIndices]; - face.mIndices[0] = static_cast(std::atoi(node.attribute(XmlTag::v1).as_string())); - face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); - face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); - - return face; - } - - void ReadBaseMaterials(XmlNode &node) { - int id = -1; - if (getNodeAttribute(node, D3MF::XmlTag::basematerials_id, id)) { - BaseMaterials *baseMaterials = new BaseMaterials(id); - - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::basematerials_base) { - baseMaterials->mMaterialIndex.push_back(mMaterialCount); - baseMaterials->mMaterials.push_back(readMaterialDef(currentNode, id)); - ++mMaterialCount; - } - } - - mResourcesDictionnary.insert(std::make_pair(id, baseMaterials)); - } - } - - bool parseColor(const char *color, aiColor4D &diffuse) { - if (nullptr == color) { - return false; - } - - //format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) - const size_t len = strlen(color); - if (9 != len && 7 != len) { - return false; - } - - const char *buf(color); - if ('#' != buf[0]) { - return false; - } - - char r[3] = { buf[1], buf[2], '\0' }; - diffuse.r = static_cast(strtol(r, nullptr, 16)) / ai_real(255.0); - - char g[3] = { buf[3], buf[4], '\0' }; - diffuse.g = static_cast(strtol(g, nullptr, 16)) / ai_real(255.0); - - char b[3] = { buf[5], buf[6], '\0' }; - diffuse.b = static_cast(strtol(b, nullptr, 16)) / ai_real(255.0); - - if (7 == len) - return true; - - char a[3] = { buf[7], buf[8], '\0' }; - diffuse.a = static_cast(strtol(a, nullptr, 16)) / ai_real(255.0); - - return true; - } - - void assignDiffuseColor(XmlNode &node, aiMaterial *mat) { - const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string(); - aiColor4D diffuse; - if (parseColor(color, diffuse)) { - mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - } - } - - aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId) { - aiMaterial *material = new aiMaterial(); - material->mNumProperties = 0; - std::string name; - bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name); - - std::string stdMaterialName; - const std::string strId(ai_to_string(basematerialsId)); - stdMaterialName += "id"; - stdMaterialName += strId; - stdMaterialName += "_"; - if (hasName) { - stdMaterialName += std::string(name); - } else { - stdMaterialName += "basemat_"; - stdMaterialName += ai_to_string(mMaterialCount - basematerialsId); - } - - aiString assimpMaterialName(stdMaterialName); - material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME); - - assignDiffuseColor(node, material); - - return material; - } - -private: - struct MetaEntry { - std::string name; - std::string value; - }; - std::vector mMetaData; - std::map mResourcesDictionnary; - unsigned int mMaterialCount, mMeshCount; - XmlParser *mXmlParser; -}; - -} //namespace D3MF using namespace D3MF; @@ -597,7 +94,9 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo const std::string extension(GetExtension(filename)); if (extension == desc.mFileExtensions) { return true; - } else if (!extension.length() || checkSig) { + } + + if (!extension.length() || checkSig) { if (nullptr == pIOHandler) { return false; } @@ -611,7 +110,7 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo return false; } -void D3MFImporter::SetupProperties(const Importer * /*pImp*/) { +void D3MFImporter::SetupProperties(const Importer*) { // empty } @@ -626,6 +125,15 @@ void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene, if (xmlParser.parse(opcPackage.RootStream())) { XmlSerializer xmlSerializer(&xmlParser); xmlSerializer.ImportXml(pScene); + + const std::vector &tex = opcPackage.GetEmbeddedTextures(); + if (!tex.empty()) { + pScene->mNumTextures = static_cast(tex.size()); + pScene->mTextures = new aiTexture *[pScene->mNumTextures]; + for (unsigned int i = 0; i < pScene->mNumTextures; ++i) { + pScene->mTextures[i] = tex[i]; + } + } } } diff --git a/code/AssetLib/3MF/D3MFImporter.h b/code/AssetLib/3MF/D3MFImporter.h index 811c463b6..2b37010d9 100644 --- a/code/AssetLib/3MF/D3MFImporter.h +++ b/code/AssetLib/3MF/D3MFImporter.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -47,17 +46,40 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { +// --------------------------------------------------------------------------- /// @brief The 3MF-importer class. +/// +/// Implements the basic topology import and embedded textures. +// --------------------------------------------------------------------------- class D3MFImporter : public BaseImporter { public: + /// @brief The default class constructor. D3MFImporter(); - ~D3MFImporter(); - bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const; - void SetupProperties(const Importer *pImp); - const aiImporterDesc *GetInfo() const; + + /// @brief The class destructor. + ~D3MFImporter() override; + + /// @brief Performs the data format detection. + /// @param pFile The filename to check. + /// @param pIOHandler The used IO-System. + /// @param checkSig true for signature checking. + /// @return true for can be loaded, false for not. + bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override; + + /// @brief Not used + /// @param pImp Not used + void SetupProperties(const Importer *pImp) override; + + /// @brief The importer description getter. + /// @return The info + const aiImporterDesc *GetInfo() const override; protected: - void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); + /// @brief Internal read function, performs the file parsing. + /// @param pFile The filename + /// @param pScene The scene to load in. + /// @param pIOHandler The io-system + void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override; }; } // Namespace Assimp diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index dbf4f2e10..c29cec368 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -43,14 +43,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "D3MFOpcPackage.h" #include - #include #include #include #include #include #include - +#include #include "3MFXmlTags.h" #include #include @@ -64,11 +63,12 @@ namespace Assimp { namespace D3MF { // ------------------------------------------------------------------------------------------------ -typedef std::shared_ptr OpcPackageRelationshipPtr; +using OpcPackageRelationshipPtr = std::shared_ptr; class OpcPackageRelationshipReader { public: - OpcPackageRelationshipReader(XmlParser &parser) { + OpcPackageRelationshipReader(XmlParser &parser) : + m_relationShips() { XmlNode root = parser.getRootNode(); ParseRootNode(root); } @@ -91,6 +91,7 @@ public: if (relPtr->id.empty() || relPtr->type.empty() || relPtr->target.empty()) { return false; } + return true; } @@ -100,7 +101,7 @@ public: } for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { - std::string name = currentNode.name(); + const std::string name = currentNode.name(); if (name == "Relationship") { OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship()); relPtr->id = currentNode.attribute(XmlTag::RELS_ATTRIB_ID).as_string(); @@ -116,11 +117,23 @@ public: std::vector m_relationShips; }; +static bool IsEmbeddedTexture( const std::string &filename ) { + const std::string extension = BaseImporter::GetExtension(filename); + if (extension == "jpg" || extension == "png") { + std::string::size_type pos = filename.find("thumbnail"); + if (pos == std::string::npos) { + return false; + } + return true; + } + + return false; +} // ------------------------------------------------------------------------------------------------ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : mRootStream(nullptr), mZipArchive() { - mZipArchive.reset(new ZipArchiveIOSystem(pIOHandler, rFile)); + mZipArchive = new ZipArchiveIOSystem(pIOHandler, rFile); if (!mZipArchive->isOpen()) { throw DeadlyImportError("Failed to open file ", rFile, "."); } @@ -141,13 +154,13 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : } std::string rootFile = ReadPackageRootRelationship(fileStream); - if (rootFile.size() > 0 && rootFile[0] == '/') { + if (!rootFile.empty() && rootFile[0] == '/') { rootFile = rootFile.substr(1); if (rootFile[0] == '/') { // deal with zip-bug rootFile = rootFile.substr(1); } - } + } ASSIMP_LOG_VERBOSE_DEBUG(rootFile); @@ -158,9 +171,12 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : if (nullptr == mRootStream) { throw DeadlyImportError("Cannot open root-file in archive : " + rootFile); } - } else if (file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) { ASSIMP_LOG_WARN("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES", file); + } else if (IsEmbeddedTexture(file)) { + IOStream *fileStream = mZipArchive->Open(file.c_str()); + LoadEmbeddedTextures(fileStream, file); + mZipArchive->Close(fileStream); } else { ASSIMP_LOG_WARN("Ignored file of unknown type: ", file); } @@ -169,20 +185,26 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : D3MFOpcPackage::~D3MFOpcPackage() { mZipArchive->Close(mRootStream); + delete mZipArchive; + mZipArchive = nullptr; } IOStream *D3MFOpcPackage::RootStream() const { return mRootStream; } -static const std::string ModelRef = "3D/3dmodel.model"; +const std::vector &D3MFOpcPackage::GetEmbeddedTextures() const { + return mEmbeddedTextures; +} + +static const char *const ModelRef = "3D/3dmodel.model"; bool D3MFOpcPackage::validate() { if (nullptr == mRootStream || nullptr == mZipArchive) { return false; } - return mZipArchive->Exists(ModelRef.c_str()); + return mZipArchive->Exists(ModelRef); } std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) { @@ -204,6 +226,31 @@ std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) { return (*itr)->target; } +void D3MFOpcPackage::LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename) { + if (nullptr == fileStream) { + return; + } + + const size_t size = fileStream->FileSize(); + if (0 == size) { + return; + } + + unsigned char *data = new unsigned char[size]; + fileStream->Read(data, 1, size); + aiTexture *texture = new aiTexture; + std::string embName = "*" + filename; + texture->mFilename.Set(embName.c_str()); + texture->mWidth = static_cast(size); + texture->mHeight = 0; + texture->achFormatHint[0] = 'p'; + texture->achFormatHint[1] = 'n'; + texture->achFormatHint[2] = 'g'; + texture->achFormatHint[3] = '\0'; + texture->pcData = (aiTexel*) data; + mEmbeddedTextures.emplace_back(texture); +} + } // Namespace D3MF } // Namespace Assimp diff --git a/code/AssetLib/3MF/D3MFOpcPackage.h b/code/AssetLib/3MF/D3MFOpcPackage.h index 22b4510d0..fda74a879 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.h +++ b/code/AssetLib/3MF/D3MFOpcPackage.h @@ -46,8 +46,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +struct aiTexture; + namespace Assimp { - class ZipArchiveIOSystem; + +class ZipArchiveIOSystem; namespace D3MF { @@ -63,16 +66,19 @@ public: ~D3MFOpcPackage(); IOStream* RootStream() const; bool validate(); + const std::vector &GetEmbeddedTextures() const; protected: std::string ReadPackageRootRelationship(IOStream* stream); + void LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename); private: IOStream* mRootStream; - std::unique_ptr mZipArchive; + ZipArchiveIOSystem *mZipArchive; + std::vector mEmbeddedTextures; }; -} // Namespace D3MF -} // Namespace Assimp +} // namespace D3MF +} // namespace Assimp #endif // D3MFOPCPACKAGE_H diff --git a/code/AssetLib/3MF/XmlSerializer.cpp b/code/AssetLib/3MF/XmlSerializer.cpp new file mode 100644 index 000000000..7a33d08ed --- /dev/null +++ b/code/AssetLib/3MF/XmlSerializer.cpp @@ -0,0 +1,594 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2021, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#include "XmlSerializer.h" +#include "D3MFOpcPackage.h" +#include "3MFXmlTags.h" +#include "3MFTypes.h" +#include + +namespace Assimp { +namespace D3MF { + +static const int IdNotSet = -1; + +namespace { + +static const size_t ColRGBA_Len = 9; +static const size_t ColRGB_Len = 7; + +// format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) +bool validateColorString(const char *color) { + const size_t len = strlen(color); + if (ColRGBA_Len != len && ColRGB_Len != len) { + return false; + } + + return true; +} + +aiFace ReadTriangle(XmlNode &node) { + aiFace face; + + face.mNumIndices = 3; + face.mIndices = new unsigned int[face.mNumIndices]; + face.mIndices[0] = static_cast(std::atoi(node.attribute(XmlTag::v1).as_string())); + face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); + face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); + + return face; +} + +aiVector3D ReadVertex(XmlNode &node) { + aiVector3D vertex; + vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr); + vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr); + vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr); + + return vertex; +} + +bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) { + pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str()); + if (!objectAttribute.empty()) { + value = objectAttribute.as_string(); + return true; + } + + return false; +} + +bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) { + std::string strValue; + const bool ret = getNodeAttribute(node, attribute, strValue); + if (ret) { + value = std::atoi(strValue.c_str()); + return true; + } + + return false; +} + +aiMatrix4x4 parseTransformMatrix(std::string matrixStr) { + // split the string + std::vector numbers; + std::string currentNumber; + for (char c : matrixStr) { + if (c == ' ') { + if (!currentNumber.empty()) { + float f = std::stof(currentNumber); + numbers.push_back(f); + currentNumber.clear(); + } + } else { + currentNumber.push_back(c); + } + } + if (!currentNumber.empty()) { + const float f = std::stof(currentNumber); + numbers.push_back(f); + } + + aiMatrix4x4 transformMatrix; + transformMatrix.a1 = numbers[0]; + transformMatrix.b1 = numbers[1]; + transformMatrix.c1 = numbers[2]; + transformMatrix.d1 = 0; + + transformMatrix.a2 = numbers[3]; + transformMatrix.b2 = numbers[4]; + transformMatrix.c2 = numbers[5]; + transformMatrix.d2 = 0; + + transformMatrix.a3 = numbers[6]; + transformMatrix.b3 = numbers[7]; + transformMatrix.c3 = numbers[8]; + transformMatrix.d3 = 0; + + transformMatrix.a4 = numbers[9]; + transformMatrix.b4 = numbers[10]; + transformMatrix.c4 = numbers[11]; + transformMatrix.d4 = 1; + + return transformMatrix; +} + +bool parseColor(const char *color, aiColor4D &diffuse) { + if (nullptr == color) { + return false; + } + + if (!validateColorString(color)) { + return false; + } + + //const char *buf(color); + if ('#' != color[0]) { + return false; + } + + char r[3] = { color[1], color[2], '\0' }; + diffuse.r = static_cast(strtol(r, nullptr, 16)) / ai_real(255.0); + + char g[3] = { color[3], color[4], '\0' }; + diffuse.g = static_cast(strtol(g, nullptr, 16)) / ai_real(255.0); + + char b[3] = { color[5], color[6], '\0' }; + diffuse.b = static_cast(strtol(b, nullptr, 16)) / ai_real(255.0); + const size_t len = strlen(color); + if (ColRGB_Len == len) { + return true; + } + + char a[3] = { color[7], color[8], '\0' }; + diffuse.a = static_cast(strtol(a, nullptr, 16)) / ai_real(255.0); + + return true; +} + +void assignDiffuseColor(XmlNode &node, aiMaterial *mat) { + const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string(); + aiColor4D diffuse; + if (parseColor(color, diffuse)) { + mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + } +} + +} // namespace + +XmlSerializer::XmlSerializer(XmlParser *xmlParser) : + mResourcesDictionnary(), + mMeshCount(0), + mXmlParser(xmlParser) { + ai_assert(nullptr != xmlParser); +} + +XmlSerializer::~XmlSerializer() { + for (auto &it : mResourcesDictionnary) { + delete it.second; + } +} + +void XmlSerializer::ImportXml(aiScene *scene) { + if (nullptr == scene) { + return; + } + + scene->mRootNode = new aiNode(XmlTag::RootTag); + XmlNode node = mXmlParser->getRootNode().child(XmlTag::model); + if (node.empty()) { + return; + } + + XmlNode resNode = node.child(XmlTag::resources); + for (auto ¤tNode : resNode.children()) { + const std::string currentNodeName = currentNode.name(); + if (currentNodeName == XmlTag::texture_2d) { + ReadEmbeddecTexture(currentNode); + } else if (currentNodeName == XmlTag::texture_group) { + ReadTextureGroup(currentNode); + } else if (currentNodeName == XmlTag::object) { + ReadObject(currentNode); + } else if (currentNodeName == XmlTag::basematerials) { + ReadBaseMaterials(currentNode); + } else if (currentNodeName == XmlTag::meta) { + ReadMetadata(currentNode); + } + } + StoreMaterialsInScene(scene); + XmlNode buildNode = node.child(XmlTag::build); + if (buildNode.empty()) { + return; + } + + for (auto ¤tNode : buildNode.children()) { + const std::string currentNodeName = currentNode.name(); + if (currentNodeName == XmlTag::item) { + int objectId = IdNotSet; + std::string transformationMatrixStr; + aiMatrix4x4 transformationMatrix; + getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId); + bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transformationMatrixStr); + + auto it = mResourcesDictionnary.find(objectId); + if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { + Object *obj = static_cast(it->second); + if (hasTransform) { + transformationMatrix = parseTransformMatrix(transformationMatrixStr); + } + + addObjectToNode(scene->mRootNode, obj, transformationMatrix); + } + } + } + + // import the metadata + if (!mMetaData.empty()) { + const size_t numMeta = mMetaData.size(); + scene->mMetaData = aiMetadata::Alloc(static_cast(numMeta)); + for (size_t i = 0; i < numMeta; ++i) { + aiString val(mMetaData[i].value); + scene->mMetaData->Set(static_cast(i), mMetaData[i].name, val); + } + } + + // import the meshes, materials are already stored + scene->mNumMeshes = static_cast(mMeshCount); + if (scene->mNumMeshes != 0) { + scene->mMeshes = new aiMesh *[scene->mNumMeshes](); + for (auto &it : mResourcesDictionnary) { + if (it.second->getType() == ResourceType::RT_Object) { + Object *obj = static_cast(it.second); + ai_assert(nullptr != obj); + for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) { + scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i]; + } + } + } + } +} + +void XmlSerializer::addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) { + ai_assert(nullptr != obj); + + aiNode *sceneNode = new aiNode(obj->mName); + sceneNode->mNumMeshes = static_cast(obj->mMeshes.size()); + sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes]; + std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes); + + sceneNode->mTransformation = nodeTransform; + if (nullptr != parent) { + parent->addChildren(1, &sceneNode); + } + + for (Assimp::D3MF::Component c : obj->mComponents) { + auto it = mResourcesDictionnary.find(c.mObjectId); + if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { + addObjectToNode(sceneNode, static_cast(it->second), c.mTransformation); + } + } +} + +void XmlSerializer::ReadObject(XmlNode &node) { + int id = IdNotSet, pid = IdNotSet, pindex = IdNotSet; + bool hasId = getNodeAttribute(node, XmlTag::id, id); + if (!hasId) { + return; + } + + bool hasPid = getNodeAttribute(node, XmlTag::pid, pid); + bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex); + + Object *obj = new Object(id); + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == D3MF::XmlTag::mesh) { + auto mesh = ReadMesh(currentNode); + mesh->mName.Set(ai_to_string(id)); + + if (hasPid) { + auto it = mResourcesDictionnary.find(pid); + if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) { + BaseMaterials *materials = static_cast(it->second); + mesh->mMaterialIndex = materials->mMaterialIndex[pindex]; + } + } + + obj->mMeshes.push_back(mesh); + obj->mMeshIndex.push_back(mMeshCount); + mMeshCount++; + } else if (currentName == D3MF::XmlTag::components) { + for (XmlNode ¤tSubNode : currentNode.children()) { + const std::string subNodeName = currentSubNode.name(); + if (subNodeName == D3MF::XmlTag::component) { + int objectId = IdNotSet; + std::string componentTransformStr; + aiMatrix4x4 componentTransform; + if (getNodeAttribute(currentSubNode, D3MF::XmlTag::transform, componentTransformStr)) { + componentTransform = parseTransformMatrix(componentTransformStr); + } + + if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) { + obj->mComponents.push_back({ objectId, componentTransform }); + } + } + } + } + } + + mResourcesDictionnary.insert(std::make_pair(id, obj)); +} + +aiMesh *XmlSerializer::ReadMesh(XmlNode &node) { + if (node.empty()) { + return nullptr; + } + + aiMesh *mesh = new aiMesh(); + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::vertices) { + ImportVertices(currentNode, mesh); + } else if (currentName == XmlTag::triangles) { + ImportTriangles(currentNode, mesh); + } + } + + return mesh; +} + +void XmlSerializer::ReadMetadata(XmlNode &node) { + pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name); + const std::string name = attribute.as_string(); + const std::string value = node.value(); + if (name.empty()) { + return; + } + + MetaEntry entry; + entry.name = name; + entry.value = value; + mMetaData.push_back(entry); +} + +void XmlSerializer::ImportVertices(XmlNode &node, aiMesh *mesh) { + ai_assert(nullptr != mesh); + + std::vector vertices; + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::vertex) { + vertices.push_back(ReadVertex(currentNode)); + } + } + + mesh->mNumVertices = static_cast(vertices.size()); + mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + std::copy(vertices.begin(), vertices.end(), mesh->mVertices); +} + +void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) { + std::vector faces; + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::triangle) { + int pid = IdNotSet, p1 = IdNotSet; + bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid); + bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1); + + if (hasPid && hasP1) { + auto it = mResourcesDictionnary.find(pid); + if (it != mResourcesDictionnary.end()) { + if (it->second->getType() == ResourceType::RT_BaseMaterials) { + BaseMaterials *baseMaterials = static_cast(it->second); + mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1]; + } else if (it->second->getType() == ResourceType::RT_Texture2DGroup) { + if (mesh->mTextureCoords[0] == nullptr) { + Texture2DGroup *group = static_cast(it->second); + const std::string name = ai_to_string(group->mTexId); + for (size_t i = 0; i < mMaterials.size(); ++i) { + if (name == mMaterials[i]->GetName().C_Str()) { + mesh->mMaterialIndex = static_cast(i); + } + } + mesh->mTextureCoords[0] = new aiVector3D[group->mTex2dCoords.size()]; + for (unsigned int i = 0; i < group->mTex2dCoords.size(); ++i) { + mesh->mTextureCoords[0][i] = aiVector3D(group->mTex2dCoords[i].x, group->mTex2dCoords[i].y, 0); + } + } + } + } + } + + aiFace face = ReadTriangle(currentNode); + faces.push_back(face); + } + } + + mesh->mNumFaces = static_cast(faces.size()); + mesh->mFaces = new aiFace[mesh->mNumFaces]; + mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + std::copy(faces.begin(), faces.end(), mesh->mFaces); +} + +void XmlSerializer::ReadBaseMaterials(XmlNode &node) { + int id = IdNotSet; + if (getNodeAttribute(node, D3MF::XmlTag::id, id)) { + BaseMaterials *baseMaterials = new BaseMaterials(id); + + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::basematerials_base) { + baseMaterials->mMaterialIndex.push_back(static_cast(mMaterials.size())); + mMaterials.push_back(readMaterialDef(currentNode, id)); + } + } + + mResourcesDictionnary.insert(std::make_pair(id, baseMaterials)); + } +} + +void XmlSerializer::ReadEmbeddecTexture(XmlNode &node) { + if (node.empty()) { + return; + } + + std::string value; + EmbeddedTexture *tex2D = nullptr; + if (XmlParser::getStdStrAttribute(node, XmlTag::id, value)) { + tex2D = new EmbeddedTexture(atoi(value.c_str())); + } + if (nullptr == tex2D) { + return; + } + + if (XmlParser::getStdStrAttribute(node, XmlTag::path, value)) { + tex2D->mPath = value; + } + if (XmlParser::getStdStrAttribute(node, XmlTag::texture_content_type, value)) { + tex2D->mContentType = value; + } + if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestyleu, value)) { + tex2D->mTilestyleU = value; + } + if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestylev, value)) { + tex2D->mTilestyleV = value; + } + mEmbeddedTextures.emplace_back(tex2D); + StoreEmbeddedTexture(tex2D); +} + +void XmlSerializer::StoreEmbeddedTexture(EmbeddedTexture *tex) { + aiMaterial *mat = new aiMaterial; + aiString s; + s.Set(ai_to_string(tex->mId).c_str()); + mat->AddProperty(&s, AI_MATKEY_NAME); + const std::string name = "*" + tex->mPath; + s.Set(name); + mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0)); + + aiColor3D col; + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_DIFFUSE); + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_AMBIENT); + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_EMISSIVE); + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_SPECULAR); + mMaterials.emplace_back(mat); +} + +void XmlSerializer::ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup) { + if (node.empty() || nullptr == tex2DGroup) { + return; + } + + int id = IdNotSet; + if (XmlParser::getIntAttribute(node, "texid", id)) { + tex2DGroup->mTexId = id; + } + + double value = 0.0; + for (XmlNode currentNode : node.children()) { + const std::string currentName = currentNode.name(); + aiVector2D texCoord; + if (currentName == XmlTag::texture_2d_coord) { + XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_u, value); + texCoord.x = (ai_real)value; + XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_v, value); + texCoord.y = (ai_real)value; + tex2DGroup->mTex2dCoords.push_back(texCoord); + } + } +} + +void XmlSerializer::ReadTextureGroup(XmlNode &node) { + if (node.empty()) { + return; + } + + int id = IdNotSet; + if (!XmlParser::getIntAttribute(node, XmlTag::id, id)) { + return; + } + + Texture2DGroup *group = new Texture2DGroup(id); + ReadTextureCoords2D(node, group); + mResourcesDictionnary.insert(std::make_pair(id, group)); +} + +aiMaterial *XmlSerializer::readMaterialDef(XmlNode &node, unsigned int basematerialsId) { + aiMaterial *material = new aiMaterial(); + material->mNumProperties = 0; + std::string name; + bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name); + + std::string stdMaterialName; + const std::string strId(ai_to_string(basematerialsId)); + stdMaterialName += "id"; + stdMaterialName += strId; + stdMaterialName += "_"; + if (hasName) { + stdMaterialName += std::string(name); + } else { + stdMaterialName += "basemat_"; + stdMaterialName += ai_to_string(mMaterials.size()); + } + + aiString assimpMaterialName(stdMaterialName); + material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME); + + assignDiffuseColor(node, material); + + return material; +} + +void XmlSerializer::StoreMaterialsInScene(aiScene *scene) { + if (nullptr == scene || mMaterials.empty()) { + return; + } + + scene->mNumMaterials = static_cast(mMaterials.size()); + scene->mMaterials = new aiMaterial *[scene->mNumMaterials]; + for (size_t i = 0; i < mMaterials.size(); ++i) { + scene->mMaterials[i] = mMaterials[i]; + } +} + +} // namespace D3MF +} // namespace Assimp diff --git a/code/AssetLib/3MF/XmlSerializer.h b/code/AssetLib/3MF/XmlSerializer.h new file mode 100644 index 000000000..14da82e99 --- /dev/null +++ b/code/AssetLib/3MF/XmlSerializer.h @@ -0,0 +1,96 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2021, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include +#include +#include +#include + +struct aiNode; +struct aiMesh; +struct aiMaterial; + +namespace Assimp { +namespace D3MF { + +class Resource; +class D3MFOpcPackage; +class Object; +class Texture2DGroup; +class EmbeddedTexture; + +class XmlSerializer { +public: + XmlSerializer(XmlParser *xmlParser); + ~XmlSerializer(); + void ImportXml(aiScene *scene); + +private: + void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform); + void ReadObject(XmlNode &node); + aiMesh *ReadMesh(XmlNode &node); + void ReadMetadata(XmlNode &node); + void ImportVertices(XmlNode &node, aiMesh *mesh); + void ImportTriangles(XmlNode &node, aiMesh *mesh); + void ReadBaseMaterials(XmlNode &node); + void ReadEmbeddecTexture(XmlNode &node); + void StoreEmbeddedTexture(EmbeddedTexture *tex); + void ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup); + void ReadTextureGroup(XmlNode &node); + aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId); + void StoreMaterialsInScene(aiScene *scene); + +private: + struct MetaEntry { + std::string name; + std::string value; + }; + std::vector mMetaData; + std::vector mEmbeddedTextures; + std::vector mMaterials; + std::map mResourcesDictionnary; + unsigned int mMeshCount; + XmlParser *mXmlParser; +}; + +} // namespace D3MF +} // namespace Assimp diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp index 615882b6a..88a38b827 100644 --- a/code/AssetLib/AMF/AMFImporter.cpp +++ b/code/AssetLib/AMF/AMFImporter.cpp @@ -303,7 +303,7 @@ void AMFImporter::ParseNode_Root() { } XmlNode node = *root; mUnit = ai_tolower(std::string(node.attribute("unit").as_string())); - + mVersion = node.attribute("version").as_string(); // Read attributes for node . diff --git a/code/AssetLib/AMF/AMFImporter_Geometry.cpp b/code/AssetLib/AMF/AMFImporter_Geometry.cpp index 1fd2c49a4..1d2a1f5b4 100644 --- a/code/AssetLib/AMF/AMFImporter_Geometry.cpp +++ b/code/AssetLib/AMF/AMFImporter_Geometry.cpp @@ -75,7 +75,7 @@ void AMFImporter::ParseNode_Mesh(XmlNode &node) { found_volumes = true; } ParseHelper_Node_Exit(); - } + } if (!found_verts && !found_volumes) { mNodeElement_Cur->Child.push_back(ne); @@ -199,9 +199,9 @@ void AMFImporter::ParseNode_Volume(XmlNode &node) { // Read attributes for node . // and assign read data - + ((AMFVolume *)ne)->MaterialID = node.attribute("materialid").as_string(); - + ((AMFVolume *)ne)->Type = type; // Check for child nodes bool col_read = false; diff --git a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp index 43d0de52f..d56d6681d 100644 --- a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp +++ b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp @@ -69,7 +69,7 @@ aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /* } tcol = Color->Color; - + // Check if default color must be used if ((tcol.r == 0) && (tcol.g == 0) && (tcol.b == 0) && (tcol.a == 0)) { tcol.r = 0.5f; @@ -99,10 +99,10 @@ void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &nodeEleme } // all coordinates stored as child and we need to reserve space for future push_back's. - vertexCoordinateArray.reserve(vn->Child.size()); + vertexCoordinateArray.reserve(vn->Child.size()); // colors count equal vertices count. - pVertexColorArray.resize(vn->Child.size()); + pVertexColorArray.resize(vn->Child.size()); col_idx = 0; // Inside vertices collect all data and place to arrays diff --git a/code/AssetLib/ASE/ASEParser.h b/code/AssetLib/ASE/ASEParser.h index d04fc0662..f49cfc36f 100644 --- a/code/AssetLib/ASE/ASEParser.h +++ b/code/AssetLib/ASE/ASEParser.h @@ -95,8 +95,8 @@ struct Material : public D3DS::Material { Material(Material &&other) AI_NO_EXCEPT : D3DS::Material(std::move(other)), avSubMaterials(std::move(other.avSubMaterials)), - pcInstance(std::move(other.pcInstance)), - bNeed(std::move(other.bNeed)) { + pcInstance(other.pcInstance), + bNeed(other.bNeed) { other.pcInstance = nullptr; } @@ -108,8 +108,8 @@ struct Material : public D3DS::Material { //D3DS::Material::operator=(std::move(other)); avSubMaterials = std::move(other.avSubMaterials); - pcInstance = std::move(other.pcInstance); - bNeed = std::move(other.bNeed); + pcInstance = other.pcInstance; + bNeed = other.bNeed; other.pcInstance = nullptr; diff --git a/code/AssetLib/Assbin/AssbinFileWriter.cpp b/code/AssetLib/Assbin/AssbinFileWriter.cpp index 95379a303..2519b0b93 100644 --- a/code/AssetLib/Assbin/AssbinFileWriter.cpp +++ b/code/AssetLib/Assbin/AssbinFileWriter.cpp @@ -172,7 +172,7 @@ inline size_t Write(IOStream *stream, const aiQuaternion &v) { t += Write(stream, v.z); ai_assert(t == 16); - return 16; + return t; } // ----------------------------------------------------------------------------------- diff --git a/code/AssetLib/Assjson/json_exporter.cpp b/code/AssetLib/Assjson/json_exporter.cpp index b9099d392..7b2c8ec81 100644 --- a/code/AssetLib/Assjson/json_exporter.cpp +++ b/code/AssetLib/Assjson/json_exporter.cpp @@ -41,12 +41,17 @@ public: enum { Flag_DoNotIndent = 0x1, Flag_WriteSpecialFloats = 0x2, + Flag_SkipWhitespaces = 0x4 }; - + JSONWriter(Assimp::IOStream &out, unsigned int flags = 0u) : - out(out), first(), flags(flags) { + out(out), indent (""), newline("\n"), space(" "), buff (), first(false), flags(flags) { // make sure that all formatting happens using the standard, C locale and not the user's current locale buff.imbue(std::locale("C")); + if (flags & Flag_SkipWhitespaces) { + newline = ""; + space = ""; + } } ~JSONWriter() { @@ -70,7 +75,7 @@ public: void Key(const std::string &name) { AddIndentation(); Delimit(); - buff << '\"' + name + "\": "; + buff << '\"' + name + "\":" << space; } template @@ -78,12 +83,12 @@ public: AddIndentation(); Delimit(); - LiteralToString(buff, name) << '\n'; + LiteralToString(buff, name) << newline; } template void SimpleValue(const Literal &s) { - LiteralToString(buff, s) << '\n'; + LiteralToString(buff, s) << newline; } void SimpleValue(const void *buffer, size_t len) { @@ -102,7 +107,7 @@ public: } } - buff << '\"' << cur_out << "\"\n"; + buff << '\"' << cur_out << "\"" << newline; delete[] cur_out; } @@ -115,7 +120,7 @@ public: } } first = true; - buff << "{\n"; + buff << "{" << newline; PushIndent(); } @@ -123,7 +128,7 @@ public: PopIndent(); AddIndentation(); first = false; - buff << "}\n"; + buff << "}" << newline; } void StartArray(bool is_element = false) { @@ -135,19 +140,19 @@ public: } } first = true; - buff << "[\n"; + buff << "[" << newline; PushIndent(); } void EndArray() { PopIndent(); AddIndentation(); - buff << "]\n"; + buff << "]" << newline; first = false; } void AddIndentation() { - if (!(flags & Flag_DoNotIndent)) { + if (!(flags & Flag_DoNotIndent) && !(flags & Flag_SkipWhitespaces)) { buff << indent; } } @@ -156,7 +161,7 @@ public: if (!first) { buff << ','; } else { - buff << ' '; + buff << space; first = false; } } @@ -227,7 +232,9 @@ private: private: Assimp::IOStream &out; - std::string indent, newline; + std::string indent; + std::string newline; + std::string space; std::stringstream buff; bool first; @@ -765,7 +772,7 @@ void Write(JSONWriter &out, const aiScene &ai) { out.EndObj(); } -void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *scene, const Assimp::ExportProperties *) { +void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *scene, const Assimp::ExportProperties *pProperties) { std::unique_ptr str(io->Open(file, "wt")); if (!str) { throw DeadlyExportError("could not open output file"); @@ -782,7 +789,12 @@ void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *sc splitter.Execute(scenecopy_tmp); // XXX Flag_WriteSpecialFloats is turned on by default, right now we don't have a configuration interface for exporters - JSONWriter s(*str, JSONWriter::Flag_WriteSpecialFloats); + + unsigned int flags = JSONWriter::Flag_WriteSpecialFloats; + if (pProperties->GetPropertyBool("JSON_SKIP_WHITESPACES", false)) { + flags |= JSONWriter::Flag_SkipWhitespaces; + } + JSONWriter s(*str, flags); Write(s, *scenecopy_tmp); } catch (...) { diff --git a/code/AssetLib/Assjson/mesh_splitter.cpp b/code/AssetLib/Assjson/mesh_splitter.cpp index 24385f9a0..9301cc27e 100644 --- a/code/AssetLib/Assjson/mesh_splitter.cpp +++ b/code/AssetLib/Assjson/mesh_splitter.cpp @@ -110,7 +110,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormNumVertices / LIMIT) + 1; - // create a std::vector to remember which vertices have already + // create a std::vector to remember which vertices have already // been copied and to which position (i.e. output index) std::vector was_copied_to; was_copied_to.resize(in_mesh->mNumVertices,WAS_NOT_COPIED); @@ -125,7 +125,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormNumVertices = 0; out_mesh->mMaterialIndex = in_mesh->mMaterialIndex; @@ -179,7 +179,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormNumVertices + iNeed > out_vertex_index) { @@ -240,7 +240,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormTextureCoords[c][out_mesh->mNumVertices] = in_mesh->mTextureCoords[c][index]; } } - // vertex colors + // vertex colors for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { if (in_mesh->HasVertexColors( c)) { out_mesh->mColors[c][out_mesh->mNumVertices] = in_mesh->mColors[c][index]; diff --git a/code/AssetLib/Assjson/mesh_splitter.h b/code/AssetLib/Assjson/mesh_splitter.h index 326f73b41..3bb26118a 100644 --- a/code/AssetLib/Assjson/mesh_splitter.h +++ b/code/AssetLib/Assjson/mesh_splitter.h @@ -22,13 +22,13 @@ struct aiNode; // --------------------------------------------------------------------------- /** Splits meshes of unique vertices into meshes with no more vertices than - * a given, configurable threshold value. + * a given, configurable threshold value. */ -class MeshSplitter +class MeshSplitter { public: - + void SetLimit(unsigned int l) { LIMIT = l; } diff --git a/code/AssetLib/Assxml/AssxmlExporter.cpp b/code/AssetLib/Assxml/AssxmlExporter.cpp index 847ba0d7e..d244813b1 100644 --- a/code/AssetLib/Assxml/AssxmlExporter.cpp +++ b/code/AssetLib/Assxml/AssxmlExporter.cpp @@ -50,7 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -namespace Assimp { +namespace Assimp { void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) { diff --git a/code/AssetLib/B3D/B3DImporter.cpp b/code/AssetLib/B3D/B3DImporter.cpp index 3d9a5075a..11f7bcd14 100644 --- a/code/AssetLib/B3D/B3DImporter.cpp +++ b/code/AssetLib/B3D/B3DImporter.cpp @@ -143,7 +143,7 @@ AI_WONT_RETURN void B3DImporter::Oops() { } // ------------------------------------------------------------------------------------------------ -AI_WONT_RETURN void B3DImporter::Fail(string str) { +AI_WONT_RETURN void B3DImporter::Fail(const string &str) { #ifdef DEBUG_B3D ASSIMP_LOG_ERROR("Error in B3D file data: ", str); #endif diff --git a/code/AssetLib/B3D/B3DImporter.h b/code/AssetLib/B3D/B3DImporter.h index e2a75abdf..a7ed65c3b 100644 --- a/code/AssetLib/B3D/B3DImporter.h +++ b/code/AssetLib/B3D/B3DImporter.h @@ -96,7 +96,7 @@ private: }; AI_WONT_RETURN void Oops() AI_WONT_RETURN_SUFFIX; - AI_WONT_RETURN void Fail( std::string str ) AI_WONT_RETURN_SUFFIX; + AI_WONT_RETURN void Fail(const std::string &str) AI_WONT_RETURN_SUFFIX; void ReadTEXS(); void ReadBRUS(); diff --git a/code/AssetLib/Blender/BlenderLoader.cpp b/code/AssetLib/Blender/BlenderLoader.cpp index 7cf4e070e..42a7a1723 100644 --- a/code/AssetLib/Blender/BlenderLoader.cpp +++ b/code/AssetLib/Blender/BlenderLoader.cpp @@ -679,7 +679,7 @@ void BlenderImporter::BuildMaterials(ConversionData &conv_data) { BuildDefaultMaterial(conv_data); - for (std::shared_ptr mat : conv_data.materials_raw) { + for (const std::shared_ptr &mat : conv_data.materials_raw) { // reset per material global counters for (size_t i = 0; i < sizeof(conv_data.next_texture) / sizeof(conv_data.next_texture[0]); ++i) { diff --git a/code/AssetLib/Blender/BlenderModifier.h b/code/AssetLib/Blender/BlenderModifier.h index daf120087..d2fea43c1 100644 --- a/code/AssetLib/Blender/BlenderModifier.h +++ b/code/AssetLib/Blender/BlenderModifier.h @@ -52,9 +52,9 @@ namespace Assimp { namespace Blender { // ------------------------------------------------------------------------------------------- -/** +/** * Dummy base class for all blender modifiers. Modifiers are reused between imports, so - * they should be stateless and not try to cache model data. + * they should be stateless and not try to cache model data. */ // ------------------------------------------------------------------------------------------- class BlenderModifier { @@ -67,7 +67,7 @@ public: } // -------------------- - /** + /** * Check if *this* modifier is active, given a ModifierData& block. */ virtual bool IsActive( const ModifierData& /*modin*/) { @@ -75,10 +75,10 @@ public: } // -------------------- - /** + /** * Apply the modifier to a given output node. The original data used * to construct the node is given as well. Not called unless IsActive() - * was called and gave positive response. + * was called and gave positive response. */ virtual void DoIt(aiNode& /*out*/, ConversionData& /*conv_data*/, @@ -92,8 +92,8 @@ public: }; // ------------------------------------------------------------------------------------------- -/** - * Manage all known modifiers and instance and apply them if necessary +/** + * Manage all known modifiers and instance and apply them if necessary */ // ------------------------------------------------------------------------------------------- class BlenderModifierShowcase { @@ -113,8 +113,8 @@ private: // MODIFIERS ///////////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------------------------- -/** - * Mirror modifier. Status: implemented. +/** + * Mirror modifier. Status: implemented. */ // ------------------------------------------------------------------------------------------- class BlenderModifier_Mirror : public BlenderModifier { diff --git a/code/AssetLib/C4D/C4DImporter.cpp b/code/AssetLib/C4D/C4DImporter.cpp index 434d1429e..14a94958b 100644 --- a/code/AssetLib/C4D/C4DImporter.cpp +++ b/code/AssetLib/C4D/C4DImporter.cpp @@ -146,8 +146,14 @@ void C4DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS ThrowException("failed to read document " + pFile); } + // Generate the root-node pScene->mRootNode = new aiNode(""); + // convert left-handed to right-handed + pScene->mRootNode->mTransformation.a1 = 0.01f; + pScene->mRootNode->mTransformation.b2 = 0.01f; + pScene->mRootNode->mTransformation.c3 = -0.01f; + // first convert all materials ReadMaterials(doc->GetFirstMaterial()); diff --git a/code/AssetLib/COB/COBLoader.cpp b/code/AssetLib/COB/COBLoader.cpp index 94327c683..3bef260e5 100644 --- a/code/AssetLib/COB/COBLoader.cpp +++ b/code/AssetLib/COB/COBLoader.cpp @@ -230,7 +230,7 @@ void COBImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy } // ------------------------------------------------------------------------------------------------ -void ConvertTexture(std::shared_ptr tex, aiMaterial *out, aiTextureType type) { +void ConvertTexture(const std::shared_ptr &tex, aiMaterial *out, aiTextureType type) { const aiString path(tex->path); out->AddProperty(&path, AI_MATKEY_TEXTURE(type, 0)); out->AddProperty(&tex->transform, 1, AI_MATKEY_UVTRANSFORM(type, 0)); @@ -884,7 +884,7 @@ void COBImporter::ReadBinaryFile(Scene &out, StreamReaderLE *reader) { std::string type; type += reader->GetI1(); type += reader->GetI1(); - type += reader->GetI1(); + type += reader->GetI1(); type += reader->GetI1(); ChunkInfo nfo; diff --git a/code/AssetLib/COB/COBLoader.h b/code/AssetLib/COB/COBLoader.h index 2317d094e..e4d41e500 100644 --- a/code/AssetLib/COB/COBLoader.h +++ b/code/AssetLib/COB/COBLoader.h @@ -77,7 +77,7 @@ class COBImporter : public BaseImporter public: COBImporter(); ~COBImporter(); - + // -------------------- bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index 492e6971f..f7b5f2278 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -135,14 +135,15 @@ bool ColladaLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool // XML - too generic, we need to open the file and search for typical keywords if (extension == "xml" || !extension.length() || checkSig) { - /* If CanRead() is called in order to check whether we - * support a specific file extension in general pIOHandler - * might be nullptr and it's our duty to return true here. - */ - if (!pIOHandler) { + // If CanRead() is called in order to check whether we + // support a specific file extension in general pIOHandler + // might be nullptr and it's our duty to return true here. + if (nullptr == pIOHandler) { return true; } - static const char *tokens[] = { "mNumMeshes = static_cast(newMeshRefs.size()); - if (newMeshRefs.size()) { + if (!newMeshRefs.empty()) { struct UIntTypeConverter { unsigned int operator()(const size_t &v) const { return static_cast(v); @@ -619,6 +620,10 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc dstMesh->mName = pSrcMesh->mId; } + if (pSrcMesh->mPositions.empty()) { + return dstMesh.release(); + } + // count the vertices addressed by its faces const size_t numVertices = std::accumulate(pSrcMesh->mFaceSize.begin() + pStartFace, pSrcMesh->mFaceSize.begin() + pStartFace + pSubMesh.mNumFaces, size_t(0)); @@ -1540,7 +1545,7 @@ void ColladaLoader::AddTexture(aiMaterial &mat, map = -1; for (std::string::const_iterator it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) { if (IsNumeric(*it)) { - map = strtoul10(&(*it)); + map = strtoul10(&(*it)); break; } } @@ -1671,7 +1676,7 @@ void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/) const Material &material = matIt->second; // a material is only a reference to an effect ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find(material.mEffect); - if (effIt == pParser.mEffectLibrary.end()) + if (effIt == pParser.mEffectLibrary.end()) continue; Effect &effect = effIt->second; @@ -1682,7 +1687,7 @@ void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/) // store the material mMaterialIndexByName[matIt->first] = newMats.size(); - newMats.push_back(std::pair(&effect, mat)); + newMats.emplace_back(&effect, mat); } // ScenePreprocessor generates a default material automatically if none is there. // All further code here in this loader works well without a valid material so diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 5dbf0a567..94b9b18ed 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -170,10 +170,10 @@ ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) : // ------------------------------------------------------------------------------------------------ // Destructor, private as well ColladaParser::~ColladaParser() { - for (auto & it : mNodeLibrary) { + for (auto &it : mNodeLibrary) { delete it.second; } - for (auto & it : mMeshLibrary) { + for (auto &it : mMeshLibrary) { delete it.second; } } @@ -231,11 +231,7 @@ void ColladaParser::UriDecodePath(aiString &ss) { // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes... // I need to filter it without destroying linux paths starting with "/somewhere" -#if defined(_MSC_VER) if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') { -#else - if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') { -#endif --ss.length; ::memmove(ss.data, ss.data + 1, ss.length); ss.data[ss.length] = 0; @@ -396,7 +392,7 @@ void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) { std::string animName; if (!XmlParser::getStdStrAttribute(node, "name", animName)) { - if (!XmlParser::getStdStrAttribute( node, "id", animName )) { + if (!XmlParser::getStdStrAttribute(node, "id", animName)) { animName = std::string("animation_") + ai_to_string(mAnimationClipLibrary.size()); } } @@ -420,7 +416,7 @@ void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) { void ColladaParser::PostProcessControllers() { std::string meshId; - for (auto & it : mControllerLibrary) { + for (auto &it : mControllerLibrary) { meshId = it.second.mMeshId; if (meshId.empty()) { continue; @@ -445,7 +441,7 @@ void ColladaParser::PostProcessRootAnimations() { } Animation temp; - for (auto & it : mAnimationClipLibrary) { + for (auto &it : mAnimationClipLibrary) { std::string clipName = it.first; Animation *clip = new Animation(); @@ -453,7 +449,7 @@ void ColladaParser::PostProcessRootAnimations() { temp.mSubAnims.push_back(clip); - for (std::string animationID : it.second) { + for (const std::string &animationID : it.second) { AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID); if (animation != mAnimationLibrary.end()) { @@ -529,7 +525,7 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) { // have it read into a channel ChannelMap::iterator newChannel = channels.insert(std::make_pair(id, AnimationChannel())).first; ReadAnimationSampler(currentNode, newChannel->second); - } + } } else if (currentName == "channel") { std::string source_name, target; XmlParser::getStdStrAttribute(currentNode, "source", source_name); @@ -552,7 +548,7 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) { pParent->mSubAnims.push_back(anim); } - for (const auto & channel : channels) { + for (const auto &channel : channels) { anim->mChannels.push_back(channel.second); } @@ -626,8 +622,6 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); XmlNode currentNode; while (xmlIt.getNext(currentNode)) { - - //for (XmlNode ¤tNode : node.children()) { const std::string ¤tName = currentNode.name(); if (currentName == "morph") { controller.mType = Morph; @@ -644,7 +638,7 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle } else if (currentName == "skin") { std::string id; if (XmlParser::getStdStrAttribute(currentNode, "source", id)) { - controller.mMeshId = id.substr(1, id.size()-1); + controller.mMeshId = id.substr(1, id.size() - 1); } } else if (currentName == "bind_shape_matrix") { std::string v; @@ -698,7 +692,7 @@ void ColladaParser::ReadControllerJoints(XmlNode &node, Collada::Controller &pCo } else if (strcmp(attrSemantic, "INV_BIND_MATRIX") == 0) { pController.mJointOffsetMatrixSource = attrSource; } else { - throw DeadlyImportError("Unknown semantic \"" , attrSemantic , "\" in data element"); + throw DeadlyImportError("Unknown semantic \"", attrSemantic, "\" in data element"); } } } @@ -708,7 +702,7 @@ void ColladaParser::ReadControllerJoints(XmlNode &node, Collada::Controller &pCo // Reads the joint weights for the given controller void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pController) { // Read vertex count from attributes and resize the array accordingly - int vertexCount=0; + int vertexCount = 0; XmlParser::getIntAttribute(node, "count", vertexCount); pController.mWeightCounts.resize(vertexCount); @@ -723,7 +717,7 @@ void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pC // local URLS always start with a '#'. We don't support global URLs if (attrSource[0] != '#') { - throw DeadlyImportError( "Unsupported URL format in \"", attrSource, "\" in source attribute of data element"); + throw DeadlyImportError("Unsupported URL format in \"", attrSource, "\" in source attribute of data element"); } channel.mAccessor = attrSource + 1; @@ -777,7 +771,7 @@ void ColladaParser::ReadImageLibrary(XmlNode &node) { const std::string ¤tName = currentNode.name(); if (currentName == "image") { std::string id; - if (XmlParser::getStdStrAttribute( currentNode, "id", id )) { + if (XmlParser::getStdStrAttribute(currentNode, "id", id)) { mImageLibrary[id] = Image(); // read on from there ReadImage(currentNode, mImageLibrary[id]); @@ -907,7 +901,7 @@ void ColladaParser::ReadCameraLibrary(XmlNode &node) { if (!name.empty()) { cam.mName = name; } - ReadCamera(currentNode, cam); + ReadCamera(currentNode, cam); } } } @@ -920,7 +914,7 @@ void ColladaParser::ReadMaterial(XmlNode &node, Collada::Material &pMaterial) { if (currentName == "instance_effect") { std::string url; readUrlAttribute(currentNode, url); - pMaterial.mEffect = url.c_str(); + pMaterial.mEffect = url; } } } @@ -1361,8 +1355,8 @@ void ColladaParser::ReadMesh(XmlNode &node, Mesh &pMesh) { } else if (currentName == "vertices") { ReadVertexData(currentNode, pMesh); } else if (currentName == "triangles" || currentName == "lines" || currentName == "linestrips" || - currentName == "polygons" || currentName == "polylist" || currentName == "trifans" || - currentName == "tristrips") { + currentName == "polygons" || currentName == "polylist" || currentName == "trifans" || + currentName == "tristrips") { ReadIndexData(currentNode, pMesh); } } @@ -1439,9 +1433,8 @@ void ColladaParser::ReadDataArray(XmlNode &node) { throw DeadlyImportError("Expected more values while reading float_array contents."); } - ai_real value; // read a number - //SkipSpacesAndLineEnd(&content); + ai_real value; content = fast_atoreal_move(content, value); data.mValues.push_back(value); // skip whitespace after it @@ -1489,11 +1482,10 @@ void ColladaParser::ReadAccessor(XmlNode &node, const std::string &pID) { std::string name; if (XmlParser::hasAttribute(currentNode, "name")) { XmlParser::getStdStrAttribute(currentNode, "name", name); - //name = mReader->getAttributeValue(attrName); // analyse for common type components and store it's sub-offset in the corresponding field - /* Cartesian coordinates */ + // Cartesian coordinates if (name == "X") acc.mSubOffset[0] = acc.mParams.size(); else if (name == "Y") @@ -1674,12 +1666,9 @@ void ColladaParser::ReadInputChannel(XmlNode &node, std::vector &p // read set if texture coordinates if (channel.mType == IT_Texcoord || channel.mType == IT_Color) { - int attrSet = -1; - if (XmlParser::hasAttribute(node, "set")) { - XmlParser::getIntAttribute(node, "set", attrSet); - } - - channel.mIndex = attrSet; + unsigned int attrSet = 0; + if (XmlParser::getUIntAttribute(node, "set", attrSet)) + channel.mIndex = attrSet; } // store, if valid type @@ -1704,20 +1693,20 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector 0) { + if (pNumPrimitives > 0) { std::string v; XmlParser::getValueAsString(node, v); const char *content = v.c_str(); @@ -1925,87 +1914,87 @@ void ColladaParser::ExtractDataObjectFromChannel(const InputChannel &pInput, siz // now we reinterpret it according to the type we're reading here switch (pInput.mType) { - case IT_Position: // ignore all position streams except 0 - there can be only one position - if (pInput.mIndex == 0) { - pMesh.mPositions.push_back(aiVector3D(obj[0], obj[1], obj[2])); - } else { - ASSIMP_LOG_ERROR("Collada: just one vertex position stream supported"); - } - break; - case IT_Normal: + case IT_Position: // ignore all position streams except 0 - there can be only one position + if (pInput.mIndex == 0) { + pMesh.mPositions.push_back(aiVector3D(obj[0], obj[1], obj[2])); + } else { + ASSIMP_LOG_ERROR("Collada: just one vertex position stream supported"); + } + break; + case IT_Normal: + // pad to current vertex count if necessary + if (pMesh.mNormals.size() < pMesh.mPositions.size() - 1) + pMesh.mNormals.insert(pMesh.mNormals.end(), pMesh.mPositions.size() - pMesh.mNormals.size() - 1, aiVector3D(0, 1, 0)); + + // ignore all normal streams except 0 - there can be only one normal + if (pInput.mIndex == 0) { + pMesh.mNormals.push_back(aiVector3D(obj[0], obj[1], obj[2])); + } else { + ASSIMP_LOG_ERROR("Collada: just one vertex normal stream supported"); + } + break; + case IT_Tangent: + // pad to current vertex count if necessary + if (pMesh.mTangents.size() < pMesh.mPositions.size() - 1) + pMesh.mTangents.insert(pMesh.mTangents.end(), pMesh.mPositions.size() - pMesh.mTangents.size() - 1, aiVector3D(1, 0, 0)); + + // ignore all tangent streams except 0 - there can be only one tangent + if (pInput.mIndex == 0) { + pMesh.mTangents.push_back(aiVector3D(obj[0], obj[1], obj[2])); + } else { + ASSIMP_LOG_ERROR("Collada: just one vertex tangent stream supported"); + } + break; + case IT_Bitangent: + // pad to current vertex count if necessary + if (pMesh.mBitangents.size() < pMesh.mPositions.size() - 1) { + pMesh.mBitangents.insert(pMesh.mBitangents.end(), pMesh.mPositions.size() - pMesh.mBitangents.size() - 1, aiVector3D(0, 0, 1)); + } + + // ignore all bitangent streams except 0 - there can be only one bitangent + if (pInput.mIndex == 0) { + pMesh.mBitangents.push_back(aiVector3D(obj[0], obj[1], obj[2])); + } else { + ASSIMP_LOG_ERROR("Collada: just one vertex bitangent stream supported"); + } + break; + case IT_Texcoord: + // up to 4 texture coord sets are fine, ignore the others + if (pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS) { // pad to current vertex count if necessary - if (pMesh.mNormals.size() < pMesh.mPositions.size() - 1) - pMesh.mNormals.insert(pMesh.mNormals.end(), pMesh.mPositions.size() - pMesh.mNormals.size() - 1, aiVector3D(0, 1, 0)); + if (pMesh.mTexCoords[pInput.mIndex].size() < pMesh.mPositions.size() - 1) + pMesh.mTexCoords[pInput.mIndex].insert(pMesh.mTexCoords[pInput.mIndex].end(), + pMesh.mPositions.size() - pMesh.mTexCoords[pInput.mIndex].size() - 1, aiVector3D(0, 0, 0)); - // ignore all normal streams except 0 - there can be only one normal - if (pInput.mIndex == 0) { - pMesh.mNormals.push_back(aiVector3D(obj[0], obj[1], obj[2])); - } else { - ASSIMP_LOG_ERROR("Collada: just one vertex normal stream supported"); + pMesh.mTexCoords[pInput.mIndex].push_back(aiVector3D(obj[0], obj[1], obj[2])); + if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) { + pMesh.mNumUVComponents[pInput.mIndex] = 3; } - break; - case IT_Tangent: + } else { + ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping."); + } + break; + case IT_Color: + // up to 4 color sets are fine, ignore the others + if (pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS) { // pad to current vertex count if necessary - if (pMesh.mTangents.size() < pMesh.mPositions.size() - 1) - pMesh.mTangents.insert(pMesh.mTangents.end(), pMesh.mPositions.size() - pMesh.mTangents.size() - 1, aiVector3D(1, 0, 0)); + if (pMesh.mColors[pInput.mIndex].size() < pMesh.mPositions.size() - 1) + pMesh.mColors[pInput.mIndex].insert(pMesh.mColors[pInput.mIndex].end(), + pMesh.mPositions.size() - pMesh.mColors[pInput.mIndex].size() - 1, aiColor4D(0, 0, 0, 1)); - // ignore all tangent streams except 0 - there can be only one tangent - if (pInput.mIndex == 0) { - pMesh.mTangents.push_back(aiVector3D(obj[0], obj[1], obj[2])); - } else { - ASSIMP_LOG_ERROR("Collada: just one vertex tangent stream supported"); - } - break; - case IT_Bitangent: - // pad to current vertex count if necessary - if (pMesh.mBitangents.size() < pMesh.mPositions.size() - 1) { - pMesh.mBitangents.insert(pMesh.mBitangents.end(), pMesh.mPositions.size() - pMesh.mBitangents.size() - 1, aiVector3D(0, 0, 1)); + aiColor4D result(0, 0, 0, 1); + for (size_t i = 0; i < pInput.mResolved->mSize; ++i) { + result[static_cast(i)] = obj[pInput.mResolved->mSubOffset[i]]; } + pMesh.mColors[pInput.mIndex].push_back(result); + } else { + ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping."); + } - // ignore all bitangent streams except 0 - there can be only one bitangent - if (pInput.mIndex == 0) { - pMesh.mBitangents.push_back(aiVector3D(obj[0], obj[1], obj[2])); - } else { - ASSIMP_LOG_ERROR("Collada: just one vertex bitangent stream supported"); - } - break; - case IT_Texcoord: - // up to 4 texture coord sets are fine, ignore the others - if (pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS) { - // pad to current vertex count if necessary - if (pMesh.mTexCoords[pInput.mIndex].size() < pMesh.mPositions.size() - 1) - pMesh.mTexCoords[pInput.mIndex].insert(pMesh.mTexCoords[pInput.mIndex].end(), - pMesh.mPositions.size() - pMesh.mTexCoords[pInput.mIndex].size() - 1, aiVector3D(0, 0, 0)); - - pMesh.mTexCoords[pInput.mIndex].push_back(aiVector3D(obj[0], obj[1], obj[2])); - if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) { - pMesh.mNumUVComponents[pInput.mIndex] = 3; - } - } else { - ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping."); - } - break; - case IT_Color: - // up to 4 color sets are fine, ignore the others - if (pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS) { - // pad to current vertex count if necessary - if (pMesh.mColors[pInput.mIndex].size() < pMesh.mPositions.size() - 1) - pMesh.mColors[pInput.mIndex].insert(pMesh.mColors[pInput.mIndex].end(), - pMesh.mPositions.size() - pMesh.mColors[pInput.mIndex].size() - 1, aiColor4D(0, 0, 0, 1)); - - aiColor4D result(0, 0, 0, 1); - for (size_t i = 0; i < pInput.mResolved->mSize; ++i) { - result[static_cast(i)] = obj[pInput.mResolved->mSubOffset[i]]; - } - pMesh.mColors[pInput.mIndex].push_back(result); - } else { - ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping."); - } - - break; - default: - // IT_Invalid and IT_Vertex - ai_assert(false && "shouldn't ever get here"); + break; + default: + // IT_Invalid and IT_Vertex + ai_assert(false && "shouldn't ever get here"); } } @@ -2170,10 +2159,10 @@ void ColladaParser::ReadNodeTransformation(XmlNode &node, Node *pNode, Transform // read as many parameters and store in the transformation for (unsigned int a = 0; a < sNumParameters[pType]; a++) { + // skip whitespace before the number + SkipSpacesAndLineEnd(&content); // read a number content = fast_atoreal_move(content, tf.f[a]); - // skip whitespace after it - SkipSpacesAndLineEnd(&content); } // place the transformation at the queue of the node @@ -2215,8 +2204,8 @@ void ColladaParser::ReadMaterialVertexInputBinding(XmlNode &node, Collada::Seman void ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive) { // Attempt to load any undefined Collada::Image in ImageLibrary - for (ImageLibrary::iterator it = mImageLibrary.begin(); it != mImageLibrary.end(); ++it) { - Collada::Image &image = (*it).second; + for (auto & it : mImageLibrary) { + Collada::Image &image = it.second; if (image.mImageData.empty()) { std::unique_ptr image_file(zip_archive.Open(image.mFileName.c_str())); diff --git a/code/AssetLib/DXF/DXFLoader.cpp b/code/AssetLib/DXF/DXFLoader.cpp index 5d32ed121..2e38ed976 100644 --- a/code/AssetLib/DXF/DXFLoader.cpp +++ b/code/AssetLib/DXF/DXFLoader.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -549,7 +547,7 @@ void DXFImporter::ParseEntities(DXF::LineReader& reader, DXF::FileData& output) ++reader; } - ASSIMP_LOG_VERBOSE_DEBUG( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), + ASSIMP_LOG_VERBOSE_DEBUG( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), " inserted blocks in ENTITIES" ); } diff --git a/code/AssetLib/DXF/DXFLoader.h b/code/AssetLib/DXF/DXFLoader.h index 5319d2528..6649deb34 100644 --- a/code/AssetLib/DXF/DXFLoader.h +++ b/code/AssetLib/DXF/DXFLoader.h @@ -63,7 +63,7 @@ namespace DXF { } // --------------------------------------------------------------------------- -/** +/** * @brief DXF importer implementation. */ class DXFImporter : public BaseImporter { diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index a564b3e9b..fa7ee3986 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -862,7 +862,7 @@ bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std output_nodes.push_back(std::move(nd)); return false; } - + void FBXConverter::SetupNodeMetadata(const Model &model, aiNode &nd) { const PropertyTable &props = model.Props(); DirectPropertyMap unparsedProperties = props.GetUnparsedProperties(); @@ -917,8 +917,10 @@ void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root } else if (line) { const std::vector &indices = ConvertLine(*line, root_node); std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); - } else { + } else if (geo) { FBXImporter::LogWarn("ignoring unrecognized geometry: ", geo->Name()); + } else { + FBXImporter::LogWarn("skipping null geometry"); } } @@ -1766,6 +1768,7 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const TextureMap // XXX handle all kinds of UV transformations uvTrafo.mScaling = tex->UVScaling(); uvTrafo.mTranslation = tex->UVTranslation(); + uvTrafo.mRotation = tex->UVRotation(); out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, 0); const PropertyTable &props = tex->Props(); @@ -1885,6 +1888,7 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const LayeredTex // XXX handle all kinds of UV transformations uvTrafo.mScaling = tex->UVScaling(); uvTrafo.mTranslation = tex->UVTranslation(); + uvTrafo.mRotation = tex->UVRotation(); out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, texIndex); const PropertyTable &props = tex->Props(); @@ -2129,7 +2133,7 @@ void FBXConverter::SetShadingPropertiesCommon(aiMaterial *out_mat, const Propert if (ok) { out_mat->AddProperty(&Emissive, 1, AI_MATKEY_COLOR_EMISSIVE); } else { - const aiColor3D &emissiveColor = GetColorPropertyFromMaterial(props, "Maya|emissive", ok); + const aiColor3D &emissiveColor = GetColorProperty(props, "Maya|emissive", ok); if (ok) { out_mat->AddProperty(&emissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE); } @@ -2216,7 +2220,7 @@ void FBXConverter::SetShadingPropertiesCommon(aiMaterial *out_mat, const Propert } // PBR material information - const aiColor3D &baseColor = GetColorPropertyFromMaterial(props, "Maya|base_color", ok); + const aiColor3D &baseColor = GetColorProperty(props, "Maya|base_color", ok); if (ok) { out_mat->AddProperty(&baseColor, 1, AI_MATKEY_BASE_COLOR); } @@ -2324,6 +2328,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial *out_mat, const PropertyTa // XXX handle all kinds of UV transformations uvTrafo.mScaling = tex->UVScaling(); uvTrafo.mTranslation = tex->UVTranslation(); + uvTrafo.mRotation = tex->UVRotation(); out_mat->AddProperty(&uvTrafo, 1, (name + "|uvtrafo").c_str(), aiTextureType_UNKNOWN, 0); int uvIndex = 0; @@ -2599,7 +2604,7 @@ void FBXConverter::ConvertAnimationStack(const AnimationStack &st) { anim->mMorphMeshChannels = new aiMeshMorphAnim *[numMorphMeshChannels]; anim->mNumMorphMeshChannels = numMorphMeshChannels; unsigned int i = 0; - for (auto morphAnimIt : morphAnimDatas) { + for (const auto &morphAnimIt : morphAnimDatas) { morphAnimData *animData = morphAnimIt.second; unsigned int numKeys = static_cast(animData->size()); aiMeshMorphAnim *meshMorphAnim = new aiMeshMorphAnim(); @@ -3569,7 +3574,7 @@ void FBXConverter::ConvertOrphanedEmbeddedTextures() { if (texture->Media() && texture->Media()->ContentLength() > 0) { realTexture = texture; } - } + } } } catch (...) { // do nothing diff --git a/code/AssetLib/FBX/FBXConverter.h b/code/AssetLib/FBX/FBXConverter.h index d208ab429..b9a494695 100644 --- a/code/AssetLib/FBX/FBXConverter.h +++ b/code/AssetLib/FBX/FBXConverter.h @@ -76,7 +76,7 @@ namespace Assimp { namespace FBX { class Document; -/** +/** * Convert a FBX #Document to #aiScene * @param out Empty scene to be populated * @param doc Parsed FBX document @@ -182,7 +182,7 @@ private: // ------------------------------------------------------------------------------------------------ void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform); - + // ------------------------------------------------------------------------------------------------ // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed std::vector diff --git a/code/AssetLib/FBX/FBXDocument.cpp b/code/AssetLib/FBX/FBXDocument.cpp index 7adaadf6c..8e0439e18 100644 --- a/code/AssetLib/FBX/FBXDocument.cpp +++ b/code/AssetLib/FBX/FBXDocument.cpp @@ -57,9 +57,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -#include #include #include +#include +#include namespace Assimp { namespace FBX { @@ -248,10 +249,8 @@ Object::~Object() } // ------------------------------------------------------------------------------------------------ -FileGlobalSettings::FileGlobalSettings(const Document& doc, std::shared_ptr props) -: props(props) -, doc(doc) -{ +FileGlobalSettings::FileGlobalSettings(const Document &doc, std::shared_ptr props) : + props(std::move(props)), doc(doc) { // empty } @@ -636,7 +635,7 @@ std::vector Document::GetConnectionsBySourceSequenced(uint64_ } // ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsBySourceSequenced(uint64_t source, +std::vector Document::GetConnectionsBySourceSequenced(uint64_t source, const char* const* classnames, size_t count) const { return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count); diff --git a/code/AssetLib/FBX/FBXDocument.h b/code/AssetLib/FBX/FBXDocument.h index 69cda1c1a..1ee526368 100644 --- a/code/AssetLib/FBX/FBXDocument.h +++ b/code/AssetLib/FBX/FBXDocument.h @@ -500,6 +500,10 @@ public: return uvScaling; } + const ai_real &UVRotation() const { + return uvRotation; + } + const PropertyTable& Props() const { ai_assert(props.get()); return *props.get(); @@ -517,6 +521,7 @@ public: private: aiVector2D uvTrans; aiVector2D uvScaling; + ai_real uvRotation; std::string type; std::string relativeFileName; diff --git a/code/AssetLib/FBX/FBXExportNode.cpp b/code/AssetLib/FBX/FBXExportNode.cpp index 91e421420..817308ec8 100644 --- a/code/AssetLib/FBX/FBXExportNode.cpp +++ b/code/AssetLib/FBX/FBXExportNode.cpp @@ -144,9 +144,8 @@ void FBX::Node::AddP70time( // public member functions for writing nodes to stream void FBX::Node::Dump( - std::shared_ptr outfile, - bool binary, int indent -) { + const std::shared_ptr &outfile, + bool binary, int indent) { if (binary) { Assimp::StreamWriterLE outstream(outfile); DumpBinary(outstream); diff --git a/code/AssetLib/FBX/FBXExportNode.h b/code/AssetLib/FBX/FBXExportNode.h index c5f29ef0f..3aca98939 100644 --- a/code/AssetLib/FBX/FBXExportNode.h +++ b/code/AssetLib/FBX/FBXExportNode.h @@ -60,7 +60,7 @@ namespace FBX { } class FBX::Node { -public: +public: // TODO: accessors std::string name; // node name std::vector properties; // node properties @@ -157,9 +157,8 @@ public: // member functions for writing data to a file or stream // write the full node to the given file or stream void Dump( - std::shared_ptr outfile, - bool binary, int indent - ); + const std::shared_ptr &outfile, + bool binary, int indent); void Dump(Assimp::StreamWriterLE &s, bool binary, int indent); // these other functions are for writing data piece by piece. diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index e519f7e77..84a77e18d 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -498,7 +498,7 @@ void FBXExporter::WriteDocuments () if (!binary) { WriteAsciiSectionHeader("Documents Description"); } - + // not sure what the use of multiple documents would be, // or whether any end-application supports it FBX::Node docs("Documents"); @@ -541,10 +541,17 @@ void FBXExporter::WriteReferences () // (before any actual data is written) // --------------------------------------------------------------- -size_t count_nodes(const aiNode* n) { - size_t count = 1; +size_t count_nodes(const aiNode* n, const aiNode* root) { + size_t count; + if (n == root) { + count = n->mNumMeshes; // (not counting root node) + } else if (n->mNumMeshes > 1) { + count = n->mNumMeshes + 1; + } else { + count = 1; + } for (size_t i = 0; i < n->mNumChildren; ++i) { - count += count_nodes(n->mChildren[i]); + count += count_nodes(n->mChildren[i], root); } return count; } @@ -714,7 +721,7 @@ void FBXExporter::WriteDefinitions () // Model / FbxNode // <~~ node hierarchy - count = int32_t(count_nodes(mScene->mRootNode)) - 1; // (not counting root node) + count = int32_t(count_nodes(mScene->mRootNode, mScene->mRootNode)); if (count) { n = FBX::Node("ObjectType", "Model"); n.AddChild("Count", count); @@ -1251,7 +1258,7 @@ void FBXExporter::WriteObjects () indent = 2; vertexcolors.End(outstream, binary, indent, true); } - + // uvs, if any for (size_t uvi = 0; uvi < m->GetNumUVChannels(); ++uvi) { if (m->mNumUVComponents[uvi] > 2) { @@ -1681,6 +1688,10 @@ void FBXExporter::WriteObjects () // link the image data to the texture connections.emplace_back("C", "OO", image_uid, texture_uid); + aiUVTransform trafo; + unsigned int max = sizeof(aiUVTransform); + aiGetMaterialFloatArray(mat, AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE, 0), (ai_real *)&trafo, &max); + // now write the actual texture node FBX::Node tnode("Texture"); // TODO: some way to determine texture name? @@ -1691,6 +1702,9 @@ void FBXExporter::WriteObjects () tnode.AddChild("Version", int32_t(202)); tnode.AddChild("TextureName", texture_name); FBX::Node p("Properties70"); + p.AddP70vectorA("Translation", trafo.mTranslation[0], trafo.mTranslation[1], 0.0); + p.AddP70vectorA("Rotation", 0, 0, trafo.mRotation); + p.AddP70vectorA("Scaling", trafo.mScaling[0], trafo.mScaling[1], 0.0); p.AddP70enum("CurrentTextureBlendMode", 0); // TODO: verify //p.AddP70string("UVSet", ""); // TODO: how should this work? p.AddP70bool("UseMaterial", 1); @@ -1737,7 +1751,7 @@ void FBXExporter::WriteObjects () bsnode.AddProperty(blendshape_uid); bsnode.AddProperty(blendshape_name + FBX::SEPARATOR + "Blendshape"); bsnode.AddProperty("Shape"); - bsnode.AddChild("Version", int32_t(100)); + bsnode.AddChild("Version", int32_t(100)); bsnode.Begin(outstream, binary, indent); bsnode.DumpProperties(outstream, binary, indent); bsnode.EndProperties(outstream, binary, indent); @@ -1863,7 +1877,7 @@ void FBXExporter::WriteObjects () // at the same time we can build a list of all the skeleton nodes, // which will be used later to mark them as type "limbNode". std::unordered_set limbnodes; - + //actual bone nodes in fbx, without parenting-up std::unordered_set setAllBoneNamesInScene; for(unsigned int m = 0; m < mScene->mNumMeshes; ++ m) @@ -1873,7 +1887,7 @@ void FBXExporter::WriteObjects () setAllBoneNamesInScene.insert(pMesh->mBones[b]->mName.data); } aiMatrix4x4 mxTransIdentity; - + // and a map of nodes by bone name, as finding them is annoying. std::map node_by_bone; for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { @@ -1942,7 +1956,7 @@ void FBXExporter::WriteObjects () } if (end) { break; } } - + // if it was the skeleton root we can finish here if (end) { break; } } @@ -2196,7 +2210,65 @@ void FBXExporter::WriteObjects () bpnode.Dump(outstream, binary, indent); }*/ - // TODO: cameras, lights + // lights + indent = 1; + lights_uids.clear(); + for (size_t li = 0; li < mScene->mNumLights; ++li) { + aiLight* l = mScene->mLights[li]; + + int64_t uid = generate_uid(); + const std::string lightNodeAttributeName = l->mName.C_Str() + FBX::SEPARATOR + "NodeAttribute"; + + FBX::Node lna("NodeAttribute"); + lna.AddProperties(uid, lightNodeAttributeName, "Light"); + FBX::Node lnap("Properties70"); + + // Light color. + lnap.AddP70colorA("Color", l->mColorDiffuse.r, l->mColorDiffuse.g, l->mColorDiffuse.b); + + // TODO Assimp light description is quite concise and do not handle light intensity. + // Default value to 1000W. + lnap.AddP70numberA("Intensity", 1000); + + // FBXLight::EType conversion + switch (l->mType) { + case aiLightSource_POINT: + lnap.AddP70enum("LightType", 0); + break; + case aiLightSource_DIRECTIONAL: + lnap.AddP70enum("LightType", 1); + break; + case aiLightSource_SPOT: + lnap.AddP70enum("LightType", 2); + lnap.AddP70numberA("InnerAngle", AI_RAD_TO_DEG(l->mAngleInnerCone)); + lnap.AddP70numberA("OuterAngle", AI_RAD_TO_DEG(l->mAngleOuterCone)); + break; + // TODO Assimp do not handle 'area' nor 'volume' lights, but FBX does. + /*case aiLightSource_AREA: + lnap.AddP70enum("LightType", 3); + lnap.AddP70enum("AreaLightShape", 0); // 0=Rectangle, 1=Sphere + break; + case aiLightSource_VOLUME: + lnap.AddP70enum("LightType", 4); + break;*/ + default: + break; + } + + // Did not understood how to configure the decay so disabling attenuation. + lnap.AddP70enum("DecayType", 0); + + // Dump to FBX stream + lna.AddChild(lnap); + lna.AddChild("TypeFlags", FBX::FBXExportProperty("Light")); + lna.AddChild("GeometryVersion", FBX::FBXExportProperty(int32_t(124))); + lna.Dump(outstream, binary, indent); + + // Store name and uid (will be used later when parsing scene nodes) + lights_uids[l->mName.C_Str()] = uid; + } + + // TODO: cameras // write nodes (i.e. model hierarchy) // start at root node @@ -2600,10 +2672,19 @@ void FBXExporter::WriteModelNodes( // and connect them connections.emplace_back("C", "OO", node_attribute_uid, node_uid); } else { - // generate a null node so we can add children to it - WriteModelNode( - outstream, binary, node, node_uid, "Null", transform_chain - ); + const auto& lightIt = lights_uids.find(node->mName.C_Str()); + if(lightIt != lights_uids.end()) { + // Node has a light connected to it. + WriteModelNode( + outstream, binary, node, node_uid, "Light", transform_chain + ); + connections.emplace_back("C", "OO", lightIt->second, node_uid); + } else { + // generate a null node so we can add children to it + WriteModelNode( + outstream, binary, node, node_uid, "Null", transform_chain + ); + } } // if more than one child mesh, make nodes for each mesh @@ -2625,17 +2706,14 @@ void FBXExporter::WriteModelNodes( ], new_node_uid ); - // write model node - FBX::Node m("Model"); + + aiNode new_node; // take name from mesh name, if it exists - std::string name = mScene->mMeshes[node->mMeshes[i]]->mName.C_Str(); - name += FBX::SEPARATOR + "Model"; - m.AddProperties(new_node_uid, name, "Mesh"); - m.AddChild("Version", int32_t(232)); - FBX::Node p("Properties70"); - p.AddP70enum("InheritType", 1); - m.AddChild(p); - m.Dump(outstream, binary, 1); + new_node.mName = mScene->mMeshes[node->mMeshes[i]]->mName; + // write model node + WriteModelNode( + outstream, binary, &new_node, new_node_uid, "Mesh", std::vector>() + ); } } @@ -2647,16 +2725,14 @@ void FBXExporter::WriteModelNodes( } } - void FBXExporter::WriteAnimationCurveNode( - StreamWriterLE& outstream, - int64_t uid, - const std::string& name, // "T", "R", or "S" - aiVector3D default_value, - std::string property_name, // "Lcl Translation" etc - int64_t layer_uid, - int64_t node_uid -) { + StreamWriterLE &outstream, + int64_t uid, + const std::string &name, // "T", "R", or "S" + aiVector3D default_value, + const std::string &property_name, // "Lcl Translation" etc + int64_t layer_uid, + int64_t node_uid) { FBX::Node n("AnimationCurveNode"); n.AddProperties(uid, name + FBX::SEPARATOR + "AnimCurveNode", ""); FBX::Node p("Properties70"); @@ -2671,7 +2747,6 @@ void FBXExporter::WriteAnimationCurveNode( this->connections.emplace_back("C", "OP", uid, node_uid, property_name); } - void FBXExporter::WriteAnimationCurve( StreamWriterLE& outstream, double default_value, diff --git a/code/AssetLib/FBX/FBXExporter.h b/code/AssetLib/FBX/FBXExporter.h index dcd1d2727..d249b7bee 100644 --- a/code/AssetLib/FBX/FBXExporter.h +++ b/code/AssetLib/FBX/FBXExporter.h @@ -63,10 +63,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. struct aiScene; struct aiNode; -//struct aiMaterial; +struct aiLight; -namespace Assimp -{ +namespace Assimp { class IOSystem; class IOStream; class ExportProperties; @@ -95,6 +94,7 @@ namespace Assimp std::vector mesh_uids; std::vector material_uids; std::map node_uids; + std::map lights_uids; // this crude unique-ID system is actually fine int64_t last_uid = 999999; @@ -154,14 +154,13 @@ namespace Assimp FBX::TransformInheritance ti_type=FBX::TransformInheritance_RSrs ); void WriteAnimationCurveNode( - StreamWriterLE& outstream, - int64_t uid, - const std::string& name, // "T", "R", or "S" - aiVector3D default_value, - std::string property_name, // "Lcl Translation" etc - int64_t animation_layer_uid, - int64_t node_uid - ); + StreamWriterLE &outstream, + int64_t uid, + const std::string &name, // "T", "R", or "S" + aiVector3D default_value, + const std::string &property_name, // "Lcl Translation" etc + int64_t animation_layer_uid, + int64_t node_uid); void WriteAnimationCurve( StreamWriterLE& outstream, double default_value, diff --git a/code/AssetLib/FBX/FBXMaterial.cpp b/code/AssetLib/FBX/FBXMaterial.cpp index 6ada9630b..7eb047177 100644 --- a/code/AssetLib/FBX/FBXMaterial.cpp +++ b/code/AssetLib/FBX/FBXMaterial.cpp @@ -142,8 +142,8 @@ Material::~Material() { // ------------------------------------------------------------------------------------------------ Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) : - Object(id,element,name), - uvScaling(1.0f,1.0f), + Object(id,element,name), + uvScaling(1.0f,1.0f), media(0) { const Scope& sc = GetRequiredScope(element); @@ -210,6 +210,11 @@ Texture::Texture(uint64_t id, const Element& element, const Document& doc, const uvTrans.y = trans.y; } + const aiVector3D &rotation = PropertyGet(*props, "Rotation", ok); + if (ok) { + uvRotation = rotation.z; + } + // resolve video links if(doc.Settings().readTextures) { const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID()); @@ -273,8 +278,8 @@ void LayeredTexture::fillTexture(const Document& doc) { // ------------------------------------------------------------------------------------------------ Video::Video(uint64_t id, const Element& element, const Document& doc, const std::string& name) : - Object(id,element,name), - contentLength(0), + Object(id,element,name), + contentLength(0), content(0) { const Scope& sc = GetRequiredScope(element); diff --git a/code/AssetLib/FBX/FBXMeshGeometry.cpp b/code/AssetLib/FBX/FBXMeshGeometry.cpp index 5aecb61b5..6aeebcbe3 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.cpp +++ b/code/AssetLib/FBX/FBXMeshGeometry.cpp @@ -633,7 +633,7 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector& materials_out, cons { return; } - + // materials are handled separately. First of all, they are assigned per-face // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect // has a slightly different meaning for materials. diff --git a/code/AssetLib/FBX/FBXMeshGeometry.h b/code/AssetLib/FBX/FBXMeshGeometry.h index ae17860e3..862693b4b 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.h +++ b/code/AssetLib/FBX/FBXMeshGeometry.h @@ -52,8 +52,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { namespace FBX { -/** - * DOM base class for all kinds of FBX geometry +/** + * DOM base class for all kinds of FBX geometry */ class Geometry : public Object { @@ -76,7 +76,7 @@ private: typedef std::vector MatIndexArray; -/** +/** * DOM class for FBX geometry of type "Mesh" */ class MeshGeometry : public Geometry @@ -84,7 +84,7 @@ class MeshGeometry : public Geometry public: /** The class constructor */ MeshGeometry( uint64_t id, const Element& element, const std::string& name, const Document& doc ); - + /** The class destructor */ virtual ~MeshGeometry(); diff --git a/code/AssetLib/FBX/FBXParser.cpp b/code/AssetLib/FBX/FBXParser.cpp index 37cf83cf9..582940363 100644 --- a/code/AssetLib/FBX/FBXParser.cpp +++ b/code/AssetLib/FBX/FBXParser.cpp @@ -192,6 +192,10 @@ Scope::Scope(Parser& parser,bool topLevel) } const std::string& str = n->StringContents(); + if (str.empty()) { + ParseError("unexpected content: empty string."); + } + elements.insert(ElementMap::value_type(str,new_Element(*n,parser))); // Element() should stop at the next Key token (or right after a Close token) @@ -642,8 +646,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -733,8 +736,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -816,8 +818,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -892,8 +893,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 4; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -954,8 +954,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -1019,8 +1018,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 4; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -1088,8 +1086,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 8; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -1150,8 +1147,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 8; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } diff --git a/code/AssetLib/FBX/FBXProperties.cpp b/code/AssetLib/FBX/FBXProperties.cpp index 1a5ebffd1..c3f4de260 100644 --- a/code/AssetLib/FBX/FBXProperties.cpp +++ b/code/AssetLib/FBX/FBXProperties.cpp @@ -52,6 +52,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "FBXDocumentUtil.h" #include "FBXProperties.h" +#include + namespace Assimp { namespace FBX { @@ -172,10 +174,8 @@ PropertyTable::PropertyTable() } // ------------------------------------------------------------------------------------------------ -PropertyTable::PropertyTable(const Element& element, std::shared_ptr templateProps) -: templateProps(templateProps) -, element(&element) -{ +PropertyTable::PropertyTable(const Element &element, std::shared_ptr templateProps) : + templateProps(std::move(templateProps)), element(&element) { const Scope& scope = GetRequiredScope(element); for(const ElementMap::value_type& v : scope.Elements()) { if(v.first != "P") { @@ -199,7 +199,6 @@ PropertyTable::PropertyTable(const Element& element, std::shared_ptr > DirectPro typedef std::fbx_unordered_map PropertyMap; typedef std::fbx_unordered_map LazyPropertyMap; -/** +/** * Represents a property table as can be found in the newer FBX files (Properties60, Properties70) */ class PropertyTable { @@ -130,7 +130,7 @@ private: // ------------------------------------------------------------------------------------------------ template -inline +inline T PropertyGet(const PropertyTable& in, const std::string& name, const T& defaultValue) { const Property* const prop = in.Get(name); if( nullptr == prop) { @@ -148,7 +148,7 @@ T PropertyGet(const PropertyTable& in, const std::string& name, const T& default // ------------------------------------------------------------------------------------------------ template -inline +inline T PropertyGet(const PropertyTable& in, const std::string& name, bool& result, bool useTemplate=false ) { const Property* prop = in.Get(name); if( nullptr == prop) { diff --git a/code/AssetLib/FBX/FBXUtil.cpp b/code/AssetLib/FBX/FBXUtil.cpp index 66abf0565..3fe791b97 100644 --- a/code/AssetLib/FBX/FBXUtil.cpp +++ b/code/AssetLib/FBX/FBXUtil.cpp @@ -101,7 +101,7 @@ std::string GetLineAndColumnText(unsigned int line, unsigned int column) std::string GetTokenText(const Token* tok) { if(tok->IsBinary()) { - return static_cast( Formatter::format() << + return static_cast( Formatter::format() << " (" << TokenTypeString(tok->Type()) << ", offset 0x" << std::hex << tok->Offset() << ") " ); } diff --git a/code/AssetLib/HMP/HMPLoader.cpp b/code/AssetLib/HMP/HMPLoader.cpp index cd14cb9c3..97c1858fb 100644 --- a/code/AssetLib/HMP/HMPLoader.cpp +++ b/code/AssetLib/HMP/HMPLoader.cpp @@ -153,10 +153,10 @@ void HMPImporter::InternReadFile(const std::string &pFile, } else { // Print the magic word to the logger std::string szBuffer = ai_str_toprintable((const char *)&iMagic, sizeof(iMagic)); - + delete[] mBuffer; mBuffer = nullptr; - + // We're definitely unable to load this file throw DeadlyImportError("Unknown HMP subformat ", pFile, ". Magic word (", szBuffer, ") is not known"); diff --git a/code/AssetLib/IFC/IFCBoolean.cpp b/code/AssetLib/IFC/IFCBoolean.cpp index 86cac7f46..afd0ad6eb 100644 --- a/code/AssetLib/IFC/IFCBoolean.cpp +++ b/code/AssetLib/IFC/IFCBoolean.cpp @@ -513,7 +513,7 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const Schema_2x3::IfcPoly } // we got a list of in-out-combinations of intersections. That should be an even number of intersections, or - // we're fucked. + // we are facing a non-recoverable error. if ((intersections.size() & 1) != 0) { IFCImporter::LogWarn("Odd number of intersections, can't work with that. Omitting half space boundary check."); continue; diff --git a/code/AssetLib/IFC/IFCCurve.cpp b/code/AssetLib/IFC/IFCCurve.cpp index 28cd9690c..3ded43bc0 100644 --- a/code/AssetLib/IFC/IFCCurve.cpp +++ b/code/AssetLib/IFC/IFCCurve.cpp @@ -514,7 +514,7 @@ IfcFloat Curve::GetParametricRangeDelta() const { // ------------------------------------------------------------------------------------------------ size_t Curve::EstimateSampleCount(IfcFloat a, IfcFloat b) const { - (void)(a); (void)(b); + (void)(a); (void)(b); ai_assert( InRange( a ) ); ai_assert( InRange( b ) ); diff --git a/code/AssetLib/IFC/IFCGeometry.cpp b/code/AssetLib/IFC/IFCGeometry.cpp index 6e645f1ae..e70c760d8 100644 --- a/code/AssetLib/IFC/IFCGeometry.cpp +++ b/code/AssetLib/IFC/IFCGeometry.cpp @@ -740,7 +740,7 @@ bool ProcessGeometricItem(const Schema_2x3::IfcRepresentationItem& geo, unsigned bool fix_orientation = false; std::shared_ptr< TempMesh > meshtmp = std::make_shared(); if(const Schema_2x3::IfcShellBasedSurfaceModel* shellmod = geo.ToPtr()) { - for(std::shared_ptr shell :shellmod->SbsmBoundary) { + for (const std::shared_ptr &shell : shellmod->SbsmBoundary) { try { const ::Assimp::STEP::EXPRESS::ENTITY& e = shell->To<::Assimp::STEP::EXPRESS::ENTITY>(); const Schema_2x3::IfcConnectedFaceSet& fs = conv.db.MustGetObject(e).To(); diff --git a/code/AssetLib/IFC/IFCMaterial.cpp b/code/AssetLib/IFC/IFCMaterial.cpp index 2a79f0754..c26a3aa0a 100644 --- a/code/AssetLib/IFC/IFCMaterial.cpp +++ b/code/AssetLib/IFC/IFCMaterial.cpp @@ -75,7 +75,7 @@ static void FillMaterial(aiMaterial* mat,const IFC::Schema_2x3::IfcSurfaceStyle* mat->AddProperty(&name,AI_MATKEY_NAME); // now see which kinds of surface information are present - for(std::shared_ptr< const IFC::Schema_2x3::IfcSurfaceStyleElementSelect > sel2 : surf->Styles) { + for (const std::shared_ptr &sel2 : surf->Styles) { if (const IFC::Schema_2x3::IfcSurfaceStyleShading* shade = sel2->ResolveSelectPtr(conv.db)) { aiColor4D col_base,col; @@ -124,7 +124,7 @@ static void FillMaterial(aiMaterial* mat,const IFC::Schema_2x3::IfcSurfaceStyle* } } } - } + } } } @@ -134,7 +134,7 @@ unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionDat for(;range.first != range.second; ++range.first) { if(const IFC::Schema_2x3::IfcStyledItem* const styled = conv.db.GetObject((*range.first).second)->ToPtr()) { for(const IFC::Schema_2x3::IfcPresentationStyleAssignment& as : styled->Styles) { - for(std::shared_ptr sel : as.Styles) { + for (const std::shared_ptr &sel : as.Styles) { if( const IFC::Schema_2x3::IfcSurfaceStyle* const surf = sel->ResolveSelectPtr(conv.db) ) { // try to satisfy from cache diff --git a/code/AssetLib/IFC/IFCOpenings.cpp b/code/AssetLib/IFC/IFCOpenings.cpp index b7665582c..d6671b885 100644 --- a/code/AssetLib/IFC/IFCOpenings.cpp +++ b/code/AssetLib/IFC/IFCOpenings.cpp @@ -911,14 +911,14 @@ size_t CloseWindows(ContourVector& contours, // compare base poly normal and contour normal to detect if we need to reverse the face winding if(curmesh.mVertcnt.size() > 0) { IfcVector3 basePolyNormal = TempMesh::ComputePolygonNormal(curmesh.mVerts.data(), curmesh.mVertcnt.front()); - + std::vector worldSpaceContourVtx(it->contour.size()); - + for(size_t a = 0; a < it->contour.size(); ++a) worldSpaceContourVtx[a] = minv * IfcVector3(it->contour[a].x, it->contour[a].y, 0.0); - + IfcVector3 contourNormal = TempMesh::ComputePolygonNormal(worldSpaceContourVtx.data(), worldSpaceContourVtx.size()); - + reverseCountourFaces = (contourNormal * basePolyNormal) > 0.0; } diff --git a/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp b/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp index 2cfa22530..a6f7ae3eb 100644 --- a/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp +++ b/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -1063,27 +1063,27 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcRoo if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRoot"); } do { // convert the 'GlobalId' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->GlobalId, arg, db ); break; } + try { GenericConvert( in->GlobalId, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRoot to be a `IfcGloballyUniqueId`")); } } while(0); do { // convert the 'OwnerHistory' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->OwnerHistory, arg, db ); break; } + try { GenericConvert( in->OwnerHistory, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRoot to be a `IfcOwnerHistory`")); } } while(0); do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRoot to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRoot to be a `IfcText`")); } } while(0); return base; @@ -1150,27 +1150,27 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRepresentation"); } do { // convert the 'ContextOfItems' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ContextOfItems, arg, db ); break; } + try { GenericConvert( in->ContextOfItems, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentation to be a `IfcRepresentationContext`")); } } while(0); do { // convert the 'RepresentationIdentifier' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } + try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationType, arg, db ); break; } + try { GenericConvert( in->RepresentationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Items' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->Items, arg, db ); break; } + try { GenericConvert( in->Items, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRepresentation to be a `SET [1:?] OF IfcRepresentationItem`")); } } while(0); return base; @@ -1237,7 +1237,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcO std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectType, arg, db ); break; } + try { GenericConvert( in->ObjectType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcObject to be a `IfcLabel`")); } } while(0); return base; @@ -1290,20 +1290,20 @@ template <> size_t GenericFill(const DB& db, const LIS std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProductRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProductRepresentation to be a `IfcText`")); } } while(0); do { // convert the 'Representations' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->Representations, arg, db ); break; } + try { GenericConvert( in->Representations, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcProductRepresentation to be a `LIST [1:?] OF IfcRepresentation`")); } } while(0); return base; @@ -1316,14 +1316,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectPlacement, arg, db ); break; } + try { GenericConvert( in->ObjectPlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcProduct to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'Representation' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Representation, arg, db ); break; } + try { GenericConvert( in->Representation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcProduct to be a `IfcProductRepresentation`")); } } while(0); return base; @@ -1336,7 +1336,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Tag, arg, db ); break; } + try { GenericConvert( in->Tag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcElement to be a `IfcIdentifier`")); } } while(0); return base; @@ -1374,13 +1374,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCompositeCurve"); } do { // convert the 'Segments' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Segments, arg, db ); break; } + try { GenericConvert( in->Segments, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurve to be a `LIST [1:?] OF IfcCompositeCurveSegment`")); } } while(0); do { // convert the 'SelfIntersect' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurve to be a `LOGICAL`")); } } while(0); return base; @@ -1400,27 +1400,27 @@ template <> size_t GenericFill(const DB& db, std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis1, arg, db ); break; } + try { GenericConvert( in->Axis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'Axis2' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis2, arg, db ); break; } + try { GenericConvert( in->Axis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'LocalOrigin' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->LocalOrigin, arg, db ); break; } + try { GenericConvert( in->LocalOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCartesianTransformationOperator to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Scale' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale, arg, db ); break; } + try { GenericConvert( in->Scale, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCartesianTransformationOperator to be a `REAL`")); } } while(0); return base; @@ -1433,7 +1433,7 @@ template <> size_t GenericFill(const DB& d std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis3, arg, db ); break; } + try { GenericConvert( in->Axis3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCartesianTransformationOperator3D to be a `IfcDirection`")); } } while(0); return base; @@ -1445,14 +1445,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProperty"); } do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'Description' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProperty to be a `IfcText`")); } } while(0); return base; @@ -1497,7 +1497,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcElementarySurface"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcElementarySurface to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -1515,19 +1515,19 @@ template <> size_t GenericFill(const DB& db, const LIST& param if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcBooleanResult"); } do { // convert the 'Operator' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Operator, arg, db ); break; } + try { GenericConvert( in->Operator, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBooleanResult to be a `IfcBooleanOperator`")); } } while(0); do { // convert the 'FirstOperand' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->FirstOperand, arg, db ); break; } + try { GenericConvert( in->FirstOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); do { // convert the 'SecondOperand' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->SecondOperand, arg, db ); break; } + try { GenericConvert( in->SecondOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); return base; @@ -1551,7 +1551,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcManifoldSolidBrep"); } do { // convert the 'Outer' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Outer, arg, db ); break; } + try { GenericConvert( in->Outer, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcManifoldSolidBrep to be a `IfcClosedShell`")); } } while(0); return base; @@ -1630,12 +1630,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelFillsElement"); } do { // convert the 'RelatingOpeningElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelFillsElement to be a `IfcOpeningElement`")); } } while(0); do { // convert the 'RelatedBuildingElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelFillsElement to be a `IfcElement`")); } } while(0); return base; @@ -1681,12 +1681,12 @@ template <> size_t GenericFill(const DB& db, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelContainedInSpatialStructure"); } do { // convert the 'RelatedElements' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedElements, arg, db ); break; } + try { GenericConvert( in->RelatedElements, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelContainedInSpatialStructure to be a `SET [1:?] OF IfcProduct`")); } } while(0); do { // convert the 'RelatingStructure' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingStructure, arg, db ); break; } + try { GenericConvert( in->RelatingStructure, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelContainedInSpatialStructure to be a `IfcSpatialStructureElement`")); } } while(0); return base; @@ -1772,7 +1772,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcDirection"); } do { // convert the 'DirectionRatios' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->DirectionRatios, arg, db ); break; } + try { GenericConvert( in->DirectionRatios, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcDirection to be a `LIST [2:3] OF REAL`")); } } while(0); return base; @@ -1784,14 +1784,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProfileDef"); } do { // convert the 'ProfileType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ProfileType, arg, db ); break; } + try { GenericConvert( in->ProfileType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProfileDef to be a `IfcProfileTypeEnum`")); } } while(0); do { // convert the 'ProfileName' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ProfileName, arg, db ); break; } + try { GenericConvert( in->ProfileName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProfileDef to be a `IfcLabel`")); } } while(0); return base; @@ -1803,7 +1803,7 @@ template <> size_t GenericFill(const DB& db, const L if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcParameterizedProfileDef"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcParameterizedProfileDef to be a `IfcAxis2Placement2D`")); } } while(0); return base; @@ -1910,7 +1910,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcCircleProfileDef"); } do { // convert the 'Radius' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCircleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1921,7 +1921,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcCircleHollowProfileDef"); } do { // convert the 'WallThickness' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->WallThickness, arg, db ); break; } + try { GenericConvert( in->WallThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCircleHollowProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1933,7 +1933,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPlacement"); } do { // convert the 'Location' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Location, arg, db ); break; } + try { GenericConvert( in->Location, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPlacement to be a `IfcCartesianPoint`")); } } while(0); return base; @@ -1945,13 +1945,13 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcAxis2Placement3D"); } do { // convert the 'Axis' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); do { // convert the 'RefDirection' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); return base; @@ -1964,7 +1964,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyle to be a `IfcLabel`")); } } while(0); return base; @@ -1982,17 +1982,17 @@ template <> size_t GenericFill(const DB& db, const LIS size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcCompositeCurveSegment"); } do { // convert the 'Transition' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Transition, arg, db ); break; } + try { GenericConvert( in->Transition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurveSegment to be a `IfcTransitionCode`")); } } while(0); do { // convert the 'SameSense' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SameSense, arg, db ); break; } + try { GenericConvert( in->SameSense, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurveSegment to be a `BOOLEAN`")); } } while(0); do { // convert the 'ParentCurve' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ParentCurve, arg, db ); break; } + try { GenericConvert( in->ParentCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCompositeCurveSegment to be a `IfcCurve`")); } } while(0); return base; @@ -2004,13 +2004,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcRectangleProfileDef"); } do { // convert the 'XDim' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2106,12 +2106,12 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLocalPlacement"); } do { // convert the 'PlacementRelTo' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PlacementRelTo, arg, db ); break; } + try { GenericConvert( in->PlacementRelTo, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLocalPlacement to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'RelativePlacement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelativePlacement, arg, db ); break; } + try { GenericConvert( in->RelativePlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLocalPlacement to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -2123,13 +2123,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcSweptAreaSolid"); } do { // convert the 'SweptArea' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SweptArea, arg, db ); break; } + try { GenericConvert( in->SweptArea, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptAreaSolid to be a `IfcProfileDef`")); } } while(0); do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptAreaSolid to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -2140,12 +2140,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRevolvedAreaSolid"); } do { // convert the 'Axis' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRevolvedAreaSolid to be a `IfcAxis1Placement`")); } } while(0); do { // convert the 'Angle' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Angle, arg, db ); break; } + try { GenericConvert( in->Angle, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRevolvedAreaSolid to be a `IfcPlaneAngleMeasure`")); } } while(0); return base; @@ -2170,28 +2170,28 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcSweptDiskSolid"); } do { // convert the 'Directrix' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Directrix, arg, db ); break; } + try { GenericConvert( in->Directrix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptDiskSolid to be a `IfcCurve`")); } } while(0); do { // convert the 'Radius' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'InnerRadius' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->InnerRadius, arg, db ); break; } + try { GenericConvert( in->InnerRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'StartParam' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->StartParam, arg, db ); break; } + try { GenericConvert( in->StartParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); do { // convert the 'EndParam' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->EndParam, arg, db ); break; } + try { GenericConvert( in->EndParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); return base; @@ -2203,13 +2203,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcHalfSpaceSolid"); } do { // convert the 'BaseSurface' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->BaseSurface, arg, db ); break; } + try { GenericConvert( in->BaseSurface, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcHalfSpaceSolid to be a `IfcSurface`")); } } while(0); do { // convert the 'AgreementFlag' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->AgreementFlag, arg, db ); break; } + try { GenericConvert( in->AgreementFlag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcHalfSpaceSolid to be a `BOOLEAN`")); } } while(0); return base; @@ -2220,12 +2220,12 @@ template <> size_t GenericFill(const DB& db, const size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPolygonalBoundedHalfSpace"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPolygonalBoundedHalfSpace to be a `IfcAxis2Placement3D`")); } } while(0); do { // convert the 'PolygonalBoundary' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } + try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPolygonalBoundedHalfSpace to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -2251,23 +2251,23 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc if (params.GetSize() < 9) { throw STEP::TypeError("expected 9 arguments to IfcProject"); } do { // convert the 'LongName' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcProject to be a `IfcLabel`")); } } while(0); do { // convert the 'Phase' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Phase, arg, db ); break; } + try { GenericConvert( in->Phase, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcProject to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationContexts' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RepresentationContexts, arg, db ); break; } + try { GenericConvert( in->RepresentationContexts, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcProject to be a `SET [1:?] OF IfcRepresentationContext`")); } } while(0); do { // convert the 'UnitsInContext' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->UnitsInContext, arg, db ); break; } + try { GenericConvert( in->UnitsInContext, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcProject to be a `IfcUnitAssignment`")); } } while(0); return base; @@ -2327,27 +2327,27 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcTrimmedCurve"); } do { // convert the 'BasisCurve' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->BasisCurve, arg, db ); break; } + try { GenericConvert( in->BasisCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcTrimmedCurve to be a `IfcCurve`")); } } while(0); do { // convert the 'Trim1' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim1, arg, db ); break; } + try { GenericConvert( in->Trim1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'Trim2' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim2, arg, db ); break; } + try { GenericConvert( in->Trim2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'SenseAgreement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SenseAgreement, arg, db ); break; } + try { GenericConvert( in->SenseAgreement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcTrimmedCurve to be a `BOOLEAN`")); } } while(0); do { // convert the 'MasterRepresentation' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MasterRepresentation, arg, db ); break; } + try { GenericConvert( in->MasterRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcTrimmedCurve to be a `IfcTrimmingPreference`")); } } while(0); return base; @@ -2359,7 +2359,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcRelDefines"); } do { // convert the 'RelatedObjects' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelDefines to be a `SET [1:?] OF IfcObject`")); } } while(0); return base; @@ -2371,7 +2371,7 @@ template <> size_t GenericFill(const DB& db, const LI if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelDefinesByProperties"); } do { // convert the 'RelatingPropertyDefinition' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } + try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelDefinesByProperties to be a `IfcPropertySetDefinition`")); } } while(0); return base; @@ -2404,7 +2404,7 @@ template <> size_t GenericFill(const DB& db, const L if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryOpenProfileDef"); } do { // convert the 'Curve' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Curve, arg, db ); break; } + try { GenericConvert( in->Curve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryOpenProfileDef to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -2570,13 +2570,13 @@ template <> size_t GenericFill(const DB& db, const LIST& param if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelDecomposes"); } do { // convert the 'RelatingObject' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->RelatingObject, arg, db ); break; } + try { GenericConvert( in->RelatingObject, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelDecomposes to be a `IfcObjectDefinition`")); } } while(0); do { // convert the 'RelatedObjects' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelDecomposes to be a `SET [1:?] OF IfcObjectDefinition`")); } } while(0); return base; @@ -2594,7 +2594,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyline"); } do { // convert the 'Points' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Points, arg, db ); break; } + try { GenericConvert( in->Points, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyline to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -2626,12 +2626,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMappedItem"); } do { // convert the 'MappingSource' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingSource, arg, db ); break; } + try { GenericConvert( in->MappingSource, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMappedItem to be a `IfcRepresentationMap`")); } } while(0); do { // convert the 'MappingTarget' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingTarget, arg, db ); break; } + try { GenericConvert( in->MappingTarget, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMappedItem to be a `IfcCartesianTransformationOperator`")); } } while(0); return base; @@ -2658,13 +2658,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Dimensions, arg, db ); break; } + try { GenericConvert( in->Dimensions, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcNamedUnit to be a `IfcDimensionalExponents`")); } } while(0); do { // convert the 'UnitType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->UnitType, arg, db ); break; } + try { GenericConvert( in->UnitType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcNamedUnit to be a `IfcUnitEnum`")); } } while(0); return base; @@ -2719,13 +2719,13 @@ template <> size_t GenericFill(const DB& db, const L std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSpatialStructureElement to be a `IfcLabel`")); } } while(0); do { // convert the 'CompositionType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->CompositionType, arg, db ); break; } + try { GenericConvert( in->CompositionType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSpatialStructureElement to be a `IfcElementCompositionEnum`")); } } while(0); return base; @@ -2737,19 +2737,19 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 12) { throw STEP::TypeError("expected 12 arguments to IfcBuilding"); } do { // convert the 'ElevationOfRefHeight' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } + try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'ElevationOfTerrain' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } + try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'BuildingAddress' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->BuildingAddress, arg, db ); break; } + try { GenericConvert( in->BuildingAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcBuilding to be a `IfcPostalAddress`")); } } while(0); return base; @@ -2761,7 +2761,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConnectedFaceSet"); } do { // convert the 'CfsFaces' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CfsFaces, arg, db ); break; } + try { GenericConvert( in->CfsFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConnectedFaceSet to be a `SET [1:?] OF IfcFace`")); } } while(0); return base; @@ -2787,7 +2787,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcCo if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConic"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConic to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -2834,32 +2834,32 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 8) { throw STEP::TypeError("expected 8 arguments to IfcIShapeProfileDef"); } do { // convert the 'OverallWidth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallDepth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->OverallDepth, arg, db ); break; } + try { GenericConvert( in->OverallDepth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'WebThickness' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->WebThickness, arg, db ); break; } + try { GenericConvert( in->WebThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FlangeThickness' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->FlangeThickness, arg, db ); break; } + try { GenericConvert( in->FlangeThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FilletRadius' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FilletRadius, arg, db ); break; } + try { GenericConvert( in->FilletRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2933,13 +2933,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertyListValue"); } do { // convert the 'ListValues' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ListValues, arg, db ); break; } + try { GenericConvert( in->ListValues, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertyListValue to be a `LIST [1:?] OF IfcValue`")); } } while(0); do { // convert the 'Unit' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertyListValue to be a `IfcUnit`")); } } while(0); return base; @@ -2965,13 +2965,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcDoo if (params.GetSize() < 10) { throw STEP::TypeError("expected 10 arguments to IfcDoor"); } do { // convert the 'OverallHeight' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallHeight, arg, db ); break; } + try { GenericConvert( in->OverallHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallWidth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2984,20 +2984,20 @@ template <> size_t GenericFill(const DB& db, const LIST& params, std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Item, arg, db ); break; } + try { GenericConvert( in->Item, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcStyledItem to be a `IfcRepresentationItem`")); } } while(0); do { // convert the 'Styles' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcStyledItem to be a `SET [1:?] OF IfcPresentationStyleAssignment`")); } } while(0); do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcStyledItem to be a `IfcLabel`")); } } while(0); return base; @@ -3023,7 +3023,7 @@ template <> size_t GenericFill(const DB& db, const if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryClosedProfileDef"); } do { // convert the 'OuterCurve' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->OuterCurve, arg, db ); break; } + try { GenericConvert( in->OuterCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryClosedProfileDef to be a `IfcCurve`")); } } while(0); return base; @@ -3041,12 +3041,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcLin size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLine"); } do { // convert the 'Pnt' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Pnt, arg, db ); break; } + try { GenericConvert( in->Pnt, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLine to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Dir' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Dir, arg, db ); break; } + try { GenericConvert( in->Dir, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLine to be a `IfcVector`")); } } while(0); return base; @@ -3072,13 +3072,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertySingleValue"); } do { // convert the 'NominalValue' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->NominalValue, arg, db ); break; } + try { GenericConvert( in->NominalValue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertySingleValue to be a `IfcValue`")); } } while(0); do { // convert the 'Unit' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertySingleValue to be a `IfcUnit`")); } } while(0); return base; @@ -3111,7 +3111,7 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcSurfaceStyleShading"); } do { // convert the 'SurfaceColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SurfaceColour, arg, db ); break; } + try { GenericConvert( in->SurfaceColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleShading to be a `IfcColourRgb`")); } } while(0); return base; diff --git a/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp b/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp index c58c7c42f..0d7051195 100644 --- a/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp +++ b/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -59,12 +59,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcSurfaceStyle"); } do { // convert the 'Side' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Side, arg, db ); break; } + try { GenericConvert( in->Side, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyle to be a `IfcSurfaceSide`")); } } while(0); do { // convert the 'Styles' argument std::shared_ptr arg = params[ base++ ]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyle to be a `SET [1:5] OF IfcSurfaceStyleElementSelect`")); } } while(0); return base; @@ -118,7 +118,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcFac if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFace"); } do { // convert the 'Bounds' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bounds, arg, db ); break; } + try { GenericConvert( in->Bounds, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFace to be a `SET [1:?] OF IfcFaceBound`")); } } while(0); return base; @@ -173,7 +173,7 @@ template <> size_t GenericFill(const DB& db, const LIST& std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcColourSpecification to be a `IfcLabel`")); } } while(0); return base; @@ -184,12 +184,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcV size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcVector"); } do { // convert the 'Orientation' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcVector to be a `IfcDirection`")); } } while(0); do { // convert the 'Magnitude' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Magnitude, arg, db ); break; } + try { GenericConvert( in->Magnitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcVector to be a `IfcLengthMeasure`")); } } while(0); return base; @@ -207,17 +207,17 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcColourRgb"); } do { // convert the 'Red' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Red, arg, db ); break; } + try { GenericConvert( in->Red, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Green' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Green, arg, db ); break; } + try { GenericConvert( in->Green, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Blue' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Blue, arg, db ); break; } + try { GenericConvert( in->Blue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); return base; @@ -243,31 +243,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSit if (params.GetSize() < 14) { throw STEP::TypeError("expected 14 arguments to IfcSite"); } do { // convert the 'RefLatitude' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLatitude, arg, db ); break; } + try { GenericConvert( in->RefLatitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefLongitude' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLongitude, arg, db ); break; } + try { GenericConvert( in->RefLongitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefElevation' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefElevation, arg, db ); break; } + try { GenericConvert( in->RefElevation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcSite to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'LandTitleNumber' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LandTitleNumber, arg, db ); break; } + try { GenericConvert( in->LandTitleNumber, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 12 to IfcSite to be a `IfcLabel`")); } } while(0); do { // convert the 'SiteAddress' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SiteAddress, arg, db ); break; } + try { GenericConvert( in->SiteAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 13 to IfcSite to be a `IfcPostalAddress`")); } } while(0); return base; @@ -412,31 +412,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcBSplineCurve"); } do { // convert the 'Degree' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Degree, arg, db ); break; } + try { GenericConvert( in->Degree, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBSplineCurve to be a `INTEGER`")); } } while(0); do { // convert the 'ControlPointsList' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->ControlPointsList, arg, db ); break; } + try { GenericConvert( in->ControlPointsList, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBSplineCurve to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); do { // convert the 'CurveForm' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->CurveForm, arg, db ); break; } + try { GenericConvert( in->CurveForm, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBSplineCurve to be a `IfcBSplineCurveForm`")); } } while(0); do { // convert the 'ClosedCurve' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->ClosedCurve, arg, db ); break; } + try { GenericConvert( in->ClosedCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBSplineCurve to be a `LOGICAL`")); } } while(0); do { // convert the 'SelfIntersect' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcBSplineCurve to be a `LOGICAL`")); } } while(0); return base; @@ -474,7 +474,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcShellBasedSurfaceModel"); } do { // convert the 'SbsmBoundary' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SbsmBoundary, arg, db ); break; } + try { GenericConvert( in->SbsmBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcShellBasedSurfaceModel to be a `SET [1:?] OF IfcShell`")); } } while(0); return base; @@ -492,12 +492,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcExtrudedAreaSolid"); } do { // convert the 'ExtrudedDirection' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } + try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcExtrudedAreaSolid to be a `IfcDirection`")); } } while(0); do { // convert the 'Depth' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Depth, arg, db ); break; } + try { GenericConvert( in->Depth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcExtrudedAreaSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -522,12 +522,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelVoidsElement"); } do { // convert the 'RelatingBuildingElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelVoidsElement to be a `IfcElement`")); } } while(0); do { // convert the 'RelatedOpeningElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelVoidsElement to be a `IfcFeatureElementSubtraction`")); } } while(0); return base; @@ -546,13 +546,13 @@ template <> size_t GenericFill(c if (params.GetSize() < 7) { throw STEP::TypeError("expected 7 arguments to IfcCartesianTransformationOperator3DnonUniform"); } do { // convert the 'Scale2' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale2, arg, db ); break; } + try { GenericConvert( in->Scale2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcCartesianTransformationOperator3DnonUniform to be a `REAL`")); } } while(0); do { // convert the 'Scale3' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale3, arg, db ); break; } + try { GenericConvert( in->Scale3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcCartesianTransformationOperator3DnonUniform to be a `REAL`")); } } while(0); return base; @@ -634,7 +634,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis2Placement2D"); } do { // convert the 'RefDirection' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement2D to be a `IfcDirection`")); } } while(0); return base; @@ -658,7 +658,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcCartesianPoint"); } do { // convert the 'Coordinates' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Coordinates, arg, db ); break; } + try { GenericConvert( in->Coordinates, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianPoint to be a `LIST [1:3] OF IfcLengthMeasure`")); } } while(0); return base; @@ -682,7 +682,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyLoop"); } do { // convert the 'Polygon' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Polygon, arg, db ); break; } + try { GenericConvert( in->Polygon, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyLoop to be a `LIST [3:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -716,14 +716,14 @@ template <> size_t GenericFill(const DB& db, const LIS std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextIdentifier, arg, db ); break; } + try { GenericConvert( in->ContextIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); do { // convert the 'ContextType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextType, arg, db ); break; } + try { GenericConvert( in->ContextType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); return base; @@ -735,27 +735,27 @@ template <> size_t GenericFill(const DB& db, if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcGeometricRepresentationContext"); } do { // convert the 'CoordinateSpaceDimension' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } + try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcGeometricRepresentationContext to be a `IfcDimensionCount`")); } } while(0); do { // convert the 'Precision' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Precision, arg, db ); break; } + try { GenericConvert( in->Precision, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcGeometricRepresentationContext to be a `REAL`")); } } while(0); do { // convert the 'WorldCoordinateSystem' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } + try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcGeometricRepresentationContext to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'TrueNorth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TrueNorth, arg, db ); break; } + try { GenericConvert( in->TrueNorth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcGeometricRepresentationContext to be a `IfcDirection`")); } } while(0); return base; @@ -774,12 +774,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcS if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcSIUnit"); } do { // convert the 'Prefix' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Prefix, arg, db ); break; } + try { GenericConvert( in->Prefix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSIUnit to be a `IfcSIPrefix`")); } } while(0); do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSIUnit to be a `IfcSIUnitName`")); } } while(0); return base; @@ -805,7 +805,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis1Placement"); } do { // convert the 'Axis' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis1Placement to be a `IfcDirection`")); } } while(0); return base; @@ -858,12 +858,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcRepresentationMap"); } do { // convert the 'MappingOrigin' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingOrigin, arg, db ); break; } + try { GenericConvert( in->MappingOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationMap to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'MappedRepresentation' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappedRepresentation, arg, db ); break; } + try { GenericConvert( in->MappedRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationMap to be a `IfcRepresentation`")); } } while(0); return base; @@ -1012,12 +1012,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMeasureWithUnit"); } do { // convert the 'ValueComponent' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ValueComponent, arg, db ); break; } + try { GenericConvert( in->ValueComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMeasureWithUnit to be a `IfcValue`")); } } while(0); do { // convert the 'UnitComponent' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->UnitComponent, arg, db ); break; } + try { GenericConvert( in->UnitComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMeasureWithUnit to be a `IfcUnit`")); } } while(0); return base; @@ -1125,7 +1125,7 @@ template <> size_t GenericFill(const DB& db, const LIS size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFaceBasedSurfaceModel"); } do { // convert the 'FbsmFaces' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->FbsmFaces, arg, db ); break; } + try { GenericConvert( in->FbsmFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBasedSurfaceModel to be a `SET [1:?] OF IfcConnectedFaceSet`")); } } while(0); return base; @@ -1172,13 +1172,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcFaceBound"); } do { // convert the 'Bound' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bound, arg, db ); break; } + try { GenericConvert( in->Bound, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBound to be a `IfcLoop`")); } } while(0); do { // convert the 'Orientation' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcFaceBound to be a `BOOLEAN`")); } } while(0); return base; @@ -1216,12 +1216,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcComplexProperty"); } do { // convert the 'UsageName' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->UsageName, arg, db ); break; } + try { GenericConvert( in->UsageName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcComplexProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'HasProperties' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcComplexProperty to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -1274,7 +1274,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcUnitAssignment"); } do { // convert the 'Units' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Units, arg, db ); break; } + try { GenericConvert( in->Units, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcUnitAssignment to be a `SET [1:?] OF IfcUnit`")); } } while(0); return base; @@ -1307,12 +1307,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcElementQuantity"); } do { // convert the 'MethodOfMeasurement' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } + try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcElementQuantity to be a `IfcLabel`")); } } while(0); do { // convert the 'Quantities' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Quantities, arg, db ); break; } + try { GenericConvert( in->Quantities, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcElementQuantity to be a `SET [1:?] OF IfcPhysicalQuantity`")); } } while(0); return base; @@ -1379,7 +1379,7 @@ template <> size_t GenericFill(const DB& db, con size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPresentationStyleAssignment"); } do { // convert the 'Styles' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyleAssignment to be a `SET [1:?] OF IfcPresentationStyleSelect`")); } } while(0); return base; @@ -1418,13 +1418,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSp size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 11) { throw STEP::TypeError("expected 11 arguments to IfcSpace"); } do { // convert the 'InteriorOrExteriorSpace' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->InteriorOrExteriorSpace, arg, db ); break; } + try { GenericConvert( in->InteriorOrExteriorSpace, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSpace to be a `IfcInternalOrExternalEnum`")); } } while(0); do { // convert the 'ElevationWithFlooring' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } + try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSpace to be a `IfcLengthMeasure`")); } } while(0); return base; @@ -1484,7 +1484,7 @@ template <> size_t GenericFill(const DB& db, const size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcSurfaceStyleWithTextures"); } do { // convert the 'Textures' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Textures, arg, db ); break; } + try { GenericConvert( in->Textures, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleWithTextures to be a `LIST [1:?] OF IfcSurfaceTexture`")); } } while(0); return base; @@ -1495,22 +1495,22 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcBoundingBox"); } do { // convert the 'Corner' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Corner, arg, db ); break; } + try { GenericConvert( in->Corner, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBoundingBox to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'XDim' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'ZDim' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ZDim, arg, db ); break; } + try { GenericConvert( in->ZDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1535,7 +1535,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcC size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCircle"); } do { // convert the 'Radius' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCircle to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1623,12 +1623,12 @@ template <> size_t GenericFill(const DB& db, const LIST& size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcConversionBasedUnit"); } do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcConversionBasedUnit to be a `IfcLabel`")); } } while(0); do { // convert the 'ConversionFactor' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ConversionFactor, arg, db ); break; } + try { GenericConvert( in->ConversionFactor, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcConversionBasedUnit to be a `IfcMeasureWithUnit`")); } } while(0); return base; @@ -1744,12 +1744,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcEllipse"); } do { // convert the 'SemiAxis1' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis1, arg, db ); break; } + try { GenericConvert( in->SemiAxis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'SemiAxis2' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis2, arg, db ); break; } + try { GenericConvert( in->SemiAxis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1816,7 +1816,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcPropertySet"); } do { // convert the 'HasProperties' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcPropertySet to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -1828,48 +1828,48 @@ template <> size_t GenericFill(const DB& db, const LIS if (params.GetSize() < 9) { throw STEP::TypeError("expected 9 arguments to IfcSurfaceStyleRendering"); } do { // convert the 'Transparency' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Transparency, arg, db ); break; } + try { GenericConvert( in->Transparency, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyleRendering to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'DiffuseColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseColour, arg, db ); break; } + try { GenericConvert( in->DiffuseColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'TransmissionColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TransmissionColour, arg, db ); break; } + try { GenericConvert( in->TransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'DiffuseTransmissionColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } + try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'ReflectionColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ReflectionColour, arg, db ); break; } + try { GenericConvert( in->ReflectionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularColour, arg, db ); break; } + try { GenericConvert( in->SpecularColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularHighlight' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularHighlight, arg, db ); break; } + try { GenericConvert( in->SpecularHighlight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSurfaceStyleRendering to be a `IfcSpecularHighlightSelect`")); } } while(0); do { // convert the 'ReflectanceMethod' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } + try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSurfaceStyleRendering to be a `IfcReflectanceMethodEnum`")); } } while(0); return base; diff --git a/code/AssetLib/IFC/IFCReaderGen_4.cpp b/code/AssetLib/IFC/IFCReaderGen_4.cpp index 9eb3e2446..179322902 100644 --- a/code/AssetLib/IFC/IFCReaderGen_4.cpp +++ b/code/AssetLib/IFC/IFCReaderGen_4.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -1254,28 +1254,28 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcRoo if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRoot"); } do { // convert the 'GlobalId' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->GlobalId, arg, db ); break; } + try { GenericConvert( in->GlobalId, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRoot to be a `IfcGloballyUniqueId`")); } } while(0); do { // convert the 'OwnerHistory' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OwnerHistory, arg, db ); break; } + try { GenericConvert( in->OwnerHistory, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRoot to be a `IfcOwnerHistory`")); } } while(0); do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRoot to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRoot to be a `IfcText`")); } } while(0); return base; @@ -1294,7 +1294,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcO boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectType, arg, db ); break; } + try { GenericConvert( in->ObjectType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcObject to be a `IfcLabel`")); } } while(0); return base; @@ -1328,14 +1328,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectPlacement, arg, db ); break; } + try { GenericConvert( in->ObjectPlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcProduct to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'Representation' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Representation, arg, db ); break; } + try { GenericConvert( in->Representation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcProduct to be a `IfcProductRepresentation`")); } } while(0); return base; @@ -1348,7 +1348,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Tag, arg, db ); break; } + try { GenericConvert( in->Tag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcElement to be a `IfcIdentifier`")); } } while(0); return base; @@ -1441,7 +1441,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcManifoldSolidBrep"); } do { // convert the 'Outer' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Outer, arg, db ); break; } + try { GenericConvert( in->Outer, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcManifoldSolidBrep to be a `IfcClosedShell`")); } } while(0); return base; @@ -1473,7 +1473,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcFac if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFace"); } do { // convert the 'Bounds' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bounds, arg, db ); break; } + try { GenericConvert( in->Bounds, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFace to be a `SET [1:?] OF IfcFaceBound`")); } } while(0); return base; @@ -1624,14 +1624,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProfileDef"); } do { // convert the 'ProfileType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ProfileType, arg, db ); break; } + try { GenericConvert( in->ProfileType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProfileDef to be a `IfcProfileTypeEnum`")); } } while(0); do { // convert the 'ProfileName' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ProfileName, arg, db ); break; } + try { GenericConvert( in->ProfileName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProfileDef to be a `IfcLabel`")); } } while(0); return base; @@ -1643,7 +1643,7 @@ template <> size_t GenericFill(const DB& db, const if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryClosedProfileDef"); } do { // convert the 'OuterCurve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->OuterCurve, arg, db ); break; } + try { GenericConvert( in->OuterCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryClosedProfileDef to be a `IfcCurve`")); } } while(0); return base; @@ -1655,7 +1655,7 @@ template <> size_t GenericFill(const DB& db, const L if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryOpenProfileDef"); } do { // convert the 'Curve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Curve, arg, db ); break; } + try { GenericConvert( in->Curve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryOpenProfileDef to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -1666,7 +1666,7 @@ template <> size_t GenericFill(const DB& db, co size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcArbitraryProfileDefWithVoids"); } do { // convert the 'InnerCurves' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->InnerCurves, arg, db ); break; } + try { GenericConvert( in->InnerCurves, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcArbitraryProfileDefWithVoids to be a `SET [1:?] OF IfcCurve`")); } } while(0); return base; @@ -1693,7 +1693,7 @@ template <> size_t GenericFill(const DB& db, const L boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcParameterizedProfileDef to be a `IfcAxis2Placement2D`")); } } while(0); return base; @@ -1726,7 +1726,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPlacement"); } do { // convert the 'Location' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Location, arg, db ); break; } + try { GenericConvert( in->Location, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPlacement to be a `IfcCartesianPoint`")); } } while(0); return base; @@ -1738,7 +1738,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis1Placement"); } do { // convert the 'Axis' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis1Placement to be a `IfcDirection`")); } } while(0); return base; @@ -1750,7 +1750,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis2Placement2D"); } do { // convert the 'RefDirection' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement2D to be a `IfcDirection`")); } } while(0); return base; @@ -1762,13 +1762,13 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcAxis2Placement3D"); } do { // convert the 'Axis' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); do { // convert the 'RefDirection' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); return base; @@ -1792,31 +1792,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcBSplineCurve"); } do { // convert the 'Degree' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Degree, arg, db ); break; } + try { GenericConvert( in->Degree, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBSplineCurve to be a `IfcInteger`")); } } while(0); do { // convert the 'ControlPointsList' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->ControlPointsList, arg, db ); break; } + try { GenericConvert( in->ControlPointsList, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBSplineCurve to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); do { // convert the 'CurveForm' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->CurveForm, arg, db ); break; } + try { GenericConvert( in->CurveForm, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBSplineCurve to be a `IfcBSplineCurveForm`")); } } while(0); do { // convert the 'ClosedCurve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->ClosedCurve, arg, db ); break; } + try { GenericConvert( in->ClosedCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBSplineCurve to be a `IfcLogical`")); } } while(0); do { // convert the 'SelfIntersect' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcBSplineCurve to be a `IfcLogical`")); } } while(0); return base; @@ -1930,19 +1930,19 @@ template <> size_t GenericFill(const DB& db, const LIST& param if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcBooleanResult"); } do { // convert the 'Operator' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Operator, arg, db ); break; } + try { GenericConvert( in->Operator, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBooleanResult to be a `IfcBooleanOperator`")); } } while(0); do { // convert the 'FirstOperand' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->FirstOperand, arg, db ); break; } + try { GenericConvert( in->FirstOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); do { // convert the 'SecondOperand' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->SecondOperand, arg, db ); break; } + try { GenericConvert( in->SecondOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); return base; @@ -1960,13 +1960,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCompositeCurve"); } do { // convert the 'Segments' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Segments, arg, db ); break; } + try { GenericConvert( in->Segments, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurve to be a `LIST [1:?] OF IfcCompositeCurveSegment`")); } } while(0); do { // convert the 'SelfIntersect' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurve to be a `IfcLogical`")); } } while(0); return base; @@ -1991,22 +1991,22 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcBoundingBox"); } do { // convert the 'Corner' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Corner, arg, db ); break; } + try { GenericConvert( in->Corner, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBoundingBox to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'XDim' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'ZDim' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->ZDim, arg, db ); break; } + try { GenericConvert( in->ZDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2018,13 +2018,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcHalfSpaceSolid"); } do { // convert the 'BaseSurface' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->BaseSurface, arg, db ); break; } + try { GenericConvert( in->BaseSurface, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcHalfSpaceSolid to be a `IfcSurface`")); } } while(0); do { // convert the 'AgreementFlag' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->AgreementFlag, arg, db ); break; } + try { GenericConvert( in->AgreementFlag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcHalfSpaceSolid to be a `IfcBoolean`")); } } while(0); return base; @@ -2044,7 +2044,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSpatialElement to be a `IfcLabel`")); } } while(0); return base; @@ -2057,7 +2057,7 @@ template <> size_t GenericFill(const DB& db, const L boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->CompositionType, arg, db ); break; } + try { GenericConvert( in->CompositionType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSpatialStructureElement to be a `IfcElementCompositionEnum`")); } } while(0); return base; @@ -2069,19 +2069,19 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 12) { throw STEP::TypeError("expected 12 arguments to IfcBuilding"); } do { // convert the 'ElevationOfRefHeight' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } + try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'ElevationOfTerrain' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } + try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'BuildingAddress' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->BuildingAddress, arg, db ); break; } + try { GenericConvert( in->BuildingAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcBuilding to be a `IfcPostalAddress`")); } } while(0); return base; @@ -2266,7 +2266,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcCartesianPoint"); } do { // convert the 'Coordinates' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Coordinates, arg, db ); break; } + try { GenericConvert( in->Coordinates, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianPoint to be a `LIST [1:3] OF IfcLengthMeasure`")); } } while(0); return base; @@ -2300,27 +2300,27 @@ template <> size_t GenericFill(const DB& db, boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis1, arg, db ); break; } + try { GenericConvert( in->Axis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'Axis2' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis2, arg, db ); break; } + try { GenericConvert( in->Axis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'LocalOrigin' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->LocalOrigin, arg, db ); break; } + try { GenericConvert( in->LocalOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCartesianTransformationOperator to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Scale' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale, arg, db ); break; } + try { GenericConvert( in->Scale, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCartesianTransformationOperator to be a `IfcReal`")); } } while(0); return base; @@ -2347,7 +2347,7 @@ template <> size_t GenericFill(const DB& d boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis3, arg, db ); break; } + try { GenericConvert( in->Axis3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCartesianTransformationOperator3D to be a `IfcDirection`")); } } while(0); return base; @@ -2359,13 +2359,13 @@ template <> size_t GenericFill(c if (params.GetSize() < 7) { throw STEP::TypeError("expected 7 arguments to IfcCartesianTransformationOperator3DnonUniform"); } do { // convert the 'Scale2' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale2, arg, db ); break; } + try { GenericConvert( in->Scale2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcCartesianTransformationOperator3DnonUniform to be a `IfcReal`")); } } while(0); do { // convert the 'Scale3' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale3, arg, db ); break; } + try { GenericConvert( in->Scale3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcCartesianTransformationOperator3DnonUniform to be a `IfcReal`")); } } while(0); return base; @@ -2412,7 +2412,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcCo if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConic"); } do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConic to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -2423,7 +2423,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcC size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCircle"); } do { // convert the 'Radius' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCircle to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2435,7 +2435,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcCircleProfileDef"); } do { // convert the 'Radius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCircleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2446,7 +2446,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcCircleHollowProfileDef"); } do { // convert the 'WallThickness' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->WallThickness, arg, db ); break; } + try { GenericConvert( in->WallThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCircleHollowProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2472,7 +2472,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConnectedFaceSet"); } do { // convert the 'CfsFaces' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CfsFaces, arg, db ); break; } + try { GenericConvert( in->CfsFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConnectedFaceSet to be a `SET [1:?] OF IfcFace`")); } } while(0); return base; @@ -2505,7 +2505,7 @@ template <> size_t GenericFill(const DB& db, const LIST& boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcColourSpecification to be a `IfcLabel`")); } } while(0); return base; @@ -2516,17 +2516,17 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcColourRgb"); } do { // convert the 'Red' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Red, arg, db ); break; } + try { GenericConvert( in->Red, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Green' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Green, arg, db ); break; } + try { GenericConvert( in->Green, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Blue' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Blue, arg, db ); break; } + try { GenericConvert( in->Blue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); return base; @@ -2579,14 +2579,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProperty"); } do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'Description' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProperty to be a `IfcText`")); } } while(0); return base; @@ -2597,12 +2597,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcComplexProperty"); } do { // convert the 'UsageName' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->UsageName, arg, db ); break; } + try { GenericConvert( in->UsageName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcComplexProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'HasProperties' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcComplexProperty to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -2620,19 +2620,19 @@ template <> size_t GenericFill(const DB& db, const LIS if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcCompositeCurveSegment"); } do { // convert the 'Transition' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Transition, arg, db ); break; } + try { GenericConvert( in->Transition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurveSegment to be a `IfcTransitionCode`")); } } while(0); do { // convert the 'SameSense' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->SameSense, arg, db ); break; } + try { GenericConvert( in->SameSense, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurveSegment to be a `IfcBoolean`")); } } while(0); do { // convert the 'ParentCurve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->ParentCurve, arg, db ); break; } + try { GenericConvert( in->ParentCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCompositeCurveSegment to be a `IfcCurve`")); } } while(0); return base; @@ -2764,35 +2764,35 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectType, arg, db ); break; } + try { GenericConvert( in->ObjectType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcContext to be a `IfcLabel`")); } } while(0); do { // convert the 'LongName' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcContext to be a `IfcLabel`")); } } while(0); do { // convert the 'Phase' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Phase, arg, db ); break; } + try { GenericConvert( in->Phase, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcContext to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationContexts' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationContexts, arg, db ); break; } + try { GenericConvert( in->RepresentationContexts, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcContext to be a `SET [1:?] OF IfcRepresentationContext`")); } } while(0); do { // convert the 'UnitsInContext' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->UnitsInContext, arg, db ); break; } + try { GenericConvert( in->UnitsInContext, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcContext to be a `IfcUnitAssignment`")); } } while(0); return base; @@ -2804,13 +2804,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcNamedUnit"); } do { // convert the 'Dimensions' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Dimensions, arg, db ); break; } + try { GenericConvert( in->Dimensions, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcNamedUnit to be a `IfcDimensionalExponents`")); } } while(0); do { // convert the 'UnitType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->UnitType, arg, db ); break; } + try { GenericConvert( in->UnitType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcNamedUnit to be a `IfcUnitEnum`")); } } while(0); return base; @@ -2843,13 +2843,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcConversionBasedUnit"); } do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcConversionBasedUnit to be a `IfcLabel`")); } } while(0); do { // convert the 'ConversionFactor' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->ConversionFactor, arg, db ); break; } + try { GenericConvert( in->ConversionFactor, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcConversionBasedUnit to be a `IfcMeasureWithUnit`")); } } while(0); return base; @@ -2974,7 +2974,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyle to be a `IfcLabel`")); } } while(0); return base; @@ -2986,7 +2986,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcElementarySurface"); } do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcElementarySurface to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -3025,7 +3025,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcDirection"); } do { // convert the 'DirectionRatios' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->DirectionRatios, arg, db ); break; } + try { GenericConvert( in->DirectionRatios, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcDirection to be a `LIST [2:3] OF IfcReal`")); } } while(0); return base; @@ -3094,35 +3094,35 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcDoo boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallHeight, arg, db ); break; } + try { GenericConvert( in->OverallHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallWidth' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'PredefinedType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PredefinedType, arg, db ); break; } + try { GenericConvert( in->PredefinedType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcDoor to be a `IfcDoorTypeEnum`")); } } while(0); do { // convert the 'OperationType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OperationType, arg, db ); break; } + try { GenericConvert( in->OperationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcDoor to be a `IfcDoorTypeOperationEnum`")); } } while(0); do { // convert the 'UserDefinedOperationType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->UserDefinedOperationType, arg, db ); break; } + try { GenericConvert( in->UserDefinedOperationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 12 to IfcDoor to be a `IfcLabel`")); } } while(0); return base; @@ -3362,12 +3362,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcElementQuantity"); } do { // convert the 'MethodOfMeasurement' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } + try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcElementQuantity to be a `IfcLabel`")); } } while(0); do { // convert the 'Quantities' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Quantities, arg, db ); break; } + try { GenericConvert( in->Quantities, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcElementQuantity to be a `SET [1:?] OF IfcPhysicalQuantity`")); } } while(0); return base; @@ -3378,12 +3378,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcEllipse"); } do { // convert the 'SemiAxis1' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis1, arg, db ); break; } + try { GenericConvert( in->SemiAxis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'SemiAxis2' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis2, arg, db ); break; } + try { GenericConvert( in->SemiAxis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -3486,14 +3486,14 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcSweptAreaSolid"); } do { // convert the 'SweptArea' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SweptArea, arg, db ); break; } + try { GenericConvert( in->SweptArea, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptAreaSolid to be a `IfcProfileDef`")); } } while(0); do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptAreaSolid to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -3505,13 +3505,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcExtrudedAreaSolid"); } do { // convert the 'ExtrudedDirection' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } + try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcExtrudedAreaSolid to be a `IfcDirection`")); } } while(0); do { // convert the 'Depth' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Depth, arg, db ); break; } + try { GenericConvert( in->Depth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcExtrudedAreaSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -3529,7 +3529,7 @@ template <> size_t GenericFill(const DB& db, const LIS size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFaceBasedSurfaceModel"); } do { // convert the 'FbsmFaces' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->FbsmFaces, arg, db ); break; } + try { GenericConvert( in->FbsmFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBasedSurfaceModel to be a `SET [1:?] OF IfcConnectedFaceSet`")); } } while(0); return base; @@ -3541,13 +3541,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcFaceBound"); } do { // convert the 'Bound' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bound, arg, db ); break; } + try { GenericConvert( in->Bound, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBound to be a `IfcLoop`")); } } while(0); do { // convert the 'Orientation' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcFaceBound to be a `IfcBoolean`")); } } while(0); return base; @@ -3774,14 +3774,14 @@ template <> size_t GenericFill(const DB& db, const LIS boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextIdentifier, arg, db ); break; } + try { GenericConvert( in->ContextIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); do { // convert the 'ContextType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextType, arg, db ); break; } + try { GenericConvert( in->ContextType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); return base; @@ -3793,27 +3793,27 @@ template <> size_t GenericFill(const DB& db, if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcGeometricRepresentationContext"); } do { // convert the 'CoordinateSpaceDimension' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } + try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcGeometricRepresentationContext to be a `IfcDimensionCount`")); } } while(0); do { // convert the 'Precision' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Precision, arg, db ); break; } + try { GenericConvert( in->Precision, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcGeometricRepresentationContext to be a `IfcReal`")); } } while(0); do { // convert the 'WorldCoordinateSystem' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } + try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcGeometricRepresentationContext to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'TrueNorth' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TrueNorth, arg, db ); break; } + try { GenericConvert( in->TrueNorth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcGeometricRepresentationContext to be a `IfcDirection`")); } } while(0); return base; @@ -3879,40 +3879,40 @@ template <> size_t GenericFill(const DB& db, const LIST& pa size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 10) { throw STEP::TypeError("expected 10 arguments to IfcIShapeProfileDef"); } do { // convert the 'OverallWidth' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallDepth' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->OverallDepth, arg, db ); break; } + try { GenericConvert( in->OverallDepth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'WebThickness' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->WebThickness, arg, db ); break; } + try { GenericConvert( in->WebThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FlangeThickness' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->FlangeThickness, arg, db ); break; } + try { GenericConvert( in->FlangeThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FilletRadius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FilletRadius, arg, db ); break; } + try { GenericConvert( in->FilletRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcIShapeProfileDef to be a `IfcNonNegativeLengthMeasure`")); } } while(0); do { // convert the 'FlangeEdgeRadius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FlangeEdgeRadius, arg, db ); break; } + try { GenericConvert( in->FlangeEdgeRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcIShapeProfileDef to be a `IfcNonNegativeLengthMeasure`")); } } while(0); do { // convert the 'FlangeSlope' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FlangeSlope, arg, db ); break; } + try { GenericConvert( in->FlangeSlope, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcIShapeProfileDef to be a `IfcPlaneAngleMeasure`")); } } while(0); return base; @@ -4091,12 +4091,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcLin size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLine"); } do { // convert the 'Pnt' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Pnt, arg, db ); break; } + try { GenericConvert( in->Pnt, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLine to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Dir' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Dir, arg, db ); break; } + try { GenericConvert( in->Dir, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLine to be a `IfcVector`")); } } while(0); return base; @@ -4108,12 +4108,12 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLocalPlacement"); } do { // convert the 'PlacementRelTo' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PlacementRelTo, arg, db ); break; } + try { GenericConvert( in->PlacementRelTo, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLocalPlacement to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'RelativePlacement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelativePlacement, arg, db ); break; } + try { GenericConvert( in->RelativePlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLocalPlacement to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -4124,12 +4124,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMappedItem"); } do { // convert the 'MappingSource' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingSource, arg, db ); break; } + try { GenericConvert( in->MappingSource, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMappedItem to be a `IfcRepresentationMap`")); } } while(0); do { // convert the 'MappingTarget' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingTarget, arg, db ); break; } + try { GenericConvert( in->MappingTarget, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMappedItem to be a `IfcCartesianTransformationOperator`")); } } while(0); return base; @@ -4142,20 +4142,20 @@ template <> size_t GenericFill(const DB& db, const LIS boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProductRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProductRepresentation to be a `IfcText`")); } } while(0); do { // convert the 'Representations' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->Representations, arg, db ); break; } + try { GenericConvert( in->Representations, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcProductRepresentation to be a `LIST [1:?] OF IfcRepresentation`")); } } while(0); return base; @@ -4173,12 +4173,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMeasureWithUnit"); } do { // convert the 'ValueComponent' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->ValueComponent, arg, db ); break; } + try { GenericConvert( in->ValueComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMeasureWithUnit to be a `IfcValue`")); } } while(0); do { // convert the 'UnitComponent' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->UnitComponent, arg, db ); break; } + try { GenericConvert( in->UnitComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMeasureWithUnit to be a `IfcUnit`")); } } while(0); return base; @@ -4289,7 +4289,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PredefinedType, arg, db ); break; } + try { GenericConvert( in->PredefinedType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcOpeningElement to be a `IfcOpeningElementTypeEnum`")); } } while(0); return base; @@ -4460,7 +4460,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyLoop"); } do { // convert the 'Polygon' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Polygon, arg, db ); break; } + try { GenericConvert( in->Polygon, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyLoop to be a `LIST [3:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -4471,12 +4471,12 @@ template <> size_t GenericFill(const DB& db, const size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPolygonalBoundedHalfSpace"); } do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPolygonalBoundedHalfSpace to be a `IfcAxis2Placement3D`")); } } while(0); do { // convert the 'PolygonalBoundary' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } + try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPolygonalBoundedHalfSpace to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -4501,7 +4501,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyline"); } do { // convert the 'Points' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Points, arg, db ); break; } + try { GenericConvert( in->Points, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyline to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -4512,7 +4512,7 @@ template <> size_t GenericFill(const DB& db, con size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPresentationStyleAssignment"); } do { // convert the 'Styles' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyleAssignment to be a `SET [1:?] OF IfcPresentationStyleSelect`")); } } while(0); return base; @@ -4592,13 +4592,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertyListValue"); } do { // convert the 'ListValues' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ListValues, arg, db ); break; } + try { GenericConvert( in->ListValues, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertyListValue to be a `LIST [1:?] OF IfcValue`")); } } while(0); do { // convert the 'Unit' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertyListValue to be a `IfcUnit`")); } } while(0); return base; @@ -4616,7 +4616,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcPropertySet"); } do { // convert the 'HasProperties' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcPropertySet to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -4628,13 +4628,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertySingleValue"); } do { // convert the 'NominalValue' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->NominalValue, arg, db ); break; } + try { GenericConvert( in->NominalValue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertySingleValue to be a `IfcValue`")); } } while(0); do { // convert the 'Unit' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertySingleValue to be a `IfcUnit`")); } } while(0); return base; @@ -4758,13 +4758,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcRectangleProfileDef"); } do { // convert the 'XDim' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -4850,12 +4850,12 @@ template <> size_t GenericFill(const DB& db, const LIST& param size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelAggregates"); } do { // convert the 'RelatingObject' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingObject, arg, db ); break; } + try { GenericConvert( in->RelatingObject, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelAggregates to be a `IfcObjectDefinition`")); } } while(0); do { // convert the 'RelatedObjects' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelAggregates to be a `SET [1:?] OF IfcObjectDefinition`")); } } while(0); return base; @@ -4872,12 +4872,12 @@ template <> size_t GenericFill(const DB& db, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelContainedInSpatialStructure"); } do { // convert the 'RelatedElements' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedElements, arg, db ); break; } + try { GenericConvert( in->RelatedElements, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelContainedInSpatialStructure to be a `SET [1:?] OF IfcProduct`")); } } while(0); do { // convert the 'RelatingStructure' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingStructure, arg, db ); break; } + try { GenericConvert( in->RelatingStructure, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelContainedInSpatialStructure to be a `IfcSpatialElement`")); } } while(0); return base; @@ -4894,12 +4894,12 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelDefinesByProperties"); } do { // convert the 'RelatedObjects' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelDefinesByProperties to be a `SET [1:?] OF IfcObjectDefinition`")); } } while(0); do { // convert the 'RelatingPropertyDefinition' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } + try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelDefinesByProperties to be a `IfcPropertySetDefinitionSelect`")); } } while(0); return base; @@ -4910,12 +4910,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelFillsElement"); } do { // convert the 'RelatingOpeningElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelFillsElement to be a `IfcOpeningElement`")); } } while(0); do { // convert the 'RelatedBuildingElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelFillsElement to be a `IfcElement`")); } } while(0); return base; @@ -4926,12 +4926,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelVoidsElement"); } do { // convert the 'RelatingBuildingElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelVoidsElement to be a `IfcElement`")); } } while(0); do { // convert the 'RelatedOpeningElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelVoidsElement to be a `IfcFeatureElementSubtraction`")); } } while(0); return base; @@ -4950,27 +4950,27 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRepresentation"); } do { // convert the 'ContextOfItems' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ContextOfItems, arg, db ); break; } + try { GenericConvert( in->ContextOfItems, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentation to be a `IfcRepresentationContext`")); } } while(0); do { // convert the 'RepresentationIdentifier' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } + try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationType, arg, db ); break; } + try { GenericConvert( in->RepresentationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Items' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->Items, arg, db ); break; } + try { GenericConvert( in->Items, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRepresentation to be a `SET [1:?] OF IfcRepresentationItem`")); } } while(0); return base; @@ -4981,12 +4981,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcRepresentationMap"); } do { // convert the 'MappingOrigin' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingOrigin, arg, db ); break; } + try { GenericConvert( in->MappingOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationMap to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'MappedRepresentation' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappedRepresentation, arg, db ); break; } + try { GenericConvert( in->MappedRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationMap to be a `IfcRepresentation`")); } } while(0); return base; @@ -4998,13 +4998,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRevolvedAreaSolid"); } do { // convert the 'Axis' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRevolvedAreaSolid to be a `IfcAxis1Placement`")); } } while(0); do { // convert the 'Angle' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Angle, arg, db ); break; } + try { GenericConvert( in->Angle, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRevolvedAreaSolid to be a `IfcPlaneAngleMeasure`")); } } while(0); return base; @@ -5058,12 +5058,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcS if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcSIUnit"); } do { // convert the 'Prefix' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Prefix, arg, db ); break; } + try { GenericConvert( in->Prefix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSIUnit to be a `IfcSIPrefix`")); } } while(0); do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSIUnit to be a `IfcSIUnitName`")); } } while(0); return base; @@ -5144,7 +5144,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcShellBasedSurfaceModel"); } do { // convert the 'SbsmBoundary' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SbsmBoundary, arg, db ); break; } + try { GenericConvert( in->SbsmBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcShellBasedSurfaceModel to be a `SET [1:?] OF IfcShell`")); } } while(0); return base; @@ -5156,31 +5156,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSit if (params.GetSize() < 14) { throw STEP::TypeError("expected 14 arguments to IfcSite"); } do { // convert the 'RefLatitude' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLatitude, arg, db ); break; } + try { GenericConvert( in->RefLatitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefLongitude' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLongitude, arg, db ); break; } + try { GenericConvert( in->RefLongitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefElevation' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefElevation, arg, db ); break; } + try { GenericConvert( in->RefElevation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcSite to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'LandTitleNumber' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LandTitleNumber, arg, db ); break; } + try { GenericConvert( in->LandTitleNumber, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 12 to IfcSite to be a `IfcLabel`")); } } while(0); do { // convert the 'SiteAddress' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SiteAddress, arg, db ); break; } + try { GenericConvert( in->SiteAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 13 to IfcSite to be a `IfcPostalAddress`")); } } while(0); return base; @@ -5234,13 +5234,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSp if (params.GetSize() < 11) { throw STEP::TypeError("expected 11 arguments to IfcSpace"); } do { // convert the 'PredefinedType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PredefinedType, arg, db ); break; } + try { GenericConvert( in->PredefinedType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSpace to be a `IfcSpaceTypeEnum`")); } } while(0); do { // convert the 'ElevationWithFlooring' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } + try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSpace to be a `IfcLengthMeasure`")); } } while(0); return base; @@ -5539,18 +5539,18 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcStyledItem"); } do { // convert the 'Item' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Item, arg, db ); break; } + try { GenericConvert( in->Item, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcStyledItem to be a `IfcRepresentationItem`")); } } while(0); do { // convert the 'Styles' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcStyledItem to be a `SET [1:?] OF IfcStyleAssignmentSelect`")); } } while(0); do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcStyledItem to be a `IfcLabel`")); } } while(0); return base; @@ -5624,12 +5624,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcSurfaceStyle"); } do { // convert the 'Side' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Side, arg, db ); break; } + try { GenericConvert( in->Side, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyle to be a `IfcSurfaceSide`")); } } while(0); do { // convert the 'Styles' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyle to be a `SET [1:5] OF IfcSurfaceStyleElementSelect`")); } } while(0); return base; @@ -5641,14 +5641,14 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcSurfaceStyleShading"); } do { // convert the 'SurfaceColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SurfaceColour, arg, db ); break; } + try { GenericConvert( in->SurfaceColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleShading to be a `IfcColourRgb`")); } } while(0); do { // convert the 'Transparency' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Transparency, arg, db ); break; } + try { GenericConvert( in->Transparency, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyleShading to be a `IfcNormalisedRatioMeasure`")); } } while(0); return base; @@ -5660,42 +5660,42 @@ template <> size_t GenericFill(const DB& db, const LIS if (params.GetSize() < 9) { throw STEP::TypeError("expected 9 arguments to IfcSurfaceStyleRendering"); } do { // convert the 'DiffuseColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseColour, arg, db ); break; } + try { GenericConvert( in->DiffuseColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'TransmissionColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TransmissionColour, arg, db ); break; } + try { GenericConvert( in->TransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'DiffuseTransmissionColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } + try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'ReflectionColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ReflectionColour, arg, db ); break; } + try { GenericConvert( in->ReflectionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularColour, arg, db ); break; } + try { GenericConvert( in->SpecularColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularHighlight' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularHighlight, arg, db ); break; } + try { GenericConvert( in->SpecularHighlight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSurfaceStyleRendering to be a `IfcSpecularHighlightSelect`")); } } while(0); do { // convert the 'ReflectanceMethod' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } + try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSurfaceStyleRendering to be a `IfcReflectanceMethodEnum`")); } } while(0); return base; @@ -5706,7 +5706,7 @@ template <> size_t GenericFill(const DB& db, const size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcSurfaceStyleWithTextures"); } do { // convert the 'Textures' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Textures, arg, db ); break; } + try { GenericConvert( in->Textures, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleWithTextures to be a `LIST [1:?] OF IfcSurfaceTexture`")); } } while(0); return base; @@ -5718,34 +5718,34 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcSweptDiskSolid"); } do { // convert the 'Directrix' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Directrix, arg, db ); break; } + try { GenericConvert( in->Directrix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptDiskSolid to be a `IfcCurve`")); } } while(0); do { // convert the 'Radius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'InnerRadius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->InnerRadius, arg, db ); break; } + try { GenericConvert( in->InnerRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'StartParam' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->StartParam, arg, db ); break; } + try { GenericConvert( in->StartParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); do { // convert the 'EndParam' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->EndParam, arg, db ); break; } + try { GenericConvert( in->EndParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); return base; @@ -5924,27 +5924,27 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcTrimmedCurve"); } do { // convert the 'BasisCurve' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->BasisCurve, arg, db ); break; } + try { GenericConvert( in->BasisCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcTrimmedCurve to be a `IfcCurve`")); } } while(0); do { // convert the 'Trim1' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim1, arg, db ); break; } + try { GenericConvert( in->Trim1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'Trim2' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim2, arg, db ); break; } + try { GenericConvert( in->Trim2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'SenseAgreement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SenseAgreement, arg, db ); break; } + try { GenericConvert( in->SenseAgreement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcTrimmedCurve to be a `IfcBoolean`")); } } while(0); do { // convert the 'MasterRepresentation' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MasterRepresentation, arg, db ); break; } + try { GenericConvert( in->MasterRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcTrimmedCurve to be a `IfcTrimmingPreference`")); } } while(0); return base; @@ -5976,7 +5976,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcUnitAssignment"); } do { // convert the 'Units' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Units, arg, db ); break; } + try { GenericConvert( in->Units, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcUnitAssignment to be a `SET [1:?] OF IfcUnit`")); } } while(0); return base; @@ -6029,12 +6029,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcV size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcVector"); } do { // convert the 'Orientation' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcVector to be a `IfcDirection`")); } } while(0); do { // convert the 'Magnitude' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Magnitude, arg, db ); break; } + try { GenericConvert( in->Magnitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcVector to be a `IfcLengthMeasure`")); } } while(0); return base; diff --git a/code/AssetLib/IFC/IFCReaderGen_4.h b/code/AssetLib/IFC/IFCReaderGen_4.h index 0f184cd02..abf021911 100644 --- a/code/AssetLib/IFC/IFCReaderGen_4.h +++ b/code/AssetLib/IFC/IFCReaderGen_4.h @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -51,12 +51,12 @@ namespace Schema_4 { using namespace STEP; using namespace STEP::EXPRESS; - - + + struct NotImplemented : public ObjectHelper { - + }; - + // ****************************************************************************** // IFC Custom data types diff --git a/code/AssetLib/IFC/IFCUtil.h b/code/AssetLib/IFC/IFCUtil.h index a1190746b..b18f35052 100644 --- a/code/AssetLib/IFC/IFCUtil.h +++ b/code/AssetLib/IFC/IFCUtil.h @@ -54,6 +54,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include + struct aiNode; namespace Assimp { @@ -137,14 +139,10 @@ struct TempOpening } // ------------------------------------------------------------------------------ - TempOpening(const IFC::Schema_2x3::IfcSolidModel* solid,IfcVector3 extrusionDir, - std::shared_ptr profileMesh, - std::shared_ptr profileMesh2D) - : solid(solid) - , extrusionDir(extrusionDir) - , profileMesh(profileMesh) - , profileMesh2D(profileMesh2D) - { + TempOpening(const IFC::Schema_2x3::IfcSolidModel *solid, IfcVector3 extrusionDir, + std::shared_ptr profileMesh, + std::shared_ptr profileMesh2D) : + solid(solid), extrusionDir(extrusionDir), profileMesh(std::move(profileMesh)), profileMesh2D(std::move(profileMesh2D)) { } // ------------------------------------------------------------------------------ diff --git a/code/AssetLib/Irr/IRRLoader.h b/code/AssetLib/Irr/IRRLoader.h index 535f6481d..da90902ed 100644 --- a/code/AssetLib/Irr/IRRLoader.h +++ b/code/AssetLib/Irr/IRRLoader.h @@ -273,7 +273,7 @@ private: std::vector& anims); private: - /// Configuration option: desired output FPS + /// Configuration option: desired output FPS double fps; /// Configuration option: speed flag was set? diff --git a/code/AssetLib/LWO/LWOAnimation.h b/code/AssetLib/LWO/LWOAnimation.h index 1ed8caf88..64aa5980a 100644 --- a/code/AssetLib/LWO/LWOAnimation.h +++ b/code/AssetLib/LWO/LWOAnimation.h @@ -114,7 +114,7 @@ enum PrePostBehaviour /** \brief Data structure for a LWO animation keyframe */ struct Key { - Key() AI_NO_EXCEPT + Key() AI_NO_EXCEPT : time() , value() , inter(IT_LINE) diff --git a/code/AssetLib/LWS/LWSLoader.cpp b/code/AssetLib/LWS/LWSLoader.cpp index d469a1064..cb07787fa 100644 --- a/code/AssetLib/LWS/LWSLoader.cpp +++ b/code/AssetLib/LWS/LWSLoader.cpp @@ -200,7 +200,7 @@ void LWSImporter::ReadEnvelope(const LWS::Element &dad, LWO::Envelope &fill) { // reserve enough storage std::list::const_iterator it = dad.children.begin(); - + fill.keys.reserve(strtoul10(it->tokens[1].c_str())); for (++it; it != dad.children.end(); ++it) { @@ -318,7 +318,7 @@ void LWSImporter::SetupNodeName(aiNode *nd, LWS::NodeDesc &src) { } else { ++s; } - std::string::size_type t = src.path.substr(s).find_last_of("."); + std::string::size_type t = src.path.substr(s).find_last_of('.'); nd->mName.length = ::ai_snprintf(nd->mName.data, MAXLEN, "%s_(%08X)", src.path.substr(s).substr(0, t).c_str(), combined); return; @@ -466,7 +466,7 @@ std::string LWSImporter::FindLWOFile(const std::string &in) { std::string tmp(in); if (in.length() > 3 && in[1] == ':' && in[2] != '\\' && in[2] != '/') { tmp = in[0] + (std::string(":\\") + in.substr(2)); - } + } if (io->Exists(tmp)) { return in; diff --git a/code/AssetLib/M3D/M3DImporter.cpp b/code/AssetLib/M3D/M3DImporter.cpp index 38bbd1d4a..efa1d5475 100644 --- a/code/AssetLib/M3D/M3DImporter.cpp +++ b/code/AssetLib/M3D/M3DImporter.cpp @@ -233,12 +233,12 @@ void M3DImporter::importMaterials(const M3DWrapper &m3d) { ASSIMP_LOG_DEBUG("M3D: importMaterials ", mScene->mNumMaterials); // add a default material as first - aiMaterial *mat = new aiMaterial; - mat->AddProperty(&name, AI_MATKEY_NAME); + aiMaterial *defaultMat = new aiMaterial; + defaultMat->AddProperty(&name, AI_MATKEY_NAME); c.a = 1.0f; c.b = c.g = c.r = 0.6f; - mat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE); - mScene->mMaterials[0] = mat; + defaultMat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE); + mScene->mMaterials[0] = defaultMat; if (!m3d->nummaterial || !m3d->material) { return; @@ -300,12 +300,12 @@ void M3DImporter::importMaterials(const M3DWrapper &m3d) { m->prop[j].value.textureid < m3d->numtexture && m3d->texture[m->prop[j].value.textureid].name) { name.Set(std::string(std::string(m3d->texture[m->prop[j].value.textureid].name) + ".png")); - mat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index); + newMat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index); n = 0; - mat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index); + newMat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index); } } - mScene->mMaterials[i + 1] = mat; + mScene->mMaterials[i + 1] = newMat; } } @@ -655,7 +655,7 @@ void M3DImporter::convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned in // ------------------------------------------------------------------------------------------------ // find a node by name -aiNode *M3DImporter::findNode(aiNode *pNode, aiString name) { +aiNode *M3DImporter::findNode(aiNode *pNode, const aiString &name) { ai_assert(pNode != nullptr); ai_assert(mScene != nullptr); diff --git a/code/AssetLib/M3D/M3DImporter.h b/code/AssetLib/M3D/M3DImporter.h index 7a2a9fbd3..05e8ced7b 100644 --- a/code/AssetLib/M3D/M3DImporter.h +++ b/code/AssetLib/M3D/M3DImporter.h @@ -89,8 +89,8 @@ private: // helper functions aiColor4D mkColor(uint32_t c); void convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid); - aiNode *findNode(aiNode *pNode, aiString name); - void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m); + aiNode *findNode(aiNode *pNode, const aiString &name); + void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m); void populateMesh(const M3DWrapper &m3d, aiMesh *pMesh, std::vector *faces, std::vector *verteces, std::vector *normals, std::vector *texcoords, std::vector *colors, std::vector *vertexids); diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index 782e908d2..dcb82a83a 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef AI_M3DWRAPPER_H_INC #define AI_M3DWRAPPER_H_INC + #if !(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER #include @@ -55,44 +56,75 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Assimp specific M3D configuration. Comment out these defines to remove functionality //#define ASSIMP_USE_M3D_READFILECB +// Share stb_image's PNG loader with other importers/exporters instead of bringing our own copy. +#define STBI_ONLY_PNG +#include + #include "m3d.h" namespace Assimp { + class IOSystem; +/// brief The M3D-Wrapper, provudes c++ access to the data. class M3DWrapper { - m3d_t *m3d_ = nullptr; - unsigned char *saved_output_ = nullptr; - public: - // Construct an empty M3D model + /// Construct an empty M3D model explicit M3DWrapper(); - // Construct an M3D model from provided buffer - // NOTE: The m3d.h SDK function does not mark the data as const. Have assumed it does not write. - // BUG: SECURITY: The m3d.h SDK cannot be informed of the buffer size. BUFFER OVERFLOW IS CERTAIN + /// Construct an M3D model from provided buffer + /// @note The m3d.h SDK function does not mark the data as const. Have assumed it does not write. + /// BUG: SECURITY: The m3d.h SDK cannot be informed of the buffer size. BUFFER OVERFLOW IS CERTAIN explicit M3DWrapper(IOSystem *pIOHandler, const std::vector &buffer); - ~M3DWrapper(); + /// Theclasss destructor. + ~M3DWrapper(); - void reset(); + /// Will reset the wrapper, all data will become nullptr. + void reset(); - // Name - inline std::string Name() const { - if (m3d_) return std::string(m3d_->name); - return std::string(); - } + // The Name access, empty string returned when no m3d instance. + std::string Name() const; - // Execute a save + /// Executes a save. unsigned char *Save(int quality, int flags, unsigned int &size); + + /// Clearer void ClearSave(); - inline explicit operator bool() const { return m3d_ != nullptr; } + /// True for m3d instance exists. + explicit operator bool() const; // Allow direct access to M3D API - inline m3d_t *operator->() const { return m3d_; } - inline m3d_t *M3D() const { return m3d_; } + m3d_t *operator->() const; + m3d_t *M3D() const; + +private: + m3d_t *m3d_ = nullptr; + unsigned char *saved_output_ = nullptr; }; + +inline std::string M3DWrapper::Name() const { + if (nullptr != m3d_) { + if (nullptr != m3d_->name) { + return std::string(m3d_->name); + } + } + return std::string(); +} + +inline M3DWrapper::operator bool() const { + return m3d_ != nullptr; +} + +inline m3d_t *M3DWrapper::operator->() const { + return m3d_; +} + +inline m3d_t *M3DWrapper::M3D() const { + return m3d_; +} + } // namespace Assimp #endif diff --git a/code/AssetLib/M3D/m3d.h b/code/AssetLib/M3D/m3d.h index 68265959e..3adcd5bef 100644 --- a/code/AssetLib/M3D/m3d.h +++ b/code/AssetLib/M3D/m3d.h @@ -622,1230 +622,6 @@ static m3dcd_t m3d_commandtypes[] = { #include #include -#if !defined(M3D_NOIMPORTER) && !defined(STBI_INCLUDE_STB_IMAGE_H) -/* PNG decompressor from - - stb_image - v2.23 - public domain image loader - http://nothings.org/stb_image.h -*/ -static const char *_m3dstbi__g_failure_reason; - -enum { - STBI_default = 0, - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -enum { - STBI__SCAN_load = 0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -typedef unsigned short _m3dstbi_us; - -typedef uint16_t _m3dstbi__uint16; -typedef int16_t _m3dstbi__int16; -typedef uint32_t _m3dstbi__uint32; -typedef int32_t _m3dstbi__int32; - -typedef struct -{ - _m3dstbi__uint32 img_x, img_y; - int img_n, img_out_n; - - void *io_user_data; - - int read_from_callbacks; - int buflen; - unsigned char buffer_start[128]; - - unsigned char *img_buffer, *img_buffer_end; - unsigned char *img_buffer_original, *img_buffer_original_end; -} _m3dstbi__context; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} _m3dstbi__result_info; - -#define STBI_ASSERT(v) -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif -#define STBI__BYTECAST(x) ((unsigned char)((x)&255)) -#define STBI_MALLOC(sz) M3D_MALLOC(sz) -#define STBI_REALLOC(p, newsz) M3D_REALLOC(p, newsz) -#define STBI_FREE(p) M3D_FREE(p) -#define STBI_REALLOC_SIZED(p, oldsz, newsz) STBI_REALLOC(p, newsz) - -_inline static unsigned char _m3dstbi__get8(_m3dstbi__context *s) { - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - return 0; -} - -static void _m3dstbi__skip(_m3dstbi__context *s, int n) { - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - s->img_buffer += n; -} - -static int _m3dstbi__getn(_m3dstbi__context *s, unsigned char *buffer, int n) { - if (s->img_buffer + n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} - -static int _m3dstbi__get16be(_m3dstbi__context *s) { - int z = _m3dstbi__get8(s); - return (z << 8) + _m3dstbi__get8(s); -} - -static _m3dstbi__uint32 _m3dstbi__get32be(_m3dstbi__context *s) { - _m3dstbi__uint32 z = _m3dstbi__get16be(s); - return (z << 16) + _m3dstbi__get16be(s); -} - -#define _m3dstbi__err(x, y) _m3dstbi__errstr(y) -static int _m3dstbi__errstr(const char *str) { - _m3dstbi__g_failure_reason = str; - return 0; -} - -_inline static void *_m3dstbi__malloc(size_t size) { - return STBI_MALLOC(size); -} - -static int _m3dstbi__addsizes_valid(int a, int b) { - if (b < 0) return 0; - return a <= 2147483647 - b; -} - -static int _m3dstbi__mul2sizes_valid(int a, int b) { - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; - return a <= 2147483647 / b; -} - -static int _m3dstbi__mad2sizes_valid(int a, int b, int add) { - return _m3dstbi__mul2sizes_valid(a, b) && _m3dstbi__addsizes_valid(a * b, add); -} - -static int _m3dstbi__mad3sizes_valid(int a, int b, int c, int add) { - return _m3dstbi__mul2sizes_valid(a, b) && _m3dstbi__mul2sizes_valid(a * b, c) && - _m3dstbi__addsizes_valid(a * b * c, add); -} - -static void *_m3dstbi__malloc_mad2(int a, int b, int add) { - if (!_m3dstbi__mad2sizes_valid(a, b, add)) return NULL; - return _m3dstbi__malloc(a * b + add); -} - -static void *_m3dstbi__malloc_mad3(int a, int b, int c, int add) { - if (!_m3dstbi__mad3sizes_valid(a, b, c, add)) return NULL; - return _m3dstbi__malloc(a * b * c + add); -} - -static unsigned char _m3dstbi__compute_y(int r, int g, int b) { - return (unsigned char)(((r * 77) + (g * 150) + (29 * b)) >> 8); -} - -static unsigned char *_m3dstbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { - int i, j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *)_m3dstbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - _m3dstbi__err("outofmem", "Out of memory"); - return NULL; - } - - for (j = 0; j < (int)y; ++j) { - unsigned char *src = data + j * x * img_n; - unsigned char *dest = good + j * x * req_comp; - -#define STBI__COMBO(a, b) ((a)*8 + (b)) -#define STBI__CASE(a, b) \ - case STBI__COMBO(a, b): \ - for (i = x - 1; i >= 0; --i, src += a, dest += b) - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1, 2) { dest[0] = src[0], dest[1] = 255; } - break; - STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(1, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = 255; } - break; - STBI__CASE(2, 1) { dest[0] = src[0]; } - break; - STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(2, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = src[1]; } - break; - STBI__CASE(3, 4) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2], dest[3] = 255; } - break; - STBI__CASE(3, 1) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]); } - break; - STBI__CASE(3, 2) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]), dest[1] = 255; } - break; - STBI__CASE(4, 1) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]); } - break; - STBI__CASE(4, 2) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]), dest[1] = src[3]; } - break; - STBI__CASE(4, 3) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2]; } - break; - default: STBI_ASSERT(0); - } -#undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -static _m3dstbi__uint16 _m3dstbi__compute_y_16(int r, int g, int b) { - return (_m3dstbi__uint16)(((r * 77) + (g * 150) + (29 * b)) >> 8); -} - -static _m3dstbi__uint16 *_m3dstbi__convert_format16(_m3dstbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { - int i, j; - _m3dstbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (_m3dstbi__uint16 *)_m3dstbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - _m3dstbi__err("outofmem", "Out of memory"); - return NULL; - } - - for (j = 0; j < (int)y; ++j) { - _m3dstbi__uint16 *src = data + j * x * img_n; - _m3dstbi__uint16 *dest = good + j * x * req_comp; - -#define STBI__COMBO(a, b) ((a)*8 + (b)) -#define STBI__CASE(a, b) \ - case STBI__COMBO(a, b): \ - for (i = x - 1; i >= 0; --i, src += a, dest += b) - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1, 2) { dest[0] = src[0], dest[1] = 0xffff; } - break; - STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(1, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = 0xffff; } - break; - STBI__CASE(2, 1) { dest[0] = src[0]; } - break; - STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(2, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = src[1]; } - break; - STBI__CASE(3, 4) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2], dest[3] = 0xffff; } - break; - STBI__CASE(3, 1) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]); } - break; - STBI__CASE(3, 2) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]), dest[1] = 0xffff; } - break; - STBI__CASE(4, 1) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]); } - break; - STBI__CASE(4, 2) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]), dest[1] = src[3]; } - break; - STBI__CASE(4, 3) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2]; } - break; - default: STBI_ASSERT(0); - } -#undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -#define STBI__ZFAST_BITS 9 -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -typedef struct -{ - _m3dstbi__uint16 fast[1 << STBI__ZFAST_BITS]; - _m3dstbi__uint16 firstcode[16]; - int maxcode[17]; - _m3dstbi__uint16 firstsymbol[16]; - unsigned char size[288]; - _m3dstbi__uint16 value[288]; -} _m3dstbi__zhuffman; - -_inline static int _m3dstbi__bitreverse16(int n) { - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -_inline static int _m3dstbi__bit_reverse(int v, int bits) { - STBI_ASSERT(bits <= 16); - return _m3dstbi__bitreverse16(v) >> (16 - bits); -} - -static int _m3dstbi__zbuild_huffman(_m3dstbi__zhuffman *z, unsigned char *sizelist, int num) { - int i, k = 0; - int code, next_code[16], sizes[17]; - - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i = 0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i = 1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return _m3dstbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i = 1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (_m3dstbi__uint16)code; - z->firstsymbol[i] = (_m3dstbi__uint16)k; - code = (code + sizes[i]); - if (sizes[i]) - if (code - 1 >= (1 << i)) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - z->maxcode[i] = code << (16 - i); - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; - for (i = 0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - _m3dstbi__uint16 fastv = (_m3dstbi__uint16)((s << 9) | i); - z->size[c] = (unsigned char)s; - z->value[c] = (_m3dstbi__uint16)i; - if (s <= STBI__ZFAST_BITS) { - int j = _m3dstbi__bit_reverse(next_code[s], s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -typedef struct -{ - unsigned char *zbuffer, *zbuffer_end; - int num_bits; - _m3dstbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - _m3dstbi__zhuffman z_length, z_distance; -} _m3dstbi__zbuf; - -_inline static unsigned char _m3dstbi__zget8(_m3dstbi__zbuf *z) { - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; -} - -static void _m3dstbi__fill_bits(_m3dstbi__zbuf *z) { - do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= (unsigned int)_m3dstbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -_inline static unsigned int _m3dstbi__zreceive(_m3dstbi__zbuf *z, int n) { - unsigned int k; - if (z->num_bits < n) _m3dstbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int _m3dstbi__zhuffman_decode_slowpath(_m3dstbi__zbuf *a, _m3dstbi__zhuffman *z) { - int b, s, k; - k = _m3dstbi__bit_reverse(a->code_buffer, 16); - for (s = STBI__ZFAST_BITS + 1;; ++s) - if (k < z->maxcode[s]) - break; - if (s == 16) return -1; - b = (k >> (16 - s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -_inline static int _m3dstbi__zhuffman_decode(_m3dstbi__zbuf *a, _m3dstbi__zhuffman *z) { - int b, s; - if (a->num_bits < 16) _m3dstbi__fill_bits(a); - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return _m3dstbi__zhuffman_decode_slowpath(a, z); -} - -static int _m3dstbi__zexpand(_m3dstbi__zbuf *z, char *zout, int n) { - char *q; - int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return _m3dstbi__err("output buffer limit", "Corrupt PNG"); - cur = (int)(z->zout - z->zout_start); - limit = old_limit = (int)(z->zout_end - z->zout_start); - while (cur + n > limit) - limit *= 2; - q = (char *)STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return _m3dstbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static int _m3dstbi__zlength_base[31] = { - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, - 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, - 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 -}; - -static int _m3dstbi__zlength_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; - -static int _m3dstbi__zdist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; - -static int _m3dstbi__zdist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; - -static int _m3dstbi__parse_huffman_block(_m3dstbi__zbuf *a) { - char *zout = a->zout; - for (;;) { - int z = _m3dstbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return _m3dstbi__err("bad huffman code", "Corrupt PNG"); - if (zout >= a->zout_end) { - if (!_m3dstbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char)z; - } else { - unsigned char *p; - int len, dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = _m3dstbi__zlength_base[z]; - if (_m3dstbi__zlength_extra[z]) len += _m3dstbi__zreceive(a, _m3dstbi__zlength_extra[z]); - z = _m3dstbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return _m3dstbi__err("bad huffman code", "Corrupt PNG"); - dist = _m3dstbi__zdist_base[z]; - if (_m3dstbi__zdist_extra[z]) dist += _m3dstbi__zreceive(a, _m3dstbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return _m3dstbi__err("bad dist", "Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!_m3dstbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (unsigned char *)(zout - dist); - if (dist == 1) { - unsigned char v = *p; - if (len) { - do - *zout++ = v; - while (--len); - } - } else { - if (len) { - do - *zout++ = *p++; - while (--len); - } - } - } - } -} - -static int _m3dstbi__compute_huffman_codes(_m3dstbi__zbuf *a) { - static unsigned char length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - _m3dstbi__zhuffman z_codelength; - unsigned char lencodes[286 + 32 + 137]; - unsigned char codelength_sizes[19]; - int i, n; - - int hlit = _m3dstbi__zreceive(a, 5) + 257; - int hdist = _m3dstbi__zreceive(a, 5) + 1; - int hclen = _m3dstbi__zreceive(a, 4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i = 0; i < hclen; ++i) { - int s = _m3dstbi__zreceive(a, 3); - codelength_sizes[length_dezigzag[i]] = (unsigned char)s; - } - if (!_m3dstbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = _m3dstbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (unsigned char)c; - else { - unsigned char fill = 0; - if (c == 16) { - c = _m3dstbi__zreceive(a, 2) + 3; - if (n == 0) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n - 1]; - } else if (c == 17) - c = _m3dstbi__zreceive(a, 3) + 3; - else { - STBI_ASSERT(c == 18); - c = _m3dstbi__zreceive(a, 7) + 11; - } - if (ntot - n < c) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes + n, fill, c); - n += c; - } - } - if (n != ntot) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - if (!_m3dstbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!_m3dstbi__zbuild_huffman(&a->z_distance, lencodes + hlit, hdist)) return 0; - return 1; -} - -_inline static int _m3dstbi__parse_uncompressed_block(_m3dstbi__zbuf *a) { - unsigned char header[4]; - int len, nlen, k; - if (a->num_bits & 7) - _m3dstbi__zreceive(a, a->num_bits & 7); - k = 0; - while (a->num_bits > 0) { - header[k++] = (unsigned char)(a->code_buffer & 255); - a->code_buffer >>= 8; - a->num_bits -= 8; - } - STBI_ASSERT(a->num_bits == 0); - while (k < 4) - header[k++] = _m3dstbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return _m3dstbi__err("zlib corrupt", "Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return _m3dstbi__err("read past buffer", "Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!_m3dstbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int _m3dstbi__parse_zlib_header(_m3dstbi__zbuf *a) { - int cmf = _m3dstbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = _m3dstbi__zget8(a); - if ((cmf * 256 + flg) % 31 != 0) return _m3dstbi__err("bad zlib header", "Corrupt PNG"); - if (flg & 32) return _m3dstbi__err("no preset dict", "Corrupt PNG"); - if (cm != 8) return _m3dstbi__err("bad compression", "Corrupt PNG"); - return 1; -} - -static unsigned char _m3dstbi__zdefault_length[288], _m3dstbi__zdefault_distance[32]; -static void _m3dstbi__init_zdefaults(void) { - int i; - for (i = 0; i <= 143; ++i) - _m3dstbi__zdefault_length[i] = 8; - for (; i <= 255; ++i) - _m3dstbi__zdefault_length[i] = 9; - for (; i <= 279; ++i) - _m3dstbi__zdefault_length[i] = 7; - for (; i <= 287; ++i) - _m3dstbi__zdefault_length[i] = 8; - - for (i = 0; i <= 31; ++i) - _m3dstbi__zdefault_distance[i] = 5; -} - -static int _m3dstbi__parse_zlib(_m3dstbi__zbuf *a, int parse_header) { - int final, type; - if (parse_header) - if (!_m3dstbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = _m3dstbi__zreceive(a, 1); - type = _m3dstbi__zreceive(a, 2); - if (type == 0) { - if (!_m3dstbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - if (!_m3dstbi__zbuild_huffman(&a->z_length, _m3dstbi__zdefault_length, 288)) return 0; - if (!_m3dstbi__zbuild_huffman(&a->z_distance, _m3dstbi__zdefault_distance, 32)) return 0; - } else { - if (!_m3dstbi__compute_huffman_codes(a)) return 0; - } - if (!_m3dstbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int _m3dstbi__do_zlib(_m3dstbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) { - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - _m3dstbi__init_zdefaults(); - return _m3dstbi__parse_zlib(a, parse_header); -} - -char *_m3dstbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) { - _m3dstbi__zbuf a; - char *p = (char *)_m3dstbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (unsigned char *)buffer; - a.zbuffer_end = (unsigned char *)buffer + len; - if (_m3dstbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int)(a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -typedef struct -{ - _m3dstbi__uint32 length; - _m3dstbi__uint32 type; -} _m3dstbi__pngchunk; - -static _m3dstbi__pngchunk _m3dstbi__get_chunk_header(_m3dstbi__context *s) { - _m3dstbi__pngchunk c; - c.length = _m3dstbi__get32be(s); - c.type = _m3dstbi__get32be(s); - return c; -} - -_inline static int _m3dstbi__check_png_header(_m3dstbi__context *s) { - static unsigned char png_sig[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; - int i; - for (i = 0; i < 8; ++i) - if (_m3dstbi__get8(s) != png_sig[i]) return _m3dstbi__err("bad png sig", "Not a PNG"); - return 1; -} - -typedef struct -{ - _m3dstbi__context *s; - unsigned char *idata, *expanded, *out; - int depth; -} _m3dstbi__png; - -enum { - STBI__F_none = 0, - STBI__F_sub = 1, - STBI__F_up = 2, - STBI__F_avg = 3, - STBI__F_paeth = 4, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static unsigned char first_row_filter[5] = { - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int _m3dstbi__paeth(int a, int b, int c) { - int p = a + b - c; - int pa = abs(p - a); - int pb = abs(p - b); - int pc = abs(p - c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static unsigned char _m3dstbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01 }; - -static int _m3dstbi__create_png_image_raw(_m3dstbi__png *a, unsigned char *raw, _m3dstbi__uint32 raw_len, int out_n, _m3dstbi__uint32 x, _m3dstbi__uint32 y, int depth, int color) { - int bytes = (depth == 16 ? 2 : 1); - _m3dstbi__context *s = a->s; - _m3dstbi__uint32 i, j, stride = x * out_n * bytes; - _m3dstbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; - - int output_bytes = out_n * bytes; - int filter_bytes = img_n * bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n + 1); - a->out = (unsigned char *)_m3dstbi__malloc_mad3(x, y, output_bytes, 0); - if (!a->out) return _m3dstbi__err("outofmem", "Out of memory"); - - if (!_m3dstbi__mad3sizes_valid(img_n, x, depth, 7)) return _m3dstbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - if (s->img_x == x && s->img_y == y) { - if (raw_len != img_len) return _m3dstbi__err("not enough pixels", "Corrupt PNG"); - } else { - if (raw_len < img_len) return _m3dstbi__err("not enough pixels", "Corrupt PNG"); - } - - for (j = 0; j < y; ++j) { - unsigned char *cur = a->out + stride * j; - unsigned char *prior = cur - stride; - int filter = *raw++; - - if (filter > 4) - return _m3dstbi__err("invalid filter", "Corrupt PNG"); - - if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); - cur += x * out_n - img_width_bytes; - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; - - if (j == 0) filter = first_row_filter[filter]; - - for (k = 0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none: cur[k] = raw[k]; break; - case STBI__F_sub: cur[k] = raw[k]; break; - case STBI__F_up: cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg: cur[k] = STBI__BYTECAST(raw[k] + (prior[k] >> 1)); break; - case STBI__F_paeth: cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(0, prior[k], 0)); break; - case STBI__F_avg_first: cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; - cur[filter_bytes + 1] = 255; - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - if (depth < 8 || img_n == out_n) { - int nk = (width - 1) * filter_bytes; -#define STBI__CASE(f) \ - case f: \ - for (k = 0; k < nk; ++k) - switch (filter) { - case STBI__F_none: - memcpy(cur, raw, nk); - break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k - filter_bytes]); } - break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } - break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k - filter_bytes]) >> 1)); } - break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - filter_bytes], prior[k], prior[k - filter_bytes])); } - break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k - filter_bytes] >> 1)); } - break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - filter_bytes], 0, 0)); } - break; - } -#undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n + 1 == out_n); -#define STBI__CASE(f) \ - case f: \ - for (i = x - 1; i >= 1; --i, cur[filter_bytes] = 255, raw += filter_bytes, cur += output_bytes, prior += output_bytes) \ - for (k = 0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } - break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k - output_bytes]); } - break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } - break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k - output_bytes]) >> 1)); } - break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - output_bytes], prior[k], prior[k - output_bytes])); } - break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k - output_bytes] >> 1)); } - break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - output_bytes], 0, 0)); } - break; - } -#undef STBI__CASE - - if (depth == 16) { - cur = a->out + stride * j; - for (i = 0; i < x; ++i, cur += output_bytes) { - cur[filter_bytes + 1] = 255; - } - } - } - } - - if (depth < 8) { - for (j = 0; j < y; ++j) { - unsigned char *cur = a->out + stride * j; - unsigned char *in = a->out + stride * j + x * out_n - img_width_bytes; - unsigned char scale = (color == 0) ? _m3dstbi__depth_scale_table[depth] : 1; - - if (depth == 4) { - for (k = x * img_n; k >= 2; k -= 2, ++in) { - *cur++ = scale * ((*in >> 4)); - *cur++ = scale * ((*in) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4)); - } else if (depth == 2) { - for (k = x * img_n; k >= 4; k -= 4, ++in) { - *cur++ = scale * ((*in >> 6)); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6)); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k = x * img_n; k >= 8; k -= 8, ++in) { - *cur++ = scale * ((*in >> 7)); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7)); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - cur = a->out + stride * j; - if (img_n == 1) { - for (q = x - 1; q >= 0; --q) { - cur[q * 2 + 1] = 255; - cur[q * 2 + 0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q = x - 1; q >= 0; --q) { - cur[q * 4 + 3] = 255; - cur[q * 4 + 2] = cur[q * 3 + 2]; - cur[q * 4 + 1] = cur[q * 3 + 1]; - cur[q * 4 + 0] = cur[q * 3 + 0]; - } - } - } - } - } else if (depth == 16) { - unsigned char *cur = a->out; - _m3dstbi__uint16 *cur16 = (_m3dstbi__uint16 *)cur; - - for (i = 0; i < x * y * out_n; ++i, cur16++, cur += 2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } - - return 1; -} - -static int _m3dstbi__create_png_image(_m3dstbi__png *a, unsigned char *image_data, _m3dstbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) { - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - unsigned char *final; - int p; - if (!interlaced) - return _m3dstbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - final = (unsigned char *)_m3dstbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - for (p = 0; p < 7; ++p) { - int xorig[] = { 0, 4, 0, 2, 0, 1, 0 }; - int yorig[] = { 0, 0, 4, 0, 2, 0, 1 }; - int xspc[] = { 8, 8, 4, 4, 2, 2, 1 }; - int yspc[] = { 8, 8, 8, 4, 4, 2, 2 }; - int i, j, x, y; - x = (a->s->img_x - xorig[p] + xspc[p] - 1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p] - 1) / yspc[p]; - if (x && y) { - _m3dstbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!_m3dstbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j = 0; j < y; ++j) { - for (i = 0; i < x; ++i) { - int out_y = j * yspc[p] + yorig[p]; - int out_x = i * xspc[p] + xorig[p]; - memcpy(final + out_y * a->s->img_x * out_bytes + out_x * out_bytes, - a->out + (j * x + i) * out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int _m3dstbi__compute_transparency(_m3dstbi__png *z, unsigned char* tc, int out_n) { - _m3dstbi__context *s = z->s; - _m3dstbi__uint32 i, pixel_count = s->img_x * s->img_y; - unsigned char *p = z->out; - - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int _m3dstbi__compute_transparency16(_m3dstbi__png *z, _m3dstbi__uint16 tc[3], int out_n) { - _m3dstbi__context *s = z->s; - _m3dstbi__uint32 i, pixel_count = s->img_x * s->img_y; - _m3dstbi__uint16 *p = (_m3dstbi__uint16 *)z->out; - - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int _m3dstbi__expand_png_palette(_m3dstbi__png *a, unsigned char *palette, int len, int pal_img_n) { - _m3dstbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - unsigned char *p, *temp_out, *orig = a->out; - - p = (unsigned char *)_m3dstbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return _m3dstbi__err("outofmem", "Out of memory"); - - temp_out = p; - - if (pal_img_n == 3) { - for (i = 0; i < pixel_count; ++i) { - int n = orig[i] * 4; - p[0] = palette[n]; - p[1] = palette[n + 1]; - p[2] = palette[n + 2]; - p += 3; - } - } else { - for (i = 0; i < pixel_count; ++i) { - int n = orig[i] * 4; - p[0] = palette[n]; - p[1] = palette[n + 1]; - p[2] = palette[n + 2]; - p[3] = palette[n + 3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -#define STBI__PNG_TYPE(a, b, c, d) (((unsigned)(a) << 24) + ((unsigned)(b) << 16) + ((unsigned)(c) << 8) + (unsigned)(d)) - -static int _m3dstbi__parse_png_file(_m3dstbi__png *z, int scan, int req_comp) { - unsigned char palette[1024], pal_img_n = 0; - unsigned char has_trans = 0, tc[3] = {}; - _m3dstbi__uint16 tc16[3] = {}; - _m3dstbi__uint32 ioff = 0, idata_limit = 0, i, pal_len = 0; - int first = 1, k, interlace = 0, color = 0; - _m3dstbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!_m3dstbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - _m3dstbi__pngchunk c = _m3dstbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C', 'g', 'B', 'I'): - _m3dstbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I', 'H', 'D', 'R'): { - int comp, filter; - if (!first) return _m3dstbi__err("multiple IHDR", "Corrupt PNG"); - first = 0; - if (c.length != 13) return _m3dstbi__err("bad IHDR len", "Corrupt PNG"); - s->img_x = _m3dstbi__get32be(s); - if (s->img_x > (1 << 24)) return _m3dstbi__err("too large", "Very large image (corrupt?)"); - s->img_y = _m3dstbi__get32be(s); - if (s->img_y > (1 << 24)) return _m3dstbi__err("too large", "Very large image (corrupt?)"); - z->depth = _m3dstbi__get8(s); - if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return _m3dstbi__err("1/2/4/8/16-bit only", "PNG not supported: 1/2/4/8/16-bit only"); - color = _m3dstbi__get8(s); - if (color > 6) return _m3dstbi__err("bad ctype", "Corrupt PNG"); - if (color == 3 && z->depth == 16) return _m3dstbi__err("bad ctype", "Corrupt PNG"); - if (color == 3) - pal_img_n = 3; - else if (color & 1) - return _m3dstbi__err("bad ctype", "Corrupt PNG"); - comp = _m3dstbi__get8(s); - if (comp) return _m3dstbi__err("bad comp method", "Corrupt PNG"); - filter = _m3dstbi__get8(s); - if (filter) return _m3dstbi__err("bad filter method", "Corrupt PNG"); - interlace = _m3dstbi__get8(s); - if (interlace > 1) return _m3dstbi__err("bad interlace method", "Corrupt PNG"); - if (!s->img_x || !s->img_y) return _m3dstbi__err("0-pixel image", "Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return _m3dstbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return _m3dstbi__err("too large", "Corrupt PNG"); - } - break; - } - - case STBI__PNG_TYPE('P', 'L', 'T', 'E'): { - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256 * 3) return _m3dstbi__err("invalid PLTE", "Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return _m3dstbi__err("invalid PLTE", "Corrupt PNG"); - for (i = 0; i < pal_len; ++i) { - palette[i * 4 + 0] = _m3dstbi__get8(s); - palette[i * 4 + 1] = _m3dstbi__get8(s); - palette[i * 4 + 2] = _m3dstbi__get8(s); - palette[i * 4 + 3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t', 'R', 'N', 'S'): { - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return _m3dstbi__err("tRNS after IDAT", "Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { - s->img_n = 4; - return 1; - } - if (pal_len == 0) return _m3dstbi__err("tRNS before PLTE", "Corrupt PNG"); - if (c.length > pal_len) return _m3dstbi__err("bad tRNS len", "Corrupt PNG"); - pal_img_n = 4; - for (i = 0; i < c.length; ++i) - palette[i * 4 + 3] = _m3dstbi__get8(s); - } else { - if (!(s->img_n & 1)) return _m3dstbi__err("tRNS with alpha", "Corrupt PNG"); - if (c.length != (_m3dstbi__uint32)s->img_n * 2) return _m3dstbi__err("bad tRNS len", "Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) - tc16[k] = (_m3dstbi__uint16)_m3dstbi__get16be(s); - } else { - for (k = 0; k < s->img_n; ++k) - tc[k] = (unsigned char)(_m3dstbi__get16be(s) & 255) * _m3dstbi__depth_scale_table[z->depth]; - } - } - break; - } - - case STBI__PNG_TYPE('I', 'D', 'A', 'T'): { - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return _m3dstbi__err("no PLTE", "Corrupt PNG"); - if (scan == STBI__SCAN_header) { - s->img_n = pal_img_n; - return 1; - } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - _m3dstbi__uint32 idata_limit_old = idata_limit; - unsigned char *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (unsigned char *)STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); - if (p == NULL) return _m3dstbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!_m3dstbi__getn(s, z->idata + ioff, c.length)) return _m3dstbi__err("outofdata", "Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I', 'E', 'N', 'D'): { - _m3dstbi__uint32 raw_len, bpl; - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return _m3dstbi__err("no IDAT", "Corrupt PNG"); - bpl = (s->img_x * z->depth + 7) / 8; - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (unsigned char *)_m3dstbi_zlib_decode_malloc_guesssize_headerflag((char *)z->idata, ioff, raw_len, (int *)&raw_len, 1); - if (z->expanded == NULL) return 0; - STBI_FREE(z->idata); - z->idata = NULL; - if ((req_comp == s->img_n + 1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n + 1; - else - s->img_out_n = s->img_n; - if (!_m3dstbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!_m3dstbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!_m3dstbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (pal_img_n) { - s->img_n = pal_img_n; - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!_m3dstbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - ++s->img_n; - } - STBI_FREE(z->expanded); - z->expanded = NULL; - return 1; - } - - default: - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - return _m3dstbi__err("invalid_chunk", "PNG not supported: unknown PNG chunk type"); - } - _m3dstbi__skip(s, c.length); - break; - } - _m3dstbi__get32be(s); - } -} - -static void *_m3dstbi__do_png(_m3dstbi__png *p, int *x, int *y, int *n, int req_comp, _m3dstbi__result_info *ri) { - void *result = NULL; - if (req_comp < 0 || req_comp > 4) { - _m3dstbi__err("bad req_comp", "Internal error"); - return NULL; - } - if (_m3dstbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth < 8) - ri->bits_per_channel = 8; - else - ri->bits_per_channel = p->depth; - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = _m3dstbi__convert_format((unsigned char *)result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = _m3dstbi__convert_format16((_m3dstbi__uint16 *)result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); - p->out = NULL; - STBI_FREE(p->expanded); - p->expanded = NULL; - STBI_FREE(p->idata); - p->idata = NULL; - - return result; -} - -static void *_m3dstbi__png_load(_m3dstbi__context *s, int *x, int *y, int *comp, int req_comp, _m3dstbi__result_info *ri) { - _m3dstbi__png p; - p.s = s; - return _m3dstbi__do_png(&p, x, y, comp, req_comp, ri); -} -#define stbi__context _m3dstbi__context -#define stbi__result_info _m3dstbi__result_info -#define stbi__png_load _m3dstbi__png_load -#define stbi_zlib_decode_malloc_guesssize_headerflag _m3dstbi_zlib_decode_malloc_guesssize_headerflag -#endif - #if defined(M3D_EXPORTER) && !defined(INCLUDE_STB_IMAGE_WRITE_H) /* zlib_compressor from @@ -2053,7 +829,7 @@ unsigned char *_m3dstbi_zlib_compress(unsigned char *data, int data_len, int *ou #include #endif -#if !defined(M3D_NOIMPORTER) +#if !defined(M3D_NOIMPORTER) /* helper functions for the ASCII parser */ static char *_m3d_findarg(char *s) { while (s && *s && *s != ' ' && *s != '\t' && *s != '\r' && *s != '\n') @@ -5740,7 +4516,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size } if (length) { uint32_t v = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t *)h + len)); - memcpy( length, &v, sizeof(uint32_t)); + memcpy( length, &v, sizeof(uint32_t)); len += v; } out = NULL; @@ -5772,7 +4548,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size } } uint32_t v = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t *)h + len)); - memcpy( length, &v, sizeof(uint32_t)); + memcpy( length, &v, sizeof(uint32_t)); len += v; out = NULL; } diff --git a/code/AssetLib/MD5/MD5Loader.cpp b/code/AssetLib/MD5/MD5Loader.cpp index 0d9c77b71..9fba60c61 100644 --- a/code/AssetLib/MD5/MD5Loader.cpp +++ b/code/AssetLib/MD5/MD5Loader.cpp @@ -485,7 +485,7 @@ void MD5Importer::LoadMD5MeshFile() { } MD5::WeightDesc &weightDesc = meshSrc.mWeights[w]; - if (weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON) { + if (weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON) { continue; } diff --git a/code/AssetLib/MDC/MDCFileData.h b/code/AssetLib/MDC/MDCFileData.h index 616556f03..a8f3aea43 100644 --- a/code/AssetLib/MDC/MDCFileData.h +++ b/code/AssetLib/MDC/MDCFileData.h @@ -120,13 +120,13 @@ struct Surface { , ulFlags() , ulNumCompFrames() , ulNumBaseFrames() - , ulNumShaders() + , ulNumShaders() , ulNumVertices() , ulNumTriangles() , ulOffsetTriangles() , ulOffsetShaders() , ulOffsetTexCoords() - , ulOffsetBaseVerts() + , ulOffsetBaseVerts() , ulOffsetCompVerts() , ulOffsetFrameBaseFrames() , ulOffsetFrameCompFrames() diff --git a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp index e6576b344..1ff86fe27 100644 --- a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp +++ b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp @@ -629,7 +629,7 @@ void HL1MDLLoader::read_meshes() { +-- bodypart --+-- model -- [mesh index, mesh index, ...] | | | +-- model -- [mesh index, mesh index, ...] - | | + | | | ... | |-- bodypart -- ... @@ -1298,7 +1298,7 @@ void HL1MDLLoader::read_global_info() { * @note The structure of this method is taken from HL2 source code. * Although this is from HL2, it's implementation is almost identical * to code found in HL1 SDK. See HL1 and HL2 SDKs for more info. -* +* * source: * HL1 source code. * file: studio_render.cpp diff --git a/code/AssetLib/MMD/MMDPmxParser.cpp b/code/AssetLib/MMD/MMDPmxParser.cpp index d57dc169a..be3b10248 100644 --- a/code/AssetLib/MMD/MMDPmxParser.cpp +++ b/code/AssetLib/MMD/MMDPmxParser.cpp @@ -102,7 +102,7 @@ namespace pmx const unsigned int targetSize = size * 3; // enough to encode char *targetStart = new char[targetSize]; std::memset(targetStart, 0, targetSize * sizeof(char)); - + utf8::utf16to8( sourceStart, sourceStart + size/2, targetStart ); std::string result(targetStart); @@ -516,13 +516,13 @@ namespace pmx stream->read((char*) magic, sizeof(char) * 4); if (magic[0] != 0x50 || magic[1] != 0x4d || magic[2] != 0x58 || magic[3] != 0x20) { - throw DeadlyImportError("MMD: Invalid magic number."); - } + throw DeadlyImportError("MMD: Invalid magic number."); + } stream->read((char*) &version, sizeof(float)); if (version != 2.0f && version != 2.1f) { throw DeadlyImportError("MMD: Unsupported version (must be 2.0 or 2.1): ", ai_to_string(version)); - } + } this->setting.Read(stream); this->model_name = ReadString(stream, setting.encoding); diff --git a/code/AssetLib/OFF/OFFLoader.cpp b/code/AssetLib/OFF/OFFLoader.cpp index 360ffc51b..fb8f3b424 100644 --- a/code/AssetLib/OFF/OFFLoader.cpp +++ b/code/AssetLib/OFF/OFFLoader.cpp @@ -138,7 +138,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS const char* car = buffer; const char* end = buffer + mBuffer2.size(); NextToken(&car, end); - + if (car < end - 2 && car[0] == 'S' && car[1] == 'T') { hasTexCoord = true; car += 2; } @@ -164,7 +164,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS dimensions = 3; hasHomogenous = false; NextToken(&car, end); - + // at this point the next token should be an integer number if (car >= end - 1 || *car < '0' || *car > '9') { throw DeadlyImportError("OFF: Header is invalid"); @@ -223,7 +223,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS ASSIMP_LOG_ERROR("OFF: The number of verts in the header is incorrect"); break; } - aiVector3D& v = mesh->mVertices[i]; + aiVector3D& v = mesh->mVertices[i]; sz = line; // helper array to write a for loop over possible dimension values @@ -255,7 +255,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS SkipSpaces(&sz); fast_atoreal_move(sz,(ai_real&)n.z); } - + // reading colors is a pain because the specification says it can be // integers or floats, and any number of them between 1 and 4 included, // until the next comment or end of line @@ -321,7 +321,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS ++i; ++faces; } - + // generate the output node graph pScene->mRootNode = new aiNode(); pScene->mRootNode->mName.Set(""); diff --git a/code/AssetLib/Obj/ObjExporter.h b/code/AssetLib/Obj/ObjExporter.h index 3a46da780..a64f38f74 100644 --- a/code/AssetLib/Obj/ObjExporter.h +++ b/code/AssetLib/Obj/ObjExporter.h @@ -67,7 +67,7 @@ public: ~ObjExporter(); std::string GetMaterialLibName(); std::string GetMaterialLibFileName(); - + /// public string-streams to write all output into std::ostringstream mOutput, mOutputMat; @@ -137,13 +137,13 @@ private: } }; - struct aiVectorCompare { - bool operator() (const aiVector3D& a, const aiVector3D& b) const { - if(a.x < b.x) return true; - if(a.x > b.x) return false; - if(a.y < b.y) return true; - if(a.y > b.y) return false; - if(a.z < b.z) return true; + struct aiVectorCompare { + bool operator() (const aiVector3D& a, const aiVector3D& b) const { + if(a.x < b.x) return true; + if(a.x > b.x) return false; + if(a.y < b.y) return true; + if(a.y > b.y) return false; + if(a.z < b.z) return true; return false; } }; @@ -153,7 +153,7 @@ private: int mNextIndex; typedef std::map dataType; dataType vecMap; - + public: indexMap() : mNextIndex(1) { diff --git a/code/AssetLib/Obj/ObjFileImporter.cpp b/code/AssetLib/Obj/ObjFileImporter.cpp index d6232be81..4e943fb5f 100644 --- a/code/AssetLib/Obj/ObjFileImporter.cpp +++ b/code/AssetLib/Obj/ObjFileImporter.cpp @@ -162,7 +162,7 @@ void ObjFileImporter::InternReadFile(const std::string &file, aiScene *pScene, I // ------------------------------------------------------------------------------------------------ // Create the data from parsed obj-file void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene *pScene) { - if (0L == pModel) { + if (nullptr == pModel) { return; } @@ -468,7 +468,7 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model *pModel, } // Copy all vertex colors - if (!pModel->m_VertexColors.empty()) { + if (vertex < pModel->m_VertexColors.size()) { const aiVector3D &color = pModel->m_VertexColors[vertex]; pMesh->mColors[0][newIndex] = aiColor4D(color.x, color.y, color.z, 1.0); } diff --git a/code/AssetLib/Obj/ObjFileMtlImporter.cpp b/code/AssetLib/Obj/ObjFileMtlImporter.cpp index bf1b70c90..94e57c26b 100644 --- a/code/AssetLib/Obj/ObjFileMtlImporter.cpp +++ b/code/AssetLib/Obj/ObjFileMtlImporter.cpp @@ -146,7 +146,7 @@ void ObjFileMtlImporter::load() { ++m_DataIt; ai_real d; getFloatValue(d); - m_pModel->m_pCurrentMaterial->alpha = static_cast(1.0) - d; + m_pModel->m_pCurrentMaterial->alpha = static_cast(1.0) - d; } m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); } break; diff --git a/code/AssetLib/Ogre/OgreMaterial.cpp b/code/AssetLib/Ogre/OgreMaterial.cpp index 295dedde6..6da82f89c 100644 --- a/code/AssetLib/Ogre/OgreMaterial.cpp +++ b/code/AssetLib/Ogre/OgreMaterial.cpp @@ -415,8 +415,8 @@ bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstr // User defined Assimp config property to detect texture type from filename. if (m_detectTextureTypeFromFilename) { - size_t posSuffix = textureRef.find_last_of("."); - size_t posUnderscore = textureRef.find_last_of("_"); + size_t posSuffix = textureRef.find_last_of('.'); + size_t posUnderscore = textureRef.find_last_of('_'); if (posSuffix != string::npos && posUnderscore != string::npos && posSuffix > posUnderscore) { string identifier = ai_tolower(textureRef.substr(posUnderscore, posSuffix - posUnderscore)); diff --git a/code/AssetLib/Ply/PlyParser.cpp b/code/AssetLib/Ply/PlyParser.cpp index 59cb6b976..8af0edfdc 100644 --- a/code/AssetLib/Ply/PlyParser.cpp +++ b/code/AssetLib/Ply/PlyParser.cpp @@ -419,8 +419,7 @@ bool PLY::DOM::ParseHeader(IOStreamBuffer &streamBuffer, std::vector if (PLY::Element::ParseElement(streamBuffer, buffer, &out)) { // add the element to the list of elements alElements.push_back(out); - } else if ( TokenMatch(buffer, "end_header\r", 11) || //checks for header end with /r/n ending - TokenMatch(buffer, "end_header", 10)) { //checks for /n ending, if it doesn't end with /r/n + } else if (TokenMatch(buffer, "end_header", 10)) { //checks for /n ending, if it doesn't end with /r/n // we have reached the end of the header break; } else { @@ -501,6 +500,11 @@ bool PLY::DOM::ParseInstanceBinary(IOStreamBuffer &streamBuffer, DOM *p_pc } streamBuffer.getNextBlock(buffer); + + // remove first char if it's /n in case of file with /r/n + if (((char *)&buffer[0])[0] == '\n') + buffer.erase(buffer.begin(), buffer.begin() + 1); + unsigned int bufferSize = static_cast(buffer.size()); const char *pCur = (char *)&buffer[0]; if (!p_pcOut->ParseElementInstanceListsBinary(streamBuffer, buffer, pCur, bufferSize, loader, p_bBE)) { diff --git a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp index becfa41fc..a1da8fd74 100644 --- a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp +++ b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp @@ -99,7 +99,7 @@ static void extractIds(const std::string &key, int &id1, int &id2) { return; } - const std::string::size_type pos = key.find("."); + const std::string::size_type pos = key.find('.'); if (std::string::npos == pos) { return; } @@ -208,7 +208,7 @@ void Q3BSPFileImporter::separateMapName(const std::string &importName, std::stri return; } - const std::string::size_type pos = importName.rfind(","); + const std::string::size_type pos = importName.rfind(','); if (std::string::npos == pos) { archiveName = importName; return; diff --git a/code/AssetLib/SMD/SMDLoader.cpp b/code/AssetLib/SMD/SMDLoader.cpp index de9c65c4a..90f0b7697 100644 --- a/code/AssetLib/SMD/SMDLoader.cpp +++ b/code/AssetLib/SMD/SMDLoader.cpp @@ -438,7 +438,7 @@ void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent) { pc->mTransformation = bone.sAnim.asKeys[0].matrix; } - if (bone.iParent == static_cast(-1)) { + if (bone.iParent == static_cast(-1)) { bone.mOffsetMatrix = pc->mTransformation; } else { bone.mOffsetMatrix = asBones[bone.iParent].mOffsetMatrix * pc->mTransformation; diff --git a/code/AssetLib/STEPParser/STEPFileReader.cpp b/code/AssetLib/STEPParser/STEPFileReader.cpp index e97ea1e28..360277912 100644 --- a/code/AssetLib/STEPParser/STEPFileReader.cpp +++ b/code/AssetLib/STEPParser/STEPFileReader.cpp @@ -49,21 +49,22 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "STEPFileEncoding.h" #include #include -#include #include +#include +#include using namespace Assimp; namespace EXPRESS = STEP::EXPRESS; // ------------------------------------------------------------------------------------------------ -std::string AddLineNumber(const std::string& s,uint64_t line /*= LINE_NOT_SPECIFIED*/, const std::string& prefix = "") +std::string AddLineNumber(const std::string& s,uint64_t line /*= LINE_NOT_SPECIFIED*/, const std::string& prefix = std::string()) { return line == STEP::SyntaxError::LINE_NOT_SPECIFIED ? prefix+s : static_cast( (Formatter::format(),prefix,"(line ",line,") ",s) ); } // ------------------------------------------------------------------------------------------------ -std::string AddEntityID(const std::string& s,uint64_t entity /*= ENTITY_NOT_SPECIFIED*/, const std::string& prefix = "") +std::string AddEntityID(const std::string& s,uint64_t entity /*= ENTITY_NOT_SPECIFIED*/, const std::string& prefix = std::string()) { return entity == STEP::TypeError::ENTITY_NOT_SPECIFIED ? prefix+s : static_cast( (Formatter::format(),prefix,"(entity #",entity,") ",s)); } @@ -87,7 +88,7 @@ static const char *ISO_Token = "ISO-10303-21;"; static const char *FILE_SCHEMA_Token = "FILE_SCHEMA"; // ------------------------------------------------------------------------------------------------ STEP::DB* STEP::ReadFileHeader(std::shared_ptr stream) { - std::shared_ptr reader = std::shared_ptr(new StreamReaderLE(stream)); + std::shared_ptr reader = std::shared_ptr(new StreamReaderLE(std::move(stream))); std::unique_ptr db = std::unique_ptr(new STEP::DB(reader)); LineSplitter &splitter = db->GetSplitter(); diff --git a/code/AssetLib/STL/STLExporter.cpp b/code/AssetLib/STL/STLExporter.cpp index bd4d96c71..59c6148ee 100644 --- a/code/AssetLib/STL/STLExporter.cpp +++ b/code/AssetLib/STL/STLExporter.cpp @@ -69,7 +69,7 @@ void ExportSceneSTL(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene if (exporter.mOutput.fail()) { throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); } - + // we're still here - export successfully completed. Write the file. std::unique_ptr outfile (pIOSystem->Open(pFile,"wt")); if (outfile == nullptr) { @@ -88,7 +88,7 @@ void ExportSceneSTLBinary(const char* pFile,IOSystem* pIOSystem, const aiScene* if (exporter.mOutput.fail()) { throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); } - + // we're still here - export successfully completed. Write the file. std::unique_ptr outfile (pIOSystem->Open(pFile,"wb")); if (outfile == nullptr) { @@ -139,9 +139,9 @@ STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool expo if (exportPointClouds) { WritePointCloud("Assimp_Pointcloud", pScene ); return; - } + } - // Export the assimp mesh + // Export the assimp mesh const std::string name = "AssimpScene"; mOutput << SolidToken << " " << name << endl; for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { diff --git a/code/AssetLib/STL/STLLoader.cpp b/code/AssetLib/STL/STLLoader.cpp index 433fb14c7..8cfe63e0d 100644 --- a/code/AssetLib/STL/STLLoader.cpp +++ b/code/AssetLib/STL/STLLoader.cpp @@ -372,7 +372,7 @@ void STLImporter::LoadASCIIFile(aiNode *root) { pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; for (size_t i=0; imNumVertices; ++i ) { pMesh->mVertices[i].x = positionBuffer[i].x; - pMesh->mVertices[i].y = positionBuffer[i].y; + pMesh->mVertices[i].y = positionBuffer[i].y; pMesh->mVertices[i].z = positionBuffer[i].z; } positionBuffer.clear(); @@ -382,7 +382,7 @@ void STLImporter::LoadASCIIFile(aiNode *root) { pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; for (size_t i=0; imNumVertices; ++i ) { pMesh->mNormals[i].x = normalBuffer[i].x; - pMesh->mNormals[i].y = normalBuffer[i].y; + pMesh->mNormals[i].y = normalBuffer[i].y; pMesh->mNormals[i].z = normalBuffer[i].z; } normalBuffer.clear(); diff --git a/code/AssetLib/Step/STEPFile.h b/code/AssetLib/Step/STEPFile.h index 90eaef5f3..e09faad98 100644 --- a/code/AssetLib/Step/STEPFile.h +++ b/code/AssetLib/Step/STEPFile.h @@ -634,7 +634,7 @@ private: }; template -inline bool operator==(std::shared_ptr lo, T whatever) { +inline bool operator==(const std::shared_ptr &lo, T whatever) { return *lo == whatever; // XXX use std::forward if we have 0x } @@ -816,7 +816,7 @@ public: typedef std::pair RefMapRange; private: - DB(std::shared_ptr reader) : + DB(const std::shared_ptr &reader) : reader(reader), splitter(*reader, true, true), evaluated_count(), schema(nullptr) {} public: diff --git a/code/AssetLib/Step/StepExporter.cpp b/code/AssetLib/Step/StepExporter.cpp index dfe5bab67..1228c72ea 100644 --- a/code/AssetLib/Step/StepExporter.cpp +++ b/code/AssetLib/Step/StepExporter.cpp @@ -175,12 +175,11 @@ void StepExporter::WriteFile() fColor.b = 0.8f; int ind = 100; // the start index to be used - int faceEntryLen = 30; // number of entries for a triangle/face + std::vector faceEntryLen; // numbers of entries for a triangle/face // prepare unique (count triangles and vertices) VectorIndexUMap uniqueVerts; // use a map to reduce find complexity to log(n) VectorIndexUMap::iterator it; - int countFace = 0; for (unsigned int i=0; imNumMeshes; ++i) { @@ -189,7 +188,7 @@ void StepExporter::WriteFile() { aiFace* face = &(mesh->mFaces[j]); - if (face->mNumIndices == 3) countFace++; + if (face->mNumIndices >= 3) faceEntryLen.push_back(15 + 5 * face->mNumIndices); } for (unsigned int j=0; jmNumVertices; ++j) { @@ -218,10 +217,13 @@ void StepExporter::WriteFile() // write the top of data mOutput << "DATA" << endstr; mOutput << "#1=MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION(' ',("; - for (int i=0; imFaces[j]); - if (face->mNumIndices != 3) continue; + const int numIndices = face->mNumIndices; + if (numIndices < 3) continue; - aiVector3D* v1 = &(mesh->mVertices[face->mIndices[0]]); - aiVector3D* v2 = &(mesh->mVertices[face->mIndices[1]]); - aiVector3D* v3 = &(mesh->mVertices[face->mIndices[2]]); - aiVector3D dv12 = *v2 - *v1; - aiVector3D dv23 = *v3 - *v2; - aiVector3D dv31 = *v1 - *v3; - aiVector3D dv13 = *v3 - *v1; - dv12.Normalize(); - dv23.Normalize(); - dv31.Normalize(); - dv13.Normalize(); + std::vector pidArray(numIndices, -1); // vertex id + std::vector dvArray(numIndices); // edge dir + for (int k = 0; k < numIndices; ++k) + { + aiVector3D *v1 = &(mesh->mVertices[face->mIndices[k]]); + pidArray[k] = uniqueVerts.find(v1)->second; - aiVector3D dvY = dv12; - aiVector3D dvX = dvY ^ dv13; + aiVector3D *v2 = nullptr; + if (k + 1 == numIndices) + v2 = &(mesh->mVertices[face->mIndices[0]]); + else + v2 = &(mesh->mVertices[face->mIndices[k + 1]]); + dvArray[k] = *v2 - *v1; + dvArray[k].Normalize(); + } + + aiVector3D dvY = dvArray[1]; + aiVector3D dvX = dvY ^ dvArray[0]; dvX.Normalize(); - int pid1 = uniqueVerts.find(v1)->second; - int pid2 = uniqueVerts.find(v2)->second; - int pid3 = uniqueVerts.find(v3)->second; - // mean vertex color for the face if available if (mesh->HasVertexColors(0)) { @@ -339,35 +344,62 @@ void StepExporter::WriteFile() /* 2 directions of the plane */ mOutput << "#" << sid+9 << "=PLANE('',#" << sid+10 << ")" << endstr; - mOutput << "#" << sid+10 << "=AXIS2_PLACEMENT_3D('',#" << pid1 << ", #" << sid+11 << ",#" << sid+12 << ")" << endstr; + mOutput << "#" << sid+10 << "=AXIS2_PLACEMENT_3D('',#" << pidArray[0] << ",#" << sid+11 << ",#" << sid+12 << ")" << endstr; mOutput << "#" << sid + 11 << "=DIRECTION('',(" << dvX.x << "," << dvX.y << "," << dvX.z << "))" << endstr; mOutput << "#" << sid + 12 << "=DIRECTION('',(" << dvY.x << "," << dvY.y << "," << dvY.z << "))" << endstr; mOutput << "#" << sid+13 << "=FACE_BOUND('',#" << sid+14 << ",.T.)" << endstr; - mOutput << "#" << sid+14 << "=EDGE_LOOP('',(#" << sid+15 << ",#" << sid+16 << ",#" << sid+17 << "))" << endstr; + mOutput << "#" << sid+14 << "=EDGE_LOOP('',("; + int edgeLoopStart = sid + 15; + for (int k = 0; k < numIndices; ++k) + { + if (k == 0) + mOutput << "#"; + else + mOutput << ",#"; + mOutput << edgeLoopStart + k; + } + mOutput << "))" << endstr; /* edge loop */ - mOutput << "#" << sid+15 << "=ORIENTED_EDGE('',*,*,#" << sid+18 << ",.T.)" << endstr; - mOutput << "#" << sid+16 << "=ORIENTED_EDGE('',*,*,#" << sid+19 << ",.T.)" << endstr; - mOutput << "#" << sid+17 << "=ORIENTED_EDGE('',*,*,#" << sid+20 << ",.T.)" << endstr; + int orientedEdgesStart = edgeLoopStart + numIndices; + for (int k=0; k < numIndices; k++) + { + mOutput << "#" << edgeLoopStart+k << "=ORIENTED_EDGE('',*,*,#" << orientedEdgesStart + k << ",.T.)" << endstr; + } /* oriented edges */ - mOutput << "#" << sid+18 << "=EDGE_CURVE('',#" << pid1+1 << ",#" << pid2+1 << ",#" << sid+21 << ",.F.)" << endstr; - mOutput << "#" << sid+19 << "=EDGE_CURVE('',#" << pid2+1 << ",#" << pid3+1 << ",#" << sid+22 << ",.T.)" << endstr; - mOutput << "#" << sid+20 << "=EDGE_CURVE('',#" << pid3+1 << ",#" << pid1+1 << ",#" << sid+23 << ",.T.)" << endstr; + int lineStart = orientedEdgesStart + numIndices; + for (int k=0; k < numIndices; ++k) + { + if (k == 0) + mOutput << "#" << orientedEdgesStart+k << "=EDGE_CURVE('',#" << pidArray[k]+1 << ",#" << pidArray[k+1]+1 << ",#" << lineStart+k << ",.F.)" << endstr; + else if (k+1 == numIndices) + mOutput << "#" << orientedEdgesStart+k << "=EDGE_CURVE('',#" << pidArray[k]+1 << ",#" << pidArray[0]+1 << ",#" << lineStart+k << ",.T.)" << endstr; + else + mOutput << "#" << orientedEdgesStart+k << "=EDGE_CURVE('',#" << pidArray[k]+1 << ",#" << pidArray[k+1]+1 << ",#" << lineStart+k << ",.T.)" << endstr; + } - /* 3 lines and 3 vectors for the lines for the 3 edge curves */ - mOutput << "#" << sid+21 << "=LINE('',#" << pid1 << ",#" << sid+24 << ")" << endstr; - mOutput << "#" << sid+22 << "=LINE('',#" << pid2 << ",#" << sid+25 << ")" << endstr; - mOutput << "#" << sid+23 << "=LINE('',#" << pid3 << ",#" << sid+26 << ")" << endstr; - mOutput << "#" << sid+24 << "=VECTOR('',#" << sid+27 << ",1.0)" << endstr; - mOutput << "#" << sid+25 << "=VECTOR('',#" << sid+28 << ",1.0)" << endstr; - mOutput << "#" << sid+26 << "=VECTOR('',#" << sid+29 << ",1.0)" << endstr; - mOutput << "#" << sid+27 << "=DIRECTION('',(" << dv12.x << "," << dv12.y << "," << dv12.z << "))" << endstr; - mOutput << "#" << sid+28 << "=DIRECTION('',(" << dv23.x << "," << dv23.y << "," << dv23.z << "))" << endstr; - mOutput << "#" << sid+29 << "=DIRECTION('',(" << dv31.x << "," << dv31.y << "," << dv31.z << "))" << endstr; - ind += faceEntryLen; // increase counter + /* n lines and n vectors for the lines for the n edge curves */ + int vectorStart = lineStart + numIndices; + for (int k=0; k < numIndices; ++k) + { + mOutput << "#" << lineStart+k << "=LINE('',#" << pidArray[k] << ",#" << vectorStart+k << ")" << endstr; + } + + int directionStart = vectorStart + numIndices; + for (int k=0; k < numIndices; ++k) + { + mOutput << "#" << vectorStart+k << "=VECTOR('',#" << directionStart+k << ",1.0)" << endstr; + } + + for (int k=0; k < numIndices; ++k) + { + const aiVector3D &dv = dvArray[k]; + mOutput << "#" << directionStart + k << "=DIRECTION('',(" << dv.x << "," << dv.y << "," << dv.z << "))" << endstr; + } + ind += 15 + 5*numIndices; // increase counter } } diff --git a/code/AssetLib/X/XFileExporter.cpp b/code/AssetLib/X/XFileExporter.cpp index da20b935a..b95cb7abf 100644 --- a/code/AssetLib/X/XFileExporter.cpp +++ b/code/AssetLib/X/XFileExporter.cpp @@ -86,7 +86,7 @@ void ExportSceneXFile(const char* pFile,IOSystem* pIOSystem, const aiScene* pSce if (iDoTheExportThing.mOutput.fail()) { throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); } - + // we're still here - export successfully completed. Write result to the given IOSYstem std::unique_ptr outfile (pIOSystem->Open(pFile,"wt")); if (outfile == nullptr) { @@ -530,8 +530,8 @@ void XFileExporter::writePath(const aiString &path) while( str.find( "\\\\") != std::string::npos) str.replace( str.find( "\\\\"), 2, "\\"); - while( str.find( "\\") != std::string::npos) - str.replace( str.find( "\\"), 1, "/"); + while (str.find('\\') != std::string::npos) + str.replace(str.find('\\'), 1, "/"); mOutput << str; diff --git a/code/AssetLib/X/XFileExporter.h b/code/AssetLib/X/XFileExporter.h index 32b75b3d1..620d282b6 100644 --- a/code/AssetLib/X/XFileExporter.h +++ b/code/AssetLib/X/XFileExporter.h @@ -94,9 +94,9 @@ protected: void PushTag() { startstr.append( " "); } /// Leaves an element, decreasing the indentation - void PopTag() { - ai_assert( startstr.length() > 1); - startstr.erase( startstr.length() - 2); + void PopTag() { + ai_assert( startstr.length() > 1); + startstr.erase( startstr.length() - 2); } public: diff --git a/code/AssetLib/X/XFileImporter.cpp b/code/AssetLib/X/XFileImporter.cpp index df1aba331..4c8c54551 100644 --- a/code/AssetLib/X/XFileImporter.cpp +++ b/code/AssetLib/X/XFileImporter.cpp @@ -667,9 +667,7 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, std::vector if it comes after // or if (s == "lighting") { @@ -250,7 +250,7 @@ void XGLImporter::ReadWorld(XmlNode &node, TempScope &scope) { } } - aiNode *const nd = ReadObject(node, scope, true); + aiNode *const nd = ReadObject(node, scope); if (!nd) { ThrowException("failure reading "); } @@ -296,16 +296,13 @@ aiLight *XGLImporter::ReadDirectionalLight(XmlNode &node) { } // ------------------------------------------------------------------------------------------------ -aiNode *XGLImporter::ReadObject(XmlNode &node, TempScope &scope, bool skipFirst/*, const char *closetag */) { +aiNode *XGLImporter::ReadObject(XmlNode &node, TempScope &scope) { aiNode *nd = new aiNode; std::vector children; std::vector meshes; try { for (XmlNode &child : node.children()) { - - skipFirst = false; - const std::string &s = ai_stdStrToLower(child.name()); if (s == "mesh") { const size_t prev = scope.meshes_linear.size(); diff --git a/code/AssetLib/XGL/XGLLoader.h b/code/AssetLib/XGL/XGLLoader.h index f7da4e0a7..a2b224ac9 100644 --- a/code/AssetLib/XGL/XGLLoader.h +++ b/code/AssetLib/XGL/XGLLoader.h @@ -185,7 +185,7 @@ private: void ReadWorld(XmlNode &node, TempScope &scope); void ReadLighting(XmlNode &node, TempScope &scope); aiLight *ReadDirectionalLight(XmlNode &node); - aiNode *ReadObject(XmlNode &node, TempScope &scope, bool skipFirst = false/*, const char *closetag = "object"*/); + aiNode *ReadObject(XmlNode &node, TempScope &scope); bool ReadMesh(XmlNode &node, TempScope &scope); void ReadMaterial(XmlNode &node, TempScope &scope); aiVector2D ReadVec2(XmlNode &node); diff --git a/code/AssetLib/glTF/glTFAsset.h b/code/AssetLib/glTF/glTFAsset.h index da49a1737..4cef646d2 100644 --- a/code/AssetLib/glTF/glTFAsset.h +++ b/code/AssetLib/glTF/glTFAsset.h @@ -456,11 +456,10 @@ namespace glTF /// \param [in] pDecodedData - pointer to decoded data array. /// \param [in] pDecodedData_Length - size of encoded region, in bytes. /// \param [in] pID - ID of the region. - SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string pID) - : Offset(pOffset), EncodedData_Length(pEncodedData_Length), DecodedData(pDecodedData), DecodedData_Length(pDecodedData_Length), ID(pID) - {} + SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID) : + Offset(pOffset), EncodedData_Length(pEncodedData_Length), DecodedData(pDecodedData), DecodedData_Length(pDecodedData_Length), ID(pID) {} - /// \fn ~SEncodedRegion() + /// \fn ~SEncodedRegion() /// Destructor. ~SEncodedRegion() { delete [] DecodedData; } }; @@ -1149,8 +1148,7 @@ namespace glTF void ReadExtensionsUsed(Document& doc); - - IOStream* OpenFile(std::string path, const char* mode, bool absolute = false); + IOStream *OpenFile(const std::string &path, const char *mode, bool absolute = false); }; } diff --git a/code/AssetLib/glTF/glTFAsset.inl b/code/AssetLib/glTF/glTFAsset.inl index 6e1e60846..e915a3aee 100644 --- a/code/AssetLib/glTF/glTFAsset.inl +++ b/code/AssetLib/glTF/glTFAsset.inl @@ -1377,7 +1377,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) { #undef CHECK_EXT } -inline IOStream *Asset::OpenFile(std::string path, const char *mode, bool absolute) { +inline IOStream *Asset::OpenFile(const std::string& path, const char *mode, bool absolute) { #ifdef ASSIMP_API (void)absolute; return mIOSystem->Open(path, mode); diff --git a/code/AssetLib/glTF/glTFCommon.h b/code/AssetLib/glTF/glTFCommon.h index d99ffbe86..6f35e7881 100644 --- a/code/AssetLib/glTF/glTFCommon.h +++ b/code/AssetLib/glTF/glTFCommon.h @@ -195,11 +195,11 @@ inline void CopyValue(const glTFCommon::mat4 &v, aiMatrix4x4 &o) { inline std::string getCurrentAssetDir(const std::string &pFile) { std::string path = pFile; int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\'))); - if (pos != int(std::string::npos)) { - path = pFile.substr(0, pos + 1); + if (pos == int(std::string::npos)) { + return std::string(); } - return path; + return pFile.substr(0, pos + 1); } #if _MSC_VER # pragma warning(pop) diff --git a/code/AssetLib/glTF/glTFExporter.cpp b/code/AssetLib/glTF/glTFExporter.cpp index c2f96973b..acc195bd9 100644 --- a/code/AssetLib/glTF/glTFExporter.cpp +++ b/code/AssetLib/glTF/glTFExporter.cpp @@ -408,8 +408,7 @@ void glTFExporter::ExportMaterials() * Search through node hierarchy and find the node containing the given meshID. * Returns true on success, and false otherwise. */ -bool FindMeshNode(Ref& nodeIn, Ref& meshNode, std::string meshID) -{ +bool FindMeshNode(Ref &nodeIn, Ref &meshNode, const std::string &meshID) { for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) { if (meshID.compare(nodeIn->meshes[i]->id) == 0) { meshNode = nodeIn; @@ -530,6 +529,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref mStringValue; + Nullable mDoubleValue; + Nullable mUint64Value; + Nullable mInt64Value; + Nullable mBoolValue; + + // std::vector handles both Object and Array + Nullable> mValues; + + operator bool() const { + return Size() != 0; + } + + size_t Size() const { + if (mValues.isPresent) { + return mValues.value.size(); + } else if (mStringValue.isPresent || mDoubleValue.isPresent || mUint64Value.isPresent || mInt64Value.isPresent || mBoolValue.isPresent) { + return 1; + } + return 0; + } + + CustomExtension() = default; + + ~CustomExtension() = default; + + CustomExtension(const CustomExtension &other) : + name(other.name), + mStringValue(other.mStringValue), + mDoubleValue(other.mDoubleValue), + mUint64Value(other.mUint64Value), + mInt64Value(other.mInt64Value), + mBoolValue(other.mBoolValue), + mValues(other.mValues) { + // empty + } +}; + //! Base class for all glTF top-level objects struct Object { int index; //!< The index of this object within its property container @@ -363,6 +410,9 @@ struct Object { std::string id; //!< The globally unique ID used to reference this object std::string name; //!< The user-defined name of this object + CustomExtension customExtensions; + CustomExtension extras; + //! Objects marked as special are not exported (used to emulate the binary body buffer) virtual bool IsSpecial() const { return false; } @@ -377,6 +427,9 @@ struct Object { inline Value *FindArray(Value &val, const char *id); inline Value *FindObject(Value &val, const char *id); inline Value *FindExtension(Value &val, const char *extensionId); + + inline void ReadExtensions(Value &val); + inline void ReadExtras(Value &val); }; // @@ -408,7 +461,7 @@ public: /// \param [in] pDecodedData - pointer to decoded data array. /// \param [in] pDecodedData_Length - size of encoded region, in bytes. /// \param [in] pID - ID of the region. - SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string pID) : + SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID) : Offset(pOffset), EncodedData_Length(pEncodedData_Length), DecodedData(pDecodedData), @@ -834,50 +887,6 @@ struct Mesh : public Object { void Read(Value &pJSON_Object, Asset &pAsset_Root); }; -struct CustomExtension : public Object { - // - // A struct containing custom extension data added to a glTF2 file - // Has to contain Object, Array, String, Double, Uint64, and Int64 at a minimum - // String, Double, Uint64, and Int64 are stored in the Nullables - // Object and Array are stored in the std::vector - // - - Nullable mStringValue; - Nullable mDoubleValue; - Nullable mUint64Value; - Nullable mInt64Value; - Nullable mBoolValue; - - // std::vector handles both Object and Array - Nullable> mValues; - - operator bool() const { - return Size() != 0; - } - - size_t Size() const { - if (mValues.isPresent) { - return mValues.value.size(); - } else if (mStringValue.isPresent || mDoubleValue.isPresent || mUint64Value.isPresent || mInt64Value.isPresent || mBoolValue.isPresent) { - return 1; - } - return 0; - } - - CustomExtension() = default; - - CustomExtension(const CustomExtension &other) - : Object(other) - , mStringValue(other.mStringValue) - , mDoubleValue(other.mDoubleValue) - , mUint64Value(other.mUint64Value) - , mInt64Value(other.mInt64Value) - , mBoolValue(other.mBoolValue) - , mValues(other.mValues) - { - } -}; - struct Node : public Object { std::vector> children; std::vector> meshes; @@ -896,8 +905,6 @@ struct Node : public Object { Ref parent; //!< This is not part of the glTF specification. Used as a helper. - CustomExtension extensions; - Node() {} void Read(Value &obj, Asset &r); }; @@ -1188,7 +1195,7 @@ private: void ReadExtensionsUsed(Document &doc); void ReadExtensionsRequired(Document &doc); - IOStream *OpenFile(std::string path, const char *mode, bool absolute = false); + IOStream *OpenFile(const std::string &path, const char *mode, bool absolute = false); }; inline std::string getContextForErrorMessages(const std::string &id, const std::string &name) { diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index b51ac20c2..9f793d7c0 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -304,6 +303,43 @@ inline Value *FindObject(Document &doc, const char *memberId) { inline Value *FindExtension(Value &val, const char *extensionId) { return FindExtensionInContext(val, extensionId, "the document"); } + +inline CustomExtension ReadExtensions(const char *name, Value &obj) { + CustomExtension ret; + ret.name = name; + if (obj.IsObject()) { + ret.mValues.isPresent = true; + for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) { + auto &val = it->value; + ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val)); + } + } else if (obj.IsArray()) { + ret.mValues.value.reserve(obj.Size()); + ret.mValues.isPresent = true; + for (unsigned int i = 0; i < obj.Size(); ++i) { + ret.mValues.value.push_back(ReadExtensions(name, obj[i])); + } + } else if (obj.IsNumber()) { + if (obj.IsUint64()) { + ret.mUint64Value.value = obj.GetUint64(); + ret.mUint64Value.isPresent = true; + } else if (obj.IsInt64()) { + ret.mInt64Value.value = obj.GetInt64(); + ret.mInt64Value.isPresent = true; + } else if (obj.IsDouble()) { + ret.mDoubleValue.value = obj.GetDouble(); + ret.mDoubleValue.isPresent = true; + } + } else if (obj.IsString()) { + ReadValue(obj, ret.mStringValue); + ret.mStringValue.isPresent = true; + } else if (obj.IsBool()) { + ret.mBoolValue.value = obj.GetBool(); + ret.mBoolValue.isPresent = true; + } + return ret; +} + } // namespace inline Value *Object::FindString(Value &val, const char *memberId) { @@ -330,6 +366,18 @@ inline Value *Object::FindExtension(Value &val, const char *extensionId) { return FindExtensionInContext(val, extensionId, id.c_str(), name.c_str()); } +inline void Object::ReadExtensions(Value &val) { + if (Value *curExtensions = FindObject(val, "extensions")) { + this->customExtensions = glTF2::ReadExtensions("extensions", *curExtensions); + } +} + +inline void Object::ReadExtras(Value &val) { + if (Value *curExtras = FindObject(val, "extras")) { + this->extras = glTF2::ReadExtensions("extras", *curExtras); + } +} + #ifdef ASSIMP_ENABLE_DRACO template @@ -362,14 +410,14 @@ inline void SetDecodedIndexBuffer_Draco(const draco::Mesh &dracoMesh, Mesh::Prim // Not same size, convert switch (componentBytes) { - case sizeof(uint32_t): - CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); + case sizeof(uint32_t): + CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); break; - case sizeof(uint16_t): - CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); + case sizeof(uint16_t): + CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); break; - case sizeof(uint8_t): - CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); + case sizeof(uint8_t): + CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); break; default: ai_assert(false); @@ -412,23 +460,23 @@ inline void SetDecodedAttributeBuffer_Draco(const draco::Mesh &dracoMesh, uint32 decodedAttribBuffer->Grow(dracoMesh.num_points() * pDracoAttribute->num_components() * componentBytes); switch (accessor.componentType) { - case ComponentType_BYTE: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_BYTE: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; case ComponentType_UNSIGNED_BYTE: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; case ComponentType_SHORT: GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; - case ComponentType_UNSIGNED_SHORT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_UNSIGNED_SHORT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; - case ComponentType_UNSIGNED_INT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_UNSIGNED_INT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; - case ComponentType_FLOAT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_FLOAT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; default: ai_assert(false); @@ -569,6 +617,8 @@ Ref LazyDict::Retrieve(unsigned int i) { inst->oIndex = i; ReadMember(obj, "name", inst->name); inst->Read(obj, mAsset); + inst->ReadExtensions(obj); + inst->ReadExtras(obj); Ref result = Add(inst.release()); mRecursiveReferenceCheck.erase(i); @@ -733,12 +783,13 @@ inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncod } inline void Buffer::EncodedRegion_SetCurrent(const std::string &pID) { - if ((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) return; + if ((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) { + return; + } for (SEncodedRegion *reg : EncodedRegion_List) { if (reg->ID == pID) { EncodedRegion_Current = reg; - return; } } @@ -788,10 +839,13 @@ inline bool Buffer::ReplaceData_joint(const size_t pBufferData_Offset, const siz } inline size_t Buffer::AppendData(uint8_t *data, size_t length) { - size_t offset = this->byteLength; + const size_t offset = this->byteLength; + // Force alignment to 4 bits - Grow((length + 3) & ~3); + const size_t paddedLength = (length + 3) & ~3; + Grow(paddedLength); memcpy(mData.get() + offset, data, length); + memset(mData.get() + offset + length, 0, paddedLength - length); return offset; } @@ -820,9 +874,7 @@ inline void Buffer::Grow(size_t amount) { // // struct BufferView // - inline void BufferView::Read(Value &obj, Asset &r) { - if (Value *bufferVal = FindUInt(obj, "buffer")) { buffer = r.buffers.Retrieve(bufferVal->GetUint()); } @@ -842,16 +894,21 @@ inline void BufferView::Read(Value &obj, Asset &r) { } inline uint8_t *BufferView::GetPointer(size_t accOffset) { - if (!buffer) return nullptr; + if (!buffer) { + return nullptr; + } uint8_t *basePtr = buffer->GetPointer(); - if (!basePtr) return nullptr; + if (!basePtr) { + return nullptr; + } size_t offset = accOffset + byteOffset; if (buffer->EncodedRegion_Current != nullptr) { const size_t begin = buffer->EncodedRegion_Current->Offset; const size_t end = begin + buffer->EncodedRegion_Current->DecodedData_Length; - if ((offset >= begin) && (offset < end)) + if ((offset >= begin) && (offset < end)) { return &buffer->EncodedRegion_Current->DecodedData[offset - begin]; + } } return basePtr + offset; @@ -877,18 +934,18 @@ inline void Accessor::Sparse::PatchData(unsigned int elementSize) { while (pIndices != indicesEnd) { size_t offset; switch (indicesType) { - case ComponentType_UNSIGNED_BYTE: - offset = *pIndices; - break; - case ComponentType_UNSIGNED_SHORT: - offset = *reinterpret_cast(pIndices); - break; - case ComponentType_UNSIGNED_INT: - offset = *reinterpret_cast(pIndices); - break; - default: - // have fun with float and negative values from signed types as indices. - throw DeadlyImportError("Unsupported component type in index."); + case ComponentType_UNSIGNED_BYTE: + offset = *pIndices; + break; + case ComponentType_UNSIGNED_SHORT: + offset = *reinterpret_cast(pIndices); + break; + case ComponentType_UNSIGNED_INT: + offset = *reinterpret_cast(pIndices); + break; + default: + // have fun with float and negative values from signed types as indices. + throw DeadlyImportError("Unsupported component type in index."); } offset *= elementSize; @@ -900,7 +957,6 @@ inline void Accessor::Sparse::PatchData(unsigned int elementSize) { } inline void Accessor::Read(Value &obj, Asset &r) { - if (Value *bufferViewVal = FindUInt(obj, "bufferView")) { bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint()); } @@ -909,9 +965,9 @@ inline void Accessor::Read(Value &obj, Asset &r) { componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); { const Value* countValue = FindUInt(obj, "count"); - if (!countValue || countValue->GetUint() < 1) + if (!countValue) { - throw DeadlyImportError("A strictly positive count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); + throw DeadlyImportError("A count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); } count = countValue->GetUint(); } @@ -1466,7 +1522,7 @@ inline bool GetAttribTargetVector(Mesh::Primitive &p, const int targetIndex, con inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) { Value *curName = FindMember(pJSON_Object, "name"); - if (nullptr != curName) { + if (nullptr != curName && curName->IsString()) { name = curName->GetString(); } @@ -1612,9 +1668,9 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) { } } - Value *extras = FindObject(pJSON_Object, "extras"); - if (nullptr != extras) { - if (Value *curTargetNames = FindArray(*extras, "targetNames")) { + Value *curExtras = FindObject(pJSON_Object, "extras"); + if (nullptr != curExtras) { + if (Value *curTargetNames = FindArray(*curExtras, "targetNames")) { this->targetNames.resize(curTargetNames->Size()); for (unsigned int i = 0; i < curTargetNames->Size(); ++i) { Value &targetNameValue = (*curTargetNames)[i]; @@ -1683,42 +1739,6 @@ inline void Light::Read(Value &obj, Asset & /*r*/) { } } -inline CustomExtension ReadExtensions(const char *name, Value &obj) { - CustomExtension ret; - ret.name = name; - if (obj.IsObject()) { - ret.mValues.isPresent = true; - for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) { - auto &val = it->value; - ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val)); - } - } else if (obj.IsArray()) { - ret.mValues.value.reserve(obj.Size()); - ret.mValues.isPresent = true; - for (unsigned int i = 0; i < obj.Size(); ++i) { - ret.mValues.value.push_back(ReadExtensions(name, obj[i])); - } - } else if (obj.IsNumber()) { - if (obj.IsUint64()) { - ret.mUint64Value.value = obj.GetUint64(); - ret.mUint64Value.isPresent = true; - } else if (obj.IsInt64()) { - ret.mInt64Value.value = obj.GetInt64(); - ret.mInt64Value.isPresent = true; - } else if (obj.IsDouble()) { - ret.mDoubleValue.value = obj.GetDouble(); - ret.mDoubleValue.isPresent = true; - } - } else if (obj.IsString()) { - ReadValue(obj, ret.mStringValue); - ret.mStringValue.isPresent = true; - } else if (obj.IsBool()) { - ret.mBoolValue.value = obj.GetBool(); - ret.mBoolValue.isPresent = true; - } - return ret; -} - inline void Node::Read(Value &obj, Asset &r) { if (name.empty()) { name = id; @@ -1775,8 +1795,6 @@ inline void Node::Read(Value &obj, Asset &r) { Value *curExtensions = FindObject(obj, "extensions"); if (nullptr != curExtensions) { - this->extensions = ReadExtensions("extensions", *curExtensions); - if (r.extensionsUsed.KHR_lights_punctual) { if (Value *ext = FindObject(*curExtensions, "KHR_lights_punctual")) { Value *curLight = FindUInt(*ext, "light"); @@ -2132,7 +2150,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) { #undef CHECK_EXT } -inline IOStream *Asset::OpenFile(std::string path, const char *mode, bool /*absolute*/) { +inline IOStream *Asset::OpenFile(const std::string& path, const char *mode, bool /*absolute*/) { #ifdef ASSIMP_API return mIOSystem->Open(path, mode); #else diff --git a/code/AssetLib/glTF2/glTF2AssetWriter.inl b/code/AssetLib/glTF2/glTF2AssetWriter.inl index bf7dbbb2e..115cdf903 100644 --- a/code/AssetLib/glTF2/glTF2AssetWriter.inl +++ b/code/AssetLib/glTF2/glTF2AssetWriter.inl @@ -452,7 +452,7 @@ namespace glTF2 { WriteTex(materialClearcoat, clearcoat.clearcoatTexture, "clearcoatTexture", w.mAl); WriteTex(materialClearcoat, clearcoat.clearcoatRoughnessTexture, "clearcoatRoughnessTexture", w.mAl); WriteTex(materialClearcoat, clearcoat.clearcoatNormalTexture, "clearcoatNormalTexture", w.mAl); - + if (!materialClearcoat.ObjectEmpty()) { exts.AddMember("KHR_materials_clearcoat", materialClearcoat, w.mAl); } @@ -468,7 +468,7 @@ namespace glTF2 { } WriteTex(materialTransmission, transmission.transmissionTexture, "transmissionTexture", w.mAl); - + if (!materialTransmission.ObjectEmpty()) { exts.AddMember("KHR_materials_transmission", materialTransmission, w.mAl); } @@ -613,7 +613,7 @@ namespace glTF2 { if (n.skin) { obj.AddMember("skin", n.skin->index, w.mAl); } - + //gltf2 spec does not support "skeletons" under node if(n.skeletons.size()) { AddRefsVector(obj, "skeletons", n.skeletons, w.mAl); @@ -711,7 +711,7 @@ namespace glTF2 { if (mAsset.scene) { mDoc.AddMember("scene", mAsset.scene->index, mAl); } - + if(mAsset.extras) { mDoc.AddMember("extras", *mAsset.extras, mAl); } @@ -812,7 +812,7 @@ namespace glTF2 { uint32_t binaryChunkLength = 0; if (bodyBuffer->byteLength > 0) { binaryChunkLength = (bodyBuffer->byteLength + 3) & ~3; // Round up to next multiple of 4 - + auto curPaddingLength = binaryChunkLength - bodyBuffer->byteLength; ++GLB_Chunk_count; @@ -881,7 +881,7 @@ namespace glTF2 { if (this->mAsset.extensionsUsed.KHR_materials_sheen) { exts.PushBack(StringRef("KHR_materials_sheen"), mAl); } - + if (this->mAsset.extensionsUsed.KHR_materials_clearcoat) { exts.PushBack(StringRef("KHR_materials_clearcoat"), mAl); } @@ -893,7 +893,7 @@ namespace glTF2 { if (this->mAsset.extensionsUsed.FB_ngon_encoding) { exts.PushBack(StringRef("FB_ngon_encoding"), mAl); } - + if (this->mAsset.extensionsUsed.KHR_texture_basisu) { exts.PushBack(StringRef("KHR_texture_basisu"), mAl); } @@ -901,7 +901,7 @@ namespace glTF2 { if (!exts.Empty()) mDoc.AddMember("extensionsUsed", exts, mAl); - + //basisu extensionRequired Value extsReq; extsReq.SetArray(); diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index fe592f342..bbb8a10ff 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -88,15 +88,13 @@ namespace Assimp { } // end of namespace Assimp glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const aiScene* pScene, - const ExportProperties* pProperties, bool isBinary) + const ExportProperties* pProperties, bool isBinary) : mFilename(filename) , mIOSystem(pIOSystem) + , mScene(pScene) , mProperties(pProperties) + , mAsset(new Asset(pIOSystem)) { - mScene = pScene; - - mAsset.reset( new Asset( pIOSystem ) ); - // Always on as our triangulation process is aware of this type of encoding mAsset->extensionsUsed.FB_ngon_encoding = true; @@ -118,14 +116,14 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai ExportScene(); ExportAnimations(); - + // export extras if(mProperties->HasPropertyCallback("extras")) { std::function ExportExtras = mProperties->GetPropertyCallback("extras"); mAsset->extras = (rapidjson::Value*)ExportExtras(0); } - + AssetWriter writer(*mAsset); if (isBinary) { @@ -436,11 +434,11 @@ inline void SetSamplerWrap(SamplerWrap& wrap, aiTextureMapMode map) }; } -void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetTexSampler(const aiMaterial& mat, Ref texture, aiTextureType tt, unsigned int slot) { aiString aId; std::string id; - if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) { + if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) { id = aId.C_Str(); } @@ -455,49 +453,52 @@ void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, a SamplerMagFilter filterMag; SamplerMinFilter filterMin; - if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) { SetSamplerWrap(texture->sampler->wrapS, mapU); } - if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) { SetSamplerWrap(texture->sampler->wrapT, mapV); } - if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) { texture->sampler->magFilter = filterMag; } - if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) { texture->sampler->minFilter = filterMin; } aiString name; - if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) { + if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) { texture->sampler->name = name.C_Str(); } } } -void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot) { std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - mat->Get(textureKey.c_str(), tt, slot, prop); + mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot) { std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - mat->Get(textureKey.c_str(), tt, slot, prop); + mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, Ref& texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot = 0) { - if (mat->GetTextureCount(tt) > 0) { + if (mat.GetTextureCount(tt) > 0) { aiString tex; - if (mat->Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { + // Read texcoord (UV map index) + mat.Get(AI_MATKEY_UVWSRC(tt, slot), texCoord); + + if (mat.Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { std::string path = tex.C_Str(); if (path.size() > 0) { @@ -515,11 +516,10 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe std::string imgId = mAsset->FindUniqueID("", "image"); texture->source = mAsset->images.Create(imgId); - if (path[0] == '*') { // embedded - aiTexture* curTex = mScene->mTextures[atoi(&path[1])]; - + const aiTexture* curTex = mScene->GetEmbeddedTexture(path.c_str()); + if (curTex != nullptr) { // embedded texture->source->name = curTex->mFilename.C_Str(); - + //basisu: embedded ktx2, bu if (curTex->achFormatHint[0]) { std::string mimeType = "image/"; @@ -541,7 +541,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe mimeType += curTex->achFormatHint; texture->source->mimeType = mimeType; } - + // The asset has its own buffer, see Image::SetData //basisu: "image/ktx2", "image/basis" as is texture->source->SetData(reinterpret_cast(curTex->pcData), curTex->mWidth, *mAsset); @@ -554,7 +554,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe useBasisUniversal = true; } } - + //basisu if(useBasisUniversal) { mAsset->extensionsUsed.KHR_texture_basisu = true; @@ -568,45 +568,45 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); - if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); - } + //if (texture) { + // GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //} } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); GetMatTexProp(mat, prop.scale, "scale", tt, slot); } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); GetMatTexProp(mat, prop.strength, "strength", tt, slot); } } -aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const char* propName, int type, int idx) +aiReturn glTF2Exporter::GetMatColor(const aiMaterial& mat, vec4& prop, const char* propName, int type, int idx) const { aiColor4D col; - aiReturn result = mat->Get(propName, type, idx, col); + aiReturn result = mat.Get(propName, type, idx, col); if (result == AI_SUCCESS) { prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; prop[3] = col.a; @@ -615,37 +615,116 @@ aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const cha return result; } -aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec3& prop, const char* propName, int type, int idx) +aiReturn glTF2Exporter::GetMatColor(const aiMaterial& mat, vec3& prop, const char* propName, int type, int idx) const { aiColor3D col; - aiReturn result = mat->Get(propName, type, idx, col); + aiReturn result = mat.Get(propName, type, idx, col); if (result == AI_SUCCESS) { - prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; + prop[0] = col.r; + prop[1] = col.g; + prop[2] = col.b; } return result; } +bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG) { + bool result = false; + // If has Glossiness, a Specular Color or Specular Texture, use the KHR_materials_pbrSpecularGlossiness extension + // NOTE: This extension is being considered for deprecation (Dec 2020), may be replaced by KHR_material_specular + + if (mat.Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) == AI_SUCCESS) { + result = true; + } else { + // Don't have explicit glossiness, convert from pbr roughness or legacy shininess + float shininess; + if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way + } else if (mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = shininess / 1000; + } + } + + if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { + result = true; + } + // Add any appropriate textures + GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); + + result = result || pbrSG.specularGlossinessTexture.texture; + + if (result) { + // Likely to always have diffuse + GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); + GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); + } + + return result; +} + +bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen) { + // Return true if got any valid Sheen properties or textures + if (GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_SHEEN_COLOR_FACTOR) != aiReturn_SUCCESS) + return false; + + // Default Sheen color factor {0,0,0} disables Sheen, so do not export + if (sheen.sheenColorFactor == defaultSheenFactor) + return false; + + mat.Get(AI_MATKEY_SHEEN_ROUGHNESS_FACTOR, sheen.sheenRoughnessFactor); + + GetMatTex(mat, sheen.sheenColorTexture, AI_MATKEY_SHEEN_COLOR_TEXTURE); + GetMatTex(mat, sheen.sheenRoughnessTexture, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE); + + return true; +} + +bool glTF2Exporter::GetMatClearcoat(const aiMaterial &mat, glTF2::MaterialClearcoat &clearcoat) { + if (mat.Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat.clearcoatFactor) != aiReturn_SUCCESS) { + return false; + } + + // Clearcoat factor of zero disables Clearcoat, so do not export + if (clearcoat.clearcoatFactor == 0.0f) + return false; + + mat.Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat.clearcoatRoughnessFactor); + + GetMatTex(mat, clearcoat.clearcoatTexture, AI_MATKEY_CLEARCOAT_TEXTURE); + GetMatTex(mat, clearcoat.clearcoatRoughnessTexture, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE); + GetMatTex(mat, clearcoat.clearcoatNormalTexture, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE); + + return true; +} + +bool glTF2Exporter::GetMatTransmission(const aiMaterial &mat, glTF2::MaterialTransmission &transmission) { + bool result = mat.Get(AI_MATKEY_TRANSMISSION_FACTOR, transmission.transmissionFactor) == aiReturn_SUCCESS; + GetMatTex(mat, transmission.transmissionTexture, AI_MATKEY_TRANSMISSION_TEXTURE); + return result || transmission.transmissionTexture.texture; +} + void glTF2Exporter::ExportMaterials() { aiString aiName; for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) { - const aiMaterial* mat = mScene->mMaterials[i]; + ai_assert(mScene->mMaterials[i] != nullptr); + + const aiMaterial & mat = *(mScene->mMaterials[i]); std::string id = "material_" + ai_to_string(i); Ref m = mAsset->materials.Create(id); std::string name; - if (mat->Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) { name = aiName.C_Str(); } name = mAsset->FindUniqueID(name, "material"); m->name = name; - GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_BASE_COLOR); if (!m->pbrMetallicRoughness.baseColorTexture.texture) { //if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture @@ -654,26 +733,26 @@ void glTF2Exporter::ExportMaterials() GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR) != AI_SUCCESS) { + if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_BASE_COLOR) != AI_SUCCESS) { // if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material. //a fallback to any diffuse color should be used instead GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); } - if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { + if (mat.Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0 m->pbrMetallicRoughness.metallicFactor = 0; } // get roughness if source is gltf2 file - if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { + if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { // otherwise, try to derive and convert from specular + shininess values aiColor4D specularColor; ai_real shininess; if ( - mat->Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && - mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS + mat.Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && + mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS ) { // convert specular color to luminance float specularIntensity = specularColor[0] * 0.2125f + specularColor[1] * 0.7154f + specularColor[2] * 0.0721f; @@ -694,103 +773,60 @@ void glTF2Exporter::ExportMaterials() GetMatTex(mat, m->emissiveTexture, aiTextureType_EMISSIVE); GetMatColor(mat, m->emissiveFactor, AI_MATKEY_COLOR_EMISSIVE); - mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided); - mat->Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); + mat.Get(AI_MATKEY_TWOSIDED, m->doubleSided); + mat.Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); + float opacity; aiString alphaMode; - if (mat->Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { + if (opacity < 1) { + m->alphaMode = "BLEND"; + m->pbrMetallicRoughness.baseColorFactor[3] *= opacity; + } + } + if (mat.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { m->alphaMode = alphaMode.C_Str(); - } else { - float opacity; - - if (mat->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { - if (opacity < 1) { - m->alphaMode = "BLEND"; - m->pbrMetallicRoughness.baseColorFactor[3] *= opacity; - } - } } - bool hasPbrSpecularGlossiness = false; - mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS, hasPbrSpecularGlossiness); - - if (hasPbrSpecularGlossiness) { - - if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { - mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; - } - + { + // KHR_materials_pbrSpecularGlossiness extension + // NOTE: This extension is being considered for deprecation (Dec 2020) PbrSpecularGlossiness pbrSG; - - GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); - GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR); - - if (mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { - float shininess; - - if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = shininess / 1000; - } + if (GetMatSpecGloss(mat, pbrSG)) { + mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; + m->pbrSpecularGlossiness = Nullable(pbrSG); } - - GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); - GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - - m->pbrSpecularGlossiness = Nullable(pbrSG); } - bool unlit; - if (mat->Get(AI_MATKEY_GLTF_UNLIT, unlit) == AI_SUCCESS && unlit) { + // glTFv2 is either PBR or Unlit + aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; + mat.Get(AI_MATKEY_SHADING_MODEL, shadingMode); + if (shadingMode == aiShadingMode_Unlit) { mAsset->extensionsUsed.KHR_materials_unlit = true; m->unlit = true; - } + } else { + // These extensions are not compatible with KHR_materials_unlit or KHR_materials_pbrSpecularGlossiness + if (!m->pbrSpecularGlossiness.isPresent) { + // Sheen + MaterialSheen sheen; + if (GetMatSheen(mat, sheen)) { + mAsset->extensionsUsed.KHR_materials_sheen = true; + m->materialSheen = Nullable(sheen); + } - bool hasMaterialSheen = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_SHEEN, hasMaterialSheen); + MaterialClearcoat clearcoat; + if (GetMatClearcoat(mat, clearcoat)) { + mAsset->extensionsUsed.KHR_materials_clearcoat = true; + m->materialClearcoat = Nullable(clearcoat); + } - if (hasMaterialSheen) { - mAsset->extensionsUsed.KHR_materials_sheen = true; - - MaterialSheen sheen; - - GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR); - mat->Get(AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR, sheen.sheenRoughnessFactor); - GetMatTex(mat, sheen.sheenColorTexture, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE); - GetMatTex(mat, sheen.sheenRoughnessTexture, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE); - - m->materialSheen = Nullable(sheen); - } - - bool hasMaterialClearcoat = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT, hasMaterialClearcoat); - - if (hasMaterialClearcoat) { - mAsset->extensionsUsed.KHR_materials_clearcoat= true; - - MaterialClearcoat clearcoat; - - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR, clearcoat.clearcoatFactor); - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat.clearcoatRoughnessFactor); - GetMatTex(mat, clearcoat.clearcoatTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE); - GetMatTex(mat, clearcoat.clearcoatRoughnessTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE); - GetMatTex(mat, clearcoat.clearcoatNormalTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE); - - m->materialClearcoat = Nullable(clearcoat); - } - - bool hasMaterialTransmission = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_TRANSMISSION, hasMaterialTransmission); - - if (hasMaterialTransmission) { - mAsset->extensionsUsed.KHR_materials_transmission = true; - - MaterialTransmission transmission; - - mat->Get(AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR, transmission.transmissionFactor); - GetMatTex(mat, transmission.transmissionTexture, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE); - - m->materialTransmission = Nullable(transmission); + MaterialTransmission transmission; + if (GetMatTransmission(mat, transmission)) { + mAsset->extensionsUsed.KHR_materials_transmission = true; + m->materialTransmission = Nullable(transmission); + } + } } } } @@ -799,8 +835,7 @@ void glTF2Exporter::ExportMaterials() * Search through node hierarchy and find the node containing the given meshID. * Returns true on success, and false otherwise. */ -bool FindMeshNode(Ref& nodeIn, Ref& meshNode, std::string meshID) -{ +bool FindMeshNode(Ref &nodeIn, Ref &meshNode, const std::string &meshID) { for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) { if (meshID.compare(nodeIn->meshes[i]->id) == 0) { meshNode = nodeIn; @@ -1301,8 +1336,11 @@ unsigned int glTF2Exporter::ExportNode(const aiNode* n, Ref& parent) void glTF2Exporter::ExportScene() { - const char* sceneName = "defaultScene"; - Ref scene = mAsset->scenes.Create(sceneName); + // Use the name of the scene if specified + const std::string sceneName = (mScene->mName.length > 0) ? mScene->mName.C_Str() : "defaultScene"; + + // Ensure unique + Ref scene = mAsset->scenes.Create(mAsset->FindUniqueID(sceneName, "")); // root node will be the first one exported (idx 0) if (mAsset->nodes.Size() > 0) { diff --git a/code/AssetLib/glTF2/glTF2Exporter.h b/code/AssetLib/glTF2/glTF2Exporter.h index 86497516a..f5238297f 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.h +++ b/code/AssetLib/glTF2/glTF2Exporter.h @@ -72,6 +72,10 @@ namespace glTF2 struct OcclusionTextureInfo; struct Node; struct Texture; + struct PbrSpecularGlossiness; + struct MaterialSheen; + struct MaterialClearcoat; + struct MaterialTransmission; // Vec/matrix types, as raw float arrays typedef float (vec2)[2]; @@ -97,15 +101,19 @@ namespace Assimp protected: void WriteBinaryData(IOStream* outfile, std::size_t sceneLength); - void GetTexSampler(const aiMaterial* mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); - void GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTex(const aiMaterial* mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); - aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec4& prop, const char* propName, int type, int idx); - aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec3& prop, const char* propName, int type, int idx); + void GetTexSampler(const aiMaterial& mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); + void GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); + void GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); + void GetMatTex(const aiMaterial& mat, glTF2::Ref& texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); + aiReturn GetMatColor(const aiMaterial& mat, glTF2::vec4& prop, const char* propName, int type, int idx) const; + aiReturn GetMatColor(const aiMaterial& mat, glTF2::vec3& prop, const char* propName, int type, int idx) const; + bool GetMatSpecGloss(const aiMaterial& mat, glTF2::PbrSpecularGlossiness& pbrSG); + bool GetMatSheen(const aiMaterial& mat, glTF2::MaterialSheen& sheen); + bool GetMatClearcoat(const aiMaterial& mat, glTF2::MaterialClearcoat& clearcoat); + bool GetMatTransmission(const aiMaterial& mat, glTF2::MaterialTransmission& transmission); void ExportMetadata(); void ExportMaterials(); void ExportMeshes(); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index c62989c3b..ada7aa046 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -165,7 +165,8 @@ inline void SetMaterialTextureProperty(std::vector &embeddedTexIdxs, Asset } mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot)); - mat->AddProperty(&prop.texCoord, 1, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(texType, texSlot)); + const int uvIndex = static_cast(prop.texCoord); + mat->AddProperty(&uvIndex, 1, AI_MATKEY_UVWSRC(texType, texSlot)); if (prop.textureTransformSupported) { aiUVTransform transform; @@ -208,6 +209,11 @@ inline void SetMaterialTextureProperty(std::vector &embeddedTexIdxs, Asset if (sampler->minFilter != SamplerMinFilter::UNSET) { mat->AddProperty(&sampler->minFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(texType, texSlot)); } + } else { + // Use glTFv2 default sampler + const aiTextureMapMode default_wrap = aiTextureMapMode_Wrap; + mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_U(texType, texSlot)); + mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_V(texType, texSlot)); } } } @@ -238,16 +244,18 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M aimat->AddProperty(&str, AI_MATKEY_NAME); } + // Set Assimp DIFFUSE and BASE COLOR to the pbrMetallicRoughness base color and texture for backwards compatibility + // Technically should not load any pbrMetallicRoughness if extensionsRequired contains KHR_materials_pbrSpecularGlossiness SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); - SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR); + SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_BASE_COLOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_BASE_COLOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR); - aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR); + aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_METALLIC_FACTOR); + aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_ROUGHNESS_FACTOR); float roughnessAsShininess = 1 - mat.pbrMetallicRoughness.roughnessFactor; roughnessAsShininess *= roughnessAsShininess * 1000; @@ -259,6 +267,7 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M SetMaterialColorProperty(r, mat.emissiveFactor, aimat, AI_MATKEY_COLOR_EMISSIVE); aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED); + aimat->AddProperty(&mat.pbrMetallicRoughness.baseColorFactor[3], 1, AI_MATKEY_OPACITY); aiString alphaMode(mat.alphaMode); aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE); @@ -268,52 +277,58 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M if (mat.pbrSpecularGlossiness.isPresent) { PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value; - aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS); SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_COLOR_SPECULAR); float glossinessAsShininess = pbrSG.glossinessFactor * 1000.0f; aimat->AddProperty(&glossinessAsShininess, 1, AI_MATKEY_SHININESS); - aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR); + aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLOSSINESS_FACTOR); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, aiTextureType_DIFFUSE); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, aiTextureType_SPECULAR); } + + // glTFv2 is either PBR or Unlit + aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; if (mat.unlit) { - aimat->AddProperty(&mat.unlit, 1, AI_MATKEY_GLTF_UNLIT); + shadingMode = aiShadingMode_Unlit; } - //KHR_materials_sheen + aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL); + + + // KHR_materials_sheen if (mat.materialSheen.isPresent) { MaterialSheen &sheen = mat.materialSheen.value; - - aimat->AddProperty(&mat.materialSheen.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_SHEEN); - SetMaterialColorProperty(r, sheen.sheenColorFactor, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR); - aimat->AddProperty(&sheen.sheenRoughnessFactor, 1, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenColorTexture, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenRoughnessTexture, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE); + // Default value {0,0,0} disables Sheen + if (sheen.sheenColorFactor != defaultSheenFactor) { + SetMaterialColorProperty(r, sheen.sheenColorFactor, aimat, AI_MATKEY_SHEEN_COLOR_FACTOR); + aimat->AddProperty(&sheen.sheenRoughnessFactor, 1, AI_MATKEY_SHEEN_ROUGHNESS_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenColorTexture, aimat, AI_MATKEY_SHEEN_COLOR_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenRoughnessTexture, aimat, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE); + } } - //KHR_materials_clearcoat + // KHR_materials_clearcoat if (mat.materialClearcoat.isPresent) { MaterialClearcoat &clearcoat = mat.materialClearcoat.value; - - aimat->AddProperty(&mat.materialClearcoat.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT); - aimat->AddProperty(&clearcoat.clearcoatFactor, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR); - aimat->AddProperty(&clearcoat.clearcoatRoughnessFactor, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatRoughnessTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatNormalTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE); + // Default value 0.0 disables clearcoat + if (clearcoat.clearcoatFactor != 0.0f) { + aimat->AddProperty(&clearcoat.clearcoatFactor, 1, AI_MATKEY_CLEARCOAT_FACTOR); + aimat->AddProperty(&clearcoat.clearcoatRoughnessFactor, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatTexture, aimat, AI_MATKEY_CLEARCOAT_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatRoughnessTexture, aimat, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatNormalTexture, aimat, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE); + } } - //KHR_materials_transmission + // KHR_materials_transmission if (mat.materialTransmission.isPresent) { MaterialTransmission &transmission = mat.materialTransmission.value; - aimat->AddProperty(&mat.materialTransmission.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION); - aimat->AddProperty(&transmission.transmissionFactor, 1, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, transmission.transmissionTexture, aimat, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE); + aimat->AddProperty(&transmission.transmissionFactor, 1, AI_MATKEY_TRANSMISSION_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, transmission.transmissionTexture, aimat, AI_MATKEY_TRANSMISSION_TEXTURE); } return aimat; @@ -489,7 +504,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { "\" does not match the vertex count"); continue; } - + auto componentType = attr.color[c]->componentType; if (componentType == glTF2::ComponentType_FLOAT) { attr.color[c]->ExtractData(aim->mColors[c]); @@ -977,13 +992,21 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) { metadata->Add(extension.name.c_str(), extension.mBoolValue.value); } else if (extension.mValues.isPresent) { aiMetadata val; - for (size_t i = 0; i < extension.mValues.value.size(); ++i) { - ParseExtensions(&val, extension.mValues.value[i]); + for (auto const & subExtension : extension.mValues.value) { + ParseExtensions(&val, subExtension); } metadata->Add(extension.name.c_str(), val); } } +void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) { + if (extension.mValues.isPresent) { + for (auto const & subExtension : extension.mValues.value) { + ParseExtensions(metadata, subExtension); + } + } +} + aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector &meshOffsets, glTF2::Ref &ptr) { Node &node = *ptr; @@ -1002,9 +1025,14 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & } } - if (node.extensions) { + if (node.customExtensions || node.extras) { ainode->mMetaData = new aiMetadata; - ParseExtensions(ainode->mMetaData, node.extensions); + if (node.customExtensions) { + ParseExtensions(ainode->mMetaData, node.customExtensions); + } + if (node.extras) { + ParseExtras(ainode->mMetaData, node.extras); + } } GetNodeTransform(ainode->mTransformation, node); @@ -1308,6 +1336,23 @@ std::unordered_map GatherSamplers(Animation &an continue; } + auto& animsampler = anim.samplers[channel.sampler]; + + if (!animsampler.input) { + ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping."); + continue; + } + + if (!animsampler.output) { + ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler output. Skipping."); + continue; + } + + if (animsampler.input->count > animsampler.output->count) { + ASSIMP_LOG_WARN("Animation ", anim.name, ": Number of keyframes in sampler input ", animsampler.input->count, " exceeds number of keyframes in sampler output ", animsampler.output->count); + continue; + } + const unsigned int node_index = channel.target.node.GetIndex(); AnimationSamplers &sampler = samplers[node_index]; @@ -1442,10 +1487,11 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { } } - if (numEmbeddedTexs == 0) + if (numEmbeddedTexs == 0) { return; + } - ASSIMP_LOG_DEBUG("Importing ", numEmbeddedTexs, " embedded textures"); + ASSIMP_LOG_DEBUG("Importing ", numEmbeddedTexs, " embedded textures"); mScene->mTextures = new aiTexture *[numEmbeddedTexs]; std::fill(mScene->mTextures, mScene->mTextures + numEmbeddedTexs, nullptr); @@ -1498,7 +1544,8 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) { const bool hasVersion = !a.asset.version.empty(); const bool hasGenerator = !a.asset.generator.empty(); const bool hasCopyright = !a.asset.copyright.empty(); - if (hasVersion || hasGenerator || hasCopyright) { + const bool hasSceneMetadata = a.scene->customExtensions; + if (hasVersion || hasGenerator || hasCopyright || hasSceneMetadata) { mScene->mMetaData = new aiMetadata; if (hasVersion) { mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version)); @@ -1509,6 +1556,9 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) { if (hasCopyright) { mScene->mMetaData->Add(AI_METADATA_SOURCE_COPYRIGHT, aiString(a.asset.copyright)); } + if (hasSceneMetadata) { + ParseExtensions(mScene->mMetaData, a.scene->customExtensions); + } } } diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index be1619e81..933b5488c 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -822,7 +822,10 @@ ADD_ASSIMP_IMPORTER( GLTF AssetLib/glTF2/glTF2Importer.h ) -ADD_ASSIMP_IMPORTER( 3MF +ADD_ASSIMP_IMPORTER(3MF + AssetLib/3MF/3MFTypes.h + AssetLib/3MF/XmlSerializer.h + AssetLib/3MF/XmlSerializer.cpp AssetLib/3MF/D3MFImporter.h AssetLib/3MF/D3MFImporter.cpp AssetLib/3MF/D3MFOpcPackage.h @@ -875,6 +878,7 @@ ELSE() ../contrib/pugixml/src/pugiconfig.hpp ../contrib/pugixml/src/pugixml.hpp ) + INCLUDE_DIRECTORIES("../contrib/pugixml/src") SOURCE_GROUP( Contrib\\Pugixml FILES ${Pugixml_SRCS}) ENDIF() @@ -1035,16 +1039,26 @@ IF(ASSIMP_HUNTER_ENABLED) hunter_add_package(RapidJSON) find_package(RapidJSON CONFIG REQUIRED) ELSE() - INCLUDE_DIRECTORIES( "../contrib/rapidjson/include" ) - INCLUDE_DIRECTORIES( "../contrib" ) - INCLUDE_DIRECTORIES( "../contrib/pugixml/src" ) - ADD_DEFINITIONS( -DRAPIDJSON_HAS_STDSTRING=1 ) + INCLUDE_DIRECTORIES("../contrib/rapidjson/include") + ADD_DEFINITIONS( -DRAPIDJSON_HAS_STDSTRING=1) option( ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR "Suppress rapidjson warning on MSVC (NOTE: breaks android build)" ON ) if(ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR) ADD_DEFINITIONS( -DRAPIDJSON_NOMEMBERITERATORCLASS ) endif() ENDIF() +# stb +IF(ASSIMP_HUNTER_ENABLED) + hunter_add_package(stb) + find_package(stb CONFIG REQUIRED) +ELSE() + SET( stb_SRCS + ../contrib/stb/stb_image.h + ) + INCLUDE_DIRECTORIES("../contrib") + SOURCE_GROUP( Contrib\\stb FILES ${stb_SRCS}) +ENDIF() + # VC2010 fixes if(MSVC10) option( VC10_STDINT_FIX "Fix for VC10 Compiler regarding pstdint.h redefinition errors" OFF ) @@ -1103,6 +1117,7 @@ SET( assimp_src ${open3dgc_SRCS} ${ziplib_SRCS} ${Pugixml_SRCS} + ${stb_SRCS} # Necessary to show the headers in the project when using the VC++ generator: ${PUBLIC_HEADERS} @@ -1160,8 +1175,9 @@ IF(ASSIMP_HUNTER_ENABLED) utf8cpp zip::zip pugixml + stb::stb ) - + if (ASSIMP_BUILD_DRACO) target_link_libraries(assimp PUBLIC ${draco_LIBRARIES}) endif() diff --git a/code/Common/Assimp.cpp b/code/Common/Assimp.cpp index 4e2c7117c..3a0ec7d60 100644 --- a/code/Common/Assimp.cpp +++ b/code/Common/Assimp.cpp @@ -72,12 +72,25 @@ namespace Assimp { // underlying structure for aiPropertyStore typedef BatchLoader::PropertyMap PropertyMap; +#if defined(__has_warning) +#if __has_warning("-Wordered-compare-function-pointers") +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wordered-compare-function-pointers" +#endif +#endif + /** Stores the LogStream objects for all active C log streams */ struct mpred { bool operator()(const aiLogStream &s0, const aiLogStream &s1) const { return s0.callback < s1.callback && s0.user < s1.user; } }; + +#if defined(__has_warning) +#if __has_warning("-Wordered-compare-function-pointers") +#pragma GCC diagnostic pop +#endif +#endif typedef std::map LogStreamMap; /** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */ @@ -1251,3 +1264,36 @@ ASSIMP_API void aiQuaternionInterpolate( ai_assert(nullptr != end); aiQuaternion::Interpolate(*dst, *start, *end, factor); } + + +// stb_image is a lightweight image loader. It is shared by: +// - M3D import +// - PBRT export +// Since it's a header-only library, its implementation must be instantiated in some cpp file. +// Don't scatter this task over multiple importers/exporters. Maintain it in a central place (here!). + +#define ASSIMP_HAS_PBRT_EXPORT (!ASSIMP_BUILD_NO_EXPORT && !ASSIMP_BUILD_NO_PBRT_EXPORTER) +#define ASSIMP_HAS_M3D ((!ASSIMP_BUILD_NO_EXPORT && !ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER) + +#if ASSIMP_HAS_PBRT_EXPORT +# define ASSIMP_NEEDS_STB_IMAGE 1 +#elif ASSIMP_HAS_M3D +# define ASSIMP_NEEDS_STB_IMAGE 1 +# define STBI_ONLY_PNG +#endif + +#if ASSIMP_NEEDS_STB_IMAGE + +# if _MSC_VER // "unreferenced function has been removed" (SSE2 detection routine in x64 builds) +# pragma warning(push) +# pragma warning(disable: 4505) +# endif + +# define STB_IMAGE_IMPLEMENTATION +# include "stb/stb_image.h" + +# if _MSC_VER +# pragma warning(pop) +# endif + +#endif diff --git a/code/Common/DefaultIOStream.cpp b/code/Common/DefaultIOStream.cpp index ba9b9d625..557fbc78f 100644 --- a/code/Common/DefaultIOStream.cpp +++ b/code/Common/DefaultIOStream.cpp @@ -62,7 +62,7 @@ inline int select_fseek(FILE *file, int64_t offset, int origin) { } - + #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) template <> inline size_t select_ftell<8>(FILE *file) { @@ -75,7 +75,7 @@ inline int select_fseek<8>(FILE *file, int64_t offset, int origin) { } #endif // #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) - + } // namespace // ---------------------------------------------------------------------------------- @@ -95,7 +95,7 @@ size_t DefaultIOStream::Read(void *pvBuffer, } ai_assert(nullptr != pvBuffer); ai_assert(0 != pSize); - + return (mFile ? ::fread(pvBuffer, pSize, pCount, mFile) : 0); } diff --git a/code/Common/DefaultIOSystem.cpp b/code/Common/DefaultIOSystem.cpp index 98d51a17d..de93909de 100644 --- a/code/Common/DefaultIOSystem.cpp +++ b/code/Common/DefaultIOSystem.cpp @@ -71,7 +71,7 @@ static std::wstring Utf8ToWide(const char *in) { // size includes terminating null; std::wstring adds null automatically std::wstring out(static_cast(size) - 1, L'\0'); MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], size); - + return out; } @@ -85,7 +85,7 @@ static std::string WideToUtf8(const wchar_t *in) { // size includes terminating null; std::string adds null automatically std::string out(static_cast(size) - 1, '\0'); WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], size, nullptr, nullptr); - + return out; } #endif @@ -121,7 +121,7 @@ IOStream *DefaultIOSystem::Open(const char *strFile, const char *strMode) { if (name.empty()) { return nullptr; } - + file = ::_wfopen(name.c_str(), Utf8ToWide(strMode).c_str()); #else file = ::fopen(strFile, strMode); @@ -173,7 +173,7 @@ inline static std::string MakeAbsolutePath(const char *in) { free(ret); } #endif - if (!ret) { + else { // preserve the input path, maybe someone else is able to fix // the path before it is accessed (e.g. our file system filter) ASSIMP_LOG_WARN("Invalid path: ", std::string(in)); diff --git a/code/Common/Exporter.cpp b/code/Common/Exporter.cpp index ebcc955df..512bbf447 100644 --- a/code/Common/Exporter.cpp +++ b/code/Common/Exporter.cpp @@ -83,7 +83,7 @@ namespace Assimp { void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out); // ------------------------------------------------------------------------------------------------ -// Exporter worker function prototypes. Do not use const, because some exporter need to convert +// Exporter worker function prototypes. Do not use const, because some exporter need to convert // the scene temporary #ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER void ExportSceneCollada(const char*,IOSystem*, const aiScene*, const ExportProperties*); @@ -343,7 +343,7 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha delete pimpl->blob; pimpl->blob = nullptr; } - + auto baseName = pProperties ? pProperties->GetPropertyString(AI_CONFIG_EXPORT_BLOB_NAME, AI_BLOBIO_MAGIC) : AI_BLOBIO_MAGIC; std::shared_ptr old = pimpl->mIOSystem; diff --git a/code/Common/FileSystemFilter.h b/code/Common/FileSystemFilter.h index 6585f9df6..6782dd9e5 100644 --- a/code/Common/FileSystemFilter.h +++ b/code/Common/FileSystemFilter.h @@ -101,7 +101,7 @@ public: /** Tests for the existence of a file at the given path. */ bool Exists( const char* pFile) const { ai_assert( nullptr != mWrapped ); - + std::string tmp = pFile; // Currently this IOSystem is also used to open THE ONE FILE. @@ -126,7 +126,7 @@ public: if ( nullptr == pFile || nullptr == pMode ) { return nullptr; } - + ai_assert( nullptr != pFile ); ai_assert( nullptr != pMode ); diff --git a/code/Common/Importer.cpp b/code/Common/Importer.cpp index a2ad041fb..d0ed3c788 100644 --- a/code/Common/Importer.cpp +++ b/code/Common/Importer.cpp @@ -201,7 +201,7 @@ Importer::~Importer() { // Register a custom post-processing step aiReturn Importer::RegisterPPStep(BaseProcess* pImp) { ai_assert( nullptr != pImp ); - + ASSIMP_BEGIN_EXCEPTION_REGION(); pimpl->mPostProcessingSteps.push_back(pImp); @@ -215,7 +215,7 @@ aiReturn Importer::RegisterPPStep(BaseProcess* pImp) { // Register a custom loader plugin aiReturn Importer::RegisterLoader(BaseImporter* pImp) { ai_assert(nullptr != pImp); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // -------------------------------------------------------------------- @@ -242,7 +242,7 @@ aiReturn Importer::RegisterLoader(BaseImporter* pImp) { pimpl->mImporter.push_back(pImp); ASSIMP_LOG_INFO("Registering custom importer for these file extensions: ", baked); ASSIMP_END_EXCEPTION_REGION(aiReturn); - + return AI_SUCCESS; } @@ -296,7 +296,7 @@ aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) { // Supplies a custom IO handler to the importer to open and access files. void Importer::SetIOHandler( IOSystem* pIOHandler) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // If the new handler is zero, allocate a default IO implementation. if (!pIOHandler) { @@ -315,7 +315,7 @@ void Importer::SetIOHandler( IOSystem* pIOHandler) { // Get the currently set IO handler IOSystem* Importer::GetIOHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mIOHandler; } @@ -323,7 +323,7 @@ IOSystem* Importer::GetIOHandler() const { // Check whether a custom IO handler is currently set bool Importer::IsDefaultIOHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mIsDefaultHandler; } @@ -331,9 +331,9 @@ bool Importer::IsDefaultIOHandler() const { // Supplies a custom progress handler to get regular callbacks during importing void Importer::SetProgressHandler ( ProgressHandler* pHandler ) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); - + // If the new handler is zero, allocate a default implementation. if (!pHandler) { // Release pointer in the possession of the caller @@ -351,7 +351,7 @@ void Importer::SetProgressHandler ( ProgressHandler* pHandler ) { // Get the currently set progress handler ProgressHandler* Importer::GetProgressHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mProgressHandler; } @@ -359,7 +359,7 @@ ProgressHandler* Importer::GetProgressHandler() const { // Check whether a custom progress handler is currently set bool Importer::IsDefaultProgressHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mIsDefaultProgressHandler; } @@ -381,7 +381,7 @@ bool _ValidateFlags(unsigned int pFlags) { // Free the current scene void Importer::FreeScene( ) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); delete pimpl->mScene; @@ -396,14 +396,14 @@ void Importer::FreeScene( ) { // Get the current error string, if any const char* Importer::GetErrorString() const { ai_assert(nullptr != pimpl); - + // Must remain valid as long as ReadFile() or FreeFile() are not called return pimpl->mErrorString.c_str(); } const std::exception_ptr& Importer::GetException() const { ai_assert(nullptr != pimpl); - + // Must remain valid as long as ReadFile() or FreeFile() are not called return pimpl->mException; } @@ -412,7 +412,7 @@ const std::exception_ptr& Importer::GetException() const { // Enable extra-verbose mode void Importer::SetExtraVerbose(bool bDo) { ai_assert(nullptr != pimpl); - + pimpl->bExtraVerbose = bDo; } @@ -420,7 +420,7 @@ void Importer::SetExtraVerbose(bool bDo) { // Get the current scene const aiScene* Importer::GetScene() const { ai_assert(nullptr != pimpl); - + return pimpl->mScene; } @@ -428,7 +428,7 @@ const aiScene* Importer::GetScene() const { // Orphan the current scene and return it. aiScene* Importer::GetOrphanedScene() { ai_assert(nullptr != pimpl); - + aiScene* s = pimpl->mScene; ASSIMP_BEGIN_EXCEPTION_REGION(); @@ -437,7 +437,7 @@ aiScene* Importer::GetOrphanedScene() { pimpl->mErrorString = std::string(); pimpl->mException = std::exception_ptr(); ASSIMP_END_EXCEPTION_REGION(aiScene*); - + return s; } @@ -487,7 +487,7 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, unsigned int pFlags, const char* pHint /*= ""*/) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); if (!pHint) { pHint = ""; @@ -518,7 +518,7 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, // ------------------------------------------------------------------------------------------------ void WriteLogOpening(const std::string& file) { - + ASSIMP_LOG_INFO("Load ", file); // print a full version dump. This is nice because we don't @@ -580,7 +580,7 @@ void WriteLogOpening(const std::string& file) { // Reads the given file and returns its contents if successful. const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); const std::string pFile(_pFile); @@ -745,7 +745,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { // either successful or failure - the pointer expresses it anyways ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString, pimpl->mException); - + return pimpl->mScene; } @@ -754,7 +754,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { // Apply post-processing to the currently bound scene const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // Return immediately if no scene is active if (!pimpl->mScene) { @@ -832,7 +832,7 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) { } #endif // ! DEBUG } - pimpl->mProgressHandler->UpdatePostProcess( static_cast(pimpl->mPostProcessingSteps.size()), + pimpl->mProgressHandler->UpdatePostProcess( static_cast(pimpl->mPostProcessingSteps.size()), static_cast(pimpl->mPostProcessingSteps.size()) ); // update private scene flags @@ -845,14 +845,14 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) { ASSIMP_LOG_INFO("Leaving post processing pipeline"); ASSIMP_END_EXCEPTION_REGION(const aiScene*); - + return pimpl->mScene; } // ------------------------------------------------------------------------------------------------ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // Return immediately if no scene is active @@ -934,14 +934,14 @@ bool Importer::IsExtensionSupported(const char* szExtension) const { // ------------------------------------------------------------------------------------------------ size_t Importer::GetImporterCount() const { ai_assert(nullptr != pimpl); - + return pimpl->mImporter.size(); } // ------------------------------------------------------------------------------------------------ const aiImporterDesc* Importer::GetImporterInfo(size_t index) const { ai_assert(nullptr != pimpl); - + if (index >= pimpl->mImporter.size()) { return nullptr; } @@ -952,7 +952,7 @@ const aiImporterDesc* Importer::GetImporterInfo(size_t index) const { // ------------------------------------------------------------------------------------------------ BaseImporter* Importer::GetImporter (size_t index) const { ai_assert(nullptr != pimpl); - + if (index >= pimpl->mImporter.size()) { return nullptr; } @@ -963,7 +963,7 @@ BaseImporter* Importer::GetImporter (size_t index) const { // Find a loader plugin for a given file extension BaseImporter* Importer::GetImporter (const char* szExtension) const { ai_assert(nullptr != pimpl); - + return GetImporter(GetImporterIndex(szExtension)); } @@ -1002,7 +1002,7 @@ size_t Importer::GetImporterIndex (const char* szExtension) const { // Helper function to build a list of all file extensions supported by ASSIMP void Importer::GetExtensionList(aiString& szOut) const { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); std::set str; for (std::vector::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { @@ -1028,7 +1028,7 @@ void Importer::GetExtensionList(aiString& szOut) const { // Set a configuration property bool Importer::SetPropertyInteger(const char* szName, int iValue) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mIntProperties, szName,iValue); @@ -1040,7 +1040,7 @@ bool Importer::SetPropertyInteger(const char* szName, int iValue) { // Set a configuration property bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mFloatProperties, szName,iValue); @@ -1052,7 +1052,7 @@ bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) { // Set a configuration property bool Importer::SetPropertyString(const char* szName, const std::string& value) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mStringProperties, szName,value); @@ -1064,7 +1064,7 @@ bool Importer::SetPropertyString(const char* szName, const std::string& value) { // Set a configuration property bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mMatrixProperties, szName,value); @@ -1076,7 +1076,7 @@ bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) { // Get a configuration property int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mIntProperties,szName,iErrorReturn); } @@ -1084,7 +1084,7 @@ int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffff // Get a configuration property ai_real Importer::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mFloatProperties,szName,iErrorReturn); } @@ -1092,7 +1092,7 @@ ai_real Importer::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= // Get a configuration property std::string Importer::GetPropertyString(const char* szName, const std::string& iErrorReturn /*= ""*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mStringProperties,szName,iErrorReturn); } @@ -1100,13 +1100,13 @@ std::string Importer::GetPropertyString(const char* szName, const std::string& i // Get a configuration property aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mMatrixProperties,szName,iErrorReturn); } // ------------------------------------------------------------------------------------------------ // Get the memory requirements of a single node -inline +inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) { if ( nullptr == pcNode ) { return; @@ -1124,7 +1124,7 @@ void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) { // Get the memory requirements of the scene void Importer::GetMemoryRequirements(aiMemoryInfo& in) const { ai_assert(nullptr != pimpl); - + in = aiMemoryInfo(); aiScene* mScene = pimpl->mScene; diff --git a/code/Common/Importer.h b/code/Common/Importer.h index e7da7f439..0e04f9452 100644 --- a/code/Common/Importer.h +++ b/code/Common/Importer.h @@ -183,7 +183,7 @@ public: // ------------------------------------------------------------------- /** Construct a batch loader from a given IO system to be used - * to access external files + * to access external files */ explicit BatchLoader(IOSystem* pIO, bool validate = false ); @@ -197,13 +197,13 @@ public: * @param enable True for validation. */ void setValidation( bool enabled ); - + // ------------------------------------------------------------------- /** Returns the current validation step. * @return The current validation step. */ bool getValidation() const; - + // ------------------------------------------------------------------- /** Add a new file to the list of files to be loaded. * @param file File to be loaded diff --git a/code/Common/ImporterRegistry.cpp b/code/Common/ImporterRegistry.cpp index ddfcf6798..5df096166 100644 --- a/code/Common/ImporterRegistry.cpp +++ b/code/Common/ImporterRegistry.cpp @@ -205,8 +205,8 @@ corresponding preprocessor flag to selectively disable formats. namespace Assimp { // ------------------------------------------------------------------------------------------------ -void GetImporterInstanceList(std::vector &out) { - +void GetImporterInstanceList(std::vector &out) { + // Some importers may be unimplemented or otherwise unsuitable for general use // in their current state. Devs can set ASSIMP_ENABLE_DEV_IMPORTERS in their // local environment to enable them, otherwise they're left out of the registry. diff --git a/code/Common/RemoveComments.cpp b/code/Common/RemoveComments.cpp index d1c2ac391..e1ba99761 100644 --- a/code/Common/RemoveComments.cpp +++ b/code/Common/RemoveComments.cpp @@ -59,13 +59,16 @@ void CommentRemover::RemoveLineComments(const char* szComment, ai_assert(nullptr != szBuffer); ai_assert(*szComment); - const size_t len = strlen(szComment); + size_t len = strlen(szComment); + const size_t lenBuffer = strlen(szBuffer); + if (len > lenBuffer) { + len = lenBuffer; + } while (*szBuffer) { // skip over quotes if (*szBuffer == '\"' || *szBuffer == '\'') while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\''); - if (!strncmp(szBuffer,szComment,len)) { while (!IsLineEnd(*szBuffer)) *szBuffer++ = chReplacement; diff --git a/code/Common/SceneCombiner.cpp b/code/Common/SceneCombiner.cpp index 555d46b6a..8f10d6308 100644 --- a/code/Common/SceneCombiner.cpp +++ b/code/Common/SceneCombiner.cpp @@ -406,11 +406,25 @@ void SceneCombiner::MergeScenes(aiScene **_dest, aiScene *master, std::vector, // where n is the index of the texture. - aiString &s = *((aiString *)prop->mData); + // Copy here because we overwrite the string data in-place and the buffer inside of aiString + // will be a lie if we just reinterpret from prop->mData. The size of mData is not guaranteed to be + // MAXLEN in size. + aiString s(*(aiString *)prop->mData); if ('*' == s.data[0]) { // Offset the index and write it back .. const unsigned int idx = strtoul10(&s.data[1]) + offset[n]; - ASSIMP_itoa10(&s.data[1], sizeof(s.data) - 1, idx); + const unsigned int oldLen = s.length; + + s.length = 1 + ASSIMP_itoa10(&s.data[1], sizeof(s.data) - 1, idx); + + // The string changed in size so we need to reallocate the buffer for the property. + if (oldLen < s.length) { + prop->mDataLength += s.length - oldLen; + delete[] prop->mData; + prop->mData = new char[prop->mDataLength]; + } + + memcpy(prop->mData, static_cast(&s), prop->mDataLength); } } diff --git a/code/Common/ScenePreprocessor.cpp b/code/Common/ScenePreprocessor.cpp index 132b32df7..2ea17c643 100644 --- a/code/Common/ScenePreprocessor.cpp +++ b/code/Common/ScenePreprocessor.cpp @@ -89,6 +89,9 @@ void ScenePreprocessor::ProcessScene() { ASSIMP_LOG_DEBUG("ScenePreprocessor: Adding default material \'" AI_DEFAULT_MATERIAL_NAME "\'"); for (unsigned int i = 0; i < scene->mNumMeshes; ++i) { + if (nullptr == scene->mMeshes[i]) { + continue; + } scene->mMeshes[i]->mMaterialIndex = scene->mNumMaterials; } diff --git a/code/Common/Win32DebugLogStream.h b/code/Common/Win32DebugLogStream.h index 3385aa8ce..51f1ab178 100644 --- a/code/Common/Win32DebugLogStream.h +++ b/code/Common/Win32DebugLogStream.h @@ -71,19 +71,19 @@ public: }; // --------------------------------------------------------------------------- -inline -Win32DebugLogStream::Win32DebugLogStream(){ +inline +Win32DebugLogStream::Win32DebugLogStream(){ // empty } // --------------------------------------------------------------------------- -inline +inline Win32DebugLogStream::~Win32DebugLogStream(){ // empty } // --------------------------------------------------------------------------- -inline +inline void Win32DebugLogStream::write(const char* message) { ::OutputDebugStringA( message); } diff --git a/code/Common/material.cpp b/code/Common/material.cpp index 5230c8b43..6c90e66f0 100644 --- a/code/Common/material.cpp +++ b/code/Common/material.cpp @@ -47,10 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include // ------------------------------------------------------------------------------- -const char* TextureTypeToString(aiTextureType in) -{ - switch (in) - { +const char *TextureTypeToString(aiTextureType in) { + switch (in) { case aiTextureType_NONE: return "n/a"; case aiTextureType_DIFFUSE: @@ -87,6 +85,12 @@ const char* TextureTypeToString(aiTextureType in) return "DiffuseRoughness"; case aiTextureType_AMBIENT_OCCLUSION: return "AmbientOcclusion"; + case aiTextureType_SHEEN: + return "Sheen"; + case aiTextureType_CLEARCOAT: + return "Clearcoat"; + case aiTextureType_TRANSMISSION: + return "Transmission"; case aiTextureType_UNKNOWN: return "Unknown"; default: diff --git a/code/Material/MaterialSystem.cpp b/code/Material/MaterialSystem.cpp index c35a1aa93..23d198953 100644 --- a/code/Material/MaterialSystem.cpp +++ b/code/Material/MaterialSystem.cpp @@ -555,17 +555,23 @@ uint32_t Assimp::ComputeMaterialHash(const aiMaterial *mat, bool includeMatName } // ------------------------------------------------------------------------------------------------ -void aiMaterial::CopyPropertyList(aiMaterial *pcDest, +void aiMaterial::CopyPropertyList(aiMaterial *const pcDest, const aiMaterial *pcSrc) { ai_assert(nullptr != pcDest); ai_assert(nullptr != pcSrc); + ai_assert(pcDest->mNumProperties <= pcDest->mNumAllocated); + ai_assert(pcSrc->mNumProperties <= pcSrc->mNumAllocated); - unsigned int iOldNum = pcDest->mNumProperties; + const unsigned int iOldNum = pcDest->mNumProperties; pcDest->mNumAllocated += pcSrc->mNumAllocated; pcDest->mNumProperties += pcSrc->mNumProperties; + const unsigned int numAllocated = pcDest->mNumAllocated; aiMaterialProperty **pcOld = pcDest->mProperties; - pcDest->mProperties = new aiMaterialProperty *[pcDest->mNumAllocated]; + pcDest->mProperties = new aiMaterialProperty *[numAllocated]; + + ai_assert(!iOldNum || pcOld); + ai_assert(iOldNum < numAllocated); if (iOldNum && pcOld) { for (unsigned int i = 0; i < iOldNum; ++i) { diff --git a/code/Pbrt/PbrtExporter.cpp b/code/Pbrt/PbrtExporter.cpp index b793c37f9..1c7024c28 100644 --- a/code/Pbrt/PbrtExporter.cpp +++ b/code/Pbrt/PbrtExporter.cpp @@ -83,8 +83,7 @@ Other: #include #include -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" +#include "stb/stb_image.h" using namespace Assimp; @@ -106,14 +105,13 @@ void ExportScenePbrt ( } // end of namespace Assimp // Constructor -PbrtExporter::PbrtExporter ( - const aiScene* pScene, IOSystem* pIOSystem, - const std::string path, const std::string file) -: mScene(pScene), - mIOSystem(pIOSystem), - mPath(path), - mFile(file) -{ +PbrtExporter::PbrtExporter( + const aiScene *pScene, IOSystem *pIOSystem, + const std::string &path, const std::string &file) : + mScene(pScene), + mIOSystem(pIOSystem), + mPath(path), + mFile(file) { // Export embedded textures. if (mScene->mNumTextures > 0) if (!mIOSystem->CreateDirectory("textures")) @@ -210,12 +208,12 @@ void PbrtExporter::WriteMetaData() { aiString* value = static_cast(pMetaData->mValues[i].mData); std::string svalue = value->C_Str(); - std::size_t found = svalue.find_first_of("\n"); + std::size_t found = svalue.find_first_of('\n'); mOutput << "\n"; while (found != std::string::npos) { mOutput << "# " << svalue.substr(0, found) << "\n"; svalue = svalue.substr(found + 1); - found = svalue.find_first_of("\n"); + found = svalue.find_first_of('\n'); } mOutput << "# " << svalue << "\n"; break; @@ -596,8 +594,8 @@ void PbrtExporter::WriteMaterial(int m) { } mOutput << "\n"; - auto White = [](aiColor3D c) { return c.r == 1 && c.g == 1 && c.b == 1; }; - auto Black = [](aiColor3D c) { return c.r == 0 && c.g == 0 && c.b == 0; }; + auto White = [](const aiColor3D &c) { return c.r == 1 && c.g == 1 && c.b == 1; }; + auto Black = [](const aiColor3D &c) { return c.r == 0 && c.g == 0 && c.b == 0; }; aiColor3D diffuse, specular, transparency; bool constantDiffuse = (material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse) == AI_SUCCESS && diff --git a/code/Pbrt/PbrtExporter.h b/code/Pbrt/PbrtExporter.h index 167f318fc..e8ff03ccb 100644 --- a/code/Pbrt/PbrtExporter.h +++ b/code/Pbrt/PbrtExporter.h @@ -74,8 +74,8 @@ class PbrtExporter { public: /// Constructor for a specific scene to export - PbrtExporter(const aiScene* pScene, IOSystem* pIOSystem, - const std::string path, const std::string file); + PbrtExporter(const aiScene *pScene, IOSystem *pIOSystem, + const std::string &path, const std::string &file); /// Destructor virtual ~PbrtExporter(); diff --git a/code/PostProcessing/ArmaturePopulate.h b/code/PostProcessing/ArmaturePopulate.h index 08a72bd66..ded8b4886 100644 --- a/code/PostProcessing/ArmaturePopulate.h +++ b/code/PostProcessing/ArmaturePopulate.h @@ -97,7 +97,7 @@ public: static void BuildBoneList(aiNode *current_node, const aiNode *root_node, const aiScene *scene, - std::vector &bones); + std::vector &bones); static void BuildBoneStack(aiNode *current_node, const aiNode *root_node, const aiScene *scene, diff --git a/code/PostProcessing/DropFaceNormalsProcess.cpp b/code/PostProcessing/DropFaceNormalsProcess.cpp index c01ac35e5..21abf9693 100644 --- a/code/PostProcessing/DropFaceNormalsProcess.cpp +++ b/code/PostProcessing/DropFaceNormalsProcess.cpp @@ -104,7 +104,7 @@ bool DropFaceNormalsProcess::DropMeshFaceNormals (aiMesh* mesh) { if (nullptr == mesh->mNormals) { return false; } - + delete[] mesh->mNormals; mesh->mNormals = nullptr; return true; diff --git a/code/PostProcessing/EmbedTexturesProcess.cpp b/code/PostProcessing/EmbedTexturesProcess.cpp index 7e435e556..d7720de98 100644 --- a/code/PostProcessing/EmbedTexturesProcess.cpp +++ b/code/PostProcessing/EmbedTexturesProcess.cpp @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -41,6 +40,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "EmbedTexturesProcess.h" +#include +#include #include #include "ProcessHelper.h" @@ -48,11 +49,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; -EmbedTexturesProcess::EmbedTexturesProcess() -: BaseProcess() { +EmbedTexturesProcess::EmbedTexturesProcess() : + BaseProcess() { + // empty } EmbedTexturesProcess::~EmbedTexturesProcess() { + // empty } bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const { @@ -62,15 +65,16 @@ bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const { void EmbedTexturesProcess::SetupProperties(const Importer* pImp) { mRootPath = pImp->GetPropertyString("sourceFilePath"); mRootPath = mRootPath.substr(0, mRootPath.find_last_of("\\/") + 1u); + mIOHandler = pImp->GetIOHandler(); } void EmbedTexturesProcess::Execute(aiScene* pScene) { - if (pScene == nullptr || pScene->mRootNode == nullptr) return; + if (pScene == nullptr || pScene->mRootNode == nullptr || mIOHandler == nullptr){ + return; + } aiString path; - uint32_t embeddedTexturesCount = 0u; - for (auto matId = 0u; matId < pScene->mNumMaterials; ++matId) { auto material = pScene->mMaterials[matId]; @@ -96,32 +100,36 @@ void EmbedTexturesProcess::Execute(aiScene* pScene) { ASSIMP_LOG_INFO("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." ); } -bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { +bool EmbedTexturesProcess::addTexture(aiScene *pScene, const std::string &path) const { std::streampos imageSize = 0; std::string imagePath = path; // Test path directly - std::ifstream file(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == std::streampos(-1)) { + if (!mIOHandler->Exists(imagePath)) { ASSIMP_LOG_WARN("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder."); // Test path in root path imagePath = mRootPath + path; - file.open(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == std::streampos(-1)) { + if (!mIOHandler->Exists(imagePath)) { // Test path basename in root path imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u); - file.open(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == std::streampos(-1)) { + if (!mIOHandler->Exists(imagePath)) { ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, "."); return false; } } } + IOStream* pFile = mIOHandler->Open(imagePath); + if (pFile == nullptr) { + ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, "."); + return false; + } + imageSize = pFile->FileSize(); aiTexel* imageContent = new aiTexel[ 1ul + static_cast( imageSize ) / sizeof(aiTexel)]; - file.seekg(0, std::ios::beg); - file.read(reinterpret_cast(imageContent), imageSize); + pFile->Seek(0, aiOrigin_SET); + pFile->Read(reinterpret_cast(imageContent), imageSize, 1); + mIOHandler->Close(pFile); // Enlarging the textures table unsigned int textureId = pScene->mNumTextures++; @@ -129,7 +137,7 @@ bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { pScene->mTextures = new aiTexture*[pScene->mNumTextures]; ::memmove(pScene->mTextures, oldTextures, sizeof(aiTexture*) * (pScene->mNumTextures - 1u)); delete [] oldTextures; - + // Add the new texture auto pTexture = new aiTexture; pTexture->mHeight = 0; // Means that this is still compressed diff --git a/code/PostProcessing/EmbedTexturesProcess.h b/code/PostProcessing/EmbedTexturesProcess.h index 90970937a..b33968850 100644 --- a/code/PostProcessing/EmbedTexturesProcess.h +++ b/code/PostProcessing/EmbedTexturesProcess.h @@ -48,6 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. struct aiNode; +class IOSystem; + namespace Assimp { /** @@ -76,10 +78,11 @@ public: private: // Resolve the path and add the file content to the scene as a texture. - bool addTexture(aiScene* pScene, std::string path) const; + bool addTexture(aiScene *pScene, const std::string &path) const; private: std::string mRootPath; + IOSystem* mIOHandler = nullptr; }; } // namespace Assimp diff --git a/code/PostProcessing/FindDegenerates.cpp b/code/PostProcessing/FindDegenerates.cpp index eae91ff02..3809abf50 100644 --- a/code/PostProcessing/FindDegenerates.cpp +++ b/code/PostProcessing/FindDegenerates.cpp @@ -90,7 +90,7 @@ void FindDegeneratesProcess::Execute( aiScene* pScene) { if ( nullptr == pScene) { return; } - + std::unordered_map meshMap; meshMap.reserve(pScene->mNumMeshes); diff --git a/code/PostProcessing/FindInstancesProcess.cpp b/code/PostProcessing/FindInstancesProcess.cpp index ab5f52b78..d46afc201 100644 --- a/code/PostProcessing/FindInstancesProcess.cpp +++ b/code/PostProcessing/FindInstancesProcess.cpp @@ -137,7 +137,7 @@ void FindInstancesProcess::Execute( aiScene* pScene) aiMesh* inst = pScene->mMeshes[i]; hashes[i] = GetMeshHash(inst); - // Find an appropriate epsilon + // Find an appropriate epsilon // to compare position differences against float epsilon = ComputePositionEpsilon(inst); epsilon *= epsilon; diff --git a/code/PostProcessing/MakeVerboseFormat.h b/code/PostProcessing/MakeVerboseFormat.h index c1db76dca..4304a3afa 100644 --- a/code/PostProcessing/MakeVerboseFormat.h +++ b/code/PostProcessing/MakeVerboseFormat.h @@ -98,7 +98,7 @@ public: // ------------------------------------------------------------------- /** Checks whether the scene is already in verbose format. - * @param pScene The data to check. + * @param pScene The data to check. * @return true if the scene is already in verbose format. */ static bool IsVerboseFormat(const aiScene* pScene); diff --git a/code/PostProcessing/OptimizeGraph.cpp b/code/PostProcessing/OptimizeGraph.cpp index e33c2ab18..d7bcf3fec 100644 --- a/code/PostProcessing/OptimizeGraph.cpp +++ b/code/PostProcessing/OptimizeGraph.cpp @@ -170,7 +170,7 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode *nd, std::list &n ++it; } if (join_master && !join.empty()) { - join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%i", count_merged++); + join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%u", count_merged++); unsigned int out_meshes = 0; for (std::list::const_iterator it = join.cbegin(); it != join.cend(); ++it) { diff --git a/code/PostProcessing/PretransformVertices.cpp b/code/PostProcessing/PretransformVertices.cpp index e9a3af0d2..fa95319ff 100644 --- a/code/PostProcessing/PretransformVertices.cpp +++ b/code/PostProcessing/PretransformVertices.cpp @@ -481,7 +481,7 @@ void PretransformVertices::Execute(aiScene *pScene) { pScene->mMeshes[i]->mNumBones = 0; } } else { - apcOutMeshes.reserve(pScene->mNumMaterials << 1u); + apcOutMeshes.reserve(static_cast(pScene->mNumMaterials) << 1u); std::list aiVFormats; std::vector s(pScene->mNumMeshes, 0); diff --git a/code/PostProcessing/RemoveRedundantMaterials.cpp b/code/PostProcessing/RemoveRedundantMaterials.cpp index c252f37a5..36745fb1d 100644 --- a/code/PostProcessing/RemoveRedundantMaterials.cpp +++ b/code/PostProcessing/RemoveRedundantMaterials.cpp @@ -215,7 +215,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene) } else { - ASSIMP_LOG_INFO("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ", + ASSIMP_LOG_INFO("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ", unreferencedRemoved, " unused materials."); } } diff --git a/code/PostProcessing/ScaleProcess.cpp b/code/PostProcessing/ScaleProcess.cpp index 0a3e29c42..63dd0443d 100644 --- a/code/PostProcessing/ScaleProcess.cpp +++ b/code/PostProcessing/ScaleProcess.cpp @@ -75,7 +75,7 @@ void ScaleProcess::SetupProperties( const Importer* pImp ) { // File scaling * Application Scaling float importerScale = pImp->GetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, 1.0f ); - // apply scale to the scale + // apply scale to the scale // helps prevent bugs with backward compatibility for anyone using normal scaling. mScale *= importerScale; } @@ -84,7 +84,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { if(mScale == 1.0f) { return; // nothing to scale } - + ai_assert( mScale != 0 ); ai_assert( nullptr != pScene ); ai_assert( nullptr != pScene->mRootNode ); @@ -96,7 +96,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { if ( nullptr == pScene->mRootNode ) { return; } - + // Process animations and update position transform to new unit system for( unsigned int animationID = 0; animationID < pScene->mNumAnimations; animationID++ ) { @@ -105,7 +105,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { for( unsigned int animationChannel = 0; animationChannel < animation->mNumChannels; animationChannel++) { aiNodeAnim* anim = animation->mChannels[animationChannel]; - + for( unsigned int posKey = 0; posKey < anim->mNumPositionKeys; posKey++) { aiVectorKey& vectorKey = anim->mPositionKeys[posKey]; @@ -116,8 +116,8 @@ void ScaleProcess::Execute( aiScene* pScene ) { for( unsigned int meshID = 0; meshID < pScene->mNumMeshes; meshID++) { - aiMesh *mesh = pScene->mMeshes[meshID]; - + aiMesh *mesh = pScene->mMeshes[meshID]; + // Reconstruct mesh vertexes to the new unit system for( unsigned int vertexID = 0; vertexID < mesh->mNumVertices; vertexID++) { @@ -129,9 +129,9 @@ void ScaleProcess::Execute( aiScene* pScene ) { // bone placement / scaling for( unsigned int boneID = 0; boneID < mesh->mNumBones; boneID++) { - // Reconstruct matrix by transform rather than by scale + // Reconstruct matrix by transform rather than by scale // This prevent scale values being changed which can - // be meaningful in some cases + // be meaningful in some cases // like when you want the modeller to see 1:1 compatibility. aiBone* bone = mesh->mBones[boneID]; @@ -139,10 +139,10 @@ void ScaleProcess::Execute( aiScene* pScene ) { aiQuaternion rotation; bone->mOffsetMatrix.Decompose( scale, rotation, pos); - + aiMatrix4x4 translation; aiMatrix4x4::Translation( pos * mScale, translation ); - + aiMatrix4x4 scaling; aiMatrix4x4::Scaling( aiVector3D(scale), scaling ); @@ -157,7 +157,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { for( unsigned int animMeshID = 0; animMeshID < mesh->mNumAnimMeshes; animMeshID++) { aiAnimMesh * animMesh = mesh->mAnimMeshes[animMeshID]; - + for( unsigned int vertexID = 0; vertexID < animMesh->mNumVertices; vertexID++) { aiVector3D& vertex = animMesh->mVertices[vertexID]; @@ -169,31 +169,31 @@ void ScaleProcess::Execute( aiScene* pScene ) { traverseNodes( pScene->mRootNode ); } -void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) { +void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) { applyScaling( node ); for( size_t i = 0; i < node->mNumChildren; i++) { // recurse into the tree until we are done! - traverseNodes( node->mChildren[i], nested_node_id+1 ); + traverseNodes( node->mChildren[i], nested_node_id+1 ); } } void ScaleProcess::applyScaling( aiNode *currentNode ) { if ( nullptr != currentNode ) { - // Reconstruct matrix by transform rather than by scale + // Reconstruct matrix by transform rather than by scale // This prevent scale values being changed which can - // be meaningful in some cases - // like when you want the modeller to + // be meaningful in some cases + // like when you want the modeller to // see 1:1 compatibility. - + aiVector3D pos, scale; aiQuaternion rotation; currentNode->mTransformation.Decompose( scale, rotation, pos); - + aiMatrix4x4 translation; aiMatrix4x4::Translation( pos * mScale, translation ); - + aiMatrix4x4 scaling; // note: we do not use mScale here, this is on purpose. diff --git a/code/PostProcessing/ScaleProcess.h b/code/PostProcessing/ScaleProcess.h index d88490b1c..4dc7e3559 100644 --- a/code/PostProcessing/ScaleProcess.h +++ b/code/PostProcessing/ScaleProcess.h @@ -55,8 +55,8 @@ namespace Assimp { // --------------------------------------------------------------------------- /** ScaleProcess: Class to rescale the whole model. * Now rescales animations, bones, and blend shapes properly. - * Please note this will not write to 'scale' transform it will rewrite mesh - * and matrixes so that your scale values + * Please note this will not write to 'scale' transform it will rewrite mesh + * and matrixes so that your scale values * from your model package are preserved, so this is completely intentional * bugs should be reported as soon as they are found. */ diff --git a/code/PostProcessing/SortByPTypeProcess.cpp b/code/PostProcessing/SortByPTypeProcess.cpp index 20ab63249..3787be51e 100644 --- a/code/PostProcessing/SortByPTypeProcess.cpp +++ b/code/PostProcessing/SortByPTypeProcess.cpp @@ -127,7 +127,7 @@ void SortByPTypeProcess::Execute(aiScene *pScene) { unsigned int aiNumMeshesPerPType[4] = { 0, 0, 0, 0 }; std::vector outMeshes; - outMeshes.reserve(pScene->mNumMeshes << 1u); + outMeshes.reserve(static_cast(pScene->mNumMeshes) << 1u); bool bAnyChanges = false; diff --git a/code/PostProcessing/SplitByBoneCountProcess.cpp b/code/PostProcessing/SplitByBoneCountProcess.cpp index 2613d8561..ed5b9411e 100644 --- a/code/PostProcessing/SplitByBoneCountProcess.cpp +++ b/code/PostProcessing/SplitByBoneCountProcess.cpp @@ -209,7 +209,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector newBonesAtCurrentFace; - + const aiFace& face = pMesh->mFaces[a]; // check every vertex if its bones would still fit into the current submesh for( unsigned int b = 0; b < face.mNumIndices; ++b ) @@ -221,7 +221,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vectormNumAnimMeshes > 0) { newMesh->mNumAnimMeshes = pMesh->mNumAnimMeshes; newMesh->mAnimMeshes = new aiAnimMesh*[newMesh->mNumAnimMeshes]; - + for (unsigned int morphIdx = 0; morphIdx < newMesh->mNumAnimMeshes; ++morphIdx) { aiAnimMesh* origTarget = pMesh->mAnimMeshes[morphIdx]; aiAnimMesh* newTarget = new aiAnimMesh; @@ -421,16 +421,16 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vectormNumVertices = numSubMeshVertices; newTarget->mVertices = new aiVector3D[numSubMeshVertices]; newMesh->mAnimMeshes[morphIdx] = newTarget; - + if (origTarget->HasNormals()) { newTarget->mNormals = new aiVector3D[numSubMeshVertices]; } - + if (origTarget->HasTangentsAndBitangents()) { newTarget->mTangents = new aiVector3D[numSubMeshVertices]; newTarget->mBitangents = new aiVector3D[numSubMeshVertices]; } - + for( unsigned int vi = 0; vi < numSubMeshVertices; ++vi) { // find the source vertex for it in the source mesh unsigned int previousIndex = previousVertexIndices[vi]; diff --git a/code/PostProcessing/SplitLargeMeshes.cpp b/code/PostProcessing/SplitLargeMeshes.cpp index ed680cf7f..cb614edaa 100644 --- a/code/PostProcessing/SplitLargeMeshes.cpp +++ b/code/PostProcessing/SplitLargeMeshes.cpp @@ -40,7 +40,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/** +/** * @file Implementation of the SplitLargeMeshes postprocessing step */ @@ -353,7 +353,7 @@ void SplitLargeMeshesProcess_Vertex::Execute( aiScene* pScene) { std::vector > avList; - //Check for point cloud first, + //Check for point cloud first, //Do not process point cloud, splitMesh works only with faces data for (unsigned int a = 0; a < pScene->mNumMeshes; a++) { if ( pScene->mMeshes[a]->mPrimitiveTypes == aiPrimitiveType_POINT ) { diff --git a/code/PostProcessing/TextureTransform.cpp b/code/PostProcessing/TextureTransform.cpp index 681b047c0..74b00d92c 100644 --- a/code/PostProcessing/TextureTransform.cpp +++ b/code/PostProcessing/TextureTransform.cpp @@ -448,7 +448,7 @@ void TextureTransformStep::Execute( aiScene* pScene) if (size > AI_MAX_NUMBER_OF_TEXTURECOORDS) { if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_ERROR(static_cast(trafo.size()), " UV channels required but just ", + ASSIMP_LOG_ERROR(static_cast(trafo.size()), " UV channels required but just ", AI_MAX_NUMBER_OF_TEXTURECOORDS, " available"); } size = AI_MAX_NUMBER_OF_TEXTURECOORDS; diff --git a/code/PostProcessing/TriangulateProcess.cpp b/code/PostProcessing/TriangulateProcess.cpp index 0f71320b8..b7928ee59 100644 --- a/code/PostProcessing/TriangulateProcess.cpp +++ b/code/PostProcessing/TriangulateProcess.cpp @@ -86,11 +86,11 @@ namespace { /** * @brief Encode the current triangle, and make sure it is recognized as a triangle. - * + * * This method will rotate indices in tri if needed in order to avoid tri to be considered * part of the previous ngon. This method is to be used whenever you want to emit a real triangle, * and make sure it is seen as a triangle. - * + * * @param tri Triangle to encode. */ void ngonEncodeTriangle(aiFace * tri) { @@ -108,10 +108,10 @@ namespace { /** * @brief Encode a quad (2 triangles) in ngon encoding, and make sure they are seen as a single ngon. - * + * * @param tri1 First quad triangle * @param tri2 Second quad triangle - * + * * @pre Triangles must be properly fanned from the most appropriate vertex. */ void ngonEncodeQuad(aiFace *tri1, aiFace *tri2) { @@ -140,7 +140,7 @@ namespace { /** * @brief Check whether this triangle would be considered part of the lastly emitted ngon or not. - * + * * @param tri Current triangle. * @return true If used as is, this triangle will be part of last ngon. * @return false If used as is, this triangle is not considered part of the last ngon. @@ -512,7 +512,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) num = 0; break; - /*curOut -= (max-num); // undo all previous work + /*curOut -= (max-num); // undo all previous work for (tmp = 0; tmp < max-2; ++tmp) { aiFace& nface = *curOut++; diff --git a/contrib/draco/.ruby-version b/contrib/draco/.ruby-version deleted file mode 100644 index 276cbf9e2..000000000 --- a/contrib/draco/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.3.0 diff --git a/contrib/draco/.travis.yml b/contrib/draco/.travis.yml deleted file mode 100644 index e9ef7123f..000000000 --- a/contrib/draco/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -cache: ccache -language: cpp -matrix: - include: - - os: linux - dist: xenial - compiler: clang - - os: linux - dist: xenial - compiler: gcc - - os: osx - compiler: clang - -addons: - apt: - packages: - - cmake - -script: - # Output version info for compilers, cmake, and make - - ${CC} -v - - ${CXX} -v - - cmake --version - - make --version - # Clone googletest - - pushd .. && git clone https://github.com/google/googletest.git && popd - # Configure and build - - mkdir _travis_build && cd _travis_build - - cmake -G "Unix Makefiles" -DENABLE_TESTS=ON .. - - make -j10 - - ./draco_tests diff --git a/contrib/draco/CMakeLists.txt b/contrib/draco/CMakeLists.txt index 3da2c664a..5526e7f60 100644 --- a/contrib/draco/CMakeLists.txt +++ b/contrib/draco/CMakeLists.txt @@ -804,7 +804,7 @@ else() draco_points_enc) # Library targets that consume the object collections. - if(MSVC OR WIN32) + if(MSVC) # In order to produce a DLL and import library the Windows tools require # that the exported symbols are part of the DLL target. The unfortunate side # effect of this is that a single configuration cannot output both the @@ -889,9 +889,6 @@ else() # For Mac, we need to build a .bundle for the unity plugin. if(APPLE) set_target_properties(dracodec_unity PROPERTIES BUNDLE true) - elseif(NOT unity_decoder_lib_type STREQUAL STATIC) - set_target_properties(dracodec_unity - PROPERTIES SOVERSION ${DRACO_SOVERSION}) endif() endif() @@ -916,9 +913,6 @@ else() # For Mac, we need to build a .bundle for the plugin. if(APPLE) set_target_properties(draco_maya_wrapper PROPERTIES BUNDLE true) - else() - set_target_properties(draco_maya_wrapper - PROPERTIES SOVERSION ${DRACO_SOVERSION}) endif() endif() diff --git a/contrib/draco/README.md b/contrib/draco/README.md index add66edcb..0d980b387 100644 --- a/contrib/draco/README.md +++ b/contrib/draco/README.md @@ -2,16 +2,16 @@

    -![Build Status: master](https://travis-ci.org/google/draco.svg?branch=master) +[![Build Status](https://github.com/google/draco/workflows/Build/badge.svg)](https://github.com/google/draco/actions?query=workflow%3ABuild) News ======= ### Version 1.4.1 release -* Using the versioned gstatic.com WASM and Javascript decoders is now +* Using the versioned www.gstatic.com WASM and Javascript decoders is now recommended. To use v1.4.1, use this URL: * https://www.gstatic.com/draco/versioned/decoders/1.4.1/* * Replace the * with the files to load. E.g. - * https://gstatic.com/draco/versioned/decoders/1.4.1/draco_decoder.js + * https://www.gstatic.com/draco/versioned/decoders/1.4.1/draco_decoder.js * This works with the v1.3.6 and v1.4.0 releases, and will work with future Draco releases. * Bug fixes diff --git a/contrib/draco/cmake/draco_build_definitions.cmake b/contrib/draco/cmake/draco_build_definitions.cmake index c1ada6206..f7354c15f 100644 --- a/contrib/draco/cmake/draco_build_definitions.cmake +++ b/contrib/draco/cmake/draco_build_definitions.cmake @@ -6,7 +6,7 @@ set(DRACO_CMAKE_DRACO_BUILD_DEFINITIONS_CMAKE_ 1) # Utility for controlling the main draco library dependency. This changes in # shared builds, and when an optional target requires a shared library build. macro(set_draco_target) - if(MSVC OR WIN32) + if(MSVC) set(draco_dependency draco) set(draco_plugin_dependency ${draco_dependency}) else() @@ -63,6 +63,11 @@ macro(draco_set_build_definitions) if(BUILD_SHARED_LIBS) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) endif() + else() + if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) + # Ensure 64-bit platforms can support large files. + list(APPEND draco_defines "_LARGEFILE_SOURCE" "_FILE_OFFSET_BITS=64") + endif() endif() if(ANDROID) @@ -114,4 +119,6 @@ macro(draco_set_build_definitions) draco_check_emscripten_environment() draco_get_required_emscripten_flags(FLAG_LIST_VAR draco_base_cxx_flags) endif() + + draco_configure_sanitizer() endmacro() diff --git a/contrib/draco/cmake/draco_features.cmake b/contrib/draco/cmake/draco_features.cmake deleted file mode 100644 index be444bf24..000000000 --- a/contrib/draco/cmake/draco_features.cmake +++ /dev/null @@ -1,63 +0,0 @@ -if(DRACO_CMAKE_DRACO_FEATURES_CMAKE_) - return() -endif() -set(DRACO_CMAKE_DRACO_FEATURES_CMAKE_ 1) - -set(draco_features_file_name "${draco_build_dir}/draco/draco_features.h") -set(draco_features_list) - -# Macro that handles tracking of Draco preprocessor symbols for the purpose of -# producing draco_features.h. -# -# draco_enable_feature(FEATURE [TARGETS ]) FEATURE -# is required. It should be a Draco preprocessor symbol. TARGETS is optional. It -# can be one or more draco targets. -# -# When the TARGETS argument is not present the preproc symbol is added to -# draco_features.h. When it is draco_features.h is unchanged, and -# target_compile_options() is called for each target specified. -macro(draco_enable_feature) - set(def_flags) - set(def_single_arg_opts FEATURE) - set(def_multi_arg_opts TARGETS) - cmake_parse_arguments(DEF "${def_flags}" "${def_single_arg_opts}" - "${def_multi_arg_opts}" ${ARGN}) - if("${DEF_FEATURE}" STREQUAL "") - message(FATAL_ERROR "Empty FEATURE passed to draco_enable_feature().") - endif() - - # Do nothing/return early if $DEF_FEATURE is already in the list. - list(FIND draco_features_list ${DEF_FEATURE} df_index) - if(NOT df_index EQUAL -1) - return() - endif() - - list(LENGTH DEF_TARGETS df_targets_list_length) - if(${df_targets_list_length} EQUAL 0) - list(APPEND draco_features_list ${DEF_FEATURE}) - else() - foreach(target ${DEF_TARGETS}) - target_compile_definitions(${target} PRIVATE ${DEF_FEATURE}) - endforeach() - endif() -endmacro() - -# Function for generating draco_features.h. -function(draco_generate_features_h) - file(WRITE "${draco_features_file_name}.new" - "// GENERATED FILE -- DO NOT EDIT\n\n" "#ifndef DRACO_FEATURES_H_\n" - "#define DRACO_FEATURES_H_\n\n") - - foreach(feature ${draco_features_list}) - file(APPEND "${draco_features_file_name}.new" "#define ${feature}\n") - endforeach() - - file(APPEND "${draco_features_file_name}.new" - "\n#endif // DRACO_FEATURES_H_") - - # Will replace ${draco_features_file_name} only if the file content has - # changed. This prevents forced Draco rebuilds after CMake runs. - configure_file("${draco_features_file_name}.new" - "${draco_features_file_name}") - file(REMOVE "${draco_features_file_name}.new") -endfunction() diff --git a/contrib/draco/cmake/draco_flags.cmake b/contrib/draco/cmake/draco_flags.cmake index cb9d489e6..0397859a4 100644 --- a/contrib/draco/cmake/draco_flags.cmake +++ b/contrib/draco/cmake/draco_flags.cmake @@ -80,6 +80,12 @@ macro(draco_test_cxx_flag) # Run the actual compile test. unset(draco_all_cxx_flags_pass CACHE) message("--- Running combined CXX flags test, flags: ${all_cxx_flags}") + + # check_cxx_compiler_flag() requires that the flags are a string. When flags + # are passed as a list it will remove the list separators, and attempt to run + # a compile command using list entries concatenated together as a single + # argument. Avoid the problem by forcing the argument to be a string. + draco_set_and_stringify(SOURCE_VARS all_cxx_flags DEST all_cxx_flags) check_cxx_compiler_flag("${all_cxx_flags}" draco_all_cxx_flags_pass) if(cxx_test_FLAG_REQUIRED AND NOT draco_all_cxx_flags_pass) @@ -194,6 +200,9 @@ macro(draco_test_exe_linker_flag) else() unset(CMAKE_EXE_LINKER_FLAGS) endif() + + list(APPEND DRACO_EXE_LINKER_FLAGS ${${link_FLAG_LIST_VAR_NAME}}) + list(REMOVE_DUPLICATES DRACO_EXE_LINKER_FLAGS) endmacro() # Runs the draco compiler tests. This macro builds up the list of list var(s) diff --git a/contrib/draco/cmake/draco_install.cmake b/contrib/draco/cmake/draco_install.cmake index 5c63ecb4a..09bfb591d 100644 --- a/contrib/draco/cmake/draco_install.cmake +++ b/contrib/draco/cmake/draco_install.cmake @@ -55,7 +55,7 @@ macro(draco_setup_install_target) install(TARGETS draco_encoder DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") - if(WIN32) + if(MSVC) install(TARGETS draco DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") else() diff --git a/contrib/draco/cmake/draco_sanitizer.cmake b/contrib/draco/cmake/draco_sanitizer.cmake index ca8e23176..d2e41a6cb 100644 --- a/contrib/draco/cmake/draco_sanitizer.cmake +++ b/contrib/draco/cmake/draco_sanitizer.cmake @@ -5,28 +5,28 @@ set(DRACO_CMAKE_DRACO_SANITIZER_CMAKE_ 1) # Handles the details of enabling sanitizers. macro(draco_configure_sanitizer) - if(DRACO_SANITIZE AND NOT MSVC) + if(DRACO_SANITIZE AND NOT EMSCRIPTEN AND NOT MSVC) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(DRACO_SANITIZE MATCHES "cfi") - list(APPEND DRACO_CXX_FLAGS "-flto" "-fno-sanitize-trap=cfi") - list(APPEND DRACO_EXE_LINKER_FLAGS "-flto" "-fno-sanitize-trap=cfi" + list(APPEND SAN_CXX_FLAGS "-flto" "-fno-sanitize-trap=cfi") + list(APPEND SAN_LINKER_FLAGS "-flto" "-fno-sanitize-trap=cfi" "-fuse-ld=gold") endif() if(${CMAKE_SIZEOF_VOID_P} EQUAL 4 AND DRACO_SANITIZE MATCHES "integer|undefined") - list(APPEND DRACO_EXE_LINKER_FLAGS "--rtlib=compiler-rt" "-lgcc_s") + list(APPEND SAN_LINKER_FLAGS "--rtlib=compiler-rt" "-lgcc_s") endif() endif() - list(APPEND DRACO_CXX_FLAGS "-fsanitize=${DRACO_SANITIZE}") - list(APPEND DRACO_EXE_LINKER_FLAGS "-fsanitize=${DRACO_SANITIZE}") + list(APPEND SAN_CXX_FLAGS "-fsanitize=${DRACO_SANITIZE}") + list(APPEND SAN_LINKER_FLAGS "-fsanitize=${DRACO_SANITIZE}") # Make sanitizer callstacks accurate. - list(APPEND DRACO_CXX_FLAGS "-fno-omit-frame-pointer" - "-fno-optimize-sibling-calls") + list(APPEND SAN_CXX_FLAGS "-fno-omit-frame-pointer") + list(APPEND SAN_CXX_FLAGS "-fno-optimize-sibling-calls") - draco_test_cxx_flag(FLAG_LIST_VAR_NAMES DRACO_CXX_FLAGS FLAG_REQUIRED) - draco_test_exe_linker_flag(FLAG_LIST_VAR_NAME DRACO_EXE_LINKER_FLAGS) + draco_test_cxx_flag(FLAG_LIST_VAR_NAMES SAN_CXX_FLAGS FLAG_REQUIRED) + draco_test_exe_linker_flag(FLAG_LIST_VAR_NAME SAN_LINKER_FLAGS) endif() endmacro() diff --git a/contrib/draco/cmake/draco_targets.cmake b/contrib/draco/cmake/draco_targets.cmake index 6dfa6a0c4..0456c4d7b 100644 --- a/contrib/draco/cmake/draco_targets.cmake +++ b/contrib/draco/cmake/draco_targets.cmake @@ -87,6 +87,7 @@ macro(draco_add_executable) endif() add_executable(${exe_NAME} ${exe_SOURCES}) + set_target_properties(${exe_NAME} PROPERTIES VERSION ${DRACO_VERSION}) if(exe_OUTPUT_NAME) set_target_properties(${exe_NAME} PROPERTIES OUTPUT_NAME ${exe_OUTPUT_NAME}) @@ -109,10 +110,11 @@ macro(draco_add_executable) if(exe_LINK_FLAGS OR DRACO_EXE_LINKER_FLAGS) if(${CMAKE_VERSION} VERSION_LESS "3.13") - set(link_flags ${exe_LINK_FLAGS} ${DRACO_EXE_LINKER_FLAGS}) + list(APPEND exe_LINK_FLAGS "${DRACO_EXE_LINKER_FLAGS}") + # LINK_FLAGS is managed as a string. + draco_set_and_stringify(SOURCE "${exe_LINK_FLAGS}" DEST exe_LINK_FLAGS) set_target_properties(${exe_NAME} - PROPERTIES LINK_FLAGS ${exe_LINK_FLAGS} - ${DRACO_EXE_LINKER_FLAGS}) + PROPERTIES LINK_FLAGS "${exe_LINK_FLAGS}") else() target_link_options(${exe_NAME} PRIVATE ${exe_LINK_FLAGS} ${DRACO_EXE_LINKER_FLAGS}) @@ -130,7 +132,7 @@ macro(draco_add_executable) endif() if(BUILD_SHARED_LIBS AND (MSVC OR WIN32)) - target_compile_definitions(${lib_NAME} PRIVATE "DRACO_BUILDING_DLL=0") + target_compile_definitions(${exe_NAME} PRIVATE "DRACO_BUILDING_DLL=0") endif() if(exe_LIB_DEPS) @@ -163,8 +165,8 @@ endmacro() # cmake-format: off # - OUTPUT_NAME: Override output file basename. Target basename defaults to # NAME. OUTPUT_NAME is ignored when BUILD_SHARED_LIBS is enabled and CMake -# is generating a build for which MSVC or WIN32 are true. This is to avoid -# output basename collisions with DLL import libraries. +# is generating a build for which MSVC is true. This is to avoid output +# basename collisions with DLL import libraries. # - TEST: Flag. Presence means treat library as a test. # - DEFINES: List of preprocessor macro definitions. # - INCLUDES: list of include directories for the target. @@ -259,7 +261,7 @@ macro(draco_add_library) endif() if(lib_OUTPUT_NAME) - if(NOT (BUILD_SHARED_LIBS AND (MSVC OR WIN32))) + if(NOT (BUILD_SHARED_LIBS AND MSVC)) set_target_properties(${lib_NAME} PROPERTIES OUTPUT_NAME ${lib_OUTPUT_NAME}) endif() @@ -318,8 +320,12 @@ macro(draco_add_library) set_target_properties(${lib_NAME} PROPERTIES PREFIX "") endif() - if(lib_TYPE STREQUAL SHARED AND NOT MSVC) - set_target_properties(${lib_NAME} PROPERTIES SOVERSION ${DRACO_SOVERSION}) + # VERSION and SOVERSION as necessary + if(NOT lib_TYPE STREQUAL STATIC AND NOT lib_TYPE STREQUAL MODULE) + set_target_properties(${lib_NAME} PROPERTIES VERSION ${DRACO_VERSION}) + if(NOT MSVC) + set_target_properties(${lib_NAME} PROPERTIES SOVERSION ${DRACO_SOVERSION}) + endif() endif() if(BUILD_SHARED_LIBS AND (MSVC OR WIN32)) diff --git a/contrib/draco/src/draco/core/cycle_timer.cc b/contrib/draco/src/draco/core/cycle_timer.cc index 94b4b28b2..58df4df77 100644 --- a/contrib/draco/src/draco/core/cycle_timer.cc +++ b/contrib/draco/src/draco/core/cycle_timer.cc @@ -17,31 +17,31 @@ namespace draco { void DracoTimer::Start() { #ifdef _WIN32 - QueryPerformanceCounter(&tv_start); + QueryPerformanceCounter(&tv_start_); #else - gettimeofday(&tv_start, nullptr); + gettimeofday(&tv_start_, nullptr); #endif } void DracoTimer::Stop() { #ifdef _WIN32 - QueryPerformanceCounter(&tv_end); + QueryPerformanceCounter(&tv_end_); #else - gettimeofday(&tv_end, nullptr); + gettimeofday(&tv_end_, nullptr); #endif } int64_t DracoTimer::GetInMs() { #ifdef _WIN32 LARGE_INTEGER elapsed = {0}; - elapsed.QuadPart = tv_end.QuadPart - tv_start.QuadPart; + elapsed.QuadPart = tv_end_.QuadPart - tv_start_.QuadPart; LARGE_INTEGER frequency = {0}; QueryPerformanceFrequency(&frequency); return elapsed.QuadPart * 1000 / frequency.QuadPart; #else - const int64_t seconds = (tv_end.tv_sec - tv_start.tv_sec) * 1000; - const int64_t milliseconds = (tv_end.tv_usec - tv_start.tv_usec) / 1000; + const int64_t seconds = (tv_end_.tv_sec - tv_start_.tv_sec) * 1000; + const int64_t milliseconds = (tv_end_.tv_usec - tv_start_.tv_usec) / 1000; return seconds + milliseconds; #endif } diff --git a/contrib/draco/src/draco/core/cycle_timer.h b/contrib/draco/src/draco/core/cycle_timer.h index 172f1c2e9..f480cc9d3 100644 --- a/contrib/draco/src/draco/core/cycle_timer.h +++ b/contrib/draco/src/draco/core/cycle_timer.h @@ -20,9 +20,10 @@ #define WIN32_LEAN_AND_MEAN #endif #include -typedef LARGE_INTEGER timeval; +typedef LARGE_INTEGER DracoTimeVal; #else #include +typedef timeval DracoTimeVal; #endif #include @@ -39,8 +40,8 @@ class DracoTimer { int64_t GetInMs(); private: - timeval tv_start; - timeval tv_end; + DracoTimeVal tv_start_; + DracoTimeVal tv_end_; }; typedef DracoTimer CycleTimer; diff --git a/contrib/draco/src/draco/io/parser_utils.cc b/contrib/draco/src/draco/io/parser_utils.cc index 4f95f6f84..12afacff6 100644 --- a/contrib/draco/src/draco/io/parser_utils.cc +++ b/contrib/draco/src/draco/io/parser_utils.cc @@ -18,6 +18,7 @@ #include #include #include +#include namespace draco { namespace parser { @@ -252,7 +253,7 @@ DecoderBuffer ParseLineIntoDecoderBuffer(DecoderBuffer *buffer) { std::string ToLower(const std::string &str) { std::string out; - std::transform(str.begin(), str.end(), std::back_inserter(out), [](unsigned char c){return tolower(c);}); + std::transform(str.begin(), str.end(), std::back_inserter(out), tolower); return out; } diff --git a/contrib/draco/src/draco/io/ply_reader.cc b/contrib/draco/src/draco/io/ply_reader.cc index cb32df225..ea7f2689a 100644 --- a/contrib/draco/src/draco/io/ply_reader.cc +++ b/contrib/draco/src/draco/io/ply_reader.cc @@ -268,14 +268,14 @@ std::vector PlyReader::SplitWords(const std::string &line) { while ((end = line.find_first_of(" \t\n\v\f\r", start)) != std::string::npos) { const std::string word(line.substr(start, end - start)); - if (!std::all_of(word.begin(), word.end(), [](unsigned char c){return isspace(c);})) { + if (!std::all_of(word.begin(), word.end(), isspace)) { output.push_back(word); } start = end + 1; } const std::string last_word(line.substr(start)); - if (!std::all_of(last_word.begin(), last_word.end(), [](unsigned char c){return isspace(c);})) { + if (!std::all_of(last_word.begin(), last_word.end(), isspace)) { output.push_back(last_word); } return output; diff --git a/contrib/draco/src/draco/io/stdio_file_reader.cc b/contrib/draco/src/draco/io/stdio_file_reader.cc index 560c3e9e8..a99c96f8f 100644 --- a/contrib/draco/src/draco/io/stdio_file_reader.cc +++ b/contrib/draco/src/draco/io/stdio_file_reader.cc @@ -87,7 +87,14 @@ size_t StdioFileReader::GetFileSize() { return false; } +#if _FILE_OFFSET_BITS == 64 + const size_t file_size = static_cast(ftello(file_)); +#elif defined _WIN64 + const size_t file_size = static_cast(_ftelli64(file_)); +#else const size_t file_size = static_cast(ftell(file_)); +#endif + rewind(file_); return file_size; diff --git a/contrib/openddlparser/code/OpenDDLExport.cpp b/contrib/openddlparser/code/OpenDDLExport.cpp index 6d6f2458d..d235b553b 100644 --- a/contrib/openddlparser/code/OpenDDLExport.cpp +++ b/contrib/openddlparser/code/OpenDDLExport.cpp @@ -134,10 +134,9 @@ bool OpenDDLExport::writeToStream(const std::string &statement) { } bool OpenDDLExport::writeNode(DDLNode *node, std::string &statement) { - bool success(true); writeNodeHeader(node, statement); if (node->hasProperties()) { - success |= writeProperties(node, statement); + writeProperties(node, statement); } writeLineEnd(statement); diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index 6a9f802ec..0c9e0bd98 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -132,6 +132,24 @@ OpenDDLParser::~OpenDDLParser() { clear(); } +void OpenDDLParser::logToStream(FILE *f, LogSeverity severity, const std::string &message) { + if (f) { + const char *tag = "none"; + switch (severity) { + case ddl_debug_msg: tag = "debug"; break; + case ddl_info_msg: tag = "info"; break; + case ddl_warn_msg: tag = "warn"; break; + case ddl_error_msg: tag = "error"; break; + } + fprintf(f, "OpenDDLParser: (%5s) %s\n", tag, message.c_str()); + } +} + +OpenDDLParser::logCallback OpenDDLParser::StdLogCallback (FILE *destination) { + using namespace std::placeholders; + return std::bind(logToStream, destination ? destination : stderr, _1, _2); +} + void OpenDDLParser::setLogCallback(logCallback callback) { // install user-specific log callback; null = no log callback m_logCallback = callback; @@ -171,12 +189,8 @@ size_t OpenDDLParser::getBufferSize() const { void OpenDDLParser::clear() { m_buffer.resize(0); - if (nullptr != m_context) { - delete m_context; - m_context = nullptr; - } - - // DDLNode::releaseNodes(); + delete m_context; + m_context = nullptr; } bool OpenDDLParser::validate() { @@ -506,7 +520,9 @@ char *OpenDDLParser::parseName(char *in, char *end, Name **name) { in = parseIdentifier(in, end, &id); if (id) { currentName = new Name(ntype, id); - *name = currentName; + if (currentName) { + *name = currentName; + } } return in; diff --git a/contrib/openddlparser/code/Value.cpp b/contrib/openddlparser/code/Value.cpp index 708a6878f..5a8aa39be 100644 --- a/contrib/openddlparser/code/Value.cpp +++ b/contrib/openddlparser/code/Value.cpp @@ -113,13 +113,14 @@ Value::~Value() { if (m_data != nullptr) { if (m_type == ValueType::ddl_ref) { Reference *tmp = (Reference *)m_data; - if (tmp != nullptr) + if (tmp != nullptr) { delete tmp; - } else + } + } else { delete[] m_data; + } } - if (m_next != nullptr) - delete m_next; + delete m_next; } void Value::setBool(bool value) { diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h index 5794add90..3fbb4b6af 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h @@ -29,6 +29,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include BEGIN_ODDLPARSER_NS @@ -97,8 +98,8 @@ DLL_ODDLPARSER_EXPORT const char *getTypeToken(Value::ValueType type); //------------------------------------------------------------------------------------------------- class DLL_ODDLPARSER_EXPORT OpenDDLParser { public: - /// @brief The log callback function pointer. - typedef void (*logCallback)(LogSeverity severity, const std::string &msg); + /// @brief The log callback function. + typedef std::function logCallback; public: /// @brief The default class constructor. @@ -120,6 +121,11 @@ public: /// @return The current log callback. logCallback getLogCallback() const; + /// @brief A default log callback that writes to a FILE. + /// @param destination [in] Output stream. NULL will use stderr. + /// @return A callback that you can pass to setLogCallback. + static logCallback StdLogCallback(FILE *destination = nullptr); + /// @brief Assigns a new buffer to parse. /// @param buffer [in] The buffer /// @param len [in] Size of the buffer @@ -192,6 +198,9 @@ private: typedef std::vector DDLNodeStack; DDLNodeStack m_stack; Context *m_context; + + /// @brief Callback for StdLogCallback(). Not meant to be called directly. + static void logToStream (FILE *, LogSeverity, const std::string &); }; END_ODDLPARSER_NS diff --git a/contrib/poly2tri/poly2tri/sweep/sweep.cc b/contrib/poly2tri/poly2tri/sweep/sweep.cc index 23aeb6b57..8e3d794c0 100644 --- a/contrib/poly2tri/poly2tri/sweep/sweep.cc +++ b/contrib/poly2tri/poly2tri/sweep/sweep.cc @@ -129,7 +129,7 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl EdgeEvent( tcx, ep, *p1, triangle, *p1 ); } else { // ASSIMP_CHANGE (aramis_acg) - std::runtime_error("EdgeEvent - collinear points not supported"); + throw std::runtime_error("EdgeEvent - collinear points not supported"); } return; } diff --git a/code/Pbrt/stb_image.h b/contrib/stb/stb_image.h similarity index 100% rename from code/Pbrt/stb_image.h rename to contrib/stb/stb_image.h diff --git a/contrib/stb_image/stb_image.h b/contrib/stb_image/stb_image.h deleted file mode 100644 index d9c21bc81..000000000 --- a/contrib/stb_image/stb_image.h +++ /dev/null @@ -1,7462 +0,0 @@ -/* stb_image - v2.19 - public domain image loader - http://nothings.org/stb - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define STB_IMAGE_IMPLEMENTATION - #include "stb_image.h" - - You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. - And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free - - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8/16-bit-per-channel - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels, 8/16 bit-per-channel) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - PNM (PPM and PGM binary only) - - Animated GIF still needs a proper API, but here's one way to do it: - http://gist.github.com/urraka/685d9a6340b26b830d49 - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - - Full documentation under "DOCUMENTATION" below. - - -LICENSE - - See end of file for license information. - -RECENT REVISION HISTORY: - - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings - 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes - 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 - RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; - correct channel count for PNG & BMP - 2.10 (2016-01-22) avoid warning introduced in 2.09 - 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED - - See end of file for full revision history. - - - ============================ Contributors ========================= - - Image formats Extensions, features - Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - github:urraka (animated gif) Junggon Kim (PNM comments) - Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) - socks-the-fox (16-bit PNG) - Jeremy Sawicki (handle all ImageNet JPGs) - Optimizations & bugfixes Mikhail Morozov (1-bit BMP) - Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine - John-Mark Allen - - Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan - Dave Moore Roy Eltham Hayaki Saito Nathan Reed - Won Chun Luke Graham Johan Duparc Nick Verigakis - the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar - Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex - Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 - Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw - Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus - Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo - Christian Floisand Kevin Schmidt github:darealshinji - Blazej Dariusz Roszkowski github:Michaelangel007 -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below for HDR usage): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *channels_in_file -- outputs # of image components in image file -// int desired_channels -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data, or NULL on an allocation failure or if the image is -// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'desired_channels' if desired_channels is non-zero, or -// *channels_in_file otherwise. If desired_channels is non-zero, -// *channels_in_file has the number of components that _would_ have been -// output otherwise. E.g. if you set desired_channels to 4, you will always -// get RGBA output, but you can check *channels_in_file to see if it's trivially -// opaque because e.g. there were only 3 channels in the source image. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *channels_in_file will be unchanged. The function -// stbi_failure_reason() can be queried for an extremely brief, end-user -// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS -// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// =========================================================================== -// -// Philosophy -// -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Sometimes I let "good performance" creep up in priority over "easy to maintain", -// and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy to use ones. Nevertheless, it's important -// to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. -// -// Some secondary priorities arise directly from the first two, some of which -// make more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small source code footprint ("easy to maintain") -// - No dependencies ("ease of use") -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). -// -// =========================================================================== -// -// SIMD support -// -// The JPEG decoder will try to automatically use SIMD kernels on x86 when -// supported by the compiler. For ARM Neon support, you must explicitly -// request it. -// -// (The old do-it-yourself SIMD API is no longer supported in the current -// code.) -// -// On x86, SSE2 will automatically be used when available based on a run-time -// test; if not, the generic C versions are used as a fall-back. On ARM targets, -// the typical path is to have separate builds for NEON and non-NEON devices -// (at least this is true for iOS and Android). Therefore, the NEON support is -// toggled by a build flag: define STBI_NEON to get NEON loops. -// -// If for some reason you do not want to use any of SIMD code, or if -// you have issues compiling it, you can disable it entirely by -// defining STBI_NO_SIMD. -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image now supports loading HDR images in general, and currently -// the Radiance .HDR file format, although the support is provided -// generically. You can still load any file through the existing interface; -// if you attempt to load an HDR file, it will be automatically remapped to -// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// iPhone PNG support: -// -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// -// =========================================================================== -// -// ADDITIONAL CONFIGURATION -// -// - You can suppress implementation of any of the decoders to reduce -// your code footprint by #defining one or more of the following -// symbols before creating the implementation. -// -// STBI_NO_JPEG -// STBI_NO_PNG -// STBI_NO_BMP -// STBI_NO_PSD -// STBI_NO_TGA -// STBI_NO_GIF -// STBI_NO_HDR -// STBI_NO_PIC -// STBI_NO_PNM (.ppm and .pgm) -// -// - You can request *only* certain decoders and suppress all other ones -// (this will be more forward-compatible, as addition of new decoders -// doesn't require you to disable them explicitly): -// -// STBI_ONLY_JPEG -// STBI_ONLY_PNG -// STBI_ONLY_BMP -// STBI_ONLY_PSD -// STBI_ONLY_TGA -// STBI_ONLY_GIF -// STBI_ONLY_HDR -// STBI_ONLY_PIC -// STBI_ONLY_PNM (.ppm and .pgm) -// -// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still -// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB -// - - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for desired_channels - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -typedef unsigned char stbi_uc; -typedef unsigned short stbi_us; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -//////////////////////////////////// -// -// 8-bits-per-channel interface -// - -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -//////////////////////////////////// -// -// 16-bits-per-channel interface -// - -STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -#endif - -//////////////////////////////////// -// -// float-per-channel interface -// -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); - #endif -#endif - -#ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR - -#ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR - -// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// NOT THREADSAFE -STBIDEF const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit (char const *filename); -STBIDEF int stbi_is_16_bit_from_file(FILE *f); -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - -// flip the image vertically, so the first pixel in the output array is the bottom left -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); - -// ZLIB client - used by PNG, available for other purposes - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ - || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ - || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ - || defined(STBI_ONLY_ZLIB) - #ifndef STBI_ONLY_JPEG - #define STBI_NO_JPEG - #endif - #ifndef STBI_ONLY_PNG - #define STBI_NO_PNG - #endif - #ifndef STBI_ONLY_BMP - #define STBI_NO_BMP - #endif - #ifndef STBI_ONLY_PSD - #define STBI_NO_PSD - #endif - #ifndef STBI_ONLY_TGA - #define STBI_NO_TGA - #endif - #ifndef STBI_ONLY_GIF - #define STBI_NO_GIF - #endif - #ifndef STBI_ONLY_HDR - #define STBI_NO_HDR - #endif - #ifndef STBI_ONLY_PIC - #define STBI_NO_PIC - #endif - #ifndef STBI_ONLY_PNM - #define STBI_NO_PNM - #endif -#endif - -#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) -#define STBI_NO_ZLIB -#endif - - -#include -#include // ptrdiff_t on osx -#include -#include -#include - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp, pow -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - - -#ifdef _MSC_VER -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) -#endif - -#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) -// ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." -#endif - -#ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) -#endif - -#ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) -#endif - -// x86/x64 detection -#if defined(__x86_64__) || defined(_M_X64) -#define STBI__X64_TARGET -#elif defined(__i386) || defined(_M_IX86) -#define STBI__X86_TARGET -#endif - -#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// which in turn means it gets to use SSE2 everywhere. This is unfortunate, -// but previous attempts to provide the SSE2 functions with runtime -// detection caused numerous issues. The way architecture extensions are -// exposed in GCC/Clang is, sadly, not really suited for one-file libs. -// New behavior: if compiled with -msse2, we use SSE2 without any -// detection; if not, we don't use it at all. -#define STBI_NO_SIMD -#endif - -#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) -// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET -// -// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the -// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. -// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not -// simultaneously enabling "-mstackrealign". -// -// See https://github.com/nothings/stb/issues/81 for more information. -// -// So default to no SSE2 on 32-bit MinGW. If you've read this far and added -// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. -#define STBI_NO_SIMD -#endif - -#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) -#define STBI_SSE2 -#include - -#ifdef _MSC_VER - -#if _MSC_VER >= 1400 // not VC6 -#include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; -} -#else -static int stbi__cpuid3(void) -{ - int res; - __asm { - mov eax,1 - cpuid - mov res,edx - } - return res; -} -#endif - -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name - -static int stbi__sse2_available(void) -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -static int stbi__sse2_available(void) -{ - // If we're even attempting to compile this on GCC/Clang, that means - // -msse2 is on, which means the compiler is allowed to use SSE2 - // instructions at will, and so are we. - return 1; -} -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -// assume GCC or Clang on ARM targets -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - fseek((FILE*) user, n, SEEK_CUR); -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; -} - -enum -{ - STBI_ORDER_RGB, - STBI_ORDER_BGR -}; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} stbi__result_info; - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__png_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__psd_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -// this is not threadsafe -static const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// stb_image uses ints pervasively, including for offset calculations. -// therefore the largest decoded image size we can support with the -// current code, even on 64-bit targets, is INT_MAX. this is not a -// significant limitation for the intended use case. -// -// we do, however, need to make sure our size calculations don't -// overflow. hence a few helper functions for size calculations that -// multiply integers together, making sure that they're non-negative -// and no overflow occurs. - -// return 1 if the sum is valid, 0 on overflow. -// negative terms are considered invalid. -static int stbi__addsizes_valid(int a, int b) -{ - if (b < 0) return 0; - // now 0 <= b <= INT_MAX, hence also - // 0 <= INT_MAX - b <= INTMAX. - // And "a + b <= INT_MAX" (which might overflow) is the - // same as a <= INT_MAX - b (no overflow) - return a <= INT_MAX - b; -} - -// returns 1 if the product is valid, 0 on overflow. -// negative factors are considered invalid. -static int stbi__mul2sizes_valid(int a, int b) -{ - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; // mul-by-0 is always safe - // portable way to check for no overflows in a*b - return a <= INT_MAX/b; -} - -// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow -static int stbi__mad2sizes_valid(int a, int b, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); -} - -// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow -static int stbi__mad3sizes_valid(int a, int b, int c, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__addsizes_valid(a*b*c, add); -} - -// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); -} -#endif - -// mallocs with size overflow checking -static void *stbi__malloc_mad2(int a, int b, int add) -{ - if (!stbi__mad2sizes_valid(a, b, add)) return NULL; - return stbi__malloc(a*b + add); -} - -static void *stbi__malloc_mad3(int a, int b, int c, int add) -{ - if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; - return stbi__malloc(a*b*c + add); -} - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) -{ - if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; - return stbi__malloc(a*b*c*d + add); -} -#endif - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) -#else - #define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static int stbi__vertically_flip_on_load = 0; - -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load = flag_true_if_should_flip; -} - -static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields - ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed - ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order - ri->num_channels = 0; - - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp, ri); - #endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi_uc *reduced; - - reduced = (stbi_uc *) stbi__malloc(img_len); - if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling - - STBI_FREE(orig); - return reduced; -} - -static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi__uint16 *enlarged; - - enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); - if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff - - STBI_FREE(orig); - return enlarged; -} - -static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) -{ - int row; - size_t bytes_per_row = (size_t)w * bytes_per_pixel; - stbi_uc temp[2048]; - stbi_uc *bytes = (stbi_uc *)image; - - for (row = 0; row < (h>>1); row++) { - stbi_uc *row0 = bytes + row*bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; - } - } -} - -static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) -{ - int slice; - int slice_size = w * h * bytes_per_pixel; - - stbi_uc *bytes = (stbi_uc *)image; - for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; - } -} - -static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); - - if (result == NULL) - return NULL; - - if (ri.bits_per_channel != 8) { - STBI_ASSERT(ri.bits_per_channel == 16); - result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; - } - - // @TODO: move stbi__convert_format to here - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); - } - - return (unsigned char *) result; -} - -static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); - - if (result == NULL) - return NULL; - - if (ri.bits_per_channel != 16) { - STBI_ASSERT(ri.bits_per_channel == 8); - result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; - } - - // @TODO: move stbi__convert_format16 to here - // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); - } - - return (stbi__uint16 *) result; -} - -#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR) -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); - } -} -#endif - -#ifndef STBI_NO_STDIO - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint16 *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - stbi__uint16 *result; - if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file_16(f,x,y,comp,req_comp); - fclose(f); - return result; -} - - -#endif //!STBI_NO_STDIO - -STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - - result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); - if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); - } - - return result; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } - #endif - data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_LINEAR - -// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is -// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always -// reports false! - -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - long pos = ftell(f); - int res; - stbi__context s; - stbi__start_file(&s,f); - res = stbi__hdr_test(&s); - fseek(f, pos, SEEK_SET); - return res; - #else - STBI_NOTUSED(f); - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; - #endif -} - -#ifndef STBI_NO_LINEAR -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; - -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} - -static void stbi__skip(stbi__context *s, int n) -{ - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} - -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} - -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} - -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} - -#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) -// nothing -#else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} -#endif - -#ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); -} -#endif - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - - -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} - -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -static stbi__uint16 stbi__compute_y_16(int r, int g, int b) -{ - return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); -} - -static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - stbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - stbi__uint16 *src = data + j * x * img_n ; - stbi__uint16 *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output; - if (!data) return NULL; - output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; - } - STBI_FREE(data); - return output; -} -#endif - -#ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output; - if (!data) return NULL; - output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder -// -// simple implementation -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - some SIMD kernels for common paths on targets with SSE2/NEON -// - uses a lot of intermediate memory, could cache poorly - -#ifndef STBI_NO_JPEG - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi__uint16 dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - int jfif; - int app14_color_transform; // Adobe APP14 tag - int rgb; - - int scan_n, order[4]; - int restart_interval, todo; - -// kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0; - unsigned int code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -// build a table that decodes both magnitude and value of small ACs in -// one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; - - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (~0U << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB - k = stbi_lrot(j->code_buffer, n); - STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); -} - -// get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; -} - -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static const stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) -{ - int diff,dc,k; - int t; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * dequant[0]); - - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; -} - -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - diff = t ? stbi__extend_receive(j, t) : 0; - - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; -} - -// @OPTIMIZE: store non-zigzagged during the decode passes, -// and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->succ_high == 0) { - int shift = j->succ_low; - - if (j->eob_run) { - --j->eob_run; - return 1; - } - - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients - - short bit = (short) (1 << j->succ_low); - - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } - - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) * 4096) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0]*4; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SSE2 -// sse2 integer IDCT. not the fastest possible implementation but it -// produces bit-identical results to the generic C version so it's -// fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; - - // dot product constant: even elems=x, odd elems=y - #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) - - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y - #define dct_rot(out0,out1, x,y,c0,c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - - // out = in << 12 (in 16-bit, out 32-bit) - #define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - - // wide add - #define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) - - // wide sub - #define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - - // butterfly a/b, add bias, then shift by "s" and pack - #define dct_bfly32o(out0, out1, a,b,bias,s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } - - // 8-bit interleave step (for transposes) - #define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) - - // 16-bit interleave step (for transposes) - #define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) - - #define dct_pass(bias,shift) \ - { \ - /* even part */ \ - dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ - dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0,row7, x0,x7,bias,shift); \ - dct_bfly32o(row1,row6, x1,x6,bias,shift); \ - dct_bfly32o(row2,row5, x2,x5,bias,shift); \ - dct_bfly32o(row3,row4, x3,x4,bias,shift); \ - } - - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); - - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); - - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); - - // column pass - dct_pass(bias_0, 10); - - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); - - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); - - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } - - // row pass - dct_pass(bias_1, 17); - - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); - - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... - - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... - - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... - - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } - -#undef dct_const -#undef dct_rot -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_interleave8 -#undef dct_interleave16 -#undef dct_pass -} - -#endif // STBI_SSE2 - -#ifdef STBI_NEON - -// NEON integer IDCT. should produce bit-identical -// results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); - -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) - -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) - -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) - -// wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) - -// wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) - -// butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } - -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ - dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ - dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ - dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ - } - - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); - - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - - // column pass - dct_pass(vrshrn_n_s32, 10); - - // 16bit 8x8 transpose - { -// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. -// whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); - - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); - - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); - -#undef dct_trn16 -#undef dct_trn32 -#undef dct_trn64 - } - - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); - - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! - - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); - - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); - - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); - - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); - -#undef dct_trn8_8 -#undef dct_trn8_16 -#undef dct_trn8_32 - } - -#undef dct_long_mul -#undef dct_long_mac -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_pass -} - -#endif // STBI_NEON - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); - int t = q & 15,i; - if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } - - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - L = stbi__get16be(z->s); - if (L < 2) { - if (m == 0xFE) - return stbi__err("bad COM len","Corrupt JPEG"); - else - return stbi__err("bad APP len","Corrupt JPEG"); - } - L -= 2; - - if (m == 0xE0 && L >= 5) { // JFIF APP0 segment - static const unsigned char tag[5] = {'J','F','I','F','\0'}; - int ok = 1; - int i; - for (i=0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment - static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; - int ok = 1; - int i; - for (i=0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform - L -= 6; - } - } - - stbi__skip(z->s, L); - return 1; - } - - return stbi__err("unknown marker","Corrupt JPEG"); -} - -// after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } - - return 1; -} - -static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) -{ - int i; - for (i=0; i < ncomp; ++i) { - if (z->img_comp[i].raw_data) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } - } - return why; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - - z->rgb = 0; - for (i=0; i < s->img_n; ++i) { - static const unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != STBI__SCAN_load) return 1; - - if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - // these sizes can't be more than 17 bits - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - if (z->progressive) { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) - -#define stbi__SOF_progressive(x) ((x) == 0xc2) - -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->jfif = 0; - z->app14_color_transform = -1; // valid values are 0,1,2 - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -// decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else if (stbi__DNL(m)) { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; - - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { -#if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); - - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); - - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); -#elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); - - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); -#endif - - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } - - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); - - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} -#endif - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -// this is a reduced-precision calculation of YCbCr-to-RGB introduced -// to make sure the code produces the same results in both SIMD and scalar -#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; - -#ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel - - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); - - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); - - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); - - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); - - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } -#endif - -#ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); - - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); - - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); - - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } -#endif - - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -// set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; - -#ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; -#endif -} - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - stbi__free_jpeg_components(j, j->s->img_n, 0); -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -// fast 0..255 * 0..255 => 0..255 rounded multiplication -static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) -{ - unsigned int t = x*y + 128; - return (stbi_uc) ((t + (t >>8)) >> 8); -} - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n, is_rgb; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; - - is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - - if (z->s->img_n == 3 && n < 3 && !is_rgb) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4]; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - if (is_rgb) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else if (z->s->img_n == 4) { - if (z->app14_color_transform == 0) { // CMYK - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } else if (z->app14_color_transform == 2) { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } else { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - if (is_rgb) { - if (n == 1) - for (i=0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else { - for (i=0; i < z->s->img_x; ++i, out += 2) { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; - } - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output - return output; - } -} - -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - STBI_NOTUSED(ri); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return result; -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); - stbi__rewind(s); - STBI_FREE(j); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int result; - stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; -} -#endif - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -#ifndef STBI_NO_ZLIB - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int j = stbi__bit_reverse(next_code[s],s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s == 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) stbi__fill_bits(a); - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); -} - -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes -{ - char *q; - int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = old_limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) - limit *= 2; - q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static const int stbi__zlength_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static const int stbi__zlength_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, -257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static const int stbi__zdist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - if (len) { do *zout++ = v; while (--len); } - } else { - if (len) { do *zout++ = *p++; while (--len); } - } - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else { - stbi_uc fill = 0; - if (c == 16) { - c = stbi__zreceive(a,2)+3; - if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n-1]; - } else if (c == 17) - c = stbi__zreceive(a,3)+3; - else { - STBI_ASSERT(c == 18); - c = stbi__zreceive(a,7)+11; - } - if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes+n, fill, c); - n += c; - } - } - if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncompressed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - STBI_ASSERT(a->num_bits == 0); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -static const stbi_uc stbi__zdefault_length[288] = -{ - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 -}; -static const stbi_uc stbi__zdefault_distance[32] = -{ - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 -}; -/* -Init algorithm: -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} -*/ - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} -#endif - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - -#ifndef STBI_NO_PNG -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; - int depth; -} stbi__png; - - -enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int stbi__paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - int bytes = (depth == 16? 2 : 1); - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n*bytes; - stbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; // copy it into a local for later - - int output_bytes = out_n*bytes; - int filter_bytes = img_n*bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - - // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, - // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), - // so just check for raw_len < img_len always. - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; - int filter = *raw++; - - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } - - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - - if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4) ); - } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; - if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; - } - } - } - } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } - - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, - a->out + (j*x+i)*out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16*) z->out; - - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag = flag_true_if_should_convert; -} - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = ( t * 255 + half) / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]; - stbi__uint16 tc16[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } - - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; - } - STBI_FREE(z->expanded); z->expanded = NULL; - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) -{ - void *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth < 8) - ri->bits_per_channel = 8; - else - ri->bits_per_channel = p->depth; - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; - - return result; -} - -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp, ri); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} - -static int stbi__png_is16(stbi__context *s) -{ - stbi__png p; - p.s = s; - if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) - return 0; - if (p.depth != 16) { - stbi__rewind(p.s); - return 0; - } - return 1; -} -#endif - -// Microsoft/Windows BMP image - -#ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -// extract an arbitrarily-aligned N-bit value (N=bits) -// from v, and then make it 8-bits long and fractionally -// extend it to full full range. -static int stbi__shiftsigned(int v, int shift, int bits) -{ - static unsigned int mul_table[9] = { - 0, - 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, - 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, - }; - static unsigned int shift_table[9] = { - 0, 0,0,1,0,2,4,6,0, - }; - if (shift < 0) - v <<= -shift; - else - v >>= shift; - STBI_ASSERT(v >= 0 && v < 256); - v >>= (8-bits); - STBI_ASSERT(bits >= 0 && bits <= 8); - return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; -} - -typedef struct -{ - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; -} stbi__bmp_data; - -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *) 1; -} - - -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, all_a; - stbi_uc pal[256][4]; - int psize=0,i,j,width; - int flip_vertically, pad, target; - stbi__bmp_data info; - STBI_NOTUSED(ri); - - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set - - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; - - if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - 14 - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - 14 - info.hsz) >> 2; - } - - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - - // sanity-check size - if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); - - out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - if (info.bpp == 1) { - for (j=0; j < (int) s->img_y; ++j) { - int bit_offset = 7, v = stbi__get8(s); - for (i=0; i < (int) s->img_x; ++i) { - int color = (v>>bit_offset)&0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if((--bit_offset) < 0) { - bit_offset = 7; - v = stbi__get8(s); - } - } - stbi__skip(s, pad); - } - } else { - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - 14 - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } - - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; - - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} -#endif - -// Targa Truevision - TGA -// by Jonathan Dummer -#ifndef STBI_NO_TGA -// returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if (is_rgb16) *is_rgb16 = 0; - switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // fallthrough - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough - case 32: return bits_per_pixel/8; - default: return 0; - } -} - -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( tga_color_type == 1 ) { // colormapped (paletted) image - if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - stbi__skip(s,4); // skip image x and y origin - } else { // "normal" image w/o colormap - if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s,9); // skip colormap specification and image x/y origin - } - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 - -errorEnd: - stbi__rewind(s); - return res; -} - -// read 16bit value and convert to 24bit RGB -static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = (stbi__uint16)stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (stbi_uc)((r * 255)/31); - out[1] = (stbi_uc)((g * 255)/31); - out[2] = (stbi_uc)((b * 255)/31); - - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. -} - -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16=0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4] = {0}; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - STBI_NOTUSED(ri); - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); - - tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { - for (i=0; i < tga_height; ++i) { - int row = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } - - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - // OK, done - return tga_data; -} -#endif - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) -{ - int count, nleft, len; - - count = 0; - while ((nleft = pixelCount - count) > 0) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) return 0; // corrupt data - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - - return 1; -} - -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - int pixelCount; - int channelCount, compression; - int channel, i; - int bitdepth; - int w,h; - stbi_uc *out; - STBI_NOTUSED(ri); - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Check size - if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); - - // Create the destination image. - - if (!compression && bitdepth == 16 && bpc == 16) { - out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; - } else - out = (stbi_uc *) stbi__malloc(4 * w*h); - - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - if (channel >= channelCount) { - // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) { - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; - } else { - stbi_uc *p = out+channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } - } else { - if (ri->bits_per_channel == 16) { // output bpc - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16) stbi__get16be(s); - } else { - stbi_uc *p = out+channel; - if (bitdepth == 16) { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - } - - // remove weird white matte from PSD - if (channelCount >= 4) { - if (ri->bits_per_channel == 16) { - for (i=0; i < w*h; ++i) { - stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; - if (pixel[3] != 0 && pixel[3] != 65535) { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); - pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); - pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); - } - } - } else { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); - } - } - } - } - - // convert to desired output format - if (req_comp && req_comp != 4) { - if (ri->bits_per_channel == 16) - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = 4; - *y = h; - *x = w; - - return out; -} -#endif - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -#ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) -{ - stbi_uc *result; - int i, x,y, internal_comp; - STBI_NOTUSED(ri); - - if (!comp) comp = &internal_comp; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} -#endif - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb - -#ifndef STBI_NO_GIF -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[8192]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; - int delay; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); - stbi__rewind( s ); - return 0; - } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - int idx; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - idx = g->cur_x + g->cur_y; - p = &g->out[idx]; - g->history[idx / 4] = 1; - - c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc) init_code; - g->codes[init_code].suffix = (stbi_uc) init_code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) { - return stbi__errpuc("no clear code", "Corrupt GIF"); - } - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 8192) { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } - - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -// two back is the image from two frames ago, used for a very specific disposal format -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) -{ - int dispose; - int first_frame; - int pi; - int pcount; - - // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->history = (stbi_uc *) stbi__malloc(g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - - // image is treated as "tranparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to teh color that was there the previous frame. - memset( g->out, 0x00, 4 * g->w * g->h ); - memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent) - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - first_frame = 1; - } else { - // second frame - how do we dispoase of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; - - if ((dispose == 3) && (two_back == 0)) { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } - - if (dispose == 3) { // use previous graphic - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); - } - } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); - } - } - } else { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } - - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); - } - - // clear my history; - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - - for (;;) { - int tag = stbi__get8(s); - switch (tag) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (o == NULL) return NULL; - - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); - } - } - } - - return o; - } - - case 0x21: // Comment Extension. - { - int len; - int ext = stbi__get8(s); - if (ext == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. - - // unset old transparent - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; - } - } else { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) { - stbi__skip(s, len); - } - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } -} - -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - if (stbi__gif_test(s)) { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - memset(&g, 0, sizeof(g)); - if (delays) { - *delays = 0; - } - - do { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - - if (u) { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; - - if (out) { - out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); - if (delays) { - *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); - } - } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - } - } - memcpy( out + ((layers - 1) * stride), u, stride ); - if (layers >= 2) { - two_back = out - 2 * stride; - } - - if (delays) { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); - - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - - *z = layers; - return out; - } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); - } -} - -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - - u = stbi__gif_load_next(s, &g, comp, req_comp, 0); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - - // moved conversion to after successful load so that the same - // can be done for multiple frames. - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g.w, g.h); - } - - // free buffers needed for multiple frame loading; - STBI_FREE(g.history); - STBI_FREE(g.background); - - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s, const char *signature) -{ - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - stbi__rewind(s); - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); - stbi__rewind(s); - if(!r) { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); - } - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - const char *headerToken; - STBI_NOTUSED(ri); - - // Check identifier - headerToken = stbi__hdr_gettoken(s,buffer); - if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); - - // Read data - hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); - if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); - if (!scanline) { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); - } - } - - for (k = 0; k < 4; ++k) { - int nleft; - i = 0; - while ((nleft = width - i) > 0) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - if (scanline) - STBI_FREE(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int dummy; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -#ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; - - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) - return 0; - if (x) *x = s->img_x; - if (y) *y = s->img_y; - if (comp) *comp = info.ma ? 4 : 3; - return 1; -} -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount, dummy, depth; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 8 && depth != 16) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} - -static int stbi__psd_is16(stbi__context *s) -{ - int channelCount, depth; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - (void) stbi__get32be(s); - (void) stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 16) { - stbi__rewind( s ); - return 0; - } - return 1; -} -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained,dummy; - stbi__pic_packet packets[10]; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; - } - - stbi__skip(s, 88); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; - } - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} -#endif - -// ************************************************************************************************* -// Portable Gray Map and Portable Pixel Map loader -// by Ken Miller -// -// PGM: http://netpbm.sourceforge.net/doc/pgm.html -// PPM: http://netpbm.sourceforge.net/doc/ppm.html -// -// Known limitations: -// Does not support comments in the header section -// Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel - -#ifndef STBI_NO_PNM - -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; -} - -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - STBI_NOTUSED(ri); - - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) - return 0; - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "PNM too large"); - - out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); - - if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; -} - -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; -} - -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); - - if (stbi__at_eof(s) || *c != '#') - break; - - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); - } -} - -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; - - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - } - - return value; -} - -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv, dummy; - char c, p, t; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - stbi__rewind(s); - - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind(s); - return 0; - } - - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); - - *x = stbi__pnm_getinteger(s, &c); // read width - stbi__pnm_skip_whitespace(s, &c); - - *y = stbi__pnm_getinteger(s, &c); // read height - stbi__pnm_skip_whitespace(s, &c); - - maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); - else - return 1; -} -#endif - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; - #endif - - // test tga last because it's a crappy test! - #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; - #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -static int stbi__is_16_main(stbi__context *s) -{ - #ifndef STBI_NO_PNG - if (stbi__png_is16(s)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_is16(s)) return 1; - #endif - - return 0; -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} - -STBIDEF int stbi_is_16_bit(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; -} - -STBIDEF int stbi_is_16_bit_from_file(FILE *f) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__is_16_main(&s); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__is_16_main(&s); -} - -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__is_16_main(&s); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug - 1-bit BMP - *_is_16_bit api - avoid warnings - 2.16 (2017-07-23) all functions have 16-bit variants; - STBI_NO_STDIO works again; - compilation fixes; - fix rounding in unpremultiply; - optimize vertical flip; - disable raw_len validation; - documentation fixes - 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; - warning fixes; disable run-time SSE detection on gcc; - uniform handling of optional "return" values; - thread-safe initialization of zlib tables - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) allocate large structures on the stack - remove white matting for transparent PSD - fix reported channel count for PNG & BMP - re-enable SSE2 in non-gcc 64-bit - support RGB-formatted JPEG - read 16-bit PNGs (only as 8-bit) - 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED - 2.09 (2016-01-16) allow comments in PNM files - 16-bit-per-pixel TGA (not bit-per-component) - info() for TGA could break due to .hdr handling - info() for BMP to shares code instead of sloppy parse - can use STBI_REALLOC_SIZED if allocator doesn't support realloc - code cleanup - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) fix compiler warnings - partial animated GIF support - limited 16-bpc PSD support - #ifdef unused functions - bug with < 92 byte PIC,PNM,HDR,TGA - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) extra corruption checking (mmozeiko) - stbi_set_flip_vertically_on_load (nguillemot) - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 (2008-08-02) - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 (2006-11-19) - first released version -*/ - - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index ffe39f9f7..54273b90c 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -32,7 +32,7 @@ PROJECT_NAME = Assimp # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = "v4.1. (December 2018)" +PROJECT_NUMBER = "v5.0.1. (December 2020)" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer @@ -197,7 +197,7 @@ SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. -TAB_SIZE = 8 +TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". @@ -636,7 +636,7 @@ WARN_IF_DOC_ERROR = YES # wrong or incomplete parameter documentation, but not about the absence of # documentation. -WARN_NO_PARAMDOC = NO +WARN_NO_PARAMDOC = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text @@ -663,11 +663,7 @@ WARN_LOGFILE = # with spaces. INPUT = @doxy_main_page@ \ - @PROJECT_SOURCE_DIR@ \ - @PROJECT_BINARY_DIR@ \ - @PROJECT_SOURCE_DIR@/include/ \ - @PROJECT_SOURCE_DIR@/doc/dox.h \ - @PROJECT_SOURCE_DIR@/code/BaseImporter.h + @PROJECT_SOURCE_DIR@/include/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is @@ -690,28 +686,10 @@ FILE_PATTERNS = *.c \ *.cxx \ *.cpp \ *.c++ \ - *.d \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ *.inl \ *.h \ - *.hh \ - *.hxx \ *.hpp \ - *.h++ \ - *.idl \ - *.odl \ - *.cs \ - *.php \ - *.php3 \ - *.inc \ - *.m \ - *.mm \ - *.dox \ - *.py + *.dox # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. @@ -725,7 +703,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = irrXML.h +EXCLUDE = contrib/* # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -739,8 +717,7 @@ EXCLUDE_SYMLINKS = NO # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* -EXCLUDE_PATTERNS = */.svn/* \ - */.svn +EXCLUDE_PATTERNS = */.git/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -901,7 +878,7 @@ IGNORE_PREFIX = # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. -GENERATE_HTML = YES +GENERATE_HTML = NO # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be @@ -1484,7 +1461,7 @@ MAN_LINKS = NO # generate an XML file that captures the structure of # the code including all documentation. -GENERATE_XML = NO +GENERATE_XML = YES # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be diff --git a/doc/dox.h b/doc/dox.h index a4516dc7a..409e775d4 100644 --- a/doc/dox.h +++ b/doc/dox.h @@ -90,8 +90,8 @@ but not all of them are *open-source*. If there's an accompagning '\source @section main_install Installation assimp can be used in two ways: linking against the pre-built libraries or building the library on your own. The former -option is the easiest, but the assimp distribution contains pre-built libraries only for Visual C++ 2013, 2015 and 2017. -For other compilers you'll have to build assimp for yourself. Which is hopefully as hassle-free as the other way, but +option is the easiest, but the assimp distribution contains pre-built libraries only for Visual C++ 2013, 2015 and 2017. +For other compilers you'll have to build assimp for yourself. Which is hopefully as hassle-free as the other way, but needs a bit more work. Both ways are described at the @link install Installation page. @endlink If you want to use assimp on Ubuntu you can install it via the following command: @@ -145,7 +145,7 @@ to your include paths (Menu->Extras->Options->Projects and Solutions-&g and the assimp/lib/<Compiler> path to your linker paths (Menu->Extras->Options->Projects and Solutions->VC++ Directories->Library files). This is necessary only once to setup all paths inside you IDE. -To use the library in your C++ project you can simply generate a project file via cmake. One way is to add the assimp-folder +To use the library in your C++ project you can simply generate a project file via cmake. One way is to add the assimp-folder as a subdirectory via the cmake-command @code @@ -158,7 +158,7 @@ Now just add the assimp-dependency to your application: TARGET_LINK_LIBRARIES(my_game assimp) @endcode -If done correctly you should now be able to compile, link, run and use the application. +If done correctly you should now be able to compile, link, run and use the application. @section install_own Building the library from scratch @@ -170,7 +170,7 @@ to build the library just open a command-prompt / bash, navigate into the repo-f cmake CMakeLists.txt @endcode -A project-file of your default make-system ( like gnu-make on linux or Visual-Studio on Windows ) will be generated. +A project-file of your default make-system ( like gnu-make on linux or Visual-Studio on Windows ) will be generated. Run the build and you are done. You can find the libs at assimp/lib and the dll's / so's at bin. @section assimp_dll Windows DLL Build @@ -496,10 +496,10 @@ X3 Y3 Z3 T3 @endcode with (X1, X2, X3) being the local X base vector, (Y1, Y2, Y3) being the local Y base vector, (Z1, Z2, Z3) being the local Z base vector and (T1, T2, T3) being the -offset of the local origin (the translational part). +offset of the local origin (the translational part). All matrices in the library use row-major storage order. That means that the matrix elements are -stored row-by-row, i.e. they end up like this in memory: -[X1, Y1, Z1, T1, X2, Y2, Z2, T2, X3, Y3, Z3, T3, 0, 0, 0, 1]. +stored row-by-row, i.e. they end up like this in memory: +[X1, Y1, Z1, T1, X2, Y2, Z2, T2, X3, Y3, Z3, T3, 0, 0, 0, 1]. Note that this is neither the OpenGL format nor the DirectX format, because both of them specify the matrix layout such that the translational part occupies three consecutive addresses in memory (so those @@ -1498,7 +1498,7 @@ Just copy'n'paste the template from Appendix A and adapt it for your needs. with DefaultLogger::get()->[error, warn, debug, info].
  • -Make sure that your loader compiles against all build configurations on all supported platforms. You can use our CI-build to check several platforms +Make sure that your loader compiles against all build configurations on all supported platforms. You can use our CI-build to check several platforms like Windows and Linux ( 32 bit and 64 bit ).
  • diff --git a/doc/dox_cmd.h b/doc/dox_cmd.h index 78e755d56..79ea3a861 100644 --- a/doc/dox_cmd.h +++ b/doc/dox_cmd.h @@ -12,7 +12,7 @@ @section intro Introduction -This document describes the usage of assimp's command line tools. +This document describes the usage of assimp's command line tools. This is *not* the SDK reference and programming-related stuff is not covered here.

    NOTE: For simplicity, the following sections are written with Windows in mind. However @@ -29,7 +29,7 @@ assimp [command] [parameters] The following commands are available: - + @@ -184,7 +184,7 @@ Generate a text or binary dump of a model. This is the core component of Assimp' regression test suite but it could also be useful for other developers to quickly examine the contents of a model. Note that text dumps are not intended to be used as intermediate format, Assimp is not able to read them again, nor is the file format -stable or well-defined. It may change with every revision without notice. +stable or well-defined. It may change with every revision without notice. Binary dumps (*.assbin) are backwards- and forwards-compatible.

    Syntax:

    @@ -199,7 +199,7 @@ assimp dump [] [-b] [-s] [common parameters]

    model

    -Required. Relative or absolute path to the input model. +Required. Relative or absolute path to the input model.

    @@ -220,7 +220,7 @@ The long form of this parameter is --binary.
    Optional. If this switch is specified, the dump is shortened to include only min/max values for all vertex components and animation channels. The resulting -file is much smaller, but the original model can't be reconstructed from it. This is +file is much smaller, but the original model can't be reconstructed from it. This is used by Assimp's regression test suite, comparing those minidumps provides a fast way to verify whether a loader works correctly or not. The long form of this parameter is --short. @@ -229,7 +229,7 @@ The long form of this parameter is --short.

    common parameters

    -Optional. Import configuration & postprocessing. +Optional. Import configuration & postprocessing. See the @link common common parameters page @endlink for more information.

    @@ -248,7 +248,7 @@ The log output is included with the dump. @code assimp dump files\*.* -assimp dump files\*.* +assimp dump files\*.* @endcode Dumps all loadable model files in the 'files' subdir. The output dumps are named @@ -275,14 +275,14 @@ assimp extract [] [-t] [-f] [-ba] [-s] [common parameters]

    model

    -Required. Relative or absolute path to the input model. +Required. Relative or absolute path to the input model.

    out

    Optional. Relative or absolute path to write the output images to. If the file name is omitted the output images are named
    -The suffix _img<n> is appended to the file name if the -s switch is not specified +The suffix _img<n> is appended to the file name if the -s switch is not specified (where <n> is the zero-based index of the texture in the model file).
    The output file format is determined from the given file extension. Supported @@ -296,7 +296,7 @@ written in their native file format (e.g. jpg).

    -t<n>

    -Optional. Specifies the (zero-based) index of the embedded texture to be extracted from +Optional. Specifies the (zero-based) index of the embedded texture to be extracted from the model. If this option is *not* specified all textures found are exported. The long form of this parameter is --texture=<n>.

    @@ -348,8 +348,8 @@ imported data structure and writes it to test_img0.bmp. @code -assimp extract files\*.mdl *.bmp -assimp extract files\*.mdl *.bmp +assimp extract files\*.mdl *.bmp +assimp extract files\*.mdl *.bmp @endcode Extracts all embedded textures from all loadable .mdl files in the 'files' subdirectory @@ -361,10 +361,10 @@ and writes them to bitmaps which are named _img.bmp /** @page common Common parameters -The parameters described on this page are commonly used by almost every assimp command. They +The parameters described on this page are commonly used by almost every assimp command. They specify how the library will postprocess the imported data. This is done by several configurable pipeline stages, called 'post processing steps'. Below you can find a list -of all supported steps along with short descriptions of what they're doing.
    Programmers: +of all supported steps along with short descriptions of what they're doing.
    Programmers: more information can be found in the aiPostProcess.h header.
    @link version version @endlink Retrieve the current version of assimp
    @@ -376,7 +376,7 @@ more information can be found in the aiPostProcess.h header. - @@ -428,7 +428,7 @@ more information can be found in the aiPostProcess.h header. - @@ -515,7 +515,7 @@ For convenience some default postprocessing configurations are provided. The corresponding command line parameter is -c<name> (or --config=<name>).
    -ptv --pretransform-verticesMove all vertices into worldspace and collapse the scene graph. Animation data is lost. + Move all vertices into worldspace and collapse the scene graph. Animation data is lost. This is intended for applications which don't support scenegraph-oriented rendering.
    -icl --improve-cache-localityImprove the cache locality of the vertex buffer by reordering the index buffer + Improve the cache locality of the vertex buffer by reordering the index buffer to achieve a lower ACMR (average post-transform vertex cache miss ratio)
    - + @@ -543,7 +543,7 @@ The corresponding command line parameter is -c<name> (or --co There are also some common flags to customize Assimp's logging behaviour:
    Name Description
    - + @@ -558,7 +558,7 @@ There are also some common flags to customize Assimp's logging behaviour: -
    Name Description
    -v or --verboseEnables verbose logging. Debug messages will be produced too. This might + Enables verbose logging. Debug messages will be produced too. This might decrease loading performance and result in *very* long logs ... use with caution if you experience strange issues.
    diff --git a/fuzz/assimp_fuzzer.cc b/fuzz/assimp_fuzzer.cc index b65ee0236..33748c10f 100644 --- a/fuzz/assimp_fuzzer.cc +++ b/fuzz/assimp_fuzzer.cc @@ -54,6 +54,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) { aiProcessPreset_TargetRealtime_Quality, nullptr ); aiDetachLogStream(&stream); - + return 0; } diff --git a/include/assimp/BaseImporter.h b/include/assimp/BaseImporter.h index 38bec1afd..54b5daac1 100644 --- a/include/assimp/BaseImporter.h +++ b/include/assimp/BaseImporter.h @@ -154,7 +154,7 @@ public: /** Returns the exception of the last exception that occurred. * Note: Exceptions are not the only source of error details, so GetErrorText * should be consulted too. - * @return The last exception that occurred. + * @return The last exception that occurred. */ const std::exception_ptr& GetException() const { return m_Exception; diff --git a/include/assimp/Compiler/poppack1.h b/include/assimp/Compiler/poppack1.h index a8e9a3c7e..ff501bc0c 100644 --- a/include/assimp/Compiler/poppack1.h +++ b/include/assimp/Compiler/poppack1.h @@ -1,7 +1,7 @@ // =============================================================================== -// May be included multiple times - resets structure packing to the defaults -// for all supported compilers. Reverts the changes made by #include +// 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 diff --git a/include/assimp/Compiler/pushpack1.h b/include/assimp/Compiler/pushpack1.h index 2a5e2dfe6..b32ed172c 100644 --- a/include/assimp/Compiler/pushpack1.h +++ b/include/assimp/Compiler/pushpack1.h @@ -1,7 +1,7 @@ // =============================================================================== -// May be included multiple times - sets structure packing to 1 +// 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: @@ -37,7 +37,7 @@ #if defined(_MSC_VER) // C4103: Packing was changed after the inclusion of the header, probably missing #pragma pop -# pragma warning (disable : 4103) +# pragma warning (disable : 4103) #endif #define AI_PUSHPACK_IS_DEFINED diff --git a/include/assimp/Exceptional.h b/include/assimp/Exceptional.h index 98e2a3321..1bf399cbc 100644 --- a/include/assimp/Exceptional.h +++ b/include/assimp/Exceptional.h @@ -59,7 +59,7 @@ using std::runtime_error; class ASSIMP_API DeadlyErrorBase : public runtime_error { protected: DeadlyErrorBase(Assimp::Formatter::format f); - + template DeadlyErrorBase(Assimp::Formatter::format f, U&& u, T&&... args) : DeadlyErrorBase(std::move(f << std::forward(u)), std::forward(args)...) {} diff --git a/include/assimp/Exporter.hpp b/include/assimp/Exporter.hpp index 8e78ac954..6ab35a8f0 100644 --- a/include/assimp/Exporter.hpp +++ b/include/assimp/Exporter.hpp @@ -390,7 +390,7 @@ public: * @see SetPropertyInteger() */ bool SetPropertyMatrix(const char *szName, const aiMatrix4x4 &sValue); - + bool SetPropertyCallback(const char *szName, const std::function &f); // ------------------------------------------------------------------- diff --git a/include/assimp/IOStreamBuffer.h b/include/assimp/IOStreamBuffer.h index ee4ac0953..c3b7327ff 100644 --- a/include/assimp/IOStreamBuffer.h +++ b/include/assimp/IOStreamBuffer.h @@ -81,7 +81,7 @@ public: /// @brief Returns the file-size. /// @return The file-size. size_t size() const; - + /// @brief Returns the cache size. /// @return The cache size. size_t cacheSize() const; @@ -278,7 +278,7 @@ bool IOStreamBuffer::getNextDataLine( std::vector &buffer, T continuationT } } } - + buffer[ i ] = '\n'; ++m_cachePos; @@ -334,7 +334,7 @@ template AI_FORCE_INLINE bool IOStreamBuffer::getNextBlock( std::vector &buffer) { // Return the last block-value if getNextLine was used before - if ( 0 != m_cachePos ) { + if ( 0 != m_cachePos ) { buffer = std::vector( m_cache.begin() + m_cachePos, m_cache.end() ); m_cachePos = 0; } else { diff --git a/include/assimp/IOSystem.hpp b/include/assimp/IOSystem.hpp index 76f876440..7be373cf1 100644 --- a/include/assimp/IOSystem.hpp +++ b/include/assimp/IOSystem.hpp @@ -62,9 +62,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "types.h" #ifdef _WIN32 -# include -# include -# include +# include +# include +# include #else # include # include @@ -84,7 +84,7 @@ namespace Assimp { * to the importer library. If you implement this interface, you also want to * supply a custom implementation for IOStream. * - * @see Importer::SetIOHandler() + * @see Importer::SetIOHandler() */ class ASSIMP_API IOSystem #ifndef SWIG diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index 3ca4a6cb2..aa7ffba7c 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -99,8 +99,8 @@ public: virtual ~Logger(); // ---------------------------------------------------------------------- - /** @brief Writes a info message - * @param message Info message*/ + /** @brief Writes a debug message + * @param message Debug message*/ void debug(const char* message); template @@ -109,10 +109,10 @@ public: } // ---------------------------------------------------------------------- - /** @brief Writes a debug message + /** @brief Writes a debug message * @param message Debug message*/ void verboseDebug(const char* message); - + template void verboseDebug(T&&... args) { verboseDebug(formatMessage(std::forward(args)...).c_str()); @@ -140,7 +140,7 @@ public: // ---------------------------------------------------------------------- /** @brief Writes an error message - * @param message Info message*/ + * @param message Error message*/ void error(const char* message); template diff --git a/include/assimp/MemoryIOWrapper.h b/include/assimp/MemoryIOWrapper.h index 86dc5ea60..24f6a85d1 100644 --- a/include/assimp/MemoryIOWrapper.h +++ b/include/assimp/MemoryIOWrapper.h @@ -57,7 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include namespace Assimp { - + #define AI_MEMORYIO_MAGIC_FILENAME "$$$___magic___$$$" #define AI_MEMORYIO_MAGIC_FILENAME_LENGTH 17 @@ -85,7 +85,7 @@ public: size_t Read(void* pvBuffer, size_t pSize, size_t pCount) { ai_assert(nullptr != pvBuffer); ai_assert(0 != pSize); - + const size_t cnt = std::min( pCount, (length-pos) / pSize); const size_t ofs = pSize * cnt; @@ -209,7 +209,7 @@ public: return existing_io ? existing_io->ComparePaths(one, second) : false; } - bool PushDirectory( const std::string &path ) override { + bool PushDirectory( const std::string &path ) override { return existing_io ? existing_io->PushDirectory(path) : false; } diff --git a/include/assimp/SmallVector.h b/include/assimp/SmallVector.h index bcb8482a9..fb78f5a97 100644 --- a/include/assimp/SmallVector.h +++ b/include/assimp/SmallVector.h @@ -53,17 +53,17 @@ Based on CppCon 2016: Chandler Carruth "High Performance Code 201: Hybrid Data S namespace Assimp { // -------------------------------------------------------------------------------------------- -/// @brief Small vector with inplace storage. +/// @brief Small vector with inplace storage. /// /// Reduces heap allocations when list is shorter. It uses a small array for a dedicated size. -/// When the growing gets bigger than this small cache a dynamic growing algorithm will be +/// When the growing gets bigger than this small cache a dynamic growing algorithm will be /// used. // -------------------------------------------------------------------------------------------- template class SmallVector { public: /// @brief The default class constructor. - SmallVector() : + SmallVector() : mStorage(mInplaceStorage), mSize(0), mCapacity(Capacity) { @@ -84,7 +84,7 @@ public: mStorage[mSize++] = item; return; } - + push_back_and_grow(item); } diff --git a/include/assimp/SmoothingGroups.inl b/include/assimp/SmoothingGroups.inl index a32626e00..08c2dfa53 100644 --- a/include/assimp/SmoothingGroups.inl +++ b/include/assimp/SmoothingGroups.inl @@ -7,8 +7,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -77,7 +77,7 @@ void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& sMesh) sMesh.mNormals[face.mIndices[c]] = vNor; } - // calculate the position bounds so we have a reliable epsilon to check position differences against + // calculate the position bounds so we have a reliable epsilon to check position differences against aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f); for( unsigned int a = 0; a < sMesh.mPositions.size(); a++) { @@ -91,7 +91,7 @@ void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& sMesh) const float posEpsilon = (maxVec - minVec).Length() * 1e-5f; std::vector avNormals; avNormals.resize(sMesh.mNormals.size()); - + // now generate the spatial sort tree SGSpatialSort sSort; for( typename std::vector::iterator i = sMesh.mFaces.begin(); diff --git a/include/assimp/XmlParser.h b/include/assimp/XmlParser.h index 18d48f337..9c2dc419e 100644 --- a/include/assimp/XmlParser.h +++ b/include/assimp/XmlParser.h @@ -94,7 +94,7 @@ using XmlAttribute = pugi::xml_attribute; /// } /// } /// @endcode -/// @tparam TNodeType +/// @tparam TNodeType template class TXmlParser { public: @@ -123,7 +123,7 @@ public: /// @brief Will search for a child-node by its name /// @param name [in] The name of the child-node. - /// @return The node instance or nullptr, if nothing was found. + /// @return The node instance or nullptr, if nothing was found. TNodeType *findNode(const std::string &name) { if (name.empty()) { return nullptr; @@ -162,12 +162,12 @@ public: mData.resize(len + 1); memset(&mData[0], '\0', len + 1); stream->Read(&mData[0], 1, len); - + mDoc = new pugi::xml_document(); pugi::xml_parse_result parse_result = mDoc->load_string(&mData[0], pugi::parse_full); if (parse_result.status == pugi::status_ok) { return true; - } + } ASSIMP_LOG_DEBUG("Error while parse xml.", std::string(parse_result.description()), " @ ", parse_result.offset); @@ -457,7 +457,7 @@ public: } private: - XmlNode &mParent; + XmlNode &mParent; std::vector mNodes; size_t mIndex; }; diff --git a/include/assimp/ai_assert.h b/include/assimp/ai_assert.h index 6736b24c9..b377b6e8b 100644 --- a/include/assimp/ai_assert.h +++ b/include/assimp/ai_assert.h @@ -57,7 +57,7 @@ namespace Assimp #else # define ai_assert(expression) -# define ai_assert_entry() +# define ai_assert_entry() #endif // ASSIMP_BUILD_DEBUG #endif // AI_ASSERT_H_INC diff --git a/include/assimp/anim.h b/include/assimp/anim.h index 79841a537..dcd054d1e 100644 --- a/include/assimp/anim.h +++ b/include/assimp/anim.h @@ -39,7 +39,7 @@ 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. @@ -478,11 +478,11 @@ struct aiAnimation { 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. + * types of the arguments. */ template struct Interpolator { diff --git a/include/assimp/cimport.h b/include/assimp/cimport.h index b4a172742..d660eebb1 100644 --- a/include/assimp/cimport.h +++ b/include/assimp/cimport.h @@ -894,7 +894,7 @@ ASSIMP_API float aiMatrix3Determinant( // -------------------------------------------------------------------------------- /** Get a 3x3 rotation matrix around the Z axis. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param angle Rotation angle, in radians */ ASSIMP_API void aiMatrix3RotationZ( @@ -903,7 +903,7 @@ ASSIMP_API void aiMatrix3RotationZ( // -------------------------------------------------------------------------------- /** Returns a 3x3 rotation matrix for a rotation around an arbitrary axis. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param axis Rotation axis, should be a normalized vector * @param angle Rotation angle, in radians */ @@ -914,7 +914,7 @@ ASSIMP_API void aiMatrix3FromRotationAroundAxis( // -------------------------------------------------------------------------------- /** Get a 3x3 translation matrix. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param translation The translation vector */ ASSIMP_API void aiMatrix3Translation( @@ -923,7 +923,7 @@ ASSIMP_API void aiMatrix3Translation( // -------------------------------------------------------------------------------- /** Create a 3x3 matrix that rotates one vector to another vector. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param from Vector to rotate from * @param to Vector to rotate to */ @@ -1059,7 +1059,7 @@ ASSIMP_API void aiMatrix4DecomposeNoScaling( // -------------------------------------------------------------------------------- /** Creates a 4x4 matrix from a set of euler angles. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @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 @@ -1137,7 +1137,7 @@ ASSIMP_API void aiMatrix4FromTo( // -------------------------------------------------------------------------------- /** Create a Quaternion from euler angles. - * @param q Receives the output quaternion + * @param q Receives the output quaternion * @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 diff --git a/include/assimp/defs.h b/include/assimp/defs.h index d61fd7901..8d1011a28 100644 --- a/include/assimp/defs.h +++ b/include/assimp/defs.h @@ -161,7 +161,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma warning(disable : 4251) #endif /* Force the compiler to inline a function, if possible */ - #define AI_FORCE_INLINE inline + #define AI_FORCE_INLINE inline /* Tells the compiler that a function never returns. Used in code analysis * to skip dead paths (e.g. after an assertion evaluated to false). */ diff --git a/include/assimp/fast_atof.h b/include/assimp/fast_atof.h index aea793f35..43bbbff64 100644 --- a/include/assimp/fast_atof.h +++ b/include/assimp/fast_atof.h @@ -194,7 +194,7 @@ uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_ino if ( *in < '0' || *in > '9' ) { // The string is known to be bad, so don't risk printing the whole thing. - throw ExceptionType("The string \"", ai_str_toprintable(in, 30), "\" cannot be converted into a value." ); + throw ExceptionType("The string \"", ai_str_toprintable(in, (int)strlen(in)), "\" cannot be converted into a value." ); } for ( ;; ) { @@ -294,7 +294,7 @@ const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) if (!(c[0] >= '0' && c[0] <= '9') && !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) { // The string is known to be bad, so don't risk printing the whole thing. - throw ExceptionType("Cannot parse string \"", ai_str_toprintable(c, 30), + throw ExceptionType("Cannot parse string \"", ai_str_toprintable(c, (int)strlen(c)), "\" as a real number: does not start with digit " "or decimal point followed by digit."); } diff --git a/include/assimp/light.h b/include/assimp/light.h index bc37de43a..48efdaebd 100644 --- a/include/assimp/light.h +++ b/include/assimp/light.h @@ -257,7 +257,7 @@ struct aiLight #ifdef __cplusplus } -#endif +#endif #endif // !! AI_LIGHT_H_INC diff --git a/include/assimp/material.h b/include/assimp/material.h index 08c0491c0..250ad90d5 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -144,7 +144,7 @@ enum aiTextureMapMode { enum aiTextureMapping { /** The mapping coordinates are taken from an UV channel. * - * The #AI_MATKEY_UVWSRC key specifies from which UV channel + * #AI_MATKEY_UVWSRC property specifies from which UV channel * the texture coordinates are to be taken from (remember, * meshes can have more than one UV channel). */ @@ -194,19 +194,23 @@ enum aiTextureType { */ aiTextureType_NONE = 0, - /** LEGACY API MATERIALS - * Legacy refers to materials which + /** LEGACY API MATERIALS + * Legacy refers to materials which * Were originally implemented in the specifications around 2000. * These must never be removed, as most engines support them. */ /** The texture is combined with the result of the diffuse * lighting equation. + * OR + * PBR Specular/Glossiness */ aiTextureType_DIFFUSE = 1, /** The texture is combined with the result of the specular * lighting equation. + * OR + * PBR Specular/Glossiness */ aiTextureType_SPECULAR = 2, @@ -288,6 +292,32 @@ enum aiTextureType { aiTextureType_DIFFUSE_ROUGHNESS = 16, aiTextureType_AMBIENT_OCCLUSION = 17, + /** PBR Material Modifiers + * Some modern renderers have further PBR modifiers that may be overlaid + * on top of the 'base' PBR materials for additional realism. + * These use multiple texture maps, so only the base type is directly defined + */ + + /** Sheen + * Generally used to simulate textiles that are covered in a layer of microfibers + * eg velvet + * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_sheen + */ + aiTextureType_SHEEN = 19, + + /** Clearcoat + * Simulates a layer of 'polish' or 'laquer' layered on top of a PBR substrate + * https://autodesk.github.io/standard-surface/#closures/coating + * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat + */ + aiTextureType_CLEARCOAT = 20, + + /** Transmission + * Simulates transmission through the surface + * May include further information such as wall thickness + */ + aiTextureType_TRANSMISSION = 21, + /** Unknown texture * * A texture reference that does not match any of the definitions @@ -309,6 +339,8 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); // --------------------------------------------------------------------------- /** @brief Defines all shading models supported by the library + * + * Property: #AI_MATKEY_SHADING_MODEL * * The list of shading modes has been taken from Blender. * See Blender documentation for more information. The API does @@ -318,6 +350,7 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); * 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 modeler which wrote a particular model as closely as possible. + * */ enum aiShadingMode { /** Flat shading. Shading is done on per-face base, @@ -364,13 +397,28 @@ enum aiShadingMode { aiShadingMode_CookTorrance = 0x8, /** No shading at all. Constant light influence of 1.0. + * Also known as "Unlit" */ aiShadingMode_NoShading = 0x9, + aiShadingMode_Unlit = aiShadingMode_NoShading, // Alias /** Fresnel shading */ aiShadingMode_Fresnel = 0xa, + /** Physically-Based Rendering (PBR) shading using + * Bidirectional scattering/reflectance distribution function (BSDF/BRDF) + * There are multiple methods under this banner, and model files may provide + * data for more than one PBR-BRDF method. + * Applications should use the set of provided properties to determine which + * of their preferred PBR rendering methods are likely to be available + * eg: + * - If AI_MATKEY_METALLIC_FACTOR is set, then a Metallic/Roughness is available + * - If AI_MATKEY_GLOSSINESS_FACTOR is set, then a Specular/Glossiness is available + * Note that some PBR methods allow layering of techniques + */ + aiShadingMode_PBR_BRDF = 0xb, + #ifndef SWIG _aiShadingMode_Force32Bit = INT_MAX #endif @@ -922,12 +970,66 @@ extern "C" { // --------------------------------------------------------------------------- // PBR material support +// -------------------- +// Properties defining PBR rendering techniques #define AI_MATKEY_USE_COLOR_MAP "$mat.useColorMap", 0, 0 + +// Metallic/Roughness Workflow +// --------------------------- +// Base RGBA color factor. Will be multiplied by final base color texture values if extant +// Note: Importers may choose to copy this into AI_MATKEY_COLOR_DIFFUSE for compatibility +// with renderers and formats that do not support Metallic/Roughness PBR #define AI_MATKEY_BASE_COLOR "$clr.base", 0, 0 +#define AI_MATKEY_BASE_COLOR_TEXTURE aiTextureType_BASE_COLOR, 0 #define AI_MATKEY_USE_METALLIC_MAP "$mat.useMetallicMap", 0, 0 +// Metallic factor. 0.0 = Full Dielectric, 1.0 = Full Metal #define AI_MATKEY_METALLIC_FACTOR "$mat.metallicFactor", 0, 0 +#define AI_MATKEY_METALLIC_TEXTURE aiTextureType_METALNESS, 0 #define AI_MATKEY_USE_ROUGHNESS_MAP "$mat.useRoughnessMap", 0, 0 +// Roughness factor. 0.0 = Perfectly Smooth, 1.0 = Completely Rough #define AI_MATKEY_ROUGHNESS_FACTOR "$mat.roughnessFactor", 0, 0 +#define AI_MATKEY_ROUGHNESS_TEXTURE aiTextureType_DIFFUSE_ROUGHNESS, 0 + +// Specular/Glossiness Workflow +// --------------------------- +// Diffuse/Albedo Color. Note: Pure Metals have a diffuse of {0,0,0} +// AI_MATKEY_COLOR_DIFFUSE +// Specular Color. +// Note: Metallic/Roughness may also have a Specular Color +// AI_MATKEY_COLOR_SPECULAR +#define AI_MATKEY_SPECULAR_FACTOR "$mat.specularFactor", 0, 0 +// Glossiness factor. 0.0 = Completely Rough, 1.0 = Perfectly Smooth +#define AI_MATKEY_GLOSSINESS_FACTOR "$mat.glossinessFactor", 0, 0 + +// Sheen +// ----- +// Sheen base RGB color. Default {0,0,0} +#define AI_MATKEY_SHEEN_COLOR_FACTOR "$clr.sheen.factor", 0, 0 +// Sheen Roughness Factor. +#define AI_MATKEY_SHEEN_ROUGHNESS_FACTOR "$mat.sheen.roughnessFactor", 0, 0 +#define AI_MATKEY_SHEEN_COLOR_TEXTURE aiTextureType_SHEEN, 0 +#define AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE aiTextureType_SHEEN, 1 + +// Clearcoat +// --------- +// Clearcoat layer intensity. 0.0 = none (disabled) +#define AI_MATKEY_CLEARCOAT_FACTOR "$mat.clearcoat.factor", 0, 0 +#define AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR "$mat.clearcoat.roughnessFactor", 0, 0 +#define AI_MATKEY_CLEARCOAT_TEXTURE aiTextureType_CLEARCOAT, 0 +#define AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_CLEARCOAT, 1 +#define AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE aiTextureType_CLEARCOAT, 2 + +// Transmission +// ------------ +// https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission +// Base percentage of light transmitted through the surface. 0.0 = Opaque, 1.0 = Fully transparent +#define AI_MATKEY_TRANSMISSION_FACTOR "$mat.transmission.factor", 0, 0 +// Texture defining percentage of light transmitted through the surface. +// Multiplied by AI_MATKEY_TRANSMISSION_FACTOR +#define AI_MATKEY_TRANSMISSION_TEXTURE aiTextureType_TRANSMISSION, 0 + +// Emissive +// -------- #define AI_MATKEY_USE_EMISSIVE_MAP "$mat.useEmissiveMap", 0, 0 #define AI_MATKEY_EMISSIVE_INTENSITY "$mat.emissiveIntensity", 0, 0 #define AI_MATKEY_USE_AO_MAP "$mat.useAOMap", 0, 0 @@ -1397,8 +1499,6 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialFloatArray( ai_real *pOut, unsigned int *pMax); -#ifdef __cplusplus - // --------------------------------------------------------------------------- /** @brief Retrieve a single float property with a specific key from the material. * @@ -1418,7 +1518,7 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialFloatArray( * @return Specifies whether the key has been found. If not, the output * float remains unmodified.*/ // --------------------------------------------------------------------------- -inline aiReturn aiGetMaterialFloat(const aiMaterial *pMat, +inline aiReturn aiGetMaterialFloat(const C_STRUCT aiMaterial *pMat, const char *pKey, unsigned int type, unsigned int index, @@ -1426,14 +1526,6 @@ inline aiReturn aiGetMaterialFloat(const aiMaterial *pMat, 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 @@ -1446,8 +1538,6 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialIntegerArray(const C_STRUCT aiMaterial * int *pOut, unsigned int *pMax); -#ifdef __cplusplus - // --------------------------------------------------------------------------- /** @brief Retrieve an integer property with a specific key from a material * @@ -1461,14 +1551,6 @@ inline aiReturn aiGetMaterialInteger(const C_STRUCT aiMaterial *pMat, 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 * diff --git a/include/assimp/matrix4x4.h b/include/assimp/matrix4x4.h index c929ef2a9..6caf7686c 100644 --- a/include/assimp/matrix4x4.h +++ b/include/assimp/matrix4x4.h @@ -230,7 +230,7 @@ public: * @param out Receives the output matrix * @return Reference to the output matrix */ - static aiMatrix4x4t& Translation( const aiVector3t& v, + static aiMatrix4x4t& Translation( const aiVector3t& v, aiMatrix4x4t& out); // ------------------------------------------------------------------- diff --git a/include/assimp/matrix4x4.inl b/include/assimp/matrix4x4.inl index 88315ac28..c1dd87b65 100644 --- a/include/assimp/matrix4x4.inl +++ b/include/assimp/matrix4x4.inl @@ -421,7 +421,7 @@ void aiMatrix4x4t::Decompose(aiVector3t& pScaling, aiVector3tmArmature and aiBone->mNode generically * The point of these is it saves you later having to calculate these elements * This is useful when handling rest information or skin information - * If you have multiple armatures on your models we strongly recommend enabling this - * Instead of writing your own multi-root, multi-armature lookups we have done the + * If you have multiple armatures on your models we strongly recommend enabling this + * Instead of writing your own multi-root, multi-armature lookups we have done the * hard work for you :) */ aiProcess_PopulateArmatureData = 0x4000, @@ -579,7 +579,7 @@ enum aiPostProcessSteps * of the imported model. And if so, it uses that. */ aiProcess_EmbedTextures = 0x10000000, - + // aiProcess_GenEntityMeshes = 0x100000, // aiProcess_OptimizeAnimations = 0x200000 // aiProcess_FixTexturePaths = 0x200000 diff --git a/include/assimp/quaternion.h b/include/assimp/quaternion.h index 7c096fe00..6941fbbc3 100644 --- a/include/assimp/quaternion.h +++ b/include/assimp/quaternion.h @@ -73,7 +73,7 @@ public: explicit aiQuaterniont( const aiMatrix3x3t& pRotMatrix); /** Construct from euler angles */ - aiQuaterniont( TReal rotx, TReal roty, TReal rotz); + aiQuaterniont( TReal roty, TReal rotz, TReal rotx); /** Construct from an axis-angle pair */ aiQuaterniont( aiVector3t axis, TReal angle); diff --git a/include/assimp/scene.h b/include/assimp/scene.h index 336eb1ebd..dcf803572 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -70,7 +70,7 @@ extern "C" { #endif // ------------------------------------------------------------------------------- -/** +/** * A node in the imported hierarchy. * * Each node has name, a parent node (except for the root node), @@ -149,12 +149,12 @@ struct ASSIMP_API aiNode * @param name Name to search for * @return nullptr or a valid Node if the search was successful. */ - inline + inline const aiNode* FindNode(const aiString& name) const { return FindNode(name.data); } - inline + inline aiNode* FindNode(const aiString& name) { return FindNode(name.data); } @@ -353,34 +353,34 @@ struct 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 != nullptr && mNumMeshes > 0; + inline bool HasMeshes() const { + return mMeshes != nullptr && 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 != nullptr && mNumMaterials > 0; + inline bool HasMaterials() const { + return mMaterials != nullptr && mNumMaterials > 0; } //! Check whether the scene contains lights - inline bool HasLights() const { - return mLights != nullptr && mNumLights > 0; + inline bool HasLights() const { + return mLights != nullptr && mNumLights > 0; } //! Check whether the scene contains textures inline bool HasTextures() const { - return mTextures != nullptr && mNumTextures > 0; + return mTextures != nullptr && mNumTextures > 0; } //! Check whether the scene contains cameras inline bool HasCameras() const { - return mCameras != nullptr && mNumCameras > 0; + return mCameras != nullptr && mNumCameras > 0; } //! Check whether the scene contains animations - inline bool HasAnimations() const { - return mAnimations != nullptr && mNumAnimations > 0; + inline bool HasAnimations() const { + return mAnimations != nullptr && mNumAnimations > 0; } //! Returns a short filename from a full path @@ -395,22 +395,35 @@ struct aiScene //! Returns an embedded texture const aiTexture* GetEmbeddedTexture(const char* filename) const { + return GetEmbeddedTextureAndIndex(filename).first; + } + + //! Returns an embedded texture and its index + std::pair GetEmbeddedTextureAndIndex(const char* filename) const { + if (nullptr==filename) { + return std::make_pair(nullptr, -1); + } // lookup using texture ID (if referenced like: "*1", "*2", etc.) if ('*' == *filename) { int index = std::atoi(filename + 1); - if (0 > index || mNumTextures <= static_cast(index)) - return nullptr; - return mTextures[index]; + if (0 > index || mNumTextures <= static_cast(index)) { + return std::make_pair(nullptr, -1); + } + return std::make_pair(mTextures[index], index); } // lookup using filename const char* shortFilename = GetShortFilename(filename); + if (nullptr == shortFilename) { + return std::make_pair(nullptr, -1); + } + for (unsigned int i = 0; i < mNumTextures; i++) { const char* shortTextureFilename = GetShortFilename(mTextures[i]->mFilename.C_Str()); if (strcmp(shortTextureFilename, shortFilename) == 0) { - return mTextures[i]; + return std::make_pair(mTextures[i], i); } } - return nullptr; + return std::make_pair(nullptr, -1); } #endif // __cplusplus diff --git a/include/assimp/vector2.inl b/include/assimp/vector2.inl index 0ca440e72..b51dd0ec2 100644 --- a/include/assimp/vector2.inl +++ b/include/assimp/vector2.inl @@ -190,7 +190,7 @@ aiVector2t operator + (const aiVector2t& v1, const aiVector2t -inline +inline aiVector2t operator - (const aiVector2t& v1, const aiVector2t& v2) { return aiVector2t( v1.x - v2.x, v1.y - v2.y); } @@ -198,7 +198,7 @@ aiVector2t operator - (const aiVector2t& v1, const aiVector2t -inline +inline TReal operator * (const aiVector2t& v1, const aiVector2t& v2) { return v1.x*v2.x + v1.y*v2.y; } @@ -206,7 +206,7 @@ TReal operator * (const aiVector2t& v1, const aiVector2t& v2) { // ------------------------------------------------------------------------------------------------ // scalar multiplication template -inline +inline aiVector2t operator * ( TReal f, const aiVector2t& v) { return aiVector2t( f*v.x, f*v.y); } @@ -214,7 +214,7 @@ aiVector2t operator * ( TReal f, const aiVector2t& v) { // ------------------------------------------------------------------------------------------------ // and the other way around template -inline +inline aiVector2t operator * ( const aiVector2t& v, TReal f) { return aiVector2t( f*v.x, f*v.y); } @@ -222,7 +222,7 @@ aiVector2t operator * ( const aiVector2t& v, TReal f) { // ------------------------------------------------------------------------------------------------ // scalar division template -inline +inline aiVector2t operator / ( const aiVector2t& v, TReal f) { return v * (1/f); } @@ -230,7 +230,7 @@ aiVector2t operator / ( const aiVector2t& v, TReal f) { // ------------------------------------------------------------------------------------------------ // vector division template -inline +inline aiVector2t operator / ( const aiVector2t& v, const aiVector2t& v2) { return aiVector2t(v.x / v2.x,v.y / v2.y); } @@ -238,7 +238,7 @@ aiVector2t operator / ( const aiVector2t& v, const aiVector2t -inline +inline aiVector2t operator - ( const aiVector2t& v) { return aiVector2t( -v.x, -v.y); } diff --git a/include/assimp/vector3.h b/include/assimp/vector3.h index b084cf9c3..e3ad0b680 100644 --- a/include/assimp/vector3.h +++ b/include/assimp/vector3.h @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -65,27 +63,49 @@ template class aiMatrix3x3t; template class aiMatrix4x4t; // --------------------------------------------------------------------------- -/** Represents a three-dimensional vector. */ +/// @brief Represents a three-dimensional vector. +// --------------------------------------------------------------------------- template class aiVector3t { public: + /// @brief The default class constructor. aiVector3t() AI_NO_EXCEPT : x(), y(), z() {} + + /// @brief The class constructor with the components. + /// @param _x The x-component for the vector. + /// @param _y The y-component for the vector. + /// @param _z The z-component for the vector. aiVector3t(TReal _x, TReal _y, TReal _z) : x(_x), y(_y), z(_z) {} + + /// @brief The class constructor with a default value. + /// @param _xyz The value for x, y and z. explicit aiVector3t (TReal _xyz ) : x(_xyz), y(_xyz), z(_xyz) {} + + /// @brief The copy constructor. + /// @param o The instance to copy from. aiVector3t( const aiVector3t& o ) = default; - // combined operators + /// @brief combined operators + /// @brief The copy constructor. const aiVector3t& operator += (const aiVector3t& o); + + /// @brief The copy constructor. const aiVector3t& operator -= (const aiVector3t& o); + + /// @brief The copy constructor. const aiVector3t& operator *= (TReal f); + + /// @brief The copy constructor. const aiVector3t& operator /= (TReal f); - // transform vector by matrix + /// @brief Transform vector by matrix aiVector3t& operator *= (const aiMatrix3x3t& mat); aiVector3t& operator *= (const aiMatrix4x4t& mat); - // access a single element + /// @brief access a single element, const. TReal operator[](unsigned int i) const; + + /// @brief access a single element, non-const. TReal& operator[](unsigned int i); // comparison @@ -93,6 +113,7 @@ public: bool operator!= (const aiVector3t& other) const; bool operator < (const aiVector3t& other) const; + /// @brief bool Equal(const aiVector3t& other, TReal epsilon = 1e-6) const; template diff --git a/packaging/windows-innosetup/readme_installer.txt b/packaging/windows-innosetup/readme_installer.txt index 6ea969dc1..252c396af 100644 --- a/packaging/windows-innosetup/readme_installer.txt +++ b/packaging/windows-innosetup/readme_installer.txt @@ -10,7 +10,7 @@ http://assimp.sf.net Troubleshooting =============== -1. Missing d3dx9_(some-number).dll? +1. Missing d3dx9_(some-number).dll? Install the latest DirectX runtime or grab the file from somewhere (that's evil but mostly fine). 2. Application configuration not correct / missing msvcr***.dll? diff --git a/packaging/windows-innosetup/readme_installer_vieweronly.txt b/packaging/windows-innosetup/readme_installer_vieweronly.txt index 1e84c577d..0acd6ef52 100644 --- a/packaging/windows-innosetup/readme_installer_vieweronly.txt +++ b/packaging/windows-innosetup/readme_installer_vieweronly.txt @@ -19,7 +19,7 @@ Viewer Troubleshooting =============== -1. Missing d3dx9_(number).dll? +1. Missing d3dx9_(number).dll? Install the latest DirectX runtime or grab the file from somewhere (that's evil but mostly fine). 2. Application configuration not correct / missing msvcr***.dll? diff --git a/packaging/windows-mkzip/bin_readme.txt b/packaging/windows-mkzip/bin_readme.txt index 5cff1f30e..f4402d6bf 100644 --- a/packaging/windows-mkzip/bin_readme.txt +++ b/packaging/windows-mkzip/bin_readme.txt @@ -19,7 +19,7 @@ Viewer Troubleshooting =============== -1. Missing d3dx9_42.dll? +1. Missing d3dx9_42.dll? Install the latest DirectX runtime or grab the file from somewhere (that's evil but mostly fine). 2. Application configuration not correct / missing msv*** DLLs? diff --git a/port/AndroidJNI/CMakeLists.txt b/port/AndroidJNI/CMakeLists.txt index 43e842848..8c821c72a 100644 --- a/port/AndroidJNI/CMakeLists.txt +++ b/port/AndroidJNI/CMakeLists.txt @@ -3,10 +3,10 @@ cmake_minimum_required(VERSION 3.10) include_directories(./) include_directories(./../../) add_library( # Defines the name of the library. - android_jniiosystem + android_jniiosystem # Implements a static library. - STATIC + STATIC # relative path to source file(s). AndroidJNIIOSystem.cpp diff --git a/port/AssimpDelphi/Readme.txt b/port/AssimpDelphi/Readme.txt index 07d6935ae..1ec6d2109 100644 --- a/port/AssimpDelphi/Readme.txt +++ b/port/AssimpDelphi/Readme.txt @@ -1,6 +1,6 @@ This is a set of Delphi units for using the Assimp C DLL. This was created for use with Delphi 7, but should be usable as-is or with minimal modifications with later Delphi versions. -This set of headers is enough to load and display a model with external textures. Since I'm not familiar with animated models and some of the other functionality of the assimp library, I did not convert the headers for those features. +This set of headers is enough to load and display a model with external textures. Since I'm not familiar with animated models and some of the other functionality of the assimp library, I did not convert the headers for those features. See http://sourceforge.net/tracker/?func=detail&aid=3212646&group_id=226462&atid=1067634 for the original patch diff --git a/port/PyAssimp/pyassimp/helper.py b/port/PyAssimp/pyassimp/helper.py index 7a4b2bdcb..7c14f6097 100644 --- a/port/PyAssimp/pyassimp/helper.py +++ b/port/PyAssimp/pyassimp/helper.py @@ -34,7 +34,8 @@ if os.name=='posix': additional_dirs.extend([item for item in os.environ['LD_LIBRARY_PATH'].split(':') if item]) # check if running from anaconda. - if "conda" or "continuum" in sys.version.lower(): + anaconda_keywords = ("conda", "continuum") + if any(k in sys.version.lower() for k in anaconda_keywords): cur_path = get_python_lib() pattern = re.compile('.*\/lib\/') conda_lib = pattern.match(cur_path).group() diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index 0cf01b1e3..6661ce9c4 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -15,7 +15,7 @@ #define lprintf(...) printf (__VA_ARGS__) #endif /* ANDROID */ #else -#define lprintf +#define lprintf #endif static std::string gLastErrorString; @@ -63,7 +63,7 @@ static bool createInstance(JNIEnv *env, const char* className, jobject& newInsta newInstance = env->NewObject(clazz, ctr_id); - if (NULL == newInstance) + if (NULL == newInstance) { lprintf("error calling no-arg constructor for class %s\n", className); return false; @@ -94,7 +94,7 @@ static bool createInstance(JNIEnv *env, const char* className, const char* signa newInstance = env->NewObjectA(clazz, ctr_id, params); - if (NULL == newInstance) + if (NULL == newInstance) { lprintf("error calling constructor for class %s, signature %s\n", className, signature); return false; @@ -229,7 +229,7 @@ static bool getStaticField(JNIEnv *env, const char* className, const char* field } -static bool call(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static bool call(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature,/* const*/ jvalue* params) { jclass clazz = env->FindClass(typeName); @@ -275,7 +275,7 @@ static bool callv(JNIEnv *env, jobject object, const char* typeName, return true; } -static jobject callo(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static jobject callo(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature,/* const*/ jvalue* params) { jclass clazz = env->FindClass(typeName); @@ -300,7 +300,7 @@ static jobject callo(JNIEnv *env, jobject object, const char* typeName, const ch return jReturnValue; } -static int calli(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static int calli(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature) { jclass clazz = env->FindClass(typeName); @@ -325,7 +325,7 @@ static int calli(JNIEnv *env, jobject object, const char* typeName, const char* return (int) jReturnValue; } -static int callc(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static int callc(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature) { jclass clazz = env->FindClass(typeName); @@ -351,7 +351,7 @@ static int callc(JNIEnv *env, jobject object, const char* typeName, const char* } -static bool callStaticObject(JNIEnv *env, const char* typeName, const char* methodName, +static bool callStaticObject(JNIEnv *env, const char* typeName, const char* methodName, const char* signature,/* const*/ jvalue* params, jobject& returnValue) { jclass clazz = env->FindClass(typeName); @@ -441,13 +441,13 @@ static bool copyBufferArray(JNIEnv *env, jobject jMesh, const char* jBufferName, class JavaIOStream : public Assimp::IOStream { -private: +private: size_t pos; size_t size; char* buffer; jobject jIOStream; - + public: JavaIOStream(size_t size, char* buffer, jobject jIOStream) : pos(0), @@ -455,28 +455,28 @@ public: buffer(buffer), jIOStream(jIOStream) {}; - - - ~JavaIOStream(void) + + + ~JavaIOStream(void) { free(buffer); - }; + }; size_t Read(void* pvBuffer, size_t pSize, size_t pCount) { const size_t cnt = std::min(pCount,(size - pos)/pSize); const size_t ofs = pSize*cnt; - + memcpy(pvBuffer, buffer + pos, ofs); pos += ofs; - + return cnt; }; - size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) + size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) { return 0; }; - + aiReturn Seek(size_t pOffset, aiOrigin pOrigin) { if (aiOrigin_SET == pOrigin) { @@ -499,40 +499,40 @@ public: } return AI_SUCCESS; }; - + size_t Tell(void) const { return pos; }; - + size_t FileSize() const { return size; }; - + void Flush() {}; - - + + jobject javaObject() { return jIOStream; }; - - + + }; - + class JavaIOSystem : public Assimp::IOSystem { private: JNIEnv* mJniEnv; jobject& mJavaIOSystem; - + public: JavaIOSystem(JNIEnv* env, jobject& javaIOSystem) : mJniEnv(env), mJavaIOSystem(javaIOSystem) {}; - + bool Exists( const char* pFile) const { jvalue params[1]; @@ -544,27 +544,27 @@ class JavaIOSystem : public Assimp::IOSystem { { return (char) callc(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "getOsSeparator", "()C"); }; - + Assimp::IOStream* Open(const char* pFile,const char* pMode = "rb") { jvalue params[2]; params[0].l = mJniEnv->NewStringUTF(pFile); params[1].l = mJniEnv->NewStringUTF(pMode); - - + + jobject jStream = callo(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "open", "(Ljava/lang/String;Ljava/lang/String;)Ljassimp/AiIOStream;", params); if(NULL == jStream) { lprintf("NULL object from AiIOSystem.open\n"); return NULL; } - + size_t size = calli(mJniEnv, jStream, "jassimp/AiIOStream", "getFileSize", "()I"); lprintf("Model file size is %d\n", size); - + char* buffer = (char*)malloc(size); jobject javaBuffer = mJniEnv->NewDirectByteBuffer(buffer, size); - + jvalue readParams[1]; readParams[0].l = javaBuffer; if(call(mJniEnv, jStream, "jassimp/AiIOStream", "read", "(Ljava/nio/ByteBuffer;)Z", readParams)) @@ -581,28 +581,28 @@ class JavaIOSystem : public Assimp::IOSystem { }; void Close( Assimp::IOStream* pFile) { - + jvalue params[1]; params[0].l = ((JavaIOStream*) pFile)->javaObject(); callv(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "close", "(Ljassimp/AiIOStream;)V", params); delete pFile; }; - - + + }; class JavaProgressHandler : public Assimp::ProgressHandler { private: JNIEnv* mJniEnv; jobject& mJavaProgressHandler; - + public: JavaProgressHandler(JNIEnv* env, jobject& javaProgressHandler) : mJniEnv(env), mJavaProgressHandler(javaProgressHandler) {}; - + bool Update(float percentage) { jvalue params[1]; @@ -623,12 +623,12 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) jobject jMesh = NULL; SmartLocalRef refMesh(env, jMesh); - if (!createInstance(env, "jassimp/AiMesh", jMesh)) + if (!createInstance(env, "jassimp/AiMesh", jMesh)) { return false; } - + /* add mesh to m_meshes java.util.List */ jobject jMeshes = NULL; SmartLocalRef refMeshes(env, jMeshes); @@ -671,7 +671,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) /* determine face buffer size */ bool isPureTriangle = cMesh->mPrimitiveTypes == aiPrimitiveType_TRIANGLE; size_t faceBufferSize; - if (isPureTriangle) + if (isPureTriangle) { faceBufferSize = cMesh->mNumFaces * 3 * sizeof(unsigned int); } @@ -715,7 +715,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) /* push face data to java */ if (cMesh->mNumFaces > 0) { - if (isPureTriangle) + if (isPureTriangle) { char* faceBuffer = (char*) malloc(faceBufferSize); @@ -729,7 +729,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) free(faceBuffer); - if (!res) + if (!res) { lprintf("could not copy face data\n"); return false; @@ -750,7 +750,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) memcpy(faceBuffer + faceBufferPos, cMesh->mFaces[face].mIndices, faceDataSize); faceBufferPos += faceDataSize; } - + if (faceBufferPos != faceBufferSize) { /* this should really not happen */ @@ -766,7 +766,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) free(faceBuffer); free(offsetBuffer); - if (!res) + if (!res) { lprintf("could not copy face data\n"); return false; @@ -933,7 +933,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) jobject jBone; SmartLocalRef refBone(env, jBone); - if (!createInstance(env, "jassimp/AiBone", jBone)) + if (!createInstance(env, "jassimp/AiBone", jBone)) { return false; } @@ -988,7 +988,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) wrapParams[0].l = jMatrixArr; jobject jMatrix; SmartLocalRef refMatrix(env, jMatrix); - + if (!callStaticObject(env, "jassimp/Jassimp", "wrapMatrix", "([F)Ljava/lang/Object;", wrapParams, jMatrix)) { return false; @@ -1012,7 +1012,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) { return false; } - + if (!setFloatField(env, jBoneWeight, "m_weight", cBone->mWeights[w].mWeight)) { return false; @@ -1228,7 +1228,7 @@ static bool loadSceneNode(JNIEnv *env, const aiNode *cNode, jobject parent, jobj wrapNodeParams[3].l = jNodeName; jobject jNode; if (!callStaticObject(env, "jassimp/Jassimp", "wrapSceneNode", - "(Ljava/lang/Object;Ljava/lang/Object;[ILjava/lang/String;)Ljava/lang/Object;", wrapNodeParams, jNode)) + "(Ljava/lang/Object;Ljava/lang/Object;[ILjava/lang/String;)Ljava/lang/Object;", wrapNodeParams, jNode)) { return false; } @@ -1287,7 +1287,7 @@ static bool loadSceneGraph(JNIEnv *env, const aiScene* cScene, jobject& jScene) } -static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) { for (unsigned int m = 0; m < cScene->mNumMaterials; m++) { @@ -1320,7 +1320,7 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } /* set texture numbers */ - for (int ttInd = aiTextureType_DIFFUSE; ttInd < aiTextureType_UNKNOWN; ttInd++) + for (int ttInd = aiTextureType_DIFFUSE; ttInd < aiTextureType_UNKNOWN; ttInd++) { aiTextureType tt = static_cast(ttInd); @@ -1341,7 +1341,7 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) for (unsigned int p = 0; p < cMaterial->mNumProperties; p++) { - //printf("%s - %u - %u\n", cScene->mMaterials[m]->mProperties[p]->mKey.C_Str(), + //printf("%s - %u - %u\n", cScene->mMaterials[m]->mProperties[p]->mKey.C_Str(), // cScene->mMaterials[m]->mProperties[p]->mSemantic, // cScene->mMaterials[m]->mProperties[p]->mDataLength); @@ -1362,9 +1362,9 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) /* special case conversion for color3 */ - if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && + if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && cProperty->mType == aiPTI_Float && - cProperty->mDataLength == 3 * sizeof(float)) + cProperty->mDataLength == 3 * sizeof(float)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1380,16 +1380,16 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } /* special case conversion for color4 */ - else if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && + else if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && cProperty->mType == aiPTI_Float && - cProperty->mDataLength == 4 * sizeof(float)) + cProperty->mDataLength == 4 * sizeof(float)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1406,13 +1406,13 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else if (cProperty->mType == aiPTI_Float && cProperty->mDataLength == sizeof(float)) + else if (cProperty->mType == aiPTI_Float && cProperty->mDataLength == sizeof(float)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1425,13 +1425,13 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else if (cProperty->mType == aiPTI_Integer && cProperty->mDataLength == sizeof(int)) + else if (cProperty->mType == aiPTI_Integer && cProperty->mDataLength == sizeof(int)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1444,26 +1444,26 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else if (cProperty->mType == aiPTI_String) + else if (cProperty->mType == aiPTI_String) { /* skip length prefix */ jobject jData = env->NewStringUTF(cProperty->mData + 4); SmartLocalRef refData(env, jData); constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else + else { constructorParams[4].i = cProperty->mDataLength; @@ -1521,7 +1521,7 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } -static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) { lprintf("converting %d animations ...\n", cScene->mNumAnimations); @@ -1603,19 +1603,19 @@ static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) } /* copy keys */ - if (!copyBuffer(env, jNodeAnim, "m_posKeys", cNodeAnim->mPositionKeys, + if (!copyBuffer(env, jNodeAnim, "m_posKeys", cNodeAnim->mPositionKeys, cNodeAnim->mNumPositionKeys * sizeof(aiVectorKey))) { return false; } - if (!copyBuffer(env, jNodeAnim, "m_rotKeys", cNodeAnim->mRotationKeys, + if (!copyBuffer(env, jNodeAnim, "m_rotKeys", cNodeAnim->mRotationKeys, cNodeAnim->mNumRotationKeys * sizeof(aiQuatKey))) { return false; } - if (!copyBuffer(env, jNodeAnim, "m_scaleKeys", cNodeAnim->mScalingKeys, + if (!copyBuffer(env, jNodeAnim, "m_scaleKeys", cNodeAnim->mScalingKeys, cNodeAnim->mNumScalingKeys * sizeof(aiVectorKey))) { return false; @@ -1629,7 +1629,7 @@ static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) } -static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) { lprintf("converting %d lights ...\n", cScene->mNumLights); @@ -1712,7 +1712,7 @@ static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) params[9].l = jAmbient; params[10].f = cLight->mAngleInnerCone; params[11].f = cLight->mAngleOuterCone; - + if (!createInstance(env, "jassimp/AiLight", "(Ljava/lang/String;ILjava/lang/Object;Ljava/lang/Object;FFFLjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;FF)V", params, jLight)) { @@ -1736,13 +1736,13 @@ static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) } } - lprintf("converting lights finished ...\n"); + lprintf("converting lights finished ...\n"); return true; } -static bool loadCameras(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadCameras(JNIEnv *env, const aiScene* cScene, jobject& jScene) { lprintf("converting %d cameras ...\n", cScene->mNumCameras); @@ -1799,7 +1799,7 @@ static bool loadCameras(JNIEnv *env, const aiScene* cScene, jobject& jScene) params[5].f = cCamera->mClipPlaneNear; params[6].f = cCamera->mClipPlaneFar; params[7].f = cCamera->mAspect; - + if (!createInstance(env, "jassimp/AiCamera", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;FFFF)V", params, jCamera)) { @@ -1901,25 +1901,25 @@ JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile (JNIEnv *env, jclass jClazz, jstring jFilename, jlong postProcess, jobject ioSystem, jobject progressHandler) { - jobject jScene = NULL; + jobject jScene = NULL; /* convert params */ const char* cFilename = env->GetStringUTFChars(jFilename, NULL); - + Assimp::Importer imp; - + if(ioSystem != NULL) { - imp.SetIOHandler(new JavaIOSystem(env, ioSystem)); + imp.SetIOHandler(new JavaIOSystem(env, ioSystem)); lprintf("Created aiFileIO\n"); } - + if(progressHandler != NULL) { imp.SetProgressHandler(new JavaProgressHandler(env, progressHandler)); } - + lprintf("opening file: %s\n", cFilename); /* do import */ @@ -1931,7 +1931,7 @@ JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile goto error; } - if (!createInstance(env, "jassimp/AiScene", jScene)) + if (!createInstance(env, "jassimp/AiScene", jScene)) { goto error; } diff --git a/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h b/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h index 0e6dfab3c..1803ad122 100644 --- a/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h +++ b/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h @@ -15,7 +15,7 @@ /* workflow: - 1) create a new scene wrapper + 1) create a new scene wrapper 2) populate an array of of meshHelpers for each mesh in the original scene 3) (eventually) create an animator instance 4) scale the asset (needed?) @@ -24,16 +24,16 @@ 5b) create a static vertex buffer 5c) create index buffer 5d) populate the index buffer - 5e) (eventually) gather weights + 5e) (eventually) gather weights */ #define BUFFER_OFFSET(i) ((char *)NULL + (i)) -struct Vertex +struct Vertex { aiVector3D vPosition; aiVector3D vNormal; - + aiColor4D dColorDiffuse; aiVector3D vTangent; aiVector3D vBitangent; @@ -46,33 +46,33 @@ struct Vertex // Helper Class to store GPU related resources from a given aiMesh // Modeled after AssimpView asset helper -@interface MeshHelper : NSObject -{ +@interface MeshHelper : NSObject +{ // Display list ID, this one shots *all drawing* of the mesh. Only ever use this to draw. Booya. GLuint displayList; - + // VAO that encapsulates all VBO drawing state GLuint vao; - + // VBOs GLuint vertexBuffer; GLuint indexBuffer; GLuint normalBuffer; GLuint numIndices; - + // texture GLuint textureID; - - // Material + + // Material aiColor4D diffuseColor; aiColor4D specularColor; aiColor4D ambientColor; aiColor4D emissiveColor; - + GLfloat opacity; GLfloat shininess; GLfloat specularStrength; - + BOOL twoSided; } diff --git a/samples/SimpleAssimpViewX/MyDocument.h b/samples/SimpleAssimpViewX/MyDocument.h index 16745dc3c..67675e306 100644 --- a/samples/SimpleAssimpViewX/MyDocument.h +++ b/samples/SimpleAssimpViewX/MyDocument.h @@ -20,27 +20,27 @@ #import -@interface MyDocument : NSPersistentDocument +@interface MyDocument : NSPersistentDocument { CVDisplayLinkRef _displayLink; NSOpenGLContext* _glContext; NSOpenGLPixelFormat* _glPixelFormat; - + NSView* _view; - + // Assimp Stuff aiScene* _scene; aiVector3D scene_min, scene_max, scene_center; - double normalizedScale; - + double normalizedScale; + // Our array of textures. GLuint *textureIds; - + // only used if we use - NSMutableArray* modelMeshes; + NSMutableArray* modelMeshes; BOOL builtBuffers; - - NSMutableDictionary* textureDictionary; // Array of Dicionaries that map image filenames to textureIds + + NSMutableDictionary* textureDictionary; // Array of Dicionaries that map image filenames to textureIds } @property (retain) IBOutlet NSView* _view; diff --git a/samples/SimpleOpenGL/Sample_SimpleOpenGL.c b/samples/SimpleOpenGL/Sample_SimpleOpenGL.c index bcb109564..7aa306ed4 100644 --- a/samples/SimpleOpenGL/Sample_SimpleOpenGL.c +++ b/samples/SimpleOpenGL/Sample_SimpleOpenGL.c @@ -29,7 +29,7 @@ /* ---------------------------------------------------------------------------- */ inline static void print_run_command(const char* command_name) { - printf("Run '%s %s' for more information.\n", + printf("Run '%s %s' for more information.\n", PROJECT_NAME, command_name); } @@ -43,7 +43,7 @@ inline static void print_error(const char* msg) { /* ---------------------------------------------------------------------------- */ inline static void print_usage() { - static const char* usage_format = + static const char* usage_format = "Usage: " PROJECT_NAME " " DOUBLE_NEW_LINE diff --git a/samples/SimpleTexturedDirectx11/CMakeLists.txt b/samples/SimpleTexturedDirectx11/CMakeLists.txt index 82144caa9..007ada3af 100644 --- a/samples/SimpleTexturedDirectx11/CMakeLists.txt +++ b/samples/SimpleTexturedDirectx11/CMakeLists.txt @@ -24,13 +24,13 @@ LINK_DIRECTORIES( ) ADD_EXECUTABLE( assimp_simpletextureddirectx11 WIN32 - SimpleTexturedDirectx11/Mesh.h + SimpleTexturedDirectx11/Mesh.h SimpleTexturedDirectx11/ModelLoader.cpp SimpleTexturedDirectx11/ModelLoader.h #SimpleTexturedDirectx11/PixelShader.hlsl SimpleTexturedDirectx11/TextureLoader.cpp - SimpleTexturedDirectx11/TextureLoader.h - #SimpleTexturedDirectx11/VertexShader.hlsl + SimpleTexturedDirectx11/TextureLoader.h + #SimpleTexturedDirectx11/VertexShader.hlsl SimpleTexturedDirectx11/main.cpp SimpleTexturedDirectx11/SafeRelease.hpp ${SAMPLES_SHARED_CODE_DIR}/UTFConverter.cpp diff --git a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp index 92760d691..18bb10f1e 100644 --- a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp +++ b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp @@ -1,6 +1,6 @@ #include "ModelLoader.h" -ModelLoader::ModelLoader() : +ModelLoader::ModelLoader() : dev_(nullptr), devcon_(nullptr), meshes_(), diff --git a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp index 01ba343e8..a02c53ca6 100644 --- a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp +++ b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp @@ -140,16 +140,16 @@ static WICConvert g_WICConvert[] = { GUID_WICPixelFormatBlackWhite, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM - { GUID_WICPixelFormat1bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat2bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat4bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat8bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat1bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat2bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat4bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat8bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat2bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM - { GUID_WICPixelFormat4bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + { GUID_WICPixelFormat2bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + { GUID_WICPixelFormat4bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM - { GUID_WICPixelFormat16bppGrayFixedPoint, GUID_WICPixelFormat16bppGrayHalf }, // DXGI_FORMAT_R16_FLOAT - { GUID_WICPixelFormat32bppGrayFixedPoint, GUID_WICPixelFormat32bppGrayFloat }, // DXGI_FORMAT_R32_FLOAT + { GUID_WICPixelFormat16bppGrayFixedPoint, GUID_WICPixelFormat16bppGrayHalf }, // DXGI_FORMAT_R16_FLOAT + { GUID_WICPixelFormat32bppGrayFixedPoint, GUID_WICPixelFormat32bppGrayFloat }, // DXGI_FORMAT_R32_FLOAT #ifdef DXGI_1_2_FORMATS @@ -165,10 +165,10 @@ static WICConvert g_WICConvert[] = { GUID_WICPixelFormat32bppBGR101010, GUID_WICPixelFormat32bppRGBA1010102 }, // DXGI_FORMAT_R10G10B10A2_UNORM - { GUID_WICPixelFormat24bppBGR, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat24bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat32bppPBGRA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat32bppPRGBA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat24bppBGR, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat24bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppPBGRA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppPRGBA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM { GUID_WICPixelFormat48bppRGB, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat48bppBGR, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM @@ -176,21 +176,21 @@ static WICConvert g_WICConvert[] = { GUID_WICPixelFormat64bppPRGBA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat64bppPBGRA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM - { GUID_WICPixelFormat48bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat48bppBGRFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppRGBAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppBGRAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat48bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppBGRFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppBGRAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat96bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppPRGBAFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppRGBFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppRGBAFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat96bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppPRGBAFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBAFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat32bppCMYK, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppCMYK, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM { GUID_WICPixelFormat64bppCMYK, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat40bppCMYKAlpha, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat80bppCMYKAlpha, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM @@ -198,7 +198,7 @@ static WICConvert g_WICConvert[] = #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) { GUID_WICPixelFormat32bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM { GUID_WICPixelFormat64bppRGB, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM - { GUID_WICPixelFormat64bppPRGBAHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppPRGBAHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT #endif // We don't support n-channel formats diff --git a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp index 02e2b6088..3f8d15320 100644 --- a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp +++ b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp @@ -128,7 +128,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, int argc; LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc); if (!argv) { - MessageBox(nullptr, + MessageBox(nullptr, TEXT("An error occured while reading command line arguments."), TEXT("Error!"), MB_ICONERROR | MB_OK); @@ -145,8 +145,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, // Ensure that a model file has been specified. if (argc < 2) { - MessageBox(nullptr, - TEXT("No model file specified. The program will now close."), + MessageBox(nullptr, + TEXT("No model file specified. The program will now close."), TEXT("Error!"), MB_ICONERROR | MB_OK); free_command_line_allocated_memory(); @@ -157,7 +157,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, g_ModelPath = UTFConverter(argv[1]).str(); free_command_line_allocated_memory(); - + WNDCLASSEX wc; MSG msg; @@ -573,7 +573,7 @@ void InitGraphics() HRESULT CompileShaderFromFile(LPCWSTR pFileName, const D3D_SHADER_MACRO* pDefines, LPCSTR pEntryPoint, LPCSTR pShaderModel, ID3DBlob** ppBytecodeBlob) { UINT compileFlags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR; - + #ifdef _DEBUG compileFlags |= D3DCOMPILE_DEBUG; #endif diff --git a/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp b/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp index aa2344118..be0e651f1 100644 --- a/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp +++ b/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp @@ -23,7 +23,7 @@ #endif // _MSC_VER #define STB_IMAGE_IMPLEMENTATION -#include "contrib/stb_image/stb_image.h" +#include "contrib/stb/stb_image.h" #ifdef _MSC_VER #pragma warning(default: 4100) // Enable warning 'unreferenced formal parameter' @@ -579,7 +579,7 @@ void KillGLWindow() // Properly Kill The Window if (!DestroyWindow(g_hWnd)) // Are We Able To Destroy The Window MessageBox(nullptr, TEXT("Could Not Release hWnd."), TEXT("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION); g_hWnd = nullptr; - } + } if (g_hInstance) { @@ -727,7 +727,7 @@ BOOL CreateGLWindow(const char* title, int width, int height, int bits, bool ful return FALSE; } - if (nullptr == (hRC=wglCreateContext(hDC))) + if (nullptr == (hRC=wglCreateContext(hDC))) { abortGLInit("Can't Create A GL Rendering Context."); return FALSE; diff --git a/test/models-nonbsd/3D/mar_rifle.source.txt b/test/models-nonbsd/3D/mar_rifle.source.txt index c0cd5fe6d..d7183b7b6 100644 --- a/test/models-nonbsd/3D/mar_rifle.source.txt +++ b/test/models-nonbsd/3D/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/3DS/cart_wheel.source.txt b/test/models-nonbsd/3DS/cart_wheel.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/3DS/cart_wheel.source.txt +++ b/test/models-nonbsd/3DS/cart_wheel.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/3DS/mar_rifle.source.txt b/test/models-nonbsd/3DS/mar_rifle.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/3DS/mar_rifle.source.txt +++ b/test/models-nonbsd/3DS/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/3DS/mp5_sil.source.txt b/test/models-nonbsd/3DS/mp5_sil.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/3DS/mp5_sil.source.txt +++ b/test/models-nonbsd/3DS/mp5_sil.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/ASE/Rifle.source.txt b/test/models-nonbsd/ASE/Rifle.source.txt index 1b96f8564..802c57045 100644 --- a/test/models-nonbsd/ASE/Rifle.source.txt +++ b/test/models-nonbsd/ASE/Rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/ASE/Rifle2.source.txt b/test/models-nonbsd/ASE/Rifle2.source.txt index 3fa628db4..7b50d1645 100644 --- a/test/models-nonbsd/ASE/Rifle2.source.txt +++ b/test/models-nonbsd/ASE/Rifle2.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/BLEND/fleurOptonl.source.txt b/test/models-nonbsd/BLEND/fleurOptonl.source.txt index b9c58b5d9..d4c583620 100644 --- a/test/models-nonbsd/BLEND/fleurOptonl.source.txt +++ b/test/models-nonbsd/BLEND/fleurOptonl.source.txt @@ -10,7 +10,7 @@ Puoi utilizzarlo liberamente, modificarlo e migliorarlo. ************* Ma attenzione!! ********************* Nel modificare e utilizzare i modelli, ricorda comunque sempre che : -"il diritto morale all'integrità dell'opera (diritto dell'autore originale) non ti consente di apportare all'opera deformazioni o modificazioni, od ogni altro atto a danno dell'opera stessa, che possano essere di pregiudizio all'onore o alla reputazione dell'autore (la valutazione della lesione dell'onore o della reputazione avviene sulla base di elementi psicologici soggettivi)" +"il diritto morale all'integrità dell'opera (diritto dell'autore originale) non ti consente di apportare all'opera deformazioni o modificazioni, od ogni altro atto a danno dell'opera stessa, che possano essere di pregiudizio all'onore o alla reputazione dell'autore (la valutazione della lesione dell'onore o della reputazione avviene sulla base di elementi psicologici soggettivi)" (dalle faq di Creative Commons Italia) http://www.creativecommons.it/node/165#27 @@ -26,7 +26,7 @@ In particolare, sara' da me considerata lesione d'onore l'uso e/o l'adattamento Se lo fate rischiate la denuncia e il risarcimento danni. Per qualsiasi chiarimento in proposito potete comunque scrivermi. -Questo e' un diritto garantito per legge a me come ad ogni altro artista. +Questo e' un diritto garantito per legge a me come ad ogni altro artista. L'utilizzo della Creative Commons non influisce su questo diritto. ************************************************ @@ -45,7 +45,7 @@ work, so you are not allowed to do exactely what you want with my models. The author (that is me) has the right to prevent distortion, mutilation, or other modification of his work which would be prejudicial to his or her honor or reputation. Me, i consider to be prejudicial to my honor, the modification and/or the use of my models in projects that are related to: - + Racism and hatred instigation. War promotion. Cruelty toward animals. @@ -69,15 +69,15 @@ Note Questo e' stato il mio secondo modello completo. Per questo motivo potrebbe contenere errori e imprecisioni. - + Al momento, questi sono i difetti che ho notato: - + - Non e' nell'origine degli assi. - lo scheletro ha il centro spostato di lato - Nel texture sheet c'e' spazio sprecato. - - + + ################### Notes @@ -86,7 +86,7 @@ This was my first complete model, so it probably contains something wrong. At the moment, this is what i noticed: - + - She's not in the origin of axis - Armature's center is not in armature's center. - Texture sheet contains wasted space. diff --git a/test/models-nonbsd/DXF/rifle.source.txt b/test/models-nonbsd/DXF/rifle.source.txt index a2585afad..fa25f10db 100644 --- a/test/models-nonbsd/DXF/rifle.source.txt +++ b/test/models-nonbsd/DXF/rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt b/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt b/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt index 94ee48e4a..7c81e612f 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt @@ -1,9 +1,9 @@ From kwxport http://www.kwxport.org/ ->> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt b/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt b/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt b/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt b/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt index 94ee48e4a..7c81e612f 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt @@ -1,9 +1,9 @@ From kwxport http://www.kwxport.org/ ->> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt b/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt b/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt b/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt index aaa244217..80afd30a7 100644 --- a/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt +++ b/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt @@ -1,10 +1,10 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas -"These 3d models are contributed by John Hoffman and are based on -characters from a cartoon show called "Jayce and the wheel warriors" +"These 3d models are contributed by John Hoffman and are based on +characters from a cartoon show called "Jayce and the wheel warriors" (except the marauder) John's site: http://www3.sympatico.ca/john.hoffman" ===================================================================== @@ -15,9 +15,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/LWO/LWO2/rifle.source.txt b/test/models-nonbsd/LWO/LWO2/rifle.source.txt index 5523bbc0c..5774ecc0e 100644 --- a/test/models-nonbsd/LWO/LWO2/rifle.source.txt +++ b/test/models-nonbsd/LWO/LWO2/rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/MD2/source.txt b/test/models-nonbsd/MD2/source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/MD2/source.txt +++ b/test/models-nonbsd/MD2/source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/MD5/BoarMan.source.txt b/test/models-nonbsd/MD5/BoarMan.source.txt index 50b2dfb53..69f0c79a2 100644 --- a/test/models-nonbsd/MD5/BoarMan.source.txt +++ b/test/models-nonbsd/MD5/BoarMan.source.txt @@ -1,8 +1,8 @@ -License: Creative Commons +License: Creative Commons - Remix - Share alike - Attribution Author: zphr (Christian Lenke) - + diff --git a/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt b/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt index 2febb0583..84ce9fd95 100644 --- a/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt +++ b/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt @@ -4,9 +4,9 @@ Version : 1 Date : 11/05/97 Author : Kenneth Whelan Email : JWHELAN@pop.prodigy.net -Credits : id software, Larry Hama, Steven Polge, and Rene Post for making Quake ME - - +Credits : id software, Larry Hama, Steven Polge, and Rene Post for making Quake ME + + Build time: ??? Time??? @@ -14,7 +14,7 @@ Type of Mod ----------- Quake C : no Sound : no -MDL : Yes +MDL : Yes Format of QuakeC (if a Quake C Mod) @@ -29,8 +29,8 @@ Description of the Modification ------------------------------- This is a new player.mdl for quake. It's main use is for bots. The Skins are Snake Eyes v4, Duke v3, Low-Light, -Storm Shadow v2, Shockwave, Repeater, Gung-Ho, Shipwreck, Dusty v3, and -Tunnel Rat v2. +Storm Shadow v2, Shockwave, Repeater, Gung-Ho, Shipwreck, Dusty v3, and +Tunnel Rat v2. @@ -40,7 +40,7 @@ None that I know of. How to Install the Modification ------------------------------- -First back up the current player.mdl(copy player.mdl player.bak). Just put +First back up the current player.mdl(copy player.mdl player.bak). Just put it in the progs dir in the hack your using, if any. Technical Details diff --git a/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt b/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt index c07c2f126..cbddccb7e 100644 --- a/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt +++ b/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt @@ -16,17 +16,17 @@ E-mail: sgalbrai@linknet.kitsap.lib.wa.us WWW: www.oz.net/~simitar -This model can be used or modified for any purpose +This model can be used or modified for any purpose as long as this text document is included with it -and all modellers listed in this document are +and all modellers listed in this document are listed wherever credits are appropriate for that purpose. -Help Wanted: +Help Wanted: The Free Models Project can use help with -models and in other areas, such as legal boilerplate +models and in other areas, such as legal boilerplate for models. WWW: www.oz.net/~simitar/model.html diff --git a/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt b/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt index dc0149b01..3b3dd9042 100644 --- a/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt +++ b/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt @@ -8,7 +8,7 @@ that the FMP is a great concept !! There is some movement in the model. The legs can be animated for walking, stomping, or running -around ! +around ! Ok, it's my first model, so I will work on less polygony in the future ;-) @@ -18,9 +18,9 @@ Contact: ebuy@optelnow.net (E-MAIL) Date Created: 7/07/2000 ====================================================== -This model can be used or modified for any purpose +This model can be used or modified for any purpose as long as this text document is included with it -and all modelers listed in this document are +and all modelers listed in this document are listed wherever credits are appropriate for that purpose. diff --git a/test/models-nonbsd/NFF/NFFSense8/credits.txt b/test/models-nonbsd/NFF/NFFSense8/credits.txt index f3cef4d09..bad7fbf15 100644 --- a/test/models-nonbsd/NFF/NFFSense8/credits.txt +++ b/test/models-nonbsd/NFF/NFFSense8/credits.txt @@ -1,4 +1,4 @@ teapot.nff, home4.nff - http://www.martinreddy.net/ukvrsig/wtk.html -cokecan.nff -www.vrupl.evl.uic.edu/Eng591_Pages/cokecan.nff +cokecan.nff -www.vrupl.evl.uic.edu/Eng591_Pages/cokecan.nff TODO: License status to be confirmed diff --git a/test/models-nonbsd/OBJ/rifle.source.txt b/test/models-nonbsd/OBJ/rifle.source.txt index 1d2cec5cf..f7b93fd0f 100644 --- a/test/models-nonbsd/OBJ/rifle.source.txt +++ b/test/models-nonbsd/OBJ/rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notices found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models/3DS/UVTransformTest/note.txt b/test/models/3DS/UVTransformTest/note.txt index 4c8bfedd2..9a6bab030 100644 --- a/test/models/3DS/UVTransformTest/note.txt +++ b/test/models/3DS/UVTransformTest/note.txt @@ -1,5 +1,5 @@ -All 'mirror' files are not absolutely correct. That's mainly -because it's difficult convert Max' handling of mirroring to +All 'mirror' files are not absolutely correct. That's mainly +because it's difficult convert Max' handling of mirroring to our's. In other words: TO DO, but only if someone REALLY needs it. diff --git a/test/models/ASE/MotionCaptureROM.source.txt b/test/models/ASE/MotionCaptureROM.source.txt index 2b961906a..f34e056ef 100644 --- a/test/models/ASE/MotionCaptureROM.source.txt +++ b/test/models/ASE/MotionCaptureROM.source.txt @@ -1,3 +1,3 @@ -"MotionCaptureROM.ase" - Free for any purpose. +"MotionCaptureROM.ase" - Free for any purpose. NOTE: The errors in the middle of the animation are caused by problems during recording, it's not an importer issue. diff --git a/test/models/Collada/human.zae b/test/models/Collada/human.zae new file mode 100644 index 0000000000000000000000000000000000000000..691b09f83f6ac2baf2d6bb55026da9b95470c536 GIT binary patch literal 1093924 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

    uuGR&r@9o{lX?*e|=%DZ$(yMTN$V&+tmT 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

    *MMruzAc9`aeV4mOVXe!g)p!=rmE|XzS3l&x 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 z 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 |%F(BJFHCq3pF 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

    YBvu9UVk?n=SJ=$+2ziatfpq&?O74_ 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

    Qf((sXQc*MjpgC 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≫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�{S+3fZ-hMfu{#Z(yVPGkh*hYRfc)(cg0-9cef(Gw5tG+NE2Qh^& ztn7IO9SF_lhQ#^Xu-4lA@w?1C*fm)?-Ih3ADgxl+02u&d5~R(=gJvn-eXBY|0#nL|?fEGq~nPBe0rc`&HSHokHof$WLnlDMjPa#5ehL_B8LV08NZr_N3B&PWXOT zw3X1Xd?+5>uv_$}SqaF?hW8_oK=r>_9Ax@4DX5U(e5~&-N?Dn{;Q`p$0^TUY#f&)$M0k18G&xvcFlzz!Te5_JRgsx&DG9es5B))aLSM zY+}Tn@M@1ArC8+B*AM5Knh6^hm(rW2Vld8%J&W?Fhf(jDY4|&L?=6ToG3YC&@1D?Q zlEmjDM6Xbbmq7FhezYvE)P3y7i;32~SpRIm_64h9(v3v!ro)2fE?Lj&J}BMp8(;0Fxz=oe zF;kA`r|B*+;-hm_KDbUC*vD!_)QKet?9ddMabk*e2e*zyZXafUA+z0LsAYxY)RH5? zpCZ_gngRlS<4m5z2|1syb9YBCs2tl(m*R&~o%*JV3(mOIab?eHO@8FT$-o%I z$sh(*%mCI_3pA`GwW6f7D79EWsVKiZFFv(0Rj(i~J;0ll4WyI_2&;kg> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models/IRR/warn_dwarf_scaling_is_intended.txt b/test/models/IRR/warn_dwarf_scaling_is_intended.txt index f651a8643..d0e4ea361 100644 --- a/test/models/IRR/warn_dwarf_scaling_is_intended.txt +++ b/test/models/IRR/warn_dwarf_scaling_is_intended.txt @@ -1,3 +1,3 @@ for dawfInCellar_ChildOfCellar & dawfInCellar_SameHierarchy: -the strange scalings of cellar and dwarf are intended. +the strange scalings of cellar and dwarf are intended. diff --git a/test/models/MD2/faerie-source.txt b/test/models/MD2/faerie-source.txt index 4906ff4d1..afb00a170 100644 --- a/test/models/MD2/faerie-source.txt +++ b/test/models/MD2/faerie-source.txt @@ -1,5 +1,5 @@ -From IRRLICHT/media +From IRRLICHT/media The Irrlicht Engine License diff --git a/test/models/MD2/sidney-source.txt b/test/models/MD2/sidney-source.txt index 4906ff4d1..afb00a170 100644 --- a/test/models/MD2/sidney-source.txt +++ b/test/models/MD2/sidney-source.txt @@ -1,5 +1,5 @@ -From IRRLICHT/media +From IRRLICHT/media The Irrlicht Engine License diff --git a/test/models/Q3D/E-AT-AT.source.txt b/test/models/Q3D/E-AT-AT.source.txt index 2df8826f1..900ee552c 100644 --- a/test/models/Q3D/E-AT-AT.source.txt +++ b/test/models/Q3D/E-AT-AT.source.txt @@ -5,4 +5,4 @@ Downloaded 4th November 08 (Obama ftw!) Copyright notice found on the page: Where do the models in the archive come from? -All 3D files available from the3darchive.com are from the public domain. +All 3D files available from the3darchive.com are from the public domain. diff --git a/test/models/Q3D/earth.source.txt b/test/models/Q3D/earth.source.txt index 2df8826f1..900ee552c 100644 --- a/test/models/Q3D/earth.source.txt +++ b/test/models/Q3D/earth.source.txt @@ -5,4 +5,4 @@ Downloaded 4th November 08 (Obama ftw!) Copyright notice found on the page: Where do the models in the archive come from? -All 3D files available from the3darchive.com are from the public domain. +All 3D files available from the3darchive.com are from the public domain. diff --git a/test/models/WRL/credits.txt b/test/models/WRL/credits.txt index 055f73734..7be7fa192 100644 --- a/test/models/WRL/credits.txt +++ b/test/models/WRL/credits.txt @@ -1,2 +1,2 @@ "MotionCaptureROM.ase" Recorded using ViconIQ. -Converted to VRML with 3DS Max 2008. +Converted to VRML with 3DS Max 2008. diff --git a/test/models/X/anim_test.txt b/test/models/X/anim_test.txt index 3d270dc7d..f2ef6c056 100644 --- a/test/models/X/anim_test.txt +++ b/test/models/X/anim_test.txt @@ -4,6 +4,6 @@ Frame 1 - 10: Zylinder knickt ein, so dass der Knick in Richtung z+ zeigt. Frame 10 - 18: Zylinder-Spitze streckt sich in Richtung z-. Frame 18 - 24: Zylinder-Spitze bewegt sich zu Position in Richtung x+ -Remarks: The exporter failed here for some reasons... although the mesh referres to four bones, only two of them are stored in the corresponding node hierarchy. So you have a mesh with 4 bones, a hirarchy with 2 nodes and a animation that affects only those two nodes. +Remarks: The exporter failed here for some reasons... although the mesh referres to four bones, only two of them are stored in the corresponding node hierarchy. So you have a mesh with 4 bones, a hirarchy with 2 nodes and a animation that affects only those two nodes. There is no timing given for the animation. You have to scale the animation manually. For this file, the timing seems to be 24 ticks per second. diff --git a/test/models/X/kwxport_test_cubewithvcolors.source.txt b/test/models/X/kwxport_test_cubewithvcolors.source.txt index 94ee48e4a..7c81e612f 100644 --- a/test/models/X/kwxport_test_cubewithvcolors.source.txt +++ b/test/models/X/kwxport_test_cubewithvcolors.source.txt @@ -1,9 +1,9 @@ From kwxport http://www.kwxport.org/ ->> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models/X/test.txt b/test/models/X/test.txt index eaf9d9c3c..9452855b3 100644 --- a/test/models/X/test.txt +++ b/test/models/X/test.txt @@ -1,3 +1,3 @@ Simple textured test cube exported from Maya. Has a texture that does label each cube side uniquely, but the sides do not match DirectX coordinate space. -Is not readable using D3DXLoadFrameHierarchy, needs custom text parsing. +Is not readable using D3DXLoadFrameHierarchy, needs custom text parsing. diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png b/test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png new file mode 100644 index 0000000000000000000000000000000000000000..d47f2f5e1f06116fca5ddd9697001ffedcc5bd68 GIT binary patch literal 10270 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 literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin new file mode 100644 index 0000000000000000000000000000000000000000..e7c6a93a4995917f931d580f7d25c94f33d1639a GIT binary patch literal 50328 zcma%@2Xqxh^#3;^D7|-tASHmbKtLebnS@@X7m*^NNJl`BCSZ_W4bpoMK@bI`NXwf^ zq$?;$w-J;gO+^Je{O@O-{0{$|^Dmq;^WB+!Gxy%l+xOn?%nhEGU_CEEzxDB}p_(;E zSSi}`!fT-E$DIE^yoT4y`2TtRqsPHKGEVybo8KMVM{K3P{!zRCGv}kPXAatKEWV|W z{h#AUU*nF`G+d z%H+de_(xkdwQUwvv$qDH^8JZz?If?XJyt4Z$M?(H*oYG0w%?Ew{?sL%?EJ_)wq4GB zJ9_QwXkYG-+qV4auwTDTXWM?rzoy&e4|kj!|GXW0_-`|zhViG#>!*(X(zLw&?v5Wn z?`WU@;exp{XGbvqhKqYlvBZ@-${%cF$Go)Lbm{l5zd-za`P*e?YE0seva_4o#OX^- zqq@s|{B1vEf;l~K@{Uhm_3iY3#+lAj=J>?)@0V>%gBnA2l)GEamU*|8IlFp-pCG<> zO0H&#&hD_I`WxZ4(19xEz52cVMDe}$^To~fFZ>-Jr{uPOEhuWH{@&cj_xd9Xm_N9wJMKh{Hfabof)Y?Xc%mk#g;AS)0Sw{;QE%{CG$E_u2cVQd3_Q zc&)u%tIn9cTWhEfmb9|da_lfqEi13a&1!68rY$rLUn{D%za4E$ZyRCiozJdTwy0_! z=4oo;`~KsPezKH}`Z~gFt@NGWe`qedxK#l&`rl*z=q7)eSru}bg~xaLmDij%Q#TYe z158OdP~REW}5g#7Nx4Pweo33ryeyFId43jxdBPd|?Z3xWoL! zceN~KL9UhRvjTCpQ@09sN3IpB)|6&;Y?h+-N|B}NiEmrk{9~T9O`9)JtM9b2**E@Z zDts|V?OEO0-k5saJoEDuRrNu0JLBSJv*-KKYI@xUw$qilW?+v#s>Z51w#@Q=W@!F4 zDslcZ_VWt9dG$!N`eShkTf0z66Vaf8Iue`1=2@8C%xGLl4eD{^jn5s7~`Nvx=G`sgVGQZ_Jp?WO_8Ro6ZZ?3!!&?D{v3s(n8-wTEXuX4_Z(SUpz1 zg+1Nody{khUe&E(b6fLNs@WI2RZaIA+j`%wHH%+br^Y5JTW|Xm(_qG8l`=TWcD>Na zw2zpn_8*I|uVt-j4$T>@_SGzEV`t_u;a_xDs$N!m=GOXk8D%+G%oGmkILuWZ&tvntXze;>=P&ObfSP})aiRrGN#V?W~< z&sgSTUgpFO_OOM0{J;nN!asb&U;M^r;vgPkAwJ?HM&c%ZVuuG@U;-z2!3utGgdtqv z3tM=@9p=-LA}mD>{#X5QqlO(8SezxgUopu;_Npz#tJ%B@ zf%Z*ufsQu#X@3fM58BZ}^Mf_)HwcLoCEcoWw}n#82$-fD26E1TR>@4~{T| zD|}%KZ@9yJUDlG8(xp>DZF^O;P2S66C-y9$e`{33);Stx+s?_W&vmV158nFTZ2u*P zp3*VMdS`!bd#3H#Q18drs%D2gTm8BHp{$?1Dc99y^Z1y3 zp$oecRO891X5=?tgoeJ~UoDqwFZOVl`K?hWHNMysQ@DLO6MMJ0n!3B4p`3WCiK35l z8T%Q>c*Zgx^D-xPu!k+|;|D(A7yjWJ{^B=269@4S3-J*rF%mcN6FWTM0uwmF3s&%h zBMjjRU)aJM?l6zIlh0CKm{v)@*14q3l$6<~yj)SQA0A;(UB6?t)hMsaU8-Vx{qmXl z_ing0JtJ+i#I2@7T2X!NT%>JsKheZC$g4}$lk4T}{^s?ePv~OnE85>j)iM)i|EUhP zDs2bFXEVpUf3Hf@ug8YEJ+WJj z8Ggc4|D|nc&*^na72Rsu)tVWamv^D+ek0M$-EBkHyUtK+3ida)W@dA*+$0rMv!Wtx^vSKXS$mY7gJv@i2t>R$feO`d$8CqI`pO-&s0jTw{s{p6m7 zj;gfjspf~~SCf|(*`faJ@Qz7p8WS4&?OIjf;WRTS*V52Sc^9e4EjyaV=dXnNRhz3q zhr$gd)3jNNKF($AXB^`h%Y4ksoY=u0wy=*M_<&#dhi~|c-}p=%#6v8^N1Vh++{91p z@PG?U-~=yN!4Hlwge!bu3val?{AT27L)rU!4_z$rrm0kFuW6X~MSZIM1M__6Rp$58 zFX-0aWU(Lpm|$kyZKo3o<*+M%ZDv0FpqVcGb}rkdXhE~Qx6)7c%VmFi|C3O=HzM`Q ze%bABnO+Gc&M2c>rDnD>I$TaZ^{{|`>G{8n_ssVF`A28b8*X1S3A0Z`C;j!aipl=D zNyv9AI!~SRs?h44CS`c7{ok!iQB^lBHBHZil53UNqKdsf#;gwS66*YXl6v)StoWH6 zGG8xIzkHg*P)|qQ0_<;}jg@5>lzxa*M#6dj7LVUzY zjKod+#10R*zywb4f))JW2t&BS7q;+*JIqJd*kLHI<{qL;9{R*o?Xb)ooHa<-E_=z0 zX*=Gm%-&CTp8k{hW>g~+V|wW3E&n#vqw<(>H#_Q*VPW>`N}*7eyUlcw?P2zZY|TTP zo5bkEmmZpvUv^18HL{9+r^ue=;$-+OF3(1-M<7;y1rSG3LS3WG`Us?80 zsdd)GzFowx+T@D*`t%0V`QD+JMjhlHuf`nX^)H+U&`)!2iB~LEU}g0rU0z z<^F`rnf0u`Tg@*G=K2%L{G|LAOU(xTyni*vIkm9VMDttfS21CuQq{T6%}jjE?ftzf zZdYTQ6)?S5Hww)PZB+N~r-mpM-dwNf<6Opm#xb6;%*VXUi5=`=3;Xzi5BP zntvC(t{Z1tX=+B_4&B^7PPd!0#k>$oY~e~`bk#dG?(%qPswa$EcywPW zdD~3QHrM?7o3rWi6IPo^H%|I>xBsqkRG4WNSKjC!-}Q~!`Ef5Zu0vP9S+Aq&cDd>% zYR0^nH)ifp@#p^tjcav0x$BWF>f_Q2LX@+wZC3PgE@MCA7|&SdV_xRO4)(Bxef+=& z{KCJ$H}Mz0@tHV?hggV@IEj(CiJ#cv0T-CS30|;*9~@x_SNOsf-f)Nc^>zgerP-;; zdi}|UX4W?cLJ@zyp?{n;+}v!fLb=8!=x_VTcSWIA^(vnpqhFi4((D;h%kO)2h~6+` zqeX=Rx%A0AbIstE*;V~-|5dF@jWCtxUGwwBe6Q}$Z)|pF-{4P+ zKcTKwD`cWdRP#4I9a7J}cO>LRkJwlL=^ZNVK%)?4cj{I}ALlamGmi0$Wj^L*PV8V0 zTiC}Be84aK!#DiJZ+s>W;vp8|BTix@ZsI3)c)$fFaDo@C;0H$-!WF)-g*V(`zB=2Y z5M^)5R9*6q`=L>%4ki!0Iz{hWSNItu5aimrQ4g8x99jh$Boy&4D3PU;H)L)))V#ByZamHgL9XdlD36v7d2_XDstE zFLPoCd)UH0e&7Rs;UB)?FMi`QaS#u&5Fc?8BXJWyvBLu{Fo6@iUP_rzyBW0{Y6nG-wM!xr}O10V1U|L_fe z@f)9sgLsIA_=uAjWj#z0Ke59DE---;ykG@CIKmLF@P#eB;STdw*%tW}J{R>)9*jBp z`+a|qyhq;YkCPXytf+3wdw1>joKTO_?Nu*%PfOpp6go3}l$s&$^|YA$ra{q}s<&JN zb$_jDp563T@VR*M$@*s3tR-rrTr+iT3-i(3C90)dOSSj5FeSIYrGAlXEc$+&*)3~q zTzl=#RWesiczdsRlR)sC|+g*JXsQE?5266S;|4*A`mF4y+5e^ZjX zPFdv7l+Q)~J%?i`E#-4TALlamGmi0$Wj^L*PV8V0TiC}Be84aK3qBX(FMi`QaS#u& z5Fc?8BXJWyp9^@v1txHU7p&k1M;O8tzOaQi++m*5u7IMnkl%}#Zw~kqPc~E)<-4m@ zYvmuFHC*ME?=&yE)xP6>=BR4&UDx#GTA=|`SE~N<9e7~>#?Yb}8&w{c=3NW^iP? z$i2jS<+g-cWj>*@%RNSsC1*l&n;cbVv zqfc0^>c~CJo0T_)a#fhAK9zf$mpgO~ee`iJ)l}|z!e`7&{;6Db#l6s5t&Yd^J^zQ# z{nC-r3w+8MxsRfca~b;?$9TpvAM-LNcCd#n?BfSM;1~Yk8~)-qJ`)G=5DW1UCovK? z@e?~d-~tmk!3$RKgCh*#3SZd58}2ZV89QH5_RI6d_;-e?stq=&*>bO2a#1~%wXsoa zSyLJ5A~RMKyeR$v%%cZoW5Jt zc)7P z?xjS?^96mJ%h=C2#xs`rn3p-RgFS3vA3yK`zwi&=@E5=FnK+1tScs1}iIKR8pV;96 z7nr~aUa*269AOAo_`(+6aEE!{8aotaygYY5)nS>+dFT`Mi97>#ZaZF;FMCP7EzgFb zQL-;M{U=plo*DB+231tS^Z*{m^WT} zsLshVX`A2sB`>XVU$vKK)e50Zp#p_}P$%UXw(|Bep?UA0RX63?HsS3eq1x-LijrsE zllKlKpFO=nwUB4wgaOO;CDfRsj>t3e!WI9-^qtjHJt@!5jn^gmQ))h=_Q-Shw|9Q@ zDedLCn?BBE>}MR~8Owak%beK39=5QLANYV@_=j)!i{JQ69K=H`#7CUONZiCv?C^jK zOyC4BSiujDFoY|7VGD1#!~Cnr(~9yj>rtils%(ijRZCeLY0`O>N^So@J(M+*jXx%+ zrr%`IiL6Kc+Dwftltb^8HI}HN1y!E6bLqEb?d9P6pZJgU%cWnCHJOE(Uhxbl&YP|nMG z6n&h_*v~k|GnV<7mpQS6J#1kgKkxy+@DJbc7r*hDIEaT>h%bm!VkB?UvjineKBeb%{YB|(d3C(3rH4&8tNfVax}U7Ehqv6VrsQ~9FOaqO ztG_K(ISQ256J$+(yFXf0e-vW7qCL>{$!T)4g^Yx~)LIpyE_ zt%!b6*8DddSmd92m`h)ky?}W8q96b7V|umh5$t(A)}L_Xrs^wu2i`Aj{a1#cP^q$~ z5L0WWU#I9+wOIBV#_qO$=NpOYC)tD8T{E*IO8OM0W zG9U9YCw8!hE$rh5KHwMr;T!(qH$D>w@em8~5hpPcH}MlYJm3NoIKc~6@Pi`^;R;{a z!W-@|55JQyD1BsK@1Hf9^~la8^`|da)CaELQLBeX=>4+y)ajScRH;i<^pn4YYd>+T z+S4;qzbJcE(f1Qo+PO&Gyg^=_{C0m;v|cqmQ}(vLA5}{gTwhVolRd8&;r(Z1m$bl*Q1Yf8T%Q>c*Zgx z^D-xPu!k+|;|D(A7yjWJ{^B=269@4S3-J*rF%mcN6FWTM0uwmF3s&%hBMjjRU)aJM z?l6BRYssK=mi_Lg@8!`cy(;SQviDl|Xqdj&sD^GWd$NabeXq`Ut)sWeUhVl+2bApL z>NMHI9eZV+@>)gfd9t^g`ukM&ymgf9`BwR-qw0S+QfHODVA&&P&s%>gd&Hv;W@67< z&yl_33vJK(>-Xo@Js)4IqMqCDH$UX*CuFbr$uax`kk|vNtVz z)=KuQ)m_=MF4Ddnd)~_bZ@-&8Z}q9{chkqYjQxybJY$)Ud6^SC*uxg~@dF?53;*y9 zfAJfiiGz5Eh4_e*7>S$si5(trfeD=81uOW$5r%MuFKporcbH2KNKooZ9?0;>V*197 z8hWSXBuK7;mRtp0L~<1*hk;xWy-ji$8r(iZE{JX}xeb!@KrV=mlbi?1g&-G1Pm)}S zqB&nt>prQY_ezdL{)KVmg6LC{I}vxGm>Tl$)B1+wRK$Pqz?WPL{j}s-{1%<&*YBNK zACw%7qtn0g%ND$;w!PeioDE-cHq;f#1Cd+~b;xd1g(a8cnXWa|wAHiJCCLNXkgfZLtF!W2KHa%b18v?ost(M2 zOz-}wsU9r3J)gGuUOk9wq033mPnA=ts!PM>`jq4XMai`{#cQlLOO8;~_9<#qlG2?d zcc}e^PRb09(tTuIVQAK>YR|Cdefe_S7t@V}@0cfb=5Jf+ zc9Qd!b>olZ4(b|T%u&~;9#1mdXnVeMHMQkeqUTd*O1)2Tnn?S!?8K^TavT)a_1~+K#%)svE=f7 zzBrd^Q}~SfKyv(wJSd~4{k2{BlKc13{zmH4Rtr_8{Ac`qlV4Gk36eKRALlamGmi0$ zWj^L*PV8V0TiC}Be84aK!#DiJZ+s>W;vp8|BTix@ZsI3)c)$fFaDo@C;0H$-!WF)- zg*V(`F1fUta$NFhCAU*cZm0fMayupGQ%laLZZhmSazVA^g6b-g3o1FHVnJ4lWR&&t@b6?RB}+ssnsUEN{ zD!Hsma#{5d$z_!sS0y>FYPRIKO75$Y+*dVHa$hAUR!L5*ij$mJ$(vP@H>>jhmrtuC zpH|hBd|LWAm$9F5jAtzKF)wps2YcAUK7QZ>e&HX!;V*vUGjR|Pu@E0|5+iXFKe59D zE---;ykG@CIKmLF@P#eB;STey5x%DUCb|8s7FE-STQ=2Qhn(`umI&8r%i8EOk{3O= zLv9_vucLfV9rj;6{I@zY{(1eK&eoszRXXZlk0^z5d3 zN!{iCq<_Y#PhR!)I?4Sm{cbBI`Qf_TnhAb^165SDH^Oz<2EG0J3yP|gl-&BRiN8E~=6>%lZ`tS5@;O_oyq9)4p(JYgJD#Q|Bcg{$|v8MOh~~ z^Yn2pV?W~<&sgSTUgpFO_OOM0{J;nN!asb&U;M^r;vgPkAwJ?HM&c%ZVuuG@U;-z2 z!3utGgdtqv3tM=@9p;i3uPL7VL5Ad_Ysp0qjwN4ROTKz=EII93a@vDq$#qxqHx^Pf+ReOMbq>2DT)JUr7#sa4dQLO7i@JWBFSEg$-UK ze=nfq?*)QmUN3)B5FE?jBgpgLvfx;9`{nf$f@AtP$Jj^v8OM0~nU8t@dyT{*_Bh5q zesGLm>95CM{HC8c(rpo6x(~!n{JaJpaN!tE>9N2Mj`Z_-_;L*IbSBdFW^QVzn_e!% zx&Nh~`fWhwJNJnC<}(cTt+J*S;@`JjKH++aJi(LpmZwXT1D z(I7kRn~%+iNqhbKN5=HiIKc3Rv4RcmcA ze{+M5w)ldaT7{L0d9Qd!TOyU?H8F$c*Rlg%jnZD2S2s8w9i_v=66==yDwpI_chI#a z6-zGpL~gq=u7i$>Tb?|t?jdub+F)HHb!qaPJ92z=kdB;GB(yNDxw-$R_nEot%2phjJX& zC?t$EML36qy`~8N5U*tnujjRl;q{Nc=Fzd(#5Q)ZiEZrSBfjD%KH@8W5(n`ROAw#L zNsPoz{KO6qxWEKX@PZZm;0Qyw!WXvihC9p$$os(i!F$2`!!hp>?;GzQ?;Y#fpQW8^}sC8Z2+Co2NlYFxV>WeA^?D(9<3}0JE z{akpA9e>$3Et{2Be@~xae+c_6y7emKi?eagLQzrFW|NQVlc6OsfD(dnwf76l< z_Nhj>bWG`z{-8JIx*Z#(tA`b-S8BRkpQ-J2%}GT=i8qdzbJqvz>Zg{3-U(}Ge#tym z@5}p5XwszE9RmOVCKeNycuJ2>x0CV%&t z>c*szw#TGu=BELpm0ZKY^|gL&JN4sNqwELEI;rPU%c(O*2H5$_j;c?>uKMrpZ*NPM z&aKb)9OMt0E$_u^QTk+9|9Yjn=CK>@wA06W_6aSH{M4LoFhC!?dpWdwephp6?I_)B zQduLPv*ddd#_4r?+nUZ5E2|q@N9rFhjxq!P+^Iet@{-Qy&ontlWYH^HwA5un^Nskf zDdNAOk8>IO8OM0WG9U9YCw8!hE$rh5KHwMr;T!(qH$D>w@em8~5hpPcH}MlYJm3No zK8Nsv75v}`L%6~hw(y2K%v<>pmNMhc0X0uIv5%egY(kY3W#8>#JB4jF1B>obm!=N2 z&(1Gy8W&urZby%@5k2$!h0e}W*C&s#NALDlyC!u}pO5Wlm)AO>s{UC>t)JV@mToQg z3Xv=PLDS{En8fE|X}vO&^V)ZgwAEGSFAl8vg3p<@*=zs)Q5TUQ7MOTnyb-AFk_c>)l&vOu>;(aWl!bR6Xj1U z2ThXqVtSMw6P8@B{O|ef+O}==C(;b_=Xa33iz~}pA{_vxw&eaua`}du-`Q}@5wb3ss;ZLnEesJ4}@0ue18~Qkx zv7d2_XDstEFLPoCd)UH0e&7Rs;UB)?FMi`QaS#u&5Fc?8BXJWyvBLu{Fo6@iU#s!H%jjb`?_Amy#?&5H(Tjl^S=!3ef4XTx-MRCi|lN2-I-?2)Lre4nctS&q@JHBg*_;2asT*iLJ zF`lu^$Gpsm9qeHX`}lzm_=SJ?hQIiY&%{AI#6o0Q|-BKF$ds@FRy0Tqb@VL<}OX;}-8r$!(4mXvbDx|j`>ufW%ofOJd z?FoJOaxZ&q(iQ)B#cS$Vvp%-;kVG}G^e(mU*KYP;+;{3;vvF#9nU?mMy#;mHT2Boc zF7L&nDE&v+KlQ4n7P8BxKPT7UpCOsU>|NDO|9E+nDYyPDbF@|;-9L4Qx!Cr0=x|tX z{Yk~E=62~({`Rad=v@4~{T| zD|}%KZ@9zUd+^Xu24mLZW13uVkdcPT|NB54lvu-prt=mTE)nB!;Z%#UrT;+p8 zde@*XwnE+lDy)&G_pj}4qeH9I!mG#Bhh00{^TThc!V_kz#YdXk>IIAFQay94LH*>t zSQDk?9x%E_&Z2fEm3+e#SAL zvCPN3%!wWBVGH~Cfe-kFfB1&K_>IrRK|I7le8fqN#7+Ff4iC7%1WxdR75v}`L%6~h zw(y2K%s<_9)llYq-$+-unBUg^X1SSCyuN<1ZZ%sWu7G*@Mr}RgdIMWEY)MSBy=C>= zMcdjDJ?p9>(K+-7Ejrm32E4EK?)qB2+pN94wdkIToUl|SKGW3J`mmU;ms&y%>Mrla zrYPBiEf)Rkg5vhgT}^cLyhY4U^M5xR^2-|FfW@Z!fGy^Oww?4xS-&#l!hEx?Wm{c% zO?KNYZcFlv$cDPv3uSG&X0moPxtbobzqYN_bGfRJwSbOu6TDyrKRCh= zuJDB|yx|V>w@M!~l=m)n&^f-!Z2#Un!MuH}jc%CrX*)5@hGyD^dRXQyv)XHP>d2Qk8rkUSSvtMXrqbEP5+b*l12DO*> zVpo)YF06dC-?pTk^?O6zWPTa*SgkO-;@y_I;jy)5d@W;E4Qi#&FS}(viF?V+i*BMT zS1V)-_M8qvDws`P%pQOBqC%()HiU&qF)9NDwzwV^gvd{-3l-_pmqjQxyb zJY$)Ud6^SC*uxg~@dF?53;*y9fAJfiiGz5Eh4_e*7>S$si5(trfeD=81uOW$5r%Mu zFKporcbIqF^q!#{JkV2red>;xayQQGAJJV`_%*++yS7AV?bbH>`RAXpXX0-9W9~%j zG1X#h&Pmf$?BAvIob!$BijF7LA1D4+iMN{A92K+ZZhwBDrnawdf*g*S^-qzL7OfeI5Cu+TX9c72g#_{I~RRE@MCA z7|&SdV_xRO4)(Bxef+=&{K7wc!(aTyXW}3pVj(`_Bu3&Ueqx6QTwnqxc)<#OaD*XT z;R{=M!yV=YXU;N|i<1WHRc${rS?)e&PX60ZXDgoB){k2ovui~c9rNc?w&Su&YSyKO zy4UB`ZN0cvs`au6J??UhHU1^_X;oSWep}yUXa&FGQ<>b>+P{ z5v7NP)roG_v8S7oaxor+!v^R}}Hz(#N@s{fuKgW0{Y6 znG-wM!xr}O10V1U|L_fe@f)9sgLsIA_=uAjiJSO|9UgFj37p^sEBL_?h7BgIje#$0 z;SG0~zf-ZDq1^m)q+G8bn9OV62wiS5OrLLd%QOvp$3HZ*mk!UF#~y0dOD(K0WSGvKn(X+P2CZq2-!V1FOh;@l}*g z2#bwwJv+jV4#{ULF2+Q>n%|D@7p+Iu+GnnJ|JC%rSX1X-@Pr-m(nqGn3*~f|^Ofzf zy?spS(RuWe5iM-%`R|4<{CP_qe72W;>{No^?dk_AEqa(0-xWptxAbu?V?W~<&sgST zUgpFO_OOM0{J;nN!asb&U;M^r;vgPkAwJ?HM&c%ZVuuG@U;-z2!3utGgdtqv3tM=@ z9p=U2avIA0{IBW4AI>)6VR@qOG#RCr4EfZQ*_&6L%->&U+7)I?oSm!Qe%MAw+9GyE z&l9T8tw`PB*NQgtn~&)tnRDm?>!WPVp+)uNv8ihKkUF;G&`NsV<;H4YDS0ohN9h@1 z4Wm1KRmsXZ{l?u`6P2l0@E%RPXiS#6nQX__m2{zd*|@e%jY#=>@rX!U^51#p@rcLt z+Ntt?M-9ktmX?9b-V5;E50j=_;2asT*iLJF`lu^$Gpsm9qeHX z`}lzmFvmZ9!(aTyXW}3pVj(`_Bu3&Ueqx6QTwnqxc)<#OaD*XT;R{=M!yV=&?#>NS z9vqk`|NlTc^VINp{%=>u>P~mxGwVNWr_R4NNXz}P=~QsD+L*V4F4`i8J<;r%;u$oy zT(~XTHHR*dKbP(%e`g^Y6s@5 zrDq&#VB)rh+iq2=>dEUh?5wJoA`+x9&mvPoZtm3_`wl|aD^{y;SG0~r|)pEUsLQ< z4kLv%VqKuzEF(o-Tr*hDUv<<-QSTM*s2>i0Xr%Dmpex4}vQl)(1G#jotg^0^HA2sd zJEVFKk+t6;k$Q1z^WeUpXP3mVrqR9EOaJ;vJ#()-w+xN2QuNeCADgNN3Rx+-_y;*{ zSQC$;S$si5(trfeD=81uOW$5r%MuFKpor zcbGqNUGW}; zGhyYL`R{XaPxhzs)(Sosa*bKJ#)8j5_*_uciM{<-G!;&U!zKjRqB=Ysi|m(K-uu!k+|2Y$%s0>AK& z&jtSCcksE8IOMYtd@hKOIQd)ptt$ZhIzLVv<+RArz@OvTO;lce7-{JDz&Tod| zyIsEXjeO?^zZdemVB~ip+Wju1|K9OC63w}c{fuKgzZcBMyv)gc7xu7)ef;3}0>AJN z-|!c|gWn5@W1z(2V$sA&jKod+{9eEVF8p4=30|;*9~@x_SNQUK0dKg&{E_<*e$##V zO;`M;%Wr*1e(M##^>Pnj0iFP9ygQntKDeXE1Wlpt)y|dkHJ|62X0x++$d| z$N29)N@9|GkKjH^?n$iNlLYrsa<5|LUM0AXl6x2{_b`fk7`eCMUP^IqBlkSqODXPo z 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 MV6NHY-@g5S0BBn6`Tzg` literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf new file mode 100644 index 000000000..ae12bfc11 --- /dev/null +++ b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf @@ -0,0 +1,1669 @@ +{ + "asset" : { + "generator" : "Khronos glTF Blender I/O v1.2.8 with hand-edits for clearcoat", + "version" : "2.0" + }, + "extensionsUsed": [ + "KHR_materials_clearcoat" + ], + "scene" : 0, + "scenes" : [ + { + "name" : "Scene", + "nodes" : [ + 3, + 7, + 11, + 15, + 19, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32 + ] + } + ], + "nodes" : [ + { + "mesh" : 0, + "name" : "BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 1, + "name" : "ClearCoatSample" + }, + { + "mesh" : 2, + "name" : "CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 0, + 1, + 2 + ], + "name" : "R0_SimpleCoatTest", + "translation" : [ + 0, + 5.25, + 0 + ] + }, + { + "mesh" : 3, + "name" : "R1_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 4, + "name" : "R1_ClearCoatSample" + }, + { + "mesh" : 5, + "name" : "R1_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 4, + 5, + 6 + ], + "name" : "R1_PartialCoatTest", + "translation" : [ + 0, + 3.1500000953674316, + 0 + ] + }, + { + "mesh" : 6, + "name" : "R2_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 7, + "name" : "R2_ClearCoatSample" + }, + { + "mesh" : 8, + "name" : "R2_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 8, + 9, + 10 + ], + "name" : "R2_RoughnessVariations", + "translation" : [ + 0, + 1.0499999523162842, + 0 + ] + }, + { + "mesh" : 9, + "name" : "R3_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 10, + "name" : "R3_ClearCoatSample" + }, + { + "mesh" : 11, + "name" : "R3_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 12, + 13, + 14 + ], + "name" : "R3_BaseNormals", + "translation" : [ + 0, + -1.0499999523162842, + 0 + ] + }, + { + "mesh" : 12, + "name" : "R4_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 13, + "name" : "R4_ClearCoatSample" + }, + { + "mesh" : 14, + "name" : "R4_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 16, + 17, + 18 + ], + "name" : "R4_CoatNormals", + "translation" : [ + 0, + -5.25, + 0 + ] + }, + { + "mesh" : 15, + "name" : "R5_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 16, + "name" : "R5_ClearCoatSample" + }, + { + "mesh" : 17, + "name" : "R5_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 20, + 21, + 22 + ], + "name" : "R5_SharedNormals", + "translation" : [ + 0, + -3.1500000953674316, + 0 + ] + }, + { + "mesh" : 18, + "name" : "X2_Label_CoatingOnly", + "translation" : [ + 2.0712804794311523, + 6.619500160217285, + 0 + ] + }, + { + "mesh" : 19, + "name" : "Y0_Label_SimpleCoating", + "translation" : [ + -5.3578033447265625, + 5.25, + 0 + ] + }, + { + "mesh" : 20, + "name" : "Y1_Label_PartialCoating", + "translation" : [ + -5.3578033447265625, + 3.1673200130462646, + 0 + ] + }, + { + "mesh" : 21, + "name" : "Y2_Label_Roughness", + "translation" : [ + -5.3578033447265625, + 1.1383899450302124, + 0 + ] + }, + { + "mesh" : 22, + "name" : "Y3_Label_BaseNormals", + "translation" : [ + -5.3578033447265625, + -1.099429965019226, + 0 + ] + }, + { + "mesh" : 23, + "name" : "Y4_Label_CoatNormals", + "translation" : [ + -5.3578033447265625, + -5.252500057220459, + 0 + ] + }, + { + "mesh" : 24, + "name" : "Y5_Label_SharedNormals", + "translation" : [ + -5.3578033447265625, + -3.1963000297546387, + 0 + ] + }, + { + "mesh" : 25, + "name" : "X0_Label_BaseLayer", + "translation" : [ + -2.087031602859497, + 6.616230010986328, + 0 + ] + }, + { + "mesh" : 26, + "name" : "X1_Label_Coated", + "translation" : [ + 0, + 6.614150047302246, + 0 + ] + } + ], + "materials" : [ + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.5, + 0.019999999552965164, + 0.009999999776482582, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.5, + 0.019999999552965164, + 0.009999999776482582, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatTexture": { + "index": 0, + "texCoord": 0 + } + } + } + }, + { + "alphaMode" : "BLEND", + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Coating", + "pbrMetallicRoughness" : { + "baseColorTexture" : { + "index" : 1, + "texCoord" : 0 + }, + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.6000000238418579 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.6000000238418579 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 1, + "clearcoatRoughnessTexture": { + "index": 2, + "texCoord": 0 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "metallicRoughnessTexture" : { + "index" : 2, + "texCoord" : 0 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Base", + "normalTexture" : { + "index" : 3, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Coated", + "normalTexture" : { + "index" : 4, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012848637998104095, + 0.021861059591174126, + 0.11068868637084961, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatNormalTexture": { + "index": 5, + "texCoord": 0, + "scale": 1 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Coating", + "normalTexture" : { + "index" : 5, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Base", + "normalTexture" : { + "index" : 6, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Coated", + "normalTexture" : { + "index" : 7, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatNormalTexture": { + "index": 7, + "texCoord": 0 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Coating", + "normalTexture" : { + "index" : 8, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 1, + 1, + 1 + ], + "emissiveTexture" : { + "index" : 9, + "texCoord" : 0 + }, + "name" : "LabelMaterial", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.8999999761581421 + } + } + ], + "meshes" : [ + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 0 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 1 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 2 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 3 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 4 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 5 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 6 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 7 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 8 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 9 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 10 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 11 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 12 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 13 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 14 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 15 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 16 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 17 + } + ] + }, + { + "name" : "Labels_Mesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 4, + "NORMAL" : 5, + "TEXCOORD_0" : 6 + }, + "indices" : 7, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.001", + "primitives" : [ + { + "attributes" : { + "POSITION" : 8, + "NORMAL" : 9, + "TEXCOORD_0" : 10 + }, + "indices" : 7, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.002", + "primitives" : [ + { + "attributes" : { + "POSITION" : 11, + "NORMAL" : 12, + "TEXCOORD_0" : 13 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.003", + "primitives" : [ + { + "attributes" : { + "POSITION" : 15, + "NORMAL" : 16, + "TEXCOORD_0" : 17 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.004", + "primitives" : [ + { + "attributes" : { + "POSITION" : 18, + "NORMAL" : 19, + "TEXCOORD_0" : 20 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.005", + "primitives" : [ + { + "attributes" : { + "POSITION" : 21, + "NORMAL" : 22, + "TEXCOORD_0" : 23 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.006", + "primitives" : [ + { + "attributes" : { + "POSITION" : 24, + "NORMAL" : 25, + "TEXCOORD_0" : 26 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.007", + "primitives" : [ + { + "attributes" : { + "POSITION" : 27, + "NORMAL" : 28, + "TEXCOORD_0" : 29 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.008", + "primitives" : [ + { + "attributes" : { + "POSITION" : 30, + "NORMAL" : 31, + "TEXCOORD_0" : 32 + }, + "indices" : 7, + "material" : 18 + } + ] + } + ], + "textures" : [ + { + "source" : 0 + }, + { + "source" : 1 + }, + { + "source" : 2 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 4 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 5 + } + ], + "images" : [ + { + "mimeType" : "image/png", + "name" : "PartialCoating", + "uri" : "PartialCoating.png" + }, + { + "mimeType" : "image/png", + "name" : "PartialCoating_Alpha", + "uri" : "PartialCoating_Alpha.png" + }, + { + "mimeType" : "image/png", + "name" : "RoughnessStripes", + "uri" : "RoughnessStripes.png" + }, + { + "mimeType" : "image/png", + "name" : "RibsNormal", + "uri" : "RibsNormal.png" + }, + { + "mimeType" : "image/jpeg", + "name" : "PlasticWrap_normals", + "uri" : "PlasticWrap_normals.jpg" + }, + { + "mimeType" : "image/png", + "name" : "ClearCoatLabels", + "uri" : "ClearCoatLabels.png" + } + ], + "accessors" : [ + { + "bufferView" : 0, + "componentType" : 5126, + "count" : 1113, + "max" : [ + 1, + 1, + 1.0499999523162842 + ], + "min" : [ + -1, + -1, + -0.06000000983476639 + ], + "type" : "VEC3" + }, + { + "bufferView" : 1, + "componentType" : 5126, + "count" : 1113, + "type" : "VEC3" + }, + { + "bufferView" : 2, + "componentType" : 5126, + "count" : 1113, + "type" : "VEC2" + }, + { + "bufferView" : 3, + "componentType" : 5123, + "count" : 6180, + "type" : "SCALAR" + }, + { + "bufferView" : 4, + "componentType" : 5126, + "count" : 8, + "max" : [ + 1.0280373096466064, + 0.23501670360565186, + 3.8289083903464416e-08 + ], + "min" : [ + -0.968224287033081, + -0.2350165843963623, + -0.010000125505030155 + ], + "type" : "VEC3" + }, + { + "bufferView" : 5, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 6, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 7, + "componentType" : 5123, + "count" : 12, + "type" : "SCALAR" + }, + { + "bufferView" : 8, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.23026323318481445, + 3.751463495405005e-08 + ], + "min" : [ + -2, + -0.23026317358016968, + -0.010000579059123993 + ], + "type" : "VEC3" + }, + { + "bufferView" : 9, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 10, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 11, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.2302631139755249, + 3.7514624295909016e-08 + ], + "min" : [ + -2, + -0.23026323318481445, + -0.010000428184866905 + ], + "type" : "VEC3" + }, + { + "bufferView" : 12, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 13, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 14, + "componentType" : 5123, + "count" : 12, + "type" : "SCALAR" + }, + { + "bufferView" : 15, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.22039484977722168, + 3.590687924770464e-08 + ], + "min" : [ + -2, + -0.22039473056793213, + -0.010000280104577541 + ], + "type" : "VEC3" + }, + { + "bufferView" : 16, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 17, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 18, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.21764808893203735, + 3.545937588000925e-08 + ], + "min" : [ + -2, + -0.21764802932739258, + -0.010000137612223625 + ], + "type" : "VEC3" + }, + { + "bufferView" : 19, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 20, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 21, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.20775499939918518, + 3.3847587843638394e-08 + ], + "min" : [ + -2, + -0.20775499939918518, + -0.009999996051192284 + ], + "type" : "VEC3" + }, + { + "bufferView" : 22, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 23, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 24, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.22341907024383545, + 3.6399587344249085e-08 + ], + "min" : [ + -2, + -0.22341907024383545, + -0.009999859146773815 + ], + "type" : "VEC3" + }, + { + "bufferView" : 25, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 26, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 27, + "componentType" : 5126, + "count" : 8, + "max" : [ + 0.9169960618019104, + 0.22670458257198334, + 3.69348676088066e-08 + ], + "min" : [ + -0.9199233651161194, + -0.22670456767082214, + -0.010000176727771759 + ], + "type" : "VEC3" + }, + { + "bufferView" : 28, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 29, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 30, + "componentType" : 5126, + "count" : 8, + "max" : [ + 0.8968609571456909, + 0.20853587985038757, + 3.397480696776256e-08 + ], + "min" : [ + -0.9147982001304626, + -0.2085357904434204, + -0.010000113397836685 + ], + "type" : "VEC3" + }, + { + "bufferView" : 31, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 32, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteLength" : 13356, + "byteOffset" : 0 + }, + { + "buffer" : 0, + "byteLength" : 13356, + "byteOffset" : 13356 + }, + { + "buffer" : 0, + "byteLength" : 8904, + "byteOffset" : 26712 + }, + { + "buffer" : 0, + "byteLength" : 12360, + "byteOffset" : 35616 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 47976 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48072 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48168 + }, + { + "buffer" : 0, + "byteLength" : 24, + "byteOffset" : 48232 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48256 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48352 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48448 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48512 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48608 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48704 + }, + { + "buffer" : 0, + "byteLength" : 24, + "byteOffset" : 48768 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48792 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48888 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48984 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49048 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49144 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49240 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49304 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49400 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49496 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49560 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49656 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49752 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49816 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49912 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 50008 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 50072 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 50168 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 50264 + } + ], + "buffers" : [ + { + "byteLength" : 50328, + "uri" : "ClearCoatTest.bin" + } + ] +} diff --git a/test/models/glTF2/ClearCoat-glTF/PartialCoating.png b/test/models/glTF2/ClearCoat-glTF/PartialCoating.png new file mode 100644 index 0000000000000000000000000000000000000000..d579bbebb8a423eedb16594a7ef715fe7521a752 GIT binary patch literal 5077 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 Q@JSA$qoJ>ky=fQyKcnMWr2qf` literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png b/test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..b1911d9f0a303a57181d44856f3f31ea18c11063 GIT binary patch literal 5065 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 Q_!|ylXYGipvh+*(4_Os-!2kdN literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg b/test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg new file mode 100644 index 0000000000000000000000000000000000000000..73e6260ef7126ccb11113179c58cf6611d3f3e9d GIT binary patch literal 144210 zcmbTddpy&9{QtkM4mwFHL}gd0Tv3)p6n2#džOVOJDoNkR^>T_xnWONE@;Rbq*4 zIn0@rb2*>3a?BiOIgBl{!+XD%>+?JP@%`ib`wh3fUNvRhT{&|%F#j%c0KH#lWzblU9v1#^pwmR5Gx>>be89Wm}V zZ+Up$zT@TRAMh|RC^#hiX+&hyv*?)TFJ2}kr@Tr{dz!pljPw6`{4d9)2*&l> zx^-*U$*qoS&2NF=XRYG8_4{=<>^OTx?uPHq11BDD+;uMTO~L0)8hTe3l-wS4ZB{;b za`X^yb!h*M?0;`yPyT<6?EfCv|1++B*w(dcz~rq}gdt&#orEzV+B#P%5TW(|mBE@8 zS=JvGIDFd7SL`O?FBfPEX_wMR7@O`>;h^bu&`Yma)=B7aXdi*y(i>1J>Z1 zz1CTd`n94v@k@%hY{oRAvM!yOmqt`BfRxOCOs>p8Il6b?qs|=`>mHp9NH~CHPDbQ; z^oD7};Qa5Vbo$NjC2*@cyYwj$^JG%CMwAih&>(lyF~Q^4J92YNnA906$eL`kJ?7NQ}nL{3T+omFSO1Ea7EQ zTxSKZ_o0e>q!F98vd4Y}Z_XsjVCpFQd==<$452>{V<>~2^J#qB!-5X~DQ4yFd#eRL zee{YtiA-pEFG8mWeJ;wShuB0ZXK`FT*>#y$7;1}uAVP9Qo_nmJ9n&W9 z%!KlIG!>J9Ipm+Chwh$Od4VOo_sRCdsGROWTfQf}_nP$IDTD1KQ$tIoUujv&Au`yg z_2#Os@77RmP0}QEXm@!CKh-F{CTU7t6SNJ(xF_%PXyRhW>>q?OJe}z*9d#x>hDJp! zC*R&{li2yZPVQ*O%Fa&{l8X-unY5?d{?aG1!y~a2FX8dY4K#~gj8P*ITt6~X7};cb z$PY6$qSSpcnCjz<)DNArk@(FmO+d{zhKc+en0`%32(7!2T25EWd&|4auoC^mE|_}O zO;~+vk-_{siFsk#&v-s|0_ud^Fm+EuCW={JIL~gLW)oIaq!Zp7*?RnZAH@<+ipwsx z-u3<^4?ovFmsH^|PlOQlM0X_ne8@H8vX_B=BQZL9+Eot8Lr~{!!5Z~*R(XOPt7iq@ zXeg(h(Q};|Bv{^mA%oq@hIY1A-Pe1h$$URRlUR2b8{=d!cwv}Vc7+U9Uyd8j>gj^N zV8$FRT5q+iu@!XA;r8RJ;G4EC_w0f5Gvz61e`AsA6+!kBLfKtWTmx1~Y5dOTT4vz0vYu>_Lx!1B3foKu+rhynMZ%q} z1SUtDhCW@B!L}I3_y{@#l!3KS_`bhdAUZ^^{EN`}ye5WJ-!aj;NBXllz*qcOkiQ9~ z-|3Yl-OVzU6y%Z`Z^dA14)vs~z~e&>zc_r zt70#$&;vxQ!1umnQuh5ccv~KgSy_#WUCXB-R3#W(t5hyRMXY?-m8|&zE=-0~?vGD` zMaQ~^`-^Am6(+Jhg zP~0eXW#ZA@U(V`qK~l%`F%Y}jz4SuaR?`b6~~l7y`jt`!3FzW4P9%7`&Y-nERpCt&zbJ9|=GRiZ(OYB%peJ))q5GEEI3AM+2VT9(2^r%(Z>3wB0nS-s6MBo?zDb zFY-9mq6t?K#LfF#!gcP5PdV|1dUr>O#&{TZr)g-3b=%&C&kc!6af6AN0Uh{z6V~;pYpC zPcf_NZkKqzS*ZI?@E*1?SPBO63urDg7O z>xgW#FL()mE@@w;+~P8LGdP=V=!N{YK*+?#w@cW?|27eNawM^}zvPgTMLk*X7$&0N zcTpd5`_ch)fX`D2VISg`LsgeTWV?QlDV_UO2Gc?lg`s2iQC!!eJ>%xu?Rk3|5xpW? zC6dmL4@>5UKj^(Tx%rk*|8>orw)<7pUiBh(lrU|8_$wlyZq#tbEFA<-ABg;}UROk6)ZhT~SS>VDG9MhJlyrJh4{MWLdDWHNJb7i3Dpk

    8#fXvU&U@TnHe66M)}` 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≫PJyOL#2noO42?ak;#zU>=*qI;o{}k?8jYM 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�-h@x9GW3t>NHF#%4~dx zEU&mZ@U(e%qT0v}VdEZ;WQcqyl0Fk{h;`!6pK1!@;X^c`JuBdpCD7%eR1X)jFjGXm4D0C(ZRS>2*HOwI%kcV~opf7Ruhyx8>={j5jS5nDHCJ{{ z=B5ADoY0zPkz>SUXP)$4B2j(wvb7k~HC-R}365%utzJQkG{XRVB^T%;NL+7Gj$1fW zl*x0AUTjc8kxJ+!SK2SdW<(gGt00on4Kyx5!GyjCG-W)#jF^ENSk>M8iB=FI2}h!I< z!JXCJ%R_fN@tjQRkCU$^ZA^vwyLB$Q7xAzZwZ9(jwD_1uijsHQ43tK%b$8JOER&y3 z)gQmQJsm=B#GB;$$J57mNz>_f*XeZxV3~q1{eOSXh_x3|Webw8;I4sG6LtLrnd@ol zKg~U9UzmK5$^J%~RpU9s0;XZ8fj=s5j+uMbL-Z;vnv(5H9D>w(ojL@xR?j2tq) zJ;+-rpzj;Rv@eIW4nD&sOoO&SAktx%S!_o`!5bqg5|BKsx_=kpcDBQb$E$0-K8GHu z03#JK2eefl60FW^n(=xF20_cih^lS8*w|^5XnDZDRO>A|r}%)s`|Wn#$wSnY6$Go0 zGMbf{GN`}VNGr6`^8T~Y;e-!y0+pVqQ-|n@oV(DuABNm%HeI3V5V*IS6(A7 zy;(&t!?sF`38{S{x=+vK@)(2+6A4IhzfxOz1a6yaq*28d>?v4oQ_?=7v1xo(H+o!9 z%fL6U-^Lk2i~GU8arM(rK;f7XJ>-Pk9&2r&3dn|#Sr>Mcr<@D6pC#HQ6pY=v^-&WZ z>k_lpue6}{(Pnn#51pr^SgoIZlBHj_ld7)QC$EYEDs{K)HICZrxfn?Z;@mgxhzrh6 z;QUkIklWs+>YvTHt%Y1=RhR;_{9a16?6hegQ-lAWKCGWCa9+hOVfx*o$RF4Yjy#;n zDiW??l^*#o#99z~7uZfRFfF@WD>Qa+WZV5t%N#z+f4EK)ioLD^<+Jm7ernG z4+>|B*^}W$_au&;54k*BQK6IjvIe_5RiQ^LdEWE_>+_H3xW%6pC0z`^o!W|@u)*VN zJCa77^`hhv8#;Vdn5K@+^n`)-|3fgK7yUD_cSzG)2P&763{QI72RQoak6d!KzWJFk z${YFVX63_^dCE=r#93zaZjGeTE7p>(ZfW+4p@wh!hrZN1dVw!DV1zm}b3xf!8uz`e z@~{4`M?v?K$8OJlO|AqV(3VOjZ=*w+n`*(}tWx2V#+*G9&=Z`aGG9Q$iOxbrDY%W; z|DeZ!6s@#}lhI8fE&O++z!XCzY@07`S zVHB1+M@FrtOkBIZF&e=xHMW0&VZ9X^*8{wmjWRG{)xIh%e?i%OS9~X%tjmE)%-O8G z24Af4XzX)o{|{SKYZE#4XOUeY9lC8(7SOW@GPc0l#73>VRlaL)Q-y`vC1m@D%!zAj zNVprnGAOr9elAEECr{u<--r2z#3<_uF7r$VWo7`hY7T-1Yk#O}Vbs_Ch%|Jn@jsGI|2XZ3(-m-Opcbp7D z$2Q~1Q_mCxeLG(vS+d!Ca1X1DU3;fG)Z<_%lC_Ouo`YO4d|6mtsJK9CU@F((!rqM) zi+)ZwrFD+n2ja0^G5P;90$Ow*MBX@45WlCmPVnBh0u z_fTH^NmlNFy#fCk^cWT`%VwIir56P#FvH6Z5u1G%i|YWg zkQ-qeq_Au9TSyHwuA;|P52*&@eHSz}+~o!Qu*=B#7}+`h5^be^xBNtXW7tQ^nU&{- zD49bZ^I_gc^~)JF48*KEogiR*eY zZub)>IV0nUTj9da{Td9pMa5qZh+X^FhF?fpC=4k97P!vZpzG>!ZPll%VA@gcwFm++PF(rb@Iit}|SH>6EdzXTqhHyV`%i-j(K zj$KzW1fjvQzFM!@mC_gcmX3Y#fcAFj>_*`^Ns1a;ihFOrI{JCxfD8 z7@rSgqWWM^(*&aHT&kz~XCJpI?{jUsE7hTu_7EINAi6JN2lWgoR!;sTD4J@tm7}LZ z<;^#qurJA5xVYZh6P{1rexBS7G21l>@`dCNqRWxzW$z0Bw>r*&S25h!O=983Dg^=A z_;A6fAC!0CwZ)GUo%WJ5&7QV{i#r3|ufwYFQ`V41Xc@#Yg>I&q?PX_?PxVEkFymqg+`rd?pla(s+UcsD{8CZix%>>!EN$1#Dw1c?yQa^UAb zW6Xil^Wnsym2|XyFML*Hd|gG=+QxB87!Ste@aoNBjvDxAU%dO!N6?m1)OU~N8vXIV zZ#LWaVdlG7G?kWo^XLYv@pI;0hA6<)5Fgd9 zS@~+Sl7>0g%k0xbzoH1UqOv)nOR8qnf4e{RkxTNo}vv9h&Qx&e@kCE{iNgRvt(k|5$t`&pFNc0~5zDsYNfO2oK)q zYJ0mjY%b+EH43ULsh!rN&h0u|;2jv$q>o;4+nI#6ec#vrDl_|5GrRQX-4_(PCce^3 zkb<)5Al+og{3~)s>v&t2wr)cjuLHC%J*$94h}`C%UB;>wTa8~r<_15oTKooZX z21FJX{xO4<@D~M39g$Jzf2USK12ZWr!qtuWDRT+!^TD`{(%$??xgel^f-%{ZABdQy zvcihL3C=ylS*6!;2VHhq!scdq1XgGKnljD>-g?l)Cko2prlJiMLp%VA6pbDtNMHK1 zF50&#v+|j8M!_Y-ka46TI&HMTljb22)4L2JsMWG(?k2op!0c`1yGGqi`!Q*Vr zbDpO_tKgy8`FM+g5eC|lxp-$x$Cxtquqm6ESTL%psCU$l`J|C7#afVJ^)0^`5T`6{ z`#z+<-3j;XTDsgerCJmpqyV+_E+#PCZ81Sv`$Hr4RxEqWv@j+WOYDHmtug(aBysFj zf5cD>ufB>EEe@G%bIsF{SDLWi2p_-$sZv4kSGqXyuB?VTGz@^>zE)jp|IhwzZkX2QlTMw2cpXjgqb=Lt82hH$p>2i)Ki46sZa$XgUZeQX zyR7?&X441DZUpO=QS-ABj^F3`{)XR=t_}&68>dw{_L56N)cS`U`GHefcS(Ip)vnpJ z_d2XfO(K7>aPx1TKcW%^$ymWJ@~)X7zU0SHBczqHfwaHl_sQKn2VvrSG%odm*`|{P zvy-%+B$cl7PVVjUUk&c`r~dfPIB)Fd*n6VFTz)ofdU*nDHrBFt2I~z&!y}B zx^D|d7h6Q--lVdIKF+6Ydi#@>PkX=o`NrOur@!s~7mZ_N9gFPn>PTix`100#&si(@ zwm6*pwV*qof6y*;8ltApVc6A!M?|w|ZT1*FcrVzuY`ER)j=)jw2QUmiGVp_l_$=!n z;)z63^_dk2Jq0D@@BjPeFLrHO@b6sQ`(CaZj17lbTOijy9tn0ec!Jaqy3qQ5Mw`P_y`F-cOC zL5(x^mAv&ZVj+cgw$EX=it?ZSu=B5Ky|z*xxkk+g;O9}EUNAgT(`Qv@50SD?hLuG# zCsg4T?W5-;pKnO|54eQzoj*KT(!0)Az7XYXbXykWYk-}=Cag4W)r7G;eUzNESxqrs z*oz0Tt8V_=3GAL6H1wlj-E-2iBI0jR?TUr+hY|gkmJiL216(2QMe{7->05|Ms7;*G z`Wg0&A`$H|#n~Jc93SAgY$7+UC{wj=I=e(v%^A&tX{0KqDNmLn`dR<$>&G*_wN_k+ z$%Xd6&VG1SEG!m$WwLX9xB{^Ro8j~seYB}+wAS-^LQ@(cYw>FPQ>^i8cvxC+tUohF zPaOkS*_fOb(#V^U2l^_SK0v^v+Mp}%Ezv`Up}W7A3hC* z(}PIS={KTGnNZ4}K+i_8-Z1E*RuMg>jWm+AjCScn4`Ewsc9VdPc^A?pK`}4`b$#^y)Dks#aQ!NTxgSRgVvJmm@QxMImUG2k5g9Lo3ltM_!Z8t5&uKx=lM$u@ir=|M z%EPfK5KAD}Wm%lqe+x(%uh*I`8bb7@*3?F7HvJUEufBNlwyW2kAT(s9l_V)oIu_4I zEp%MZZOm63tj}96_l@x^g+W_N6nX}WjNAXTvFZYB!J~e{?5=EI@m8VNR^p13HGV8k zOlGIm5ISdx*Tq`atq#p2a-UwrKm8ZQOpO7dNz&<#mK6@qMO=S=CvH>+Xae0D$*uCM zC`P=#VlXE|JOufsJ^)>U$*k(9V=7*bbllSn`p<%8uPZ()?Fpo9E6Dti1bxZ5yUHcQ z8xI}P)ayQS968JD<5H1=VC^*4PfYx}O@erTl&?tfZK(rp0!O*%x-e>#zHY-}2%1gr z1N!CH>0j^G5=`)4$op8x!Ig-CnU~vlm8cd$ z__5a9Ro9)nN{}|}TI9@Btr|?aM$bl&XDqUuv}2ac64lG`)BryCoSpmJP1ZnE7j~YU z72u`+yZY@qc7gB^UeoF1?aEBAz#wTtV*zJl?w(Hty{(D4Wm6H%-L@k!ka$SD1&S8m zdmOcm&WT;}NE2EAK*$*bL(ZF8bt6HiX58|Du$S@m$AM#Vb2Amf0jjnB?Pn(C zv|QWg6L?X9eH6#P=+QKL=y{ehw|`1ACNmF|pwlDR zEdTJo!RS<3QW|&jnicB05d*~0u(Yd|wLjB;ehS~Ch33>G$6@VPsTcHvJ4#dJj8$nH zDH_wg6hc-sZ<`IxCWT{Pcn;@3B!%OybQEKIo1y9MVt>mAO%*I0H`6E!4m&@mFfz)d zcStq1`Fo!yX3PeS98m9tEaqwU8Zmg@8ZyoC@^#f;o1^1?L$wOcV6J^DDx+s4&D#s; z1&Xq=O#l3`VqxJ)G*mJ>;G?xro(ry8kA(CeX?oGL;6@-IR4j$GRB*rBJD0_#D))Cd zw@Ka#tp}%G&Rd=dO}4p%<-1Zoi<(kh@jtfZuxqe&w(z`p?|g;hvz-`){Qk%dmhv2( zju_GV^8@;}u9pk48V8HvS6ZQN%`S?6a)H~SEnvR$2kY`4(%5wrBOBd^%(~bEVye@X z%69TL+6@Q9<=_P9sDB%<j

    9ZTzLU5p=nSX`X?Z}N;w_i@!rVE(mv-*?L{lDF#2D@(G1!lJ$fvUq2 z`v=BbC4Ytu87pPmIge5&Y#5^pDH}4!EFa)%MqmDspR`CWW*)!Q!}>_m?T82=jd@zv zgx$;^H=b0UKnskaKj(^97z6p_-yarAXRr z;261J4svdsdAXv)YlJL$UIvPhKk)OAh9mpprkKPpzWf6SCMBa=VlnQFw3)`piD+4W zcgVSMXM9#$Stg9zBy7O2bF>&%MMX)5| zDG!d@LJh9_pGJ2?qN`S*W$9{=40DgYu8ti9y!^WL(tIO4xx6i7q{mUu%pJhSiKF0-b@o*A%OaA-o0B z_15pg1HePxSDOj#s90fYWELQW^g7x*QV5Y@yLt%B6w-a@bws0BQu#_s7)E{%SE;{N zv(4DQ^8a6xs42w-tJI69rBeJ*Xe7S`KaZ;)k zzumDz7i7NkqmWq!!C_PiH_Nvk=od(68w;REx1mQ~6I;_s<@5v}I5Szvbsq_$fVwUL z<2}?#GIs@FsF6ohahK3X$ATjeEzOUKJVHfJNUt@k{Dsvu+*}E1WgQ|bXbko)(*WKM+|N4pzgkIajQl1+;y{MI) zbN|nj&rX@ki~`FS5!v~O8cF}adfe*}AGY-wyTh)}Jf8Ag7U~loJSn$QKM0S5`uF#r z@~^m~hkaIMyKc4q&_f>dWHGxsyxA_@ga6S}mR;@DG?(tpf8R>fAa_FrZI1H29VsLy zNAXd~$e;^+5-B-%@sHY{__!kn~7-KSvt+O{KvxsTugtUZ|R_kzMZr+s{yhu1kf_yhpH;ztKk z34HPaOWdsyWp^Bj2`#=2(_eDy8EZ;T-=f)X#&kZ~hLw`Jw|*V(fC(Qi=ULMr{4DY`u2gci)1urIzpP;MowrRA~k;k<1_Pt+IAgQ zc`|_XFRMvi(+}*QjWfc+PkT*i>hGS??VIl-Whzxuw<-iub_$Rna$S^xtQT-~eU(+7 z!ok5cAlXPUdN~j++D~U*yeg$gpStWq#_xwN+-yIMEW%aQ->hEF_DV3F zOKUy$JL<{ai)eHkv&WITRPVeeM^0so@_n_oj&64Y7*1 zu_txv*H5YTtYVd71(Z%Qf5Dy&gwGEOlgS-uE>JA)EyGLA%kp@?t~LqhOm^BJ)&CCi zsel%1YgU4r?MRZDi!8pt*2G{;90p`B)J+mJ%R@DItCSa*_`RBuf-&z&ZbQz@Z9g^H z!=IKnDW!~Oo&yCxAxGHlmdB}h%;v7?O~>CRkbin_eB{x`T8uc~OtL>ty(ogRaEY|t zARgdKnA#)MO?~AW*KiMI1-MgiTyreo6=^$hMakGY6L}Q22ROX> z9hJ}v&w(-Bz4c1e&UAb<5=~CzH}_~|jV8pJkIP+2V>kK-BsWN)d+g-K$wf6Lf!Q@Aaqvb4iwLH93!!@_-FYS&OWfLD#W)zT(T*wUC~4?JtawNqZUSC$JJbV1Wyxh6JUVhXf2L<>x4f<> z3Wz-aLBISUD{}A#{L1ys|_ zm753b(OcUUU%IveSi#=gj-&>m*2~7qWuvglY0P)i)2h9AiM5_hS@sF7AUcJZ z7#)DJroyQPF5^rNhy-T%K@mD*nfy?<_1TSwJNi%DlTAwZ(U0|CeBAG(y4M4QelDB`5}GP;QOXG(r5(Dq*di?V zRm>TT>__iqlOLGfeJejtbfD_5PivI)*h}!*eDA7hNAsC=5E$q%4)|6jSjs7EU>=lW zpM-n{9%@p_82>yxZ9fsYez&Z`Zl7ZOyCWWO)G1xFJ%?RU4)RBKCE-*Q1f2$$Q~vR= zd`?G2p)3Pd$STcJ8~!~ExiE6;*((g$2g;}gl`MssPK^_oI5?LoR!nP1VkY&L zy~o1X z2gfM2IEZD`)%rLlD!{*?YpWxP`Ynp!O^Q~1Bi%;)(ndfiUe`L>uvwA<L zPx^%ej(}H)2bQhc%|Sf zsO)?UF=BrT8Fe5%3A!RR`cTK52Pj!rR}}-6HnB~*AVhGC!7pa)h*aG`S_Iry7aHJf zLu6o9K?=E!YDI#!Gc3HoORzl-63jaRMywb5A7V+VL;?{-O{tjEQJ$KrM6MJ3(r>)V zwG;u@vN-b4D~2q$YYa#^_~E^Cb4-`qu9;nFw+X^1FY1rVnUh}qESwU-)Z(j4x z1v?$ZcjLVCz0J}S_Pdo9w+?ufa_2K=IuDDC-99~a=(2Y#T=5x;pOKjAwr)wfOHBMt z0WF1eOug}?rnAPcI|^=H|C^%EbG^5yxcvnC?XJ!bLd*-;V`l6L`gNet9SMyQfBI9~T#wkw%_-Uoa$C}ufTZLtNi7(0fn`IQ(>^#o z-j~Ai-wqpcb)d*kf@lQ90@!BS-2%)uq`#cZH$)QZd#T}G^-Z1k(IXK1?y%^;LiA!w zLgvy)p9*=OUT}H8ThAj{>5nrN2Mr+TtM*8$81$qwA7aH_a-C_?6_~FBYy_Y?dI@o>h)`LpCBP4>HZ?QZ{$z4Wg z8TQ|Jr7L4*bm2A)lin4Cz4>Bwi=;7hl8|Xily9>*#tbx>`jg+h^Ah&ks@q`;5mtWg z`$C0!RgsZf?3J_R4#^m4uE^Ee*sqZXCdZmJb$qC|`>PVk<1QW&Vm8uSC^ieJ3^U5c zx{I~fmQTP^v6ndPz(aYBD@NpC2QAqWA1Egj8K0pVECs5j=aX`cGj&D^=j;`yXhUkv zYm$(1^)#zDD97AmTe!Gl;5lcr?}j$l5bu3O)PrMKCkwhpusjS*U3-*?T2Dx~nKn;X z!HN~VOAijN8~YTcAYi%QtW;5{heSisJ%Jh{Kx+?d4&dr&xOKNS1--BePzJKo%=GPJ zSr0vNv%L_5+{RmD1l-EgriHBVV@j3JhQ>aIp3ZTr_=I@|U5U@~^EX)lztu^^3oP#{ zR&sS_(y&%9^G=`VMMr(BwU*xEMhuBn>j^*ndbDia?sLB+C+2Tjo&T6Vz6s&2u8Fyi zdDA@C+x0hBq@3ve_BEbvNxIjZbz>ec4&;sn=bvK|X5=wt2gV zqfG_8D79!&hWz&?oFh4JX0WUSkl{PR-I84(lM7DyyYmS|R`2{s{EDF-5Ta5~a8&G8 z)O?*3->^OYh6AV#GlV>3ux07*H-po8Hh?q#$IV1v`z$}^d*1UjIKbd+K=$hoxMb~rGZ|WExW*u$p68CQl zeo7lY8R@avV}Ha>x4H8v%VzrOM+}@RIZoqeRxWdXVf5N(Na6#ikDu(rR6|myg}H&V zCMlHos)~zPJP=jaIj^~DLRO1Gv@mi$7r)@^HcU^bW_`Ywn1z*Kx}`R7oPN*{dsmg9=&yO(CudsT!Rvwrje5=C$HC5>bxWM;=ZQhSJpQxXXxsmAg_8SnwJ%RvJ5)R z;D3rbkLAGc@lS~FZdGM-xpb9GG+89y438Pf?mhMhDtz5Ec>DywwMnvt{M-0{7 z=rE?JWz13sM#pM~Hk(Xc|Abz+RAcn3S&{vmtJ4ht{kb6luvyg*6PE|3Wd$Ns*}Z)o z@r0JtRV?8N@PvRAC{c{r!GD2%@XZQ6Pls97w@Q&aP(R&mv=yj4)PR#V3pw^vE0KL;Y1u@*tq{$F79`!zYaa zwOVG^5A9n-W4hB0Lv2VI#8LFX&It7uQ=$b?UF8)>d4{46YPjM$J?aAq2o?TIAXNT& zt^*422zN20ksMDOuy^J>j)J+o6tbZ;&<8uY$DIdKy*ny7UfF;oPb|_5n^Oa6T^Qi1 zZ%ZBfl{-Q$P;&>-^GV0Fp?dFvAPQ4Qo3Wmj`y@R^XN3wtX|;m1j1O*w_!sji)57Li z0_FmM`>RiaX-NX)y)Mb)n?(&#JQwh&D);v+k5qg_vrSl~a?of9j+6N=7bO?xJ(d`N zYw^kcdB!)(qYvdTrMoKa!fOE}tWH-$HAo)WdQwD1w~X^6V}*U-3nu zmsVJR)wj=$FNg|Kg1N@ch)0qXT*pTLdd8)bK6BICjW6ZrRb-JK_5J1523nodW9#TA zO4SEkpMLIdte`oEmu?|O@rE|;p85wM${9apF6WV?0anSp&sRQKWL0h5gk0JUc}-Tf z@@4}(9)FV0drKEzp*$6|Ge>PU zwlr1!A4DN-I&#f&&NVp90BXx#hg>D~^zvK+hjZIkI=93v?~l(y_d6IPtI@b}=Irb% z0OiJuCBy-Rnf;e7Pl_3*WcP4);t3|8!@fYM`W^<_Ym)ZJ)uo!}Y3Lx4% z%ghZXdjsVH@4}T;h@8`hnu}<}h+j}jF7~d~Mkz?$2v+sjWDplg4YQl+hz5=YPx~}3 zqW}Aj87QTs`|TCy{XQqx{tz>Jgfu=}o7fhDR=5P3;05zso0W6DtK%QhVZZ@(cJkzQ zXGOh;76o|;1ZPo~v{*z{sH7Ux+Bpi&RA`01j4%8JP&PTixhoFnuJ;AmK!=n6c5aR* zjla(~To|(#8jPJhy6avWR&g^RXwC6vp7Y@9C)4YF53cdHm>`nLq_ofT$g87sm%nT| z@|2HdT#VlAHIU|_!%nOH#ofuKtxrfOt~v|Mg4!)~_`V>O;{h-m52`q@e&LtwG8FlE zM1LOo=ObNGgM3H4V2Ofb1aZra9lMk$cPZOQ(zC=EhI(FpB~pG5>?PB#`_1}2U^q6z zT-pTRGgb0v`Z9Fj7C^3)c<9{R^J@uC=iqN>Q8MNRcQvJ|v8t`%jv>#~GUhc*|9c(L z|7qj0b>3*9@8f*=V`{MCk3vgjfq>c(tZ<8|S8fXD6x>yQk0G5P%?1AW8hJo!FU)Ej zK3wnss5x6a#H3{^`fAqx{XlS8ak0Cem99VhIyUPtGpP1Ee3SLuZC0!)Nkh>m?PF&u ze~DGjof@~Vi}dOLr}GgL$dzNl49E-D-q(>jMyd<_e>}Z?T+93aKkmF8$8p&qiO>$A z!!SuG>UHQ4xfoK@R69heA*ra@_B!W;BpH(E!X#N&Ym!yE8oH$Gt4&eUN>`&=ZMC+& zUg!6i^Z9-Mc%R$WJ8Q4k>-l^Oh26ch!MhU# z-mqs+jorS6;+~(swsqd)gZ}zb)wxvik|O`rMv%5JcZ}b?%@55PGQRJ#E|P+hl1J9<({W2uT4mV2U0a&z{s!-f)6ZF%e1gB!TTZ$dH)1zFjl_O3x}gvX zBF6x_bnQp_y^4(COc|LCj7tEVu@ysI9v!sgpSh8WP`Kfa>@f0ELCuPQ{5s=mmi$>` z%#g$?QxPnsDq~9Ph^G9QCe`v>^dYf@xe1j~VHEh-MQ!u|ZerbJrlEu;0C6BX!XivA z=$Wfr=L-^PQpb@5N`eDE#e)x5P?60dzSqyC7%qiLuRG`uk7AYxI-IuF{CuL;hCFX( z|B#Z`7w=l3Oezhvxr`B`p#_MOI;I3*@iQO2q}=ACF7)GXPh}~bYRX12-&ywejmC^N zd5_Lss(s<%^$d78(<_vnopvVF-arnFdo-wNp=)ipZ@oXAM26-OqO6CExav5VJ<5*x z=M}1dP1)NS_3+P(FT+($O6&6%iv1}ddDO=cnyoFHk-kfl<&-s$ln&hF&ChNLv}I&A z1J|-YZ7il*xHD}m3eSJFRAJVYg3eva!i8xMjQkGDE~cgL++T5B-O4|0eXGdwEiuB$ zgYE?{k+)S57H%O?IkO$)izhGYSv9f9m@p7qXkRA%4qDm+@Z|K;ZSH_$U*W#H-vKFVFT9bkW9(R2lf>9vP}xC7$xX1FLj+0j;BAs6HG zHwE_Z41Rdy-$|aSBf2{DulDSGFn-$#Q>e*XV02ZtDk~6i^w)B)vRO-FQZY+P7dtFU z-GuMpxE2$jkzFn3Mmp&Bd%RGZniHs)%_7j;-&rA2JA(nHT(i6EDeCxW6u14@f5^uk zn~jY79)n&@IL2pPV)|9%PhY2>f*4@Xcf!YW{F{pJGC;T7LnsEX<43}lSdmn)BRJ^T z&(epXvf7RPV7g%9T3rJc<%*E<1lWNecc1wLmfh(@U}^tWOBajOlz&E64cHr(0DaJT zDDDrtmR_5bGGL*f2JiNQoMIFEQRFFQ^)ystbc%*q_R^UotMk|WW`Icbw9eZDHDihnv zmBIAM!)M-&c=|lzKZUKC&sxo*#Cd6)S-%02FmKH8{jS$P+(`SDI>CMZPP&eLjT|PD z>^VyKrF^Gf-(vEL~spHV9Lb4x?|A_AyE375|PE$tn<(Uid=c&y5B++;7iZ+Sqm3+tWSQN#op9F~ET?Zt9(bHhWnGk};cWevQ2qGgRA z)~zEqM(<${c!VYY9j4t!h}|T6;`i#0Bjj>ys&|SwQ03&SL__R=215J1L-iWmkLm04?^u24k?p~=Vw}6WD9dU%_3BS0|FQ{ zWL|R`?9rdh{f$uEL>%AebX)oEhwJDC=Pa<>zEbt?q|nVh>@j ze^^(oiZsx%tXM9DPVe4_N!VB8hY?+O@SE52s6&N;qP_^XS-xWq-ONAuoR~`JAr99g znWS#&XJ~Xg%01+c-$Vj=Z9?8~w#O2Dt7YLn>DtrkA93aMD#faa{XW5ypv7YP4e1P} zt<85p1D0Q$cD9ks147kpWy`l-jMC{r=;BNmKo5P)`z2o}-R!HfWjXHl5~amjQI`zi zUtEPhrGPgbSk|?g>n=~G9dqy;S)9l`HP26(4dbilHVOr6BSlTK7%|t7?)lMO-n2pJ z>q|aQDth>C!qQRxz$B#(sEi4u-HP3X?R8#Qp#gXB*oien*t^TwUDv5qx-}z<9$8I8 zJNQfB(U*=8_};L}D|DhE=WpMCK;p9#K`y;|%cV0%&~lOGhZEWerO~W$-Q_*X)Wq+v zhxD%nl>lK19YB{mC0!l{{iW2?fzmsm<{CASbgHMO&0c)0Z z|FX`8&FfkGgDI$t#A6NRkBJuzwTVXow5@w=Cj>v{JBK1@`OoUo-tHY&#XO9bIqB8- zLuvtW5Tua|QuypMpo|$0oo8DW0?#SQ^Vmq);6$|L^TrATw~Fh#);-WloRc}x>Gu`p z#^jdVIt535PZ!4DpnsO$#j06BACRG={>29#+r-$jG__lK0jj-=j@N71}(AL{tm zwe;$iMzoo2)DTSPYQb9V ze>$94Q)+axcpkV}y72I(byXb0l=#XTeXrQ9;755jcODPC@2Q$;I*eouZ z_fTW$b(m;go&@c)d+q(Cv2}`+3F6nWOw6FtqAu>Yxb=Zez>rEVoq|VZ4`yt!QfRm; zdI2}VLYbC6oK#Y|Lcy5Z7Y3rn2h|0<&cl754zPvB5nEZr2>b*kQMn+7`DAbPXyo1@ z4*%4<4q*^dY$uE|XSC`ZWYNg=atEDrVrNn$71mzGxQ8G(ud6IIfCV_W@RFy7)}R{3dZUN5?4gI)M6Hoo1*be=BK- z&sL@9lQo*Bn6ExS5R9tGGHB`t7ri+_B-J-(C!uRId6IQc? z5q2$R{*J=;)lAT;YG!WBdqGP+@0Iys6Z>7K&hM6AWqls>4oMw{L45bJl^9jkeVU_HAl?BZ=?Ld^g*0ZS@ z=kgVZ{kuZgLQB0f5`K|=VG9L7^Uf&pJY8YD7gw>lrC?)&f!@rxvCzw(2V&e+_SS`F9mUzuL;(ngvlvmk{$qU9 zjb^}SAfXe_Vb6;5EP{y*x<$3|VQo3p8%y>_y~zT69B!7lWc3rps`RkrBLum;b67K~ zAv8oQyO+l$dkrLEi^We9h?dUKM4W$~H2z$*p`6&jO)LjUGnbrvL1TtK>*~noyP=7D zm6`Ut&iGE2)1Ee|4SGL)8#w_n$(`)c$kA*J@;==20bBj-9@^lkaV~PeZEKh(C0Z)m z`|C96By3I;5Uzr|Sxn{zVRmA?n@Id(Ti}LkMR|g;+&=cD*n;l;A@PDR3bx#(2&#cr zUcvqW0o2$qO}T(-YYU~@@QACeCbt2h`{C)%^P;}!-bc&a+;1p-Dm1?eY!x=L zK1zCw==#L$l^pFhT4GZ$3AZf^+Kdj&y3heJi*ww4f3N16AfZ|$?HaKg4FgH7eMPg+ z^e@R)?+ZZ`31eULM)jkk8R^|}4MJF4gr9yUeg4c!-7fv7}k5^;EzIICL zq)It^Yn_w)a0fS`SU4qDqsE05V5#KGEwjD68&RwR`sJZ~+rS2FKf~oN3{fNCx7t#t zVTzoNs$x>dnfr!9-wKxITSHjB36jbM{any+*9tpL5Zx8Hm5d@O00GVdv|TU*&fH6! zRF&yMft14DWmkabQEV4t!=|bRx=4OCz@Mv3HdTH7Q`Fwo7wA3s;T%Ez_*$qt>+@)27YxccNG`p_gEVUEz zcn0byl2-Uo)(NOj(iDR)m%~xl?%?~2%AtF~Z=+sn&hVqN860JaitjgLvF|ij#)t1! z*O!GG{8K8_lx$m{s(N+d)Pjd3g;_HMUhvDxs4}%@(GSqnDr8XXkpNw7Ow%cB881~^ zd=h%^eHa@SJz7#6edlMuOIGQ@{$?!VY6DeI@sqIE?4XBRWy=by1&cXKD}kNh4@D)g z*Hjp9f2p-Vh_d~b(-_N+ip1v4l*fHt<1;*1#v0HkP4posK#;GEFoP6^aLkNL!}vZ- z1nDQbdaT$M+pLD=E`E|zS1&Kw+Iw4NOCc2Kh$XrUhzzy53tHThIIST>hF#J?!kMLyg!3eE; z{YMAgZmep~nCNXCK_BS)(T;S+Dp)O_P%8f-qMvDGJZm^$m4XX3@x_J{zHvD#kuDI+ z3&Rexrfh&rklTMC6mw*)%KfkTU3Z_WeX9?XjQmb1uPR(2$b{Mc=@A}?d@v4}4k@t539RZ89s+E-=k z*W)X2iTgskWk?^4h19<6!XLrBFMBD{cUa$_54sgULl9N(^xdtCh*&ae;_dg0b6Y}^ z`G<#(X9~!K0;{Uvr^jmoD`dC5(s9Elr+#Pup^<=LLa(FVXz2$GE_loU7_XIpZ6+kz zIF$qMwMh@a(rL-x2Q`#}b}O@epxG>J_p@A%z6HVpW5AY5=!t(vcrv#WT`wS_bnK`73 z-bybNmsLkj6E*HXqDa9S(oLp;MP>@q7YU?)TcJbms0Yp59`RDE`uSjOB7!U6Y=1?k{RTDPY7 zxWuICY9b6rtu!z5z{HeMU;X+UhsP0@q1FYy!c}f@CN;Ii- zdr9fPlm2IVvu6?C9qM&Gshg>}$pqycx8`vNy=G!KARG0B?+rrXYWKakU2IZL&(0KX zsrNCppt=18t;0bjI8|EMyrtgf7X>H|6o{JCgnK*wOQB z2VAsa{j@lTpaJx-i#oY41c^ku*Qvsj7rTR|rLfB`=u*c1jWPwa*EFT-ER@J?OTxrL z2pWdEI-jQC7o7jF3;=V-{af*EfTsfwx1%Z0*m{|QDFE}Sko4gQ+%O3z)W2`g6rFZn z^6PmR>h#Erufx7v`-A^acx`Y!QUSEQ8vNW$F|+IF-bur&e`HLw92o% zm@sRs$e?eZL#t{f9D`{F$^a_4@xPMOXYNqaz=C9kp>j)&8~V5g)bjV7@imjX=R8O1 zp&UFTImZeZQ+C{bZF(~vBy*^WNWy|_qa2aGJ1qq`Fb>I23CiEnzKv@_m4dq|qPCb+ zjcPN~0L#D0`UO%@kvN-qX7f`1tOYU{VsI!o`>o~M3cLw58?7#rn%^Duw(KkeiYQ$Z zGq>o-@BsV^liXAoo%B^Ey&5@cb*6|B|L8FaYQ#kbZ$%0j*KxGy5BMeA3I@G;-q=M~ z9qYWqnI(T%+3^AaAv__#n!|M<64w$p3PxhF-#cd3(6oz-92_lA6YC0(T3itJ-Q_3` zd0dt$BCcjuztT23)GGcs8%nP4D>Q&;0`;w(fMzmj5Qh{!#1w#A2k9l%AlE^-GvSZ*(&+qV?2#Si|D! zUAjx~;cJh6;Vx#)3{Kxy{Hn)-H6UkNRc7uY719O6A}i&+G4Ih@N5PHoIN9;=*jJUP zm>>9PwahE?96=S~Y}@e2iysk_Dk+sJNCZdz@haOr0Bs7LHg*8HY0@%yt1VNtO0!@b z$m3)3gC7;!P%89LV3%3Gzd_e$Gi7nHS|9 z+bn5Cg1(!Pm$0)JhPldaQ&p4vlOr+u3Ve9kRIT$@@R{oBTW5B$T%=eMm-#0x+5VhJ zq+9)Q9lYE=UB(#k2EqOwc33D+``)i!NRTGKlG&U=<*OvLf%IOP2%Y?GH8K_wPrfnH zs^arEqNRywJIIR!WXq4W`NCgG2COA4-Fm!pfb;%B-l*3HfH~fo0!@gfrEYe-?;*@L zr`Q5|CGGz5Pjyfk4cY#Q8C*>q>_NsxY+dZ}>+oqcn+J2nFP}n8CaGt6`4laZ(`JOa zBsw)?RqHR69a0i$1S!&({TXmQML1bImbM<{8!4M(1{)@7t))hOmX_sR*w2&%Q(?d9 zriCwzdVdUXDPF*R0Q2}a2~P z5yJgqrxofnoybx}o+HtjAA%gQFW?T!r|~bsF;nrEhGIwnWOd^kMC;qZ8cOS-R_^VS zqLI=1lD`0sCC#@7Iq1;TZ=0Zf$qfFv(>WMoL>d$5HHGNljlBZZET!O`=QHzO4Y%d6#9#z7 zuz+nAK8hB3M4W}a)Yb1c6LL_)a==jqA3e2yD+QT#?CIWbhR=jJqb>ndi7`xieDpZG z^G`X?I2~RdI8)L1vJ~>r!#3;vu;P4+!r#&QFB-}tAL+;(0#&&LO4&~;ZPV*sN++i2 zb!a!;ld&Dj4oqA{U54LG?ZJV=p{JdBHp4S(*Xn71>il{q1H|i)VR3Yo{>s7R!hg_LBbHhV-hwdor#!f3X zJA({|e<1s1aMXQT@&&X?)yHz)f1y=iS*ywlpK{XemP2EXup#{FNspOE8kfd)?p=yY z>aw512D{U3%ym~D!^%i6!&7U>NsE2ggVzRM=I5Y(Sw!if`}A&z?O#Dt9??)Nc?9Ul zZrN?=#dR$IY2sqmZ7o4Pb~g*!>RPIfxgd&fD^9dy@8}-A{2Ta8+lAG+9ai+cTV5GC z7c0_De9B&D9zDmoouo+Afbf=ZQ`19=d@noTLbGdtjRE7Z`l=IE(A<|H8Q1eU8xtdd zEqvH2&^WT_O@R&2QcHL+p$-@zazQ%Ma2VP*qL-9d7>uV0u8X<`ieLRZX{PQ5PZx3Mjz{!xvMA(N z!KNkjhGL_|`&Wb zztch1w>0egylNNR#@uaC30fLXoL+$=uvr}mNdT84potWG)hB@L?A(@x^Hxh!~w^|&5oVa%of}A$4&T>T_=XF-wD}} zGkvOMR{*TAakxFfD87Rb$)Mdb(9B>Ft95YbF8x6zcv&TCPv!olc=oE>Zdcg#FwJ3* z3^BvE?{S+7m)=Pf4Lj)mxqDNBHKJzqpH)yNZ&G^iV6@od`TQ0qy!iis&`doLIu~v4 zM0jQOnBlx+H@my#&-QeO=B8f+yF9{-Gti2AQpZuuIxCeko?dU2xrV1P87(mUuXxN( z_zs2WGTs?yf;{!B#=7Pv%~5o@dTPI`W>w7QGm6dv!_`k)i>H8x=`QFe zZj{QM3tcvw9mzj-0u=8E;l(F?$M{<91mTePwnNBD*U!D>mVCTEhf;Eq2v}+ zVs)uPW_NieKZfMQX}+}%vs_J?9~Np2rqVm*#fy4awWxAz(OaG#wmL=Qk59%W)W4oScRjqnM`@YU>g!^Iz(`}}7{>=mQ*a+-|gxvK*c+CX`ngR8z z@m;R@Ih)wLeUbQItJ)~*ARdMv!+t)JkF1m0Q;QmeLsV`eCk3ql$K;>&GcUa6YwNi3 zgWK^}cHe0pji`*xOZPFym;XCyc>3sBGLD=CD8z!d-=UB{K%s7~(_vhaI>xlmG@jLG zvRQZK8_?E*kS4V2J!zc&=B3}nH0bm!@)&&vmvoOdD5>j31{Er3UmUJeM`Lp$p(%@R z^zJpxTZ8(ftIR+ZUGXt~pbu$-QUN|hu#XFO&ytQQ`W{qI?ssnRw@CN3JZBd@X~F&C zb54!)X3TFYnd(=6MCN!*q}tekzeotdpXL23VojE2fTiF;nac65qG&iuU9$rgls6L({$z!gRgkP+{(opdh|07_S!ltLtM4{HFd!krt1d}^gbEf9g(vkLZa!e{ zo9>4{KcaaE2;TRaWax%wH#&_$h?gZO{F-YO5Pr7{w%G1xkZ*=*j5|HfFk($5Q7W}pIk>z_?0l=uaD%Hi@STZ`DElTa1AHUsAdZS`c zo-8EffpCH6xYc>}!~ULhEVMgd4w?=^w+2^4-G*?hF4~!We`la`d;-T@Ja|%yjsyk+ z!_a%Xe(ZA73(G?VY z8|GT{6w`dj`Yb%E&)NX|S&Y5x4})^a`Sxz!p8DU0}0t$9Ygjon2*Ha*BBMJ@i`@QW z>|Y5YBTnkf)hy!0Tx3mY#d!4eJ7~(?y0xh7Q^DWjsis+U zolF&3rexEUPfb<*7bnk8h4Gavn(`%doVd=^H;FM7cn`4}zxMfOYS2+RSGCq-sSLQs zS#=)L06aClL_ctPX|p)%9dZ@9{hb6#8X$LeYNO%eQsD{O4Ge{WfSa~FH8=vdlgnwt zb(I7C_ba+jk*G@KY9xMF)f?gQ_b`l&w)99SNryrtNw6#MI#Y&^Eo-h5dtcVl%NVCpM*LZWg)TSR5mLN zbW9K<9Qu)8&dZ0~FIiJ-AiTOK+t_qZZb42T>@Ap#gE z|K3YPi(*wEd!xP{!eI61_aZ4J(H9lD`Q3uBRhIsOJ0w}yl^r)fV}mG? zu|VOii;$c87<6P-y)|1dSp+ZY-cIhZQXW*hstE?^U4ME9*M+yeI*qF{)5}8*W4{;< zO-~)XnVozl5IgV}LAg5nNq`w^^=&_;1!be@Nxx*@@g1Q>46g$z78l_;3k)>PnwErP zI6{y+C&CXzhuosnoVhvC;IB?4>;6GLTlG=}IyTJc>}(|K9QE%R&q1#*N^Y<9;C=Ac zbe_lmIV~MA%!SqeUrp<`;^@x*hlVaTA%KRGnRoyI8mB>~rCR>bbm78T5AZo zpF5K`_fh_xbpM{bPIaWD^rc*?T9#m?ZSy{RNgEqRkj+T|?~j*nAdr>m8?F;B-vx~2 zlnfdwJgrdK8j)jSnxd00rPL5LrarPDIDP-tw78S-9TwP5n&Pm2gg23=NC9FDX)fG`GLvZW|{F}E=`yx4YdH|0zPPZvZV$r*v4>S*uEXOjA6){NKXBWYHi5f>A z7ssV-);Jw*9g)*^8flaJ*#7R)g`~qVH?N%$O&@j9R>(ab$`m+EjQ04A*NKyva}Yb( zRs7?~kIVN~y`u6d646OCXXHHjA?}wzM?KX=%21bo^uc_Mv5(M<02MTg&io zd(VThqGQ-z>#Q)W!KoIx*fL?Y-+}~PvDl%#ZX40JBC{(y;j5Qmbbc5{>vYQ}>Gn%u zZj5%Jn78H9k-F$k;rldAc9a5E-99ou5Q-R|?V@XgZ~ljR)^3gaL77?T@xYO0R#VkJ zx^H`}`U)`l(k(yxpeD4`Yj`Xck|GT%1dVeDh}rz;Mfcg+=S55_k-Uz_;B;a~!q2ikD-D zm~h|C7NS=^yn^3X^=jasg@<*yV{fpJlBp>irA{j`br`PHW)k(r5$?%o1xUcP@pK`g z#)t6c1-44_{Y_yj7^gVtDYIqcxM5ik>}o^ceO#p2aZO7R9p7=6wf{&C0+S|MiUOu% zIO7i)*-4ERHI#3+mJ|#3?p@rH2g66={CvWt&~cyNdkVM1ih2hAqG;ug>Ak8UwGN?5 zhrM-Ie$90KhS-s8nPruJ}Z7{8G5Lp*XsklV=zGULBLfB6g0@#_yxM6$aW+ z>ja?to(aU6!MSnX8YpteCfLHuWz3JF#^N5d%pKlj>Jef-OWl((IPP z-%1m8D)5`!As6(zfyPz9j~T9em)?KhzT1oBIApgV-aYnnh2tRGB;Ld1bBf>q{oTX! zA9LtCCmy}!UU(!8#8z*FrMYVGJThHZF5cUW3(t+l00JT*iY<4ae-(8NTHq~bgG+Gx z4K6ES{I&urLx~30%tuz95OwbQ-sr+8gO2OBNk98DRH9xqunm=5-_J0|HiFM&W8wBvL*Q$>WM9!DT1tu9A9=9l4JfuW;rWlMI-3$Ukdo<8 z?(<9b3_jWGcq8ZB_rayk6UKVn<4L;o+CfJgu|Bq`Y?S3&p7nlnW9r8oQk^){mkHbF zBVD>rH*!GjBiL~5AHY1b^>By2gC@12h^G!cddX4D^jvf*OXWQy6|HEZJXJ5kx2Jm0 zQ${bU`t5$SZkqdws59JVIvGi0o=*SR;325OZTAbVUOhBNj)aK zR31Uc0N?|ha1vMsJpRM-jL7jb)TSGEo&q3kidx3qkmy!Abko(pqE8I?ffDJ!tN@Q` z3$h8+em}I?DrADA)sYiBhbHD$)c162ujK@ysvTJ;kyRQoIKo{ZHdZ6;t#9thBR#G{ z_9mt*g07wss}FU+{74~qyNBdP2q*0;qR z*{*r+iuD^*s9J~eox?ED8SZ&*5e%S*X8^7vl8w08JAuxJ@hIsjU_^7h7!G8O6`#1k z`~!_h7$)?4u)r38r>VyqRIP(~`;f=&#`umL$gQW3yRQK`Z7}Jb-hgxy{czRgXZO+i zrhbpv{`fb*J-UtR^amZw5i5Ten8G=v$347oa*f;0D716{Ooha-U8USUs-~XN9A3lkF8q-c8w7;2Hb}!y>l>|H#HqlgXBZgEv3i^r7 zPb*7B?atP-E)AJ$Tg3gI_zI}OT%bxjz&)hxAe<*mbWl{VG zXlPLW;%l%7T-irmXnL6R29kSX6xq0!W^0^QkBuA+sJg7Cm5@mm_<`Xctk%&E7ySP%2X+oUqn!T zt^AF(TJpB4({KEtW-;aZt)QPWqdIeSD0spxg_AjgvUj){<=J}Ed^AX9#cBz{!!5)JiXs`)6>u9OA`hBOoQYCLvk<2-F_6lBOOoB$}xJrYje@bemqiJb|efThaTzlY(D3EdFZ%HTdz z2jHW$CKbsM2HsPSqEC=UUd*7$UmT@_hEjP@O-Vz}3JR3o_Xa#M8^6?yI6JE#jkq>V z)ZfISn@A&F*+Pxr50ofVmXd%M&pF*|!$byKQg?a!B|9}h>V&qZzhITn?w!~Og6)8R z_FlX67S>M?+rcCpo~E}(;;`VY%XER>QukQ@IK6MwA;`eKcf_d5q!@1TZnO!!WmdxW zGo9S;Ru_(TsqBP}{cimwz|3yOYPpreFDjUlxdEmtw%nk~UY^tApHng~>{E1kU;Myc zmEZ*7M}tcBRq4imMlte^& zvW0K%o;4d%C(v3o33kvS83RQwuA&Dhp9n`-sK*cc7P>-<=LBi2Wq>sk?e>5@6kIHX z)4|$Kc(@7kNoPoPSAbq$i-=>j4uEt;shRyzsJ$;yOZI0!dAg6suzqg!X#Z}#r zY{geAksa6a7HZ-t*DExRE-sm(zJ4X2GuJ@*Ku$VJQ=ZaLs8q|(7VEJup7RN+xrdII zbt6O50V5t%-Z#hA8RGS+a>MsjN3N>55wbhZRl)B!z4t=b*tfsS-b$XlwR|dQe3h6r z(Cs+LOWgHAHZ>!Hqq-n2Bn)`GIES=Uxt~^M5y$oJH{sMh+|r2?;fYj3%pTK<^cCP=6b`zpy_%M8(Hm~+=ivLW zns$c7yfjcN$$T$)FSbT}|0=9XA5}(CKwJsU73t*OlfA6^vuLK~Tk+y}54Keq<?h{+h0x5{RT`VbO6zxBYhBj^H;kfExC<}@a_tn= z*!7nYyQI)AsCrql_w~6vK~?`GFY(>1l@(9F_7y8Hu8d7@pIO>+BJmZW!1w*5s9lX} z*Ehit7+_R;j}u|W2*~Wfv1HIrR$6`Of-v*hS#uM)YB+J}rl_N9R9AOmIIA{l$vL-x z$t>d3w+=Rc1Qj4)=}3uy-wmGNrky%B5r{yN#%ASauvt>*hT1=@1ti= z5ye-)Hih2s0m}+LAbQ8px51U@Q^_WZj&aS+6%wdCT_kPFP1hU!B!D&n#j^-~C-S#S zL5s1o8kKepu%;Ra)Vnz911CJfaQn!yQdy<=iI6oqDKy{5@q)V^bWK}lAZQm(%AZ{{jjs1=fI1NkIv$Dv|veL z5NX@~JS-M7qw5D3_w!&b?+8GH{DcGegSq~70*I0=BzOd!9Mw07ONF_nVqZQ4r zXx75;2SxEPQs!CXKi}Nu6K#m%BMS(1+xd5GuCca7e%x5Q&A=E~omJ2>`~e1t+S&K+ z9&Q7CmEX9B7B4}I?v5f)*6$r(&)zvLFZRA#HfN7No_B}|kg@ei(SQT@23nAD!KPsX z#+tuwOoXMVh8fLFgoNI_K|3?WyZM~&L8a)Y|-|#^9nAXSwor# zF07Ntw`$F?CT>d{W|1b2X9uR)6rj(n+?qlH1&L^hN7$Da(TYYD=AeG;RR*H7Q3^{I zy+!BvDhni1_E%`eHR4}z6Fa9H676C3A7)@3!|)EXZgglYtY!_B7m>igWw*v9qVwc= zk+!*LHnEg2GMCX`Yy#a@`q>zK$6?C;VK5SSnvt%v1S_rk$+ddO5%?IAD=aPQdu9*qvbcmqOBMLjlTD=&3 zlH2$!YH)MolgSv^S&eK2$~T0NBZepNO&sEtfixv+#Ead0!T%-H)s41Sl^GJ;;vg0| z)21W$N2mDyJ85iu;PQdL0+LIV=|kfNjoHjC0m=4C{wDB#B9{9^z{o3x{KfoWL)NB` zSpO+fSfc#1r1lKHNMYf}r_YWa^qQ@E6c!a#ao@_;wLCaZf5^J8)31U~5XCI*!YtUr zMgUGgWbq^6P~eT&d9Q{XC}(-M?tpZ8^t;nA9R;x1Zq=DJ(N(9Bk-1Qdnxp=cjXWyo z$$~EG?Hm|lIHkVg^?^k^h^7#zHIfM-!SMmpLC;gRpX&2}!2towfntNM{U8l>Z2bg9 z?vd1Ky72=ZT@nZ3es4GB2Z8!pu6OWJR(flhQ{Sc9w|Tn}6dO?fVL<0pAWq<{xE+Oo zX02wZy!f5YVI&CEb2;ga6@;ska2@8;c6!2hn=C+;nLXH*{PO}6iH^zT`>8E)W=Xt#b@IB-VKqgUg?_#=Di_!#sf|CgodjUIf;8 za6EjpZjTe5AG^r`gQ181`6V_xFk;Qw+3O7D9&slvpBf}zSZiE8wk5%T(&=BL25)o+-ofLEf+bU!Vg}Y{rb!P?JY(z7 zv;Fe-dk65vx|Y9LTvV*Zp`G7jiR?hoo?>CkU1+3c^D2TLVDE|5jSX9}IC58$cQu`S zmRZEl#X?Sce20l9jaz~cD}4TXR)j1|xM=#rmI599_W$yw|6A{q>t`wfiXB$m)ta61;p01xJ;*{H>K)q+--7HvB-z!!h6dc{P@VxaSBG9bg2L-p)wrzmqK6G7hYHqOV$IT!>VroDp@^62^BrvO!d_$63^| zLayP8KLp`b2E&5NTS(rj628?>^PD97o$g-JhmxJtc*>o!;316snf_P(R1KX)FIGr3 zqSunCj}4UNg2v~x*StP-gJN|tYjk4}t(aIii~@qmvvnGxT0~dZ%IXKHTOYqY?c@@b{Bk#79sc`y`%uEjgu!uvA0YNGQA&mh~q19(xe_rF~%Q zpdGkyfW1-+io8{e!VR<)6Ow`R_)LQ@#g-t{M6;CWOfLCMbN}(vTX5su_Deuga^M9R z-}OrlRtvgxb3;fIkMlT*(C=IZhk4-f>;<4+nr%uP5@aL5_V8<3=aNF+rDzKZ+AZtI)JhXz^}J_aN~AB zsmc?--u$8^H}Q)i0E{ZrZvF575R9;-4I6crgoaVa&@Z1j<$1@^+~2DeeWlT*rovBZ z9rLwhCZnkzdR(qMXRp3mk6xhDO1$TamvP%&>cS4JNTbc^?&NC!_}MB8f6%f60T{!d zoMXTNrh&+re$xc$~rU8 z#_o#gl7-@5L8is0cYad82dOhF!HdAZY{i)m8Sl!|f*r(a%(x`TH;Ajs3X@&z$xGM< zY{wvJJpF~jo?^t?tUCY;KA6J4M#~Ey{j_iBp+I5Q%14^b8^c$vsnghSeFGDSqp<1W z3t>w_@gHsHB>3a6d`QqEIQ>|Z_RBLV_tTB>l(jylyVh!>Z#lSX-{Go@`3^cgBEOi& zcN-A_)s+N~%8r!Gcvz=>$|B$BjDbBD4S1k@4}Q;1Sd~)tL32Z{JIU=gle^uw2(%L_ zgUuPgupLT!59N3|=^UPZ;HqPwx^*acWkx?>JMcqw*=Fc51-bQP6md@F+mI{2sIGze ztFI`T+u`8Ks6wB`HZ*dE%mj&dF=?ePE)FY(f(BaZxd|Hc%=2MMhG`^x^E`K{2fH2RUym3e&KCBw7g#rf^Pi)VJ3YRicGb6d{)O{ZIqN)bb_GFse%c=Q z%_U?te)m-Zn&9Zz&o&__B^h*pF(;w1*iKK+I1PFg=F0wIs|miUjoRoQ+C1Z)scpp+ zV1+|sN07j->!T-!T1-7ZvFOP{R7rmOiYo)b;!Tlp%*&Kdjji0eEz>TH{^B~C3RFz) zA8;r#Y1=)_Pk`wy7jVo1PjW;*%^G;M0+N%8GaKn3YK#ehT^=N13yY)8AmQ@$6XZ*$>`7;wf`nhW>(ulP@xxB+y(YFdCN3aykXYC*Oj$ zA~#QwdE&eKnMdnd;wj;xu5WB%L!WeH7I-)mIwFl>;`~_)U=hBA%%~iF)igm^0o%D! zGN-S0YuWzl=<2@jkR{M#wO@ifp&sd84%a%r0qm8Qi~_~>2$J4)vTR{i+#x035Y8IV znro}rRMHQ;jCS9-ybzMv-3QE6m0VQ{?i}_K3uhq5C0`AQ#J-@|90~2#ioHS zmmatrfVAK(9c|xs=@>y_f$|S!f7dy=_7Z9OD0dy>%In0%qHl!v(V`Aox(&9489hT` zt!ateasqjqz6Z~f9R8%`>5)sWC4nDkHOZ}azg?F=XZ0y zuit;q<39>M&wXF_^}YrmxW2Ij;=h@ve-Lw~WHj0X{ z+#mqkmHL1B8@vMj$JU^V2siCvD!2WN0i$q49<)dd)rZXe%A?k$wh`LA*^i6w@557k z7V6Hjj3v-FL@7+UvhExn3)E4NBx1%lw^GYNZpd&eYZ@tvIeXX+2l-S&g zYPdp541#aZ*@0VxC5LCrG3f9%k{$=|c2~seKVm)&Ue$eZP>3a*{X(|yO3#8<9f*$J zuRjD|oJbuW4U3h!0^a3h4?a@rMRUftzeg{{r%48Qg?{j91`r%u6I19qWbs28G-YT^z%BTOi332zg~|S+{mq^S zPY-__?8Q1eSFk~*tgo76hTlR*=MHP`_7>V{D|h1-*RIYECak_%-_O{d*xfE4A{ zl=t%(I92H#(N>Xg`pb;u*eG$sejMILu#UP*{=*$ntXwIYCm%kI<8$E-AR1iVLxxKj zE$qMI*S&~{q?;&b>w zVUx^RPDQPeToo0IFH1Z#>$hj;lM7@<(<;J&;D>JY;FK_V%LgHfm=t@V z3T*GekBYTnGBA8ITM=AW15FUAyBo3@#E0b`bNZwv-IrYT?@!Kx;o)@YdndgP$^5j7 zahRwS)q7jI^f3L>zd!kSP;8bliK|7Rn#m&wAL)ut9NpMj-Zh-?c_q97{XO}*GIN=j z7nUury3bX;fCaZ$j`*J;)fQ~7<}+DZ^wC?M5r3-2e_IEikphXrA9Yd@6DPDca|bJSLg`r@7WCK?&^lgs0A38!gPzt=Gw^1Gpf9?@O0a-j>5b`qqx2b3 z@CIL_Z9i27zNV%;v}wv}JWhZ(!cBO=r?A?#rEdxRsHFv0-T)rhb0Dg^@3wCfuHaFA z))x&1si#p7mJ;kvw0N?xs5jj#7(jWk0p5L(FMiR^wU%aJEU&@q<4gH%t!%ROQTn4@ zkvkiHUF{9{ArwjT=W71Tl3hE$qy@758_7Grz&B|#${_LYn(H}w)Vg6+HM0D|_T+_$ zy+({a(85u_qiNS0y@urS2VTj|&%U%T^xC7r2YQ{%hydRiVV;8d|F`;v4cJeV0p4xU z>FAmd;4*dwe;<(*aoy%M#D@p+@F>4dxqI%x%zQ*|bZQ^R{~7rA!;jt(gg;Eqh=zwU zrCh~WF?seX z1MD1)lk`;*{TNrK-_6LCKvvGp|2G8RgPUHVzKDJeLi0o6fVl84>!I-hVtd7PO>S3y zOX+IaAq7hZa(byWr8B0!()+Kp1PK%k_`Ly?i_o%>{4?QJhk^g8EmYX zC%Jwc;OtPL=#94ckszXMT6yu(hzyT{MeAOtWP-g zjPr9W!0uoVK^ESX(NKQCQ-z-$bb6524T-vJ09#fO%Pj=kX#s;ve}uk{=hKm@U#jl_ zed|571H~6e1n|;5xcB{A+UOyJ8SkzQW>?#o2E7_JH#9*#uQx+{r%OfRD=y{F+#<6`G z?I2YX`d4mt@r^fkt3fQN+M;Hs)8TQrWR(A5QbEy%u5{;ayzQJRA2ev^N>1%_?b$dB zA%`B>1z{^7&Pfl6j+kAhW;74syoxkrNWTv$>=ow<3{y07YR-Q3{`!0blLs|%J=-MZR_Xj|-htKHBq-3Q8aL!B`Mi4R; z=vpfHD$G_G-CuvbxUuv;D386iZsOSURbNYfxW;zh-sEpLywc@47mH>$GI1$jL%;J;32UPHYQP zE~Z^oTI^d!aG-if2P*1xdr6A!REv=nx^W5K zq8GkbuMs*`MAZ{!11(!So047q=GV+NN=Ad}c|1LS4NeYsbNP*xdCDi|Kv3ka@RV|0 zJeXrI)>L+R9Q~m;mGO2QE0eL4UupjUvYOjd_Vq{|y`w6=$0)ds%zR3WimwL+%gs!o zzNdm}B-Npj*GezNu^BZsTZp_@d`e&q;^Maf>1!OVYgga-Qv-)=27ml+v-FCMh!dIv z&nCQ@z($n)BSQQ$Jxm%RGSmJ0`j1#o>jKo_4TYG`{Q$0djNmhPB}Obd`i&Z7^4{%` zoskM+dKy*Wm?7N+Ztrriz+9z|D;jv*7{Rxfqb*WtxZ4N;gYU&B%Ws5>v^&feuOl&` z1?snYDKU!EIUOQ|%3BSQJ=1HNP%l8JPx8Q66Du(X{ttOmA<2lKk6%|GZ12W=Tp^0X z)q|AZiF@3_qQXB|bQv(W_Ek=XkDhzAW{@^mA)X?2U66DBg zkptH+co*b7+!4$$#P65qvz9W}ybaT(`SroAJEXOek+o}hCVY^|ad2B%fv1L#yBw%@ zW`ig}72_+R1(Itb1Y~7q-Xehf;IKR3v(u$m*9kV!prq@ygvR%rw0YyYfjHHTl$ zgo%h|0)7kTobTqmGIk6~2x?cd2#{_bldGNiy;at-FH&MxZcZJ&w^8o~6a;0_-A zi2PbD8zR1%%t=ruV`LI?*aZ-x7t!~j)A3^(bt$X$GYHoAy&1TtBr`|$YcQZfw!Q?v zqA)uPl@wnNGvDC1hIi6~Fdu8-bTKq;HNjJtPpR8pu9UOpdRzMkvZ3Ip*G5m+#2!_| zmOHQql+7k|2;z}+2V7_JuJr3KF$CtU;yj_5nHw$)K1elweVg4b2m~p^j=pVgd2A$B z*WKPZj!}NDEY8{rr#4#%LcL3i*NNihc`#r=Gn2=nd1=6(&C&U6qt z#w?toybcEG8s(BLIFEz@1-QKk4IWpU{{328Jr!N-;kAnX-*7^o?Bc*Z)UX{TZ0wQU zMk7fNg0My3WN_k1aKRNGWuJo)NM#6vLH@%U7JS`2Y3DjjEs7;R<-pSsBhzoY-s#*F zE!Ekjw+03mEOQ4x&B~C4HY{c{LUO%UHu*m%axfTEPQ3)RgQZF<aHo;Rodr&>uWcOs-$~J|!*7u9GG*H6HZ71?b|@ZyGa-&El(8BXhM0 zc)okpax5wHl;C^L=-~i+k#5{{-zx{63F=~;{4mv7&o0Z|spxy=9JTs-`&u3PWKsUQ zOpRQ@Mz2l-`;4U}I8qZh(9v?bV2!{=pMN7$5DiLln**j0T5wT?=tk}aBuZXNF0z+g z1;P_^0bKV-Ivz6fa5)ws(UH>HSUY_#)dna4-c`g@!ojCJp?qWQjX)u!$9awR5Fg7& z%YifFG2;ov$CL8H-|_mL_vSHA4hRi<`kX~Yvik=u1ZRzN!W z;Gj(8RfvZDW56w-CR~c)JKN9CQGvn*kR1z9gBx>@rt%nBERW|zu+pThO)yV2UHBrM&XiMGw{E(lN(EE4?8~NL6Ao`iolDeylI#eA@m-C z-?0njgs3A_^z#3<-lZYAQ54z?@Ph-=OJRUE*ZQKEwpz+RR!#=hy&9=LxdvusU}`xd z!657oRq3Vr!x=Wi4e8>m(mN$|{JJq0&WG}fHx6?inaTp04Af@y`Uf|Ch)sl?UX*IJ zh>7^tzqybU6lMah(cdQ5TQC^~jd0WM$=L|T5qc3jaQ&bNp)m#L4-H?DmPA56FEaJI%Up^wC!uOva6!{N*h-mbtRlOXFy1eBU zM(sDIIgVb3oDXFs_c3aU&gZhr4vu8?nsH*jfaM^e~GjX%FTJQx50Vts^vsB|3wiYjlHU)S$m zz$9#gMc26t{ObcYNMC6KNidrns%n)Ar4PW1nJ!7@9L{n|x8ZDJMAD(QQNwmxaPazl z7LvVeHVO>jI@-EmLL=H#TQ?aWO@R@gx3gUuCsEeU^VS}RSO~0|LpgolRm&!YY9JY}jim0eV8Uvbu8{G{GFArJ zu>yKZ!G2ngehcIyIpRi;RCM#D6xjD3IfA?;Q;6n78~gZQ_3xT($j0WXrqRB$?d-bYjSU$tHqx@WPpX9{i2&iD0kg zLH}a9F2a8D+3Ag zXaRp_^;5-3Y#9TlQYp3sSpN>4Gs*>@?@o`~fIMoxE_8#K-5+6z zBMgR1?#=%pIkhV+j>WZGh&c^F1yJwosJKAY^#H%Ym7iG{9H@TpOQ7zspAe*qz5GCE z>lI@v_CkBI%z%WfZ@4yc$Lv6BU~1Q!KP8xQhSWuYm!Coy6lN2~A{ObT-RUnJ9R8&T2)N~QQXF0zTWYPs*X!VOApM>3{23J(qI04^e)94& z4NwWEe{877WXd9+=Gi^Tahh+xIC8bNQ1vg&c>(FtW?Ey()P1~idc>uo)v}K(qxf8) zJm$K70PaMUfH4Ec+o5VTuA8<8yO{grG5qgT>2VS&>Ad?y-KMNsdY_IP|LdqPrlXBC zluuxW>pe2bFRA<56aJ!X55+@g z0R&Maj_>PN(T_uFqSHK*OLh71yp`PoWI*Tsc}RmIu|(AjYH@p$^J%zmsrAuKO`idj zc~lxZx9Z?C29R%O44g#JyR13?*Ku!BYe9wG$wjrI?l^c7c4ZGi((U?Ekv zDx~O7!Xq%%tK08x31lir1xz!0?H~6t=3g%-y;?UoM-^VsoNU?^UAH z9>BT5DEKvPp;{jea5+|DjE#8!FBZPNef3v-K29$$*Fba`5*4|h(5MdenCFV#EWOt) zFaQP3SgJTu_CkpM>P-XqBFSf3qGeM=)s`n4aIoS#VA)hV$EOT5Ql;_JBUNR?HUIu3 zr~kj_j}X4{&pY$F5(wSxO#IAIb^i#sFPcEUjiIkf`DNdnMsajxG-Y<7H>+sW;HeZy zC^SpE-Kzs)x4ki8+W6o@pl?~mo?B{L5(UD+AL+FBao43@xU9WAC3%?4U4M@{)Tg#~U-lHz3s z-d0@hMJWfaCajHhBz1|+3qj@4#gi`0^|u6;NhhJR(0ti88)JyMTn7M<}Qds#8##8 z_CcK0w}=}oHrXNMI#4AziLHWhEF>vcp7$QqU^e`LQ;3WB3hKA%_H+nHZ#p~wn*2Zf z=XOTfaU*-@fpKEaG_hK7#-Y-+3o=}5ZQ`!OqnHipo&z<3-vzcj%FZ6?q8i)K!h~X{ zm3@P05DsT3*XV5Q3TAppj*_~&a3>+oS=`^TspppVzG~Se{oC6&97$-=Oi*)EK(w}I z-K-9peZ@1G6MMR2PAMe3;*%2^(|6rz(Fp>AB%2Vt!4SIMrkmquvxHz<)j+C!Lg}K{ z?-ty@lKcz4S|agksl*gG@_U^sozoNeF>;PF+BiLv$W(Vb2{P3uqk7U?c!v0)_*oNE zumbB1HOhd*YbG!jE;YbqQ;_sc);{`=6QbPXdX0LoN(>aspDsHcpkNlKHsD9PBgc+* zxyr~0dJcs5!oi=;)N}jXt3^tIx4_Rf1N=(0&xtBki^P;Nr1#!YG*v)P-r_m`Te1Cd z0(nLd{Ej*McOfN~wA~+wqE&BbVc()Q%8lX}yDaL(gjS zOF=Z2{lSOO<3~5YNZ-+lR&&?HPRWdQO{y&4F;C!D<}XKknDOsVB|!d--(J}vM+?jg zzsd^_9Nq8D*|9l9!L*p}Mc5Z=FYm3!?_JfNMD~YW-Lx9f&$SiizNm-jIc2_!JT>(1 zPqvQv3q*?`4$j5Vxx*dUfwm=bp0aNyFe*)s0o)6cohDZgA3`=5zg#!Z6$E6)P~JTB znKLUM}zvcHBm)r?w1&|+wX2*&LjHIVbAxp!3 zQSzeOC1DUvAHhvvky8GYGQZVX$_EUEp}gp{`4^{`kk^q=tuYWl)XGzQPYj8Kep}xq z^_8}CA!^smUkOHKzAlVLGkt9j;3bAuO973SMMr)Dv!1{4$W1|n4o@OKw-YaZ_MT1` zWe))b;E`3y$*)~@F8D$M`%icswYxy^n-_DLuvKFVEE7 z?FL^Mb~{cm3`EP(cg0YMsg(tWR|l=w3yb-7voP3;EPU?6U8e`av)p|QkmqlV z-#ZMQk^f*6uur?o0CaY{&nKkg^6Sf55?*IY22dx#xBvp!V<5G?HNMg>t$}ol!^v?8 z>t6VkL7-6VzQC=t0dT)btPfu(BVd=*;6MffI|(E=fj=V7fWk^GLaw-18<4=rc}cO` z@8Wj$wJG@qaO?jUE*QlU*TRlrJ@wZOP7{_V$^x$YqsldRYaJ=0^!jI)5?Xq*Lg-(o z>b`c4YlUjJ)$Y(v=>~7vo8V%3a%kzU3SyMq=v?^Nd6nzs3Ht4iUCv}R;gVx~aTi@4 z6o4Xd*PF?M639=>1ut@M;X|$jBfs_Lx*f^JnY#Y?s9VU~y~{;G*LP#0(GKM)jn?s` zVT03p9QvbA50kCHAY!icx`4^SJx-;#R_y3#tb^2}$=;**V!4qrs@!ra7kOjnCFs{S z_*lzY(2(!t?yhDjAY+=sOFy>NW)4VSvVh>#?(1_2W~7N^CxoO2Cej1-=DJ;eURPd@ z&}$;!Px&=tsm|yh3pT#Vs}0BzTW>Q0>$AB0mtXCpapNsM<+-Y^9K)he|27T01 z^1D+(r6KmQ&&_AbW#-MH+T+1{N~VX)wTvEP;DW-G*+IcGQZpG;+uDa}T@?+On6PRB z@?ih|^b$s6)|fK_{(f@KK>x-qnnCC0+rZ!+hLq1f{;Nf%1&|7``_ux(TL1P5vlH2|7Ox+#VDekJ>6 z4+*2)$#KLY$;p)3$&(_*Sx7^PU&NG-%XN6&2fmCeD0j|kf-1Ge)mjDQ`QB9h2ddjjs-l=s7lbI^;|%Ztz3A)~=rn>TO#}0B_}L0oK; z1ZKt0)q}@G@jvPfq!$Kc^aQ@PeNa-?9Gd6p_DW(4w0e0WhakY)>w!}y37nDU#fltU z>%Twg?By8}_9Tw$1sVhCQ(PK=62D4L0$kw(0?^#)Hee^(4ko>UQ|g;Sd@PT(eW$m# z%hNn}0Xj(sdM||9yAIjt6cIl=o2C0fR&od(`G&$>SO5JfD2>JG31$V@e~urR395P9 z>62**U)Y*EwAGm{@@`E)jCZ-3ATr?R3znvji__;D)ZwdV0?4LdI{q34SXqTEj^?%Z z@*oem{_ZOAsl!IuNqhqTJSD}0?VQw$xk3;@CY_6VeU1)F_8pOKnSu4|f*`Y^r+>-O zf**LxCis?+pAe~OweJZZC)0ukUTG4lFu!o6Fivqcpb@6Qy9Rp=^x{5v&{UYTeal?@Q5=F2 zfPm|T2%6xGYCi{{g-eQj4LZZ;VM~Y9-T+MN>N63KXZYkXMbl2@0Rj;vTy#yPGCeH z9V!R^K73=0(>b)h9aWGcz<`!mOo7}BS9&ylm_A(Nx(1yN4vlcDBXCU;#=E8@7ASSY zfjctNvF5Wspr;=r3c}x%<^wy^aATM&NEn;=Y35|;U3I%w2g9|(CtyEX{J zwb}|=$HdSi5TQf^u}bskq6 z+Km(ONXai?=m9qs;tx|EfN7d2n;8J!c%U^qB>DDkRQ9r8`}ZeJ%=#hxUc9cT3DOkP z5xJeEr>I=ncjfnW{e$U2jj9{*W^6EXId`XpMxMG@W6LX6wsW_T{PMIy0Sig^r24fx zZ_a6GV9uwXVOU{NMAFc7;`ODBLrpB_n*WGQaXaFAhxJ@?aaHIk{}CRHR) zdXD>IVw6EuZJ-}_v z%K#z&06*X*{@QQ=&NLO$3DZ6+IN!wv704Knoz=vO3)rqo%}-;OV6^cj9H_*L7c@+B z44uL8{Vs$!7dUZjbD3XQ2g3#PiXu!mK$2MaqvebrO8*ezOBU0q_7A|5^ZtUDYu0!@ zs?yi>f>$@5G4au&yDo$tsyNqu+=`RSb+g=6!HNowPX|i*8qDJad=sg%DEgwxrSj;} zEHh|O3n&PH{sP)ZTxz_5)ZzHaM+IZEz5Kmv+U&)(jFus@P-W)8=(3a3TYw`D97fiq zH~0!alrz&md#fG5@QUU(^K%ExG!MK_t&e!)7Wk46zuH7`{Y~jb!UH&#j%56Wn{kT# zB3YAQERM#SGwKsPB!luAT4;KqJ37nFfKdh6SGjC6Ts!{f6uSXr!l@gjNiYldMYck< zH7%XgrJ1^~rZ$RW0{lCKLP7i@PwlVd&p=Tc!Q2IPw8AH}`9(df$gfJrFvL!}zR+I1 z(3UQH8^H<63G|{aE=s+is4;(c`8SO2w?jEPlBQuiwhrjzLBRVvr9o-A0u`*4_)UvmPjr-OhjC2-iTp?tgBu%MP_pC)Z03OuE|Ew z2(^25X}8}f*Ny(SivBj&a|QZ6Zk116^exd>GNF@y;O;i>t?-7JqU~MVU3)$OAsA^j zjhj_v!$Q1BzCLIN+c;N%-Pob?)Y0VP#zj&^^Dc)v(~T_;82H5(#--c8h7hz?_u=!m z*XGhiRuEeUlS}vkpWf$Rs-AM73$v?b!u)djXaNuaR>O@E;;nU$Zn}M# zdmbL3(AoQ>dULnH2%8wtX0e(ByhLPwf#U~8By?NV@d}f(Us7!Hu=}NI>m~K03VSv0 z%&n3vo6Y?IV(1>KuGP!UIb2y9dBs(!Uaj`$d}Ap{Xf?)oZYj=IcN_5~S`e@K{zJF$ zud6K=!jgr&3*DVq;!tvGdDkEGn(*u8wMbygYe!1|=W0Snb`bJ#Q7*wk4IP4}Q_`Yb zrl8?neStN+8ZZ$`D>6b9zzO{ax#91%E(ERF6E*5 z7#=k#gLY6vq_n$UuHUT-kUb%W0=;o)FzUgG08oS2spaU_*cKbluO- zSZZm5G1}J(R^6LNh()_m>0rG)qKSU1c~AR=uxvn4+w5TMKe{SRcM z1dwksR61({|k=`nLlNfUVALZpDZW-EqngDMq`;PO}+aqv+Uu>sO3=m%A zdJ-EfCRBgEIh?RLL{4F&^o=HL%@>UJ8{NB>;<1?=cOTA3^1F<)XwULoU>=vd*3H+w z;61=UZkyQ$k?;C*YmQjpv==@a;`@&bd}?Pw1HG=+JvMt)#(q@i`k^Is;+v z;#2R7Pkm&Z9{WqWWLzB%pF*-Ma^=RKl?NcC>ejsc4fBB;VFmD&ET6TxS}^w27M)W5 z=5Z*PPrU^2de5%Fsl$aG4WP(&hwyN6sbnZf^iR(UC3{f1z|<`c#=>aphc3~uF^j4e z%Tf7tpC~%2$(@{-c`=6aDt1PMI9e15w9^eWD!OZ`!jVMVzVXECKD`7PiGf;|JR~Wb ziz9}0z%=8H_C^#311LMtW3n6Q`yA)Hah{+GB0M0|upv2HS70T9M(ef1Z3gqGiGWtV zWDekiGt-Tv{A`SSaca5!ihub|oR*PQe|NJOBe5U&hZ zX~oxxJI#UXe97d*zdx~0T_D9skGc_~R3th;3MwC!G4t$8p(wYqPBkx3ss*7#EWR&A zKR~EVEeiyouPrLWP=n?ue zK^fm;H>D@h0qP>B8)J(d$?M9{?&Uf?DNUMRqi=Yux^K7q1fUKM?LLH=Dbwpo5<-TA zHq|NbV&XHEJ!G!XyzwCT1O=3H`T>B{CPHj`mpCR*epDS)aRjcrdPF-F7QAI9;mGlW zvA|t>!8#0f0xe_Q+sW8{Um-tdd&l1^-8;8!m-S??wzYckQh8ecBnWx&VZ0Pv_x;Te znQ-A*$6PPuE!n})q64^B?@P8|Wb#^pYWtM1Y*S8Cs?0;bn8E46SI?z#oux{E`+bdH zc3f5_3NpI*#jpea_X7kAj5dI`Lwkzf2LW%T{RkQCyW_yn-G$Ty+0Gh_=OEdg+Kpb- zScjL~78^#y$y-p!n!nRK9bN)_Nt&c@ zA6$az88*(^g2+I9yU zGJzE`(i|D-!j}F*If2EkWcyX z>lzVEP?3`M+9s-WpLYbX(mY>Pa)#05Vr+VS0u+wi5e?yY1N9I`615QFc@$uacYOUx z!)k2b^SM>djCDe4@gH-1lCFtw{THgfeUxr7fgFM$#mX~EHSLWH$%}uw9~asAGp_Q^ z8oKOq7QTLGnCP_hI@o9ENr6KASbwkI%}u9+^ht4FaTk1I@0#Y#%I={{D_*vw2YxZP z9sP|4p(z=(Yj8E!M{~K?;SBJJ1wq=Fd0iqoQV;Tt zdKstLws!-$eg}KMgVJ~9n|dHHt6`{&>)R35^atT%7v8_p3{Ug?g2>`x;$GIPwYZLM zU$2i0Jmuu@?@v!KEgb$G$e0YnF_N1L4+R8NkT<8cZ%5haS%@whwx)iYn zzf~f6cDdhdVX4gG?#rp216fO;h~k=0743NK3*q>do>-fX?-4+Ak>GYLvJpW0QHdg^ zkRU;V{iDUtnXbDbeuijj{_G7g0{PKX(ql+=-vnqu>6lV1cwGzdNKoMJbT|7Hk4#Sr zvoT2;OKW+WX-4U4w3a49Ic0hgr~5vI`#5#8MTgbDb%#CJ%q=?r$zY;7s+w?!a}`u zX}0%>i~E#(AvHg>t#5YBRF;ohmL2jKl#9SX_JpofkGrGy8GVVF!b!*khj``n4`{!K z#GKWjx-)<@lk7&?M(gEG&b9RMN;V(IDT9LheQ-wocD#Zxd!NPXKKr?a;gsaH2~)me zCcfu0Sp|}^1Zk?sub;!-RFpDi=LjDCE@!kE`NK+~uZb1~Ij~zcdFa7t?ln0YE(|@^ zUNDD8+>p%u!WuWxZ7HGw3#l?_BX7mf@5} zuYi5a7J?%o!Ex!81bh%|KU!@aS>t|pp--Pl5L;z(5g}2+0j@*nM5w-K_}Mc$U=Ems zmcFJHZswU(Dko$k#xG6exC=TLjL=i7=vyhqLVAuCJ$vpo;o-LWdU_^l)Ukzx3_5Zu zaYx)0H)8yX&UOAVTyApnEBRwB8Z#y7F(PNt@O#L8r#tl3A1q|ww)b{UYmkw0;8;;&Ysl#g&*|!~M($^1(tM7*@x_%#QUwlpFz{vcn}4{Q6Q|5Yy5>7I}G^{mYYjc?hwF;_t#f5aQd^CH*^jK4p3y zgcsmGb*MG@CuHafnj6xX|#>aq}PneCCQiE-5ZjJMdi=5PC=KB>&aZe5XCHf zEY8nHGKtObl_7+8;a-^_mvH_x&Vx@iTWJTuQ@oN=dZQQHgBm}PDS0IF^zWk_Z=D(N5KjQWBBUwqJj-jBH;R-F7 zI8=_j;PcXxkD!{+uE9NNaCXtz#Fj^dI>Eza&^ zyP^}?MSoIER5l1-s-EaKUK8dLhGMok+%?+9BZ;coKILeD0QgAjuY!E&^z&p!`2RdP z9*eHyAro65?0pcQPlqGr&0`$bjbR0$lS}FWm{@M<;P zu;x*I=TrY&z10HTVDJl=d>1v~CiZnX$K&@Tw!Qltt*yd!DlhNNG}5=7{U#m?n5oe&t;WB1?!CrqYTOmgG|8 zNqNzTxDk8`Ks5A1e#2dksrO6)XTK!xBm8H}352%#(;rP3sX5bLx-)r@8+4WTiic|^ zTdfVprd}xRhA!NN+Lf-R83wz%ia*snnNf~1jPe=h%vwA6A4T4X_#6+5A1ixZm#7IO z?-97B)wZK~qC9l}_Jf@WNyZoyCVnujaxDMuH3!SZ}29Q}aVY}&%2VR`% zGhI?Pb=y#z-Zm+1S-DWsO*cjDBk%Olm0*@XRz?_%;V%ec>p3<(Zy?PBJ*{CC^Rr^; zUjp2He`rG`Tzk^(OL8guJ_#J6IcRt{D0_YQ3J?qTq$`6B$KdCP$e3nyB#E&J1zl{W z;K1w1!5nSv8d5iE@F`c3oaAJA8MNhOed$CelHE|mmFjBW#1VU)mU zBjcn6D7W>ASDV;W}BhK$O7GB2rmwnJfAVyJ}5%PVQ zhO0?v5`0o+^;qpI-kHL8@g3rs8kUSvD3m`&u5PUv(&Hlga_X)7E8icXTMSnoQsdqn zv6VVj-qHO(py1?oM^TbR-|n-?$%c}}2pAfAoG=!6 zzt*ZGVe?bZ>hF>spA#*#On??LeymUgg{t90(&ig$hr@x`tFdsvMRE*Mm)9^EK8U^HddeiGK*{;GvoX4s>`? zWftTb8AL_XsVn&Ii(s^lUfrU2F2BM54#he;R0sVQkP8sg6)Qek5y(q%1@G_GIh*rH zYmuLF-v@&3kXr3AYM+krG_WTEqIlyzz*O6g1wCb3;Fc$^LB{qMR( z(~dsj+l@+1hzmYT`9?kk0j-HdQ*`8&iA0?FTw0Qj9nUKr0}?Di7RCbUB@x*%{BUp- zM9>_Y0n>?Cv6UG9OLf^cY^HJJC`fikG{=O{V090^##1K)GLXq)wZ&58-0SIuP#vv5 z6y4_^TU}|6k7SKgvkHa9Es{q&j#!tTPOi7Ub9HvO6Kv17=_+1*o4o$m4~8BwYO@4(Z(=oGrpz%U{wGa zTQC19@YLX~u9Si_ZrBaTszyNGtr6Ao(9axG=^yQEd(D3C>=F+HU&?YU>>`q}=Fx4Z z_ttT`?*b5^(cG2rn+UO6*XsNQ-0w-N)>w>?`BzW7z&0W8z)Lk zO;XPR*g#%Zuyno4(t^=Od`vE?2ZpJ33Yh(N0-KJiBv1u6Ja_!9j#1G$DxWug)qUzs z9@yRs(Jk=vF`Mn(pM%%IA7B&xUwHAuT;c?Tj77`tAbGcwfS&u>`xfwM3`jTcKpB3z`**%)5S5Q;?~=%JA0V|MO-dsWt7%(_6u~B1Sv0{*tRs1 z$3*afLSf&$*ki@-w*0p=+nv_ala@IxE$`XHoj1*9|DWx)OwD)J@z-we&hFi5x;-uT z+QjVQ%yVZ~{>-l9k9>1`d;a}@?CAT61&0I{o&yCOEF1zaI3ybUuIs5fG#mi39tv;>C|nRUV3@lu)~kV`5y(2MD4^i5 zKskZIDt7l2pt(R+o3nyLgM<44hLT-nnn0V}4lwYvI{=mUH88#?c^d?@si%Qag0B&% zT$Yjf!pmDrfi_7nG8@P+jXG>J#75KDXs#SBE=CL3(JE=Qt{iP$jJC%{8;YY%>(S20 tXfJ8BUpd+n9~~kX9aN#kFjS*?(q2{98xs{y1IOMNJYD@<);T3K0RTY9O&|aO literal 0 HcmV?d00001 diff --git a/test/models/invalid/readme.txt b/test/models/invalid/readme.txt index cab740a84..6ad8b4380 100644 --- a/test/models/invalid/readme.txt +++ b/test/models/invalid/readme.txt @@ -4,9 +4,9 @@ GENERAL ********************************************************* -The files in this directory are invalid ... some of them are empty, +The files in this directory are invalid ... some of them are empty, others have invalid vertices or faces, others are prepared to make - assimp allocate a few hundreds gigs of memory ... most are + assimp allocate a few hundreds gigs of memory ... most are actually regression tests, i.e. there was once a bugfix that fixed the respective loaders. @@ -18,8 +18,8 @@ crash. FILES ********************************************************* -OutOfMemory.off - the number of faces is invalid. There won't be - enough memory so std::vector::reserve() will most likely fail. +OutOfMemory.off - the number of faces is invalid. There won't be + enough memory so std::vector::reserve() will most likely fail. The exception should be caught in Importer.cpp. empty. - These files are completely empty. The corresponding diff --git a/test/regression/README.txt b/test/regression/README.txt index 3e90a143b..a37da9255 100644 --- a/test/regression/README.txt +++ b/test/regression/README.txt @@ -8,7 +8,7 @@ against a regression database provided with assimp (db.zip). A few failures are totally fine (see sections 7+). You need to worry if a huge majority of all files in a particular format (or post-processing configuration) fails as this might be a sign of a recent regression in assimp's codebase or -gross incompatibility with your system or compiler. +gross incompatibility with your system or compiler. 2) What do I need? --------------------------------------------------------------------------------- @@ -53,8 +53,8 @@ Edit the reg_settings.py file and add the path to your repository to The regression database includes mini dumps of the aiScene data structure, i.e. the scene hierarchy plus the sizes of all data arrays MUST match. Floating-point data buffers, such as vertex positions are handled less strictly: min, max and -average values are stored with low precision. This takes hardware- or -compiler-specific differences in floating-point computations into account. +average values are stored with low precision. This takes hardware- or +compiler-specific differences in floating-point computations into account. Generally, almost all significant regressions will be detected while the number of false positives is relatively low. diff --git a/test/unit/AbstractImportExportBase.h b/test/unit/AbstractImportExportBase.h index 72530aedc..7651d2e52 100644 --- a/test/unit/AbstractImportExportBase.h +++ b/test/unit/AbstractImportExportBase.h @@ -67,7 +67,7 @@ bool AbstractImportExportBase::importerTest() { return true; } -inline +inline bool AbstractImportExportBase::exporterTest() { return true; } diff --git a/test/unit/Common/utStandardShapes.cpp b/test/unit/Common/utStandardShapes.cpp index a5df5d898..e1bb6eef9 100644 --- a/test/unit/Common/utStandardShapes.cpp +++ b/test/unit/Common/utStandardShapes.cpp @@ -51,7 +51,7 @@ TEST_F( utStandardShapes, testMakeMesh ) { // The mNumIndices member of the second face is now incorrect const auto& face = aiMeshPtr->mFaces[0]; - EXPECT_EQ(face.mNumIndices, numIndicesPerPrimitive); + EXPECT_EQ(face.mNumIndices, numIndicesPerPrimitive); delete aiMeshPtr; } diff --git a/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp b/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp index ff3b4930c..6fa92f950 100644 --- a/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp +++ b/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp @@ -73,7 +73,7 @@ public: "Bone_3" | "" <----+ "Bone_2" | - "Bone_5" | + "Bone_5" | "" <----+ "" <----+ */ @@ -139,7 +139,7 @@ public: $body "Bodypart_1" <--+ | $body "Bodypart_2" | | $body "Bodypart1" | | - $body "Bodypart" ---|--+ + $body "Bodypart" ---|--+ $body "Bodypart_1" ---+ | $body "Bodypart2" | $body "Bodypart" ------+ diff --git a/test/unit/ImportExport/utAssjsonImportExport.cpp b/test/unit/ImportExport/utAssjsonImportExport.cpp index 7987804a9..13724f755 100644 --- a/test/unit/ImportExport/utAssjsonImportExport.cpp +++ b/test/unit/ImportExport/utAssjsonImportExport.cpp @@ -58,7 +58,18 @@ public: Exporter exporter; aiReturn res = exporter.Export(scene, "assjson", "./spider_test.json"); - return aiReturn_SUCCESS == res; + if (aiReturn_SUCCESS != res) { + return false; + } + + Assimp::ExportProperties exportProperties; + exportProperties.SetPropertyBool("JSON_SKIP_WHITESPACES", true); + aiReturn resNoWhitespace = exporter.Export(scene, "assjson", "./spider_test_nowhitespace.json", 0u, &exportProperties); + if (aiReturn_SUCCESS != resNoWhitespace) { + return false; + } + + return true; } }; diff --git a/test/unit/RandomNumberGeneration.h b/test/unit/RandomNumberGeneration.h index 81fcfb59c..892e78c06 100644 --- a/test/unit/RandomNumberGeneration.h +++ b/test/unit/RandomNumberGeneration.h @@ -53,11 +53,11 @@ class RandomUniformRealGenerator { public: RandomUniformRealGenerator() : dist_(), - rd_(), + rd_(), re_(rd_()) { // empty } - + RandomUniformRealGenerator(T min, T max) : dist_(min, max), rd_(), diff --git a/test/unit/SceneDiffer.cpp b/test/unit/SceneDiffer.cpp index 6ea28671a..368589bf3 100644 --- a/test/unit/SceneDiffer.cpp +++ b/test/unit/SceneDiffer.cpp @@ -48,7 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { -SceneDiffer::SceneDiffer() +SceneDiffer::SceneDiffer() : m_diffs() { // empty } diff --git a/test/unit/SceneDiffer.h b/test/unit/SceneDiffer.h index 9e04c7210..e0dc6005a 100644 --- a/test/unit/SceneDiffer.h +++ b/test/unit/SceneDiffer.h @@ -72,4 +72,4 @@ private: std::vector m_diffs; }; -} +} diff --git a/test/unit/TestIOSystem.h b/test/unit/TestIOSystem.h index fdc3cc49b..4a42b23f0 100644 --- a/test/unit/TestIOSystem.h +++ b/test/unit/TestIOSystem.h @@ -61,7 +61,7 @@ public: virtual ~TestIOSystem() { // empty } - + virtual bool Exists( const char* ) const { return true; } diff --git a/test/unit/utColladaImportExport.cpp b/test/unit/utColladaImportExport.cpp index d5d81e396..76a39336e 100644 --- a/test/unit/utColladaImportExport.cpp +++ b/test/unit/utColladaImportExport.cpp @@ -382,3 +382,25 @@ public: TEST_F(utColladaZaeImportExport, importBlenFromFileTest) { EXPECT_TRUE(importerTest()); } + +TEST_F(utColladaZaeImportExport, importMakeHumanTest) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/human.zae", aiProcess_ValidateDataStructure); + ASSERT_NE(nullptr, scene); + + // Expected number of items + EXPECT_EQ(scene->mNumMeshes, 2u); + EXPECT_EQ(scene->mNumMaterials, 2u); + EXPECT_EQ(scene->mNumAnimations, 0u); + EXPECT_EQ(scene->mNumTextures, 2u); + EXPECT_EQ(scene->mNumLights, 0u); + EXPECT_EQ(scene->mNumCameras, 0u); + + // Expected common metadata + aiString value; + EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, value)) << "No importer format metadata"; + EXPECT_STREQ("Collada Importer", value.C_Str()); + + EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, value)) << "No format version metadata"; + EXPECT_STREQ("1.4.1", value.C_Str()); +} diff --git a/test/unit/utDefaultIOStream.cpp b/test/unit/utDefaultIOStream.cpp index 800fddbc9..d3e2c8a7e 100644 --- a/test/unit/utDefaultIOStream.cpp +++ b/test/unit/utDefaultIOStream.cpp @@ -66,7 +66,7 @@ TEST_F( utDefaultIOStream, FileSizeTest ) { { auto written = std::fwrite(data, sizeof(*data), dataCount, fs ); EXPECT_NE( 0U, written ); - + auto vflush = std::fflush( fs ); ASSERT_EQ(vflush, 0); diff --git a/test/unit/utFBXImporterExporter.cpp b/test/unit/utFBXImporterExporter.cpp index c78d56b97..4cfc9b152 100644 --- a/test/unit/utFBXImporterExporter.cpp +++ b/test/unit/utFBXImporterExporter.cpp @@ -365,7 +365,7 @@ TEST_F(utFBXImporterExporter, importMaxPbrMaterialsMetalRoughness) { float bumpMapAmt; // Presumably amount. ASSERT_EQ(mat->Get("$raw.3dsMax|main|bump_map_amt", aiTextureType_NONE, 0, bumpMapAmt), aiReturn_SUCCESS); EXPECT_EQ(bumpMapAmt, 0.75f); - + aiColor4D emitColor; ASSERT_EQ(mat->Get("$raw.3dsMax|main|emit_color", aiTextureType_NONE, 0, emitColor), aiReturn_SUCCESS); EXPECT_EQ(emitColor, aiColor4D(1, 1, 0, 1)); @@ -418,7 +418,7 @@ TEST_F(utFBXImporterExporter, importMaxPbrMaterialsSpecularGloss) { float bumpMapAmt; // Presumably amount. ASSERT_EQ(mat->Get("$raw.3dsMax|main|bump_map_amt", aiTextureType_NONE, 0, bumpMapAmt), aiReturn_SUCCESS); EXPECT_EQ(bumpMapAmt, 0.66f); - + aiColor4D emitColor; ASSERT_EQ(mat->Get("$raw.3dsMax|main|emit_color", aiTextureType_NONE, 0, emitColor), aiReturn_SUCCESS); EXPECT_EQ(emitColor, aiColor4D(1, 0, 1, 1)); diff --git a/test/unit/utFindDegenerates.cpp b/test/unit/utFindDegenerates.cpp index 1f8e8e93f..6f2abebfb 100644 --- a/test/unit/utFindDegenerates.cpp +++ b/test/unit/utFindDegenerates.cpp @@ -199,10 +199,10 @@ TEST_F(FindDegeneratesProcessTest, meshRemoval) { scene->mRootNode->mMeshes[3] = 3; scene->mRootNode->mMeshes[4] = 4; - mProcess->Execute(scene.get()); + mProcess->Execute(scene.get()); EXPECT_EQ(scene->mNumMeshes, 1u); EXPECT_EQ(scene->mMeshes[0], meshWhichSurvives); EXPECT_EQ(scene->mRootNode->mNumMeshes, 1u); - EXPECT_EQ(scene->mRootNode->mMeshes[0], 0u); + EXPECT_EQ(scene->mRootNode->mMeshes[0], 0u); } diff --git a/test/unit/utIOStreamBuffer.cpp b/test/unit/utIOStreamBuffer.cpp index 6d0d6a7d7..a0e4660df 100644 --- a/test/unit/utIOStreamBuffer.cpp +++ b/test/unit/utIOStreamBuffer.cpp @@ -81,14 +81,14 @@ TEST_F( IOStreamBufferTest, open_close_Test ) { EXPECT_FALSE( myBuffer.open( nullptr ) ); EXPECT_FALSE( myBuffer.close() ); - + const auto dataSize = sizeof(data); const auto dataCount = dataSize / sizeof(*data); char fname[]={ "octest.XXXXXX" }; auto* fs = MakeTmpFile(fname); ASSERT_NE(nullptr, fs); - + auto written = std::fwrite( data, sizeof(*data), dataCount, fs ); EXPECT_NE( 0U, written ); auto flushResult = std::fflush( fs ); @@ -107,7 +107,7 @@ TEST_F( IOStreamBufferTest, open_close_Test ) { } TEST_F( IOStreamBufferTest, readlineTest ) { - + const auto dataSize = sizeof(data); const auto dataCount = dataSize / sizeof(*data); diff --git a/test/unit/utIOSystem.cpp b/test/unit/utIOSystem.cpp index 767984deb..1e866515e 100644 --- a/test/unit/utIOSystem.cpp +++ b/test/unit/utIOSystem.cpp @@ -50,12 +50,12 @@ using namespace Assimp; class IOSystemTest : public ::testing::Test { public: - virtual void SetUp() { - pImp = new TestIOSystem(); + virtual void SetUp() { + pImp = new TestIOSystem(); } - - virtual void TearDown() { - delete pImp; + + virtual void TearDown() { + delete pImp; } protected: diff --git a/test/unit/utIssues.cpp b/test/unit/utIssues.cpp index cb1adb22c..5eeed6ad8 100644 --- a/test/unit/utIssues.cpp +++ b/test/unit/utIssues.cpp @@ -62,7 +62,7 @@ TEST_F( utIssues, OpacityBugWhenExporting_727 ) { aiScene *scene( TestModelFacttory::createDefaultTestModel( opacity ) ); Assimp::Importer importer; Assimp::Exporter exporter; - + std::string path = "dae"; const aiExportFormatDesc *desc = exporter.GetExportFormatDescription( 0 ); EXPECT_NE( desc, nullptr ); diff --git a/test/unit/utTypes.cpp b/test/unit/utTypes.cpp index cc354eb3d..1ac9a1d5e 100644 --- a/test/unit/utTypes.cpp +++ b/test/unit/utTypes.cpp @@ -53,8 +53,8 @@ class utTypes : public ::testing::Test { TEST_F( utTypes, Color3dCpmpareOpTest ) { aiColor3D col1( 1, 2, 3 ); aiColor3D col2( 4, 5, 6 ); - aiColor3D col3( col1 ); - + const aiColor3D &col3(col1); + EXPECT_FALSE( col1 == col2 ); EXPECT_FALSE( col2 == col3 ); EXPECT_TRUE( col1 == col3 ); diff --git a/test/unit/utVersion.cpp b/test/unit/utVersion.cpp index 0189cd2a9..0de6ef39c 100644 --- a/test/unit/utVersion.cpp +++ b/test/unit/utVersion.cpp @@ -55,7 +55,7 @@ TEST_F( utVersion, aiGetLegalStringTest ) { TEST_F( utVersion, aiGetVersionMinorTest ) { EXPECT_EQ( aiGetVersionMinor(), 0U ); } - + TEST_F( utVersion, aiGetVersionMajorTest ) { EXPECT_EQ( aiGetVersionMajor(), 5U ); } diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 4110edcfc..e29d09145 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -57,10 +57,9 @@ using namespace Assimp; class utglTF2ImportExport : public AbstractImportExportBase { public: - virtual bool importerTest() { + virtual bool importerMatTest(const char *file, bool spec_gloss, std::array exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", - aiProcess_ValidateDataStructure); + const aiScene *scene = importer.ReadFile(file, aiProcess_ValidateDataStructure); EXPECT_NE(scene, nullptr); if (!scene) { return false; @@ -72,13 +71,49 @@ public: } const aiMaterial *material = scene->mMaterials[0]; + // This Material should be a PBR + aiShadingMode shadingMode; + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_SHADING_MODEL, shadingMode)); + EXPECT_EQ(aiShadingMode_PBR_BRDF, shadingMode); + + // Should import the texture as diffuse and as base color aiString path; - aiTextureMapMode modes[2]; + std::array modes; EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, - nullptr, nullptr, modes)); + nullptr, nullptr, modes.data())); EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png"); - EXPECT_EQ(modes[0], aiTextureMapMode_Mirror); - EXPECT_EQ(modes[1], aiTextureMapMode_Clamp); + EXPECT_EQ(exp_modes, modes); + + // Also as Base Color + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_BASE_COLOR, 0, &path, nullptr, nullptr, + nullptr, nullptr, modes.data())); + EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png"); + EXPECT_EQ(exp_modes, modes); + + // Should have a MetallicFactor (default is 1.0) + ai_real metal_factor = ai_real(0.5); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_METALLIC_FACTOR, metal_factor)); + EXPECT_EQ(ai_real(0.0), metal_factor); + + // And a roughness factor (default is 1.0) + ai_real roughness_factor = ai_real(0.5); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_ROUGHNESS_FACTOR, roughness_factor)); + EXPECT_EQ(ai_real(1.0), roughness_factor); + + aiColor3D spec_color = { 0, 0, 0 }; + ai_real glossiness = ai_real(0.5); + if (spec_gloss) { + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color)); + constexpr ai_real spec_val(0.20000000298023225); // From the file + EXPECT_EQ(spec_val, spec_color.r); + EXPECT_EQ(spec_val, spec_color.g); + EXPECT_EQ(spec_val, spec_color.b); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness)); + EXPECT_EQ(ai_real(1.0), glossiness); + } else { + EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color)); + EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness)); + } return true; } @@ -105,14 +140,89 @@ public: }; TEST_F(utglTF2ImportExport, importglTF2FromFileTest) { - EXPECT_TRUE(importerTest()); + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", false, {aiTextureMapMode_Mirror, aiTextureMapMode_Clamp})); } TEST_F(utglTF2ImportExport, importBinaryglTF2FromFileTest) { EXPECT_TRUE(binaryImporterTest()); } +TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) { + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", true)); +} + +void VerifyClearCoatScene(const aiScene *scene) { + ASSERT_NE(nullptr, scene); + + ASSERT_TRUE(scene->HasMaterials()); + + // Find a specific Clearcoat material and check the values + const aiString partial_coated("Partial_Coated"); + bool found_partial_coat = false; + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + const aiMaterial *material = scene->mMaterials[i]; + ASSERT_NE(nullptr, material); + if (material->GetName() == partial_coated) { + found_partial_coat = true; + + ai_real clearcoat_factor(0.0f); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat_factor)); + EXPECT_EQ(ai_real(1.0f), clearcoat_factor); + + ai_real clearcoat_rough_factor(0.0f); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat_rough_factor)); + EXPECT_EQ(ai_real(0.03f), clearcoat_rough_factor); + + // Should import the texture as diffuse and as base color + aiString path; + std::array modes; + static const std::array exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_CLEARCOAT_TEXTURE, &path, nullptr, nullptr, + nullptr, nullptr, modes.data())); + EXPECT_STREQ(path.C_Str(), "PartialCoating.png"); + EXPECT_EQ(exp_modes, modes); + } + } + EXPECT_TRUE(found_partial_coat); +} + +TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_clearcoat) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure); + VerifyClearCoatScene(scene); +} + #ifndef ASSIMP_BUILD_NO_EXPORT + +TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_clearcoat) { + { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(nullptr, scene); + // Export + EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb")); + } + + // And re-import + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb", aiProcess_ValidateDataStructure); + VerifyClearCoatScene(scene); +} + +TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_pbrSpecularGlossiness) { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", + aiProcess_ValidateDataStructure); + EXPECT_NE(nullptr, scene); + // Export + EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb")); + + // And re-import + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb", true)); +} + TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) { Assimp::Importer importer; Assimp::Exporter exporter; @@ -130,6 +240,7 @@ TEST_F(utglTF2ImportExport, importglTF2EmbeddedAndExportToOBJ) { EXPECT_NE(nullptr, scene); EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "obj", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-Embedded/BoxTextured_out.obj")); } + #endif // ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, importglTF2PrimitiveModePointsWithoutIndices) { @@ -492,32 +603,58 @@ TEST_F(utglTF2ImportExport, sceneMetadata) { } TEST_F(utglTF2ImportExport, texcoords) { + Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", - aiProcess_ValidateDataStructure); + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(scene, nullptr); + ASSERT_TRUE(scene->HasMaterials()); + const aiMaterial *material = scene->mMaterials[0]; + + aiString path; + unsigned int uvIndex = 255; + aiTextureMapMode modes[2]; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 0u); + + uvIndex = 255; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 1u); +} + +#ifndef ASSIMP_BUILD_NO_EXPORT + +TEST_F(utglTF2ImportExport, texcoords_export) { + { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(scene, nullptr); + ASSERT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf_out.glb")); + } + + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); ASSERT_NE(scene, nullptr); ASSERT_TRUE(scene->HasMaterials()); const aiMaterial *material = scene->mMaterials[0]; aiString path; + unsigned int uvIndex = 255; aiTextureMapMode modes[2]; - EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, - nullptr, nullptr, modes)); + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 0u); - int uvIndex = -1; - EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_DIFFUSE, 0), &uvIndex), aiReturn_SUCCESS); - EXPECT_EQ(uvIndex, 0); - - // Using manual macro expansion of AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE here. - // The following works with some but not all compilers: - // #define APPLY(X, Y) X(Y) - // ..., APPLY(AI_MATKEY_GLTF_TEXTURE_TEXCOORD, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE), ... - EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_UNKNOWN, 0), &uvIndex), aiReturn_SUCCESS); - EXPECT_EQ(uvIndex, 1); + uvIndex = 255; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 1u); } +#endif // ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, recursive_nodes) { Assimp::Importer importer; const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/RecursiveNodes/RecursiveNodes.gltf", aiProcess_ValidateDataStructure); @@ -555,7 +692,7 @@ TEST_F(utglTF2ImportExport, indexOutOfRange) { } }; LogObserver logObserver; - + DefaultLogger::get()->attachStream(&logObserver); const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/IndexOutOfRange/IndexOutOfRange.gltf", aiProcess_ValidateDataStructure); ASSERT_NE(scene, nullptr); @@ -611,7 +748,8 @@ TEST_F(utglTF2ImportExport, import_dracoEncoded) { TEST_F(utglTF2ImportExport, wrongTypes) { // Deliberately broken version of the BoxTextured.gltf asset. - std::vector> wrongTypes = { + using tup_T = std::tuple; + std::vector wrongTypes = { { "/glTF2/wrongTypes/badArray.gltf", "array", "primitives", "meshes[0]" }, { "/glTF2/wrongTypes/badString.gltf", "string", "name", "scenes[0]" }, { "/glTF2/wrongTypes/badUint.gltf", "uint", "index", "materials[0]" }, diff --git a/tools/assimp_cmd/CMakeLists.txt b/tools/assimp_cmd/CMakeLists.txt index 3a39fa748..5aeac0f7b 100644 --- a/tools/assimp_cmd/CMakeLists.txt +++ b/tools/assimp_cmd/CMakeLists.txt @@ -1,6 +1,6 @@ # Open Asset Import Library (assimp) # ---------------------------------------------------------------------- -# +# # Copyright (c) 2006-2021, assimp team diff --git a/tools/assimp_cmd/Export.cpp b/tools/assimp_cmd/Export.cpp index 1e2f10541..6c3c41de9 100644 --- a/tools/assimp_cmd/Export.cpp +++ b/tools/assimp_cmd/Export.cpp @@ -7,8 +7,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ diff --git a/tools/assimp_cmd/ImageExtractor.cpp b/tools/assimp_cmd/ImageExtractor.cpp index 105c4fe37..23aa9c249 100644 --- a/tools/assimp_cmd/ImageExtractor.cpp +++ b/tools/assimp_cmd/ImageExtractor.cpp @@ -7,8 +7,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -356,6 +356,6 @@ int Assimp_Extract(const char *const *params, unsigned int num) { return m; } } - + return AssimpCmdError::Success; } diff --git a/tools/assimp_cmd/Info.cpp b/tools/assimp_cmd/Info.cpp index e0d511a73..2c35ba227 100644 --- a/tools/assimp_cmd/Info.cpp +++ b/tools/assimp_cmd/Info.cpp @@ -316,7 +316,7 @@ int Assimp_Info (const char* const* params, unsigned int num) { printf("assimp info: Invalid arguments, verbose and silent at the same time are forbitten. "); return AssimpCmdInfoError::InvalidCombinaisonOfArguments; } - + // Parse post-processing flags unless -r was specified ImportData import; if (!raw) { diff --git a/tools/assimp_cmd/Main.cpp b/tools/assimp_cmd/Main.cpp index 2fb7559bb..8d76e1f5e 100644 --- a/tools/assimp_cmd/Main.cpp +++ b/tools/assimp_cmd/Main.cpp @@ -9,8 +9,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -27,16 +27,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -47,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Main.h" -const char* AICMD_MSG_ABOUT = +const char* AICMD_MSG_ABOUT = "------------------------------------------------------ \n" "Open Asset Import Library (\"Assimp\", https://github.com/assimp/assimp) \n" " -- Commandline toolchain --\n" @@ -55,7 +55,7 @@ const char* AICMD_MSG_ABOUT = "Version %i.%i %s%s%s%s%s(GIT commit %x)\n\n"; -const char* AICMD_MSG_HELP = +const char* AICMD_MSG_HELP = "assimp \n\n" " verbs:\n" " \tinfo - Quick file stats\n" @@ -106,7 +106,7 @@ int main (int argc, char* argv[]) } // assimp help - // Display some basic help (--help and -h work as well + // Display some basic help (--help and -h work as well // because people could try them intuitively) if (!strcmp(argv[1], "help") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { printf("%s",AICMD_MSG_HELP); @@ -114,7 +114,7 @@ int main (int argc, char* argv[]) } // assimp cmpdump - // Compare two mini model dumps (regression suite) + // Compare two mini model dumps (regression suite) if (! strcmp(argv[1], "cmpdump")) { return Assimp_CompareDump (&argv[2],argc-2); } @@ -125,7 +125,7 @@ int main (int argc, char* argv[]) globalImporter = &imp; #ifndef ASSIMP_BUILD_NO_EXPORT - // + // Assimp::Exporter exp; globalExporter = &exp; #endif @@ -145,7 +145,7 @@ int main (int argc, char* argv[]) // List all export file formats supported by Assimp (not the file extensions, just the format identifiers!) if (! strcmp(argv[1], "listexport")) { aiString s; - + for(size_t i = 0, end = exp.GetExportFormatCount(); i < end; ++i) { const aiExportFormatDesc* const e = exp.GetExportFormatDescription(i); s.Append( e->id ); @@ -176,7 +176,7 @@ int main (int argc, char* argv[]) return AssimpCmdError::Success; } } - + printf("Unknown file format id: \'%s\'\n",argv[2]); return AssimpCmdError::UnknownFileFormat; } @@ -207,13 +207,13 @@ int main (int argc, char* argv[]) return Assimp_Info ((const char**)&argv[2],argc-2); } - // assimp dump - // Dump a model to a file + // assimp dump + // Dump a model to a file if (! strcmp(argv[1], "dump")) { return Assimp_Dump (&argv[2],argc-2); } - // assimp extract + // assimp extract // Extract an embedded texture from a file if (! strcmp(argv[1], "extract")) { return Assimp_Extract (&argv[2],argc-2); @@ -236,7 +236,7 @@ int main (int argc, char* argv[]) void SetLogStreams(const ImportData& imp) { printf("\nAttaching log stream ... OK\n"); - + unsigned int flags = 0; if (imp.logFile.length()) { flags |= aiDefaultLogStream_FILE; @@ -264,7 +264,7 @@ void PrintHorBar() // ------------------------------------------------------------------------------ // Import a specific file const aiScene* ImportModel( - const ImportData& imp, + const ImportData& imp, const std::string& path) { // Attach log streams @@ -282,7 +282,7 @@ const aiScene* ImportModel( if (imp.showLog) { PrintHorBar(); } - + // do the actual import, measure time const clock_t first = clock(); @@ -302,7 +302,7 @@ const aiScene* ImportModel( printf("Importing file ... OK \n import took approx. %.5f seconds\n" "\n",seconds); - if (imp.log) { + if (imp.log) { FreeLogStreams(); } return scene; @@ -310,8 +310,8 @@ const aiScene* ImportModel( #ifndef ASSIMP_BUILD_NO_EXPORT // ------------------------------------------------------------------------------ -bool ExportModel(const aiScene* pOut, - const ImportData& imp, +bool ExportModel(const aiScene* pOut, + const ImportData& imp, const std::string& path, const char* pID) { @@ -352,7 +352,7 @@ bool ExportModel(const aiScene* pOut, printf("Exporting file ... OK \n export took approx. %.5f seconds\n" "\n",seconds); - if (imp.log) { + if (imp.log) { FreeLogStreams(); } @@ -363,7 +363,7 @@ bool ExportModel(const aiScene* pOut, // ------------------------------------------------------------------------------ // Process standard arguments int ProcessStandardArguments( - ImportData& fill, + ImportData& fill, const char* const * params, unsigned int num) { @@ -396,7 +396,7 @@ int ProcessStandardArguments( // // -c --config-file= - for (unsigned int i = 0; i < num;++i) + for (unsigned int i = 0; i < num;++i) { const char *param = params[ i ]; printf( "param = %s\n", param ); @@ -504,11 +504,11 @@ int ProcessStandardArguments( else if (!strncmp(params[i], "-rx=", 4) || !strncmp(params[i], "--rotation-x=", 13)) { std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4)); fill.rot.x = std::stof(value); - } + } else if (!strncmp(params[i], "-ry=", 4) || !strncmp(params[i], "--rotation-y=", 13)) { std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4)); fill.rot.y = std::stof(value); - } + } else if (!strncmp(params[i], "-rz=", 4) || !strncmp(params[i], "--rotation-z=", 13)) { std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4)); fill.rot.z = std::stof(value); @@ -530,7 +530,7 @@ int ProcessStandardArguments( // ------------------------------------------------------------------------------ int Assimp_TestBatchLoad ( - const char* const* params, + const char* const* params, unsigned int num) { for(unsigned int i = 0; i < num; ++i) { diff --git a/tools/assimp_cmd/Main.h b/tools/assimp_cmd/Main.h index e7fbb6c75..5ac306abd 100644 --- a/tools/assimp_cmd/Main.h +++ b/tools/assimp_cmd/Main.h @@ -7,8 +7,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -144,7 +144,7 @@ enum AssimpCmdError { * @param params Command line parameters to be processed * @param num NUmber of params * @return An #AssimpCmdError value. */ -int ProcessStandardArguments(ImportData& fill, +int ProcessStandardArguments(ImportData& fill, const char* const* params, unsigned int num); @@ -153,7 +153,7 @@ int ProcessStandardArguments(ImportData& fill, * @param imp Import configuration to be used * @param path Path to the file to be read */ const aiScene* ImportModel( - const ImportData& imp, + const ImportData& imp, const std::string& path); #ifndef ASSIMP_BUILD_NO_EXPORT @@ -163,8 +163,8 @@ const aiScene* ImportModel( * @param imp Import configuration to be used * @param path Path to the file to be written * @param format Format id*/ -bool ExportModel(const aiScene* pOut, - const ImportData& imp, +bool ExportModel(const aiScene* pOut, + const ImportData& imp, const std::string& path, const char* pID); @@ -176,7 +176,7 @@ bool ExportModel(const aiScene* pOut, * @param Number of params * @return An #AssimpCmdError value.*/ int Assimp_Dump ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdExportError @@ -186,7 +186,7 @@ enum AssimpCmdExportError { FailedToExportModel, // Add new error codes here... - + LastAssimpCmdExportError, // Must be last. }; @@ -196,7 +196,7 @@ enum AssimpCmdExportError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdExportError value. */ int Assimp_Export ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdExtractError @@ -217,7 +217,7 @@ enum AssimpCmdExtractError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdExtractError value. */ int Assimp_Extract ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdCompareDumpError @@ -238,7 +238,7 @@ enum AssimpCmdCompareDumpError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdCompareDumpError. */ int Assimp_CompareDump ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdInfoError @@ -257,7 +257,7 @@ enum AssimpCmdInfoError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdInfoError value. */ int Assimp_Info ( - const char* const* params, + const char* const* params, unsigned int num); // ------------------------------------------------------------------------------ @@ -266,7 +266,7 @@ int Assimp_Info ( * @param Number of params * @return An #AssimpCmdError value. */ int Assimp_TestBatchLoad ( - const char* const* params, + const char* const* params, unsigned int num); diff --git a/tools/assimp_cmd/WriteDump.cpp b/tools/assimp_cmd/WriteDump.cpp index 5809d4ce6..fd8839a17 100644 --- a/tools/assimp_cmd/WriteDump.cpp +++ b/tools/assimp_cmd/WriteDump.cpp @@ -7,8 +7,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ diff --git a/tools/assimp_cmd/resource.h b/tools/assimp_cmd/resource.h index c516b5e5c..caf3a0a69 100644 --- a/tools/assimp_cmd/resource.h +++ b/tools/assimp_cmd/resource.h @@ -9,7 +9,7 @@ // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 diff --git a/tools/assimp_view/AnimEvaluator.cpp b/tools/assimp_view/AnimEvaluator.cpp index df5167923..5a2ddc182 100644 --- a/tools/assimp_view/AnimEvaluator.cpp +++ b/tools/assimp_view/AnimEvaluator.cpp @@ -39,9 +39,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -#include "assimp_view.h" +#include "AnimEvaluator.h" -#include +#include +#include using namespace AssimpView; diff --git a/tools/assimp_view/AnimEvaluator.h b/tools/assimp_view/AnimEvaluator.h index 950763081..394ebef4a 100644 --- a/tools/assimp_view/AnimEvaluator.h +++ b/tools/assimp_view/AnimEvaluator.h @@ -1,4 +1,3 @@ -/** Calculates a pose for a given time of an animation */ /* --------------------------------------------------------------------------- Open Asset Import Library (assimp) @@ -40,15 +39,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ +#pragma once #ifndef AV_ANIMEVALUATOR_H_INCLUDED #define AV_ANIMEVALUATOR_H_INCLUDED +/** Calculates a pose for a given time of an animation */ + #include #include +#include + +struct aiAnimation; namespace AssimpView { -/** +/** * @brief Calculates transformations for a given timestamp from a set of animation tracks. Not directly useful, * better use the AnimPlayer class. */ @@ -63,18 +68,19 @@ public: /// @brief The class destructor. ~AnimEvaluator(); - /** Evaluates the animation tracks for a given time stamp. The calculated pose can be retrieved as a - * array of transformation matrices afterwards by calling GetTransformations(). - * @param pTime The time for which you want to evaluate the animation, in seconds. Will be mapped into the animation cycle, so - * it can be an arbitrary value. Best use with ever-increasing time stamps. - */ + /// @brief Evaluates the animation tracks for a given time stamp. + /// The calculated pose can be retrieved as an array of transformation + /// matrices afterwards by calling GetTransformations(). + /// @param pTime The time for which you want to evaluate the animation, in seconds. + /// Will be mapped into the animation cycle, so it can get an arbitrary + /// value. Best use with ever-increasing time stamps. void Evaluate(double pTime); - /** Returns the transform matrices calculated at the last Evaluate() call. The array matches the mChannels array of - * the aiAnimation. */ + /// @brief Returns the transform matrices calculated at the last Evaluate() call. + /// The array matches the mChannels array of the aiAnimation. const std::vector &GetTransformations() const { return mTransforms; } -protected: +private: const aiAnimation *mAnim; double mLastTime; std::vector> mLastPositions; diff --git a/tools/assimp_view/CMakeLists.txt b/tools/assimp_view/CMakeLists.txt index 8ff556f05..0199392fe 100644 --- a/tools/assimp_view/CMakeLists.txt +++ b/tools/assimp_view/CMakeLists.txt @@ -1,6 +1,6 @@ # Open Asset Import Library (assimp) # ---------------------------------------------------------------------- -# +# # Copyright (c) 2006-2021, assimp team diff --git a/tools/assimp_view/Display.cpp b/tools/assimp_view/Display.cpp index 4178ab955..ac9aa5329 100644 --- a/tools/assimp_view/Display.cpp +++ b/tools/assimp_view/Display.cpp @@ -105,7 +105,7 @@ void GetNodeCount(aiNode* pcNode, unsigned int* piCnt) int CDisplay::EnableAnimTools(BOOL hm) { EnableWindow(GetDlgItem(g_hDlg,IDC_PLAY),hm); EnableWindow(GetDlgItem(g_hDlg,IDC_SLIDERANIM),hm); - + return 1; } @@ -171,7 +171,7 @@ int CDisplay::AddNodeToDisplayList( { iIndex += iDepth * 100; } - else + else iIndex += iDepth * 10; ai_snprintf(chTemp, MAXLEN,"Node %u",iIndex); } @@ -1053,7 +1053,7 @@ int CDisplay::OnSetupTextureView(TextureInfo* pcNew) case aiTextureOp_SmoothAdd: szOp = "addsmooth"; break; - default: + default: szOp = "mul"; break; }; diff --git a/tools/assimp_view/Material.cpp b/tools/assimp_view/Material.cpp index bcc93011e..100074445 100644 --- a/tools/assimp_view/Material.cpp +++ b/tools/assimp_view/Material.cpp @@ -325,9 +325,10 @@ int CMaterialManager::FindValidPath(aiString* p_szString) // first check whether we can directly load the file FILE* pFile = fopen(p_szString->data,"rb"); - if (pFile)fclose(pFile); - else - { + if (pFile) { + fclose(pFile); + } + else { // check whether we can use the directory of the asset as relative base char szTemp[MAX_PATH*2], tmp2[MAX_PATH*2]; strcpy(szTemp, g_szFileName); diff --git a/tools/assimp_view/MaterialManager.h b/tools/assimp_view/MaterialManager.h index 77ca4224f..9aeb5e992 100644 --- a/tools/assimp_view/MaterialManager.h +++ b/tools/assimp_view/MaterialManager.h @@ -52,24 +52,11 @@ namespace AssimpView { */ //------------------------------------------------------------------------------- class CMaterialManager { -private: friend class CDisplay; - // default constructor - CMaterialManager() : - m_iShaderCount(0), sDefaultTexture() {} - - ~CMaterialManager() { - if (sDefaultTexture) { - sDefaultTexture->Release(); - } - Reset(); - } - public: //------------------------------------------------------------------ // Singleton accessors - static CMaterialManager s_cInstance; inline static CMaterialManager &Instance() { return s_cInstance; } @@ -80,24 +67,20 @@ public: // Must be called before CreateMaterial() to prevent memory leaking void DeleteMaterial(AssetHelper::MeshHelper *pcIn); - //------------------------------------------------------------------ - // Create the material for a mesh. - // - // The function checks whether an identical shader is already in use. - // A shader is considered to be identical if it has the same input - // signature and takes the same number of texture channels. - int CreateMaterial(AssetHelper::MeshHelper *pcMesh, - const aiMesh *pcSource); - - //------------------------------------------------------------------ - // Setup the material for a given mesh - // pcMesh Mesh to be rendered - // pcProj Projection matrix - // aiMe Current world matrix - // pcCam Camera matrix - // vPos Position of the camera - // TODO: Extract camera position from matrix ... - // + /// @brief Create the material for a mesh. + /// + /// The function checks whether an identical shader is already in use. + /// A shader is considered to be identical if it has the same input + /// signature and takes the same number of texture channels. + int CreateMaterial(AssetHelper::MeshHelper *pcMesh, const aiMesh *pcSource); + + /// @brief Setup the material for a given mesh. + /// @param pcMesh Mesh to be rendered + /// @param pcProj Projection matrix + /// @param aiMe Current world matrix + /// @param pcCam Camera matrix + /// @param vPos Position of the camera + /// @return 0 if successful. int SetupMaterial(AssetHelper::MeshHelper *pcMesh, const aiMatrix4x4 &pcProj, const aiMatrix4x4 &aiMe, @@ -143,14 +126,29 @@ public: // Reset the state of the class // Called whenever a new asset is loaded inline void Reset() { - this->m_iShaderCount = 0; - for (TextureCache::iterator it = sCachedTextures.begin(); it != sCachedTextures.end(); ++it) { - (*it).second->Release(); + m_iShaderCount = 0; + for (auto & sCachedTexture : sCachedTextures) { + sCachedTexture.second->Release(); } sCachedTextures.clear(); } private: + // The default constructor + CMaterialManager() : + m_iShaderCount(0), + sDefaultTexture() { + // empty + } + + // Destructor, private. + ~CMaterialManager() { + if (sDefaultTexture) { + sDefaultTexture->Release(); + } + Reset(); + } + //------------------------------------------------------------------ // find a valid path to a texture file // @@ -183,15 +181,14 @@ private: bool HasAlphaPixels(IDirect3DTexture9 *piTexture); private: - // + static CMaterialManager s_cInstance; + // Specifies the number of different shaders generated for // the current asset. This number is incremented by CreateMaterial() // each time a shader isn't found in cache and needs to be created - // unsigned int m_iShaderCount; IDirect3DTexture9 *sDefaultTexture; - - typedef std::map TextureCache; + using TextureCache = std::map; TextureCache sCachedTextures; }; diff --git a/tools/assimp_view/MeshRenderer.cpp b/tools/assimp_view/MeshRenderer.cpp index 0ec12e5b1..bc1a5236f 100644 --- a/tools/assimp_view/MeshRenderer.cpp +++ b/tools/assimp_view/MeshRenderer.cpp @@ -61,11 +61,14 @@ int CMeshRenderer::DrawUnsorted(unsigned int iIndex) { D3DPRIMITIVETYPE type = D3DPT_POINTLIST; switch (g_pcAsset->pcScene->mMeshes[iIndex]->mPrimitiveTypes) { case aiPrimitiveType_POINT: - type = D3DPT_POINTLIST;break; + type = D3DPT_POINTLIST; + break; case aiPrimitiveType_LINE: - type = D3DPT_LINELIST;break; + type = D3DPT_LINELIST; + break; case aiPrimitiveType_TRIANGLE: - type = D3DPT_TRIANGLELIST;break; + type = D3DPT_TRIANGLELIST; + break; } // and draw the mesh g_piDevice->DrawIndexedPrimitive(type, diff --git a/tools/assimp_view/MessageProc.cpp b/tools/assimp_view/MessageProc.cpp index 580d35bf1..56831eb2a 100644 --- a/tools/assimp_view/MessageProc.cpp +++ b/tools/assimp_view/MessageProc.cpp @@ -2206,7 +2206,7 @@ int APIENTRY _tWinMain(HINSTANCE hInstance, "ASSIMP ModelViewer",MB_OK); return -4; } - + CLogDisplay::Instance().AddEntry("[OK] Here we go!"); // create the log window diff --git a/tools/assimp_view/Shaders.cpp b/tools/assimp_view/Shaders.cpp index a3404d5bf..b8ee8dbf8 100644 --- a/tools/assimp_view/Shaders.cpp +++ b/tools/assimp_view/Shaders.cpp @@ -7,8 +7,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ diff --git a/tools/assimp_view/assimp_view.cpp b/tools/assimp_view/assimp_view.cpp index 260b22941..e780e2aaf 100644 --- a/tools/assimp_view/assimp_view.cpp +++ b/tools/assimp_view/assimp_view.cpp @@ -7,8 +7,8 @@ Copyright (c) 2006-2021, 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 +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 @@ -25,16 +25,16 @@ 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -489,7 +489,7 @@ int CreateAssetData() { nidx = 3; break; default: - ai_assert(false); + CLogWindow::Instance().WriteLine("Unknown primitiv type"); break; }; @@ -500,8 +500,7 @@ int CreateAssetData() { // check whether we can use 16 bit indices if (numIndices >= 65536) { // create 32 bit index buffer - if (FAILED(g_piDevice->CreateIndexBuffer(4 * - numIndices, + if (FAILED(g_piDevice->CreateIndexBuffer(4 * numIndices, D3DUSAGE_WRITEONLY | dwUsage, D3DFMT_INDEX32, D3DPOOL_DEFAULT, @@ -523,7 +522,7 @@ int CreateAssetData() { } else { // create 16 bit index buffer if (FAILED(g_piDevice->CreateIndexBuffer(2 * - numIndices, +numIndices, D3DUSAGE_WRITEONLY | dwUsage, D3DFMT_INDEX16, D3DPOOL_DEFAULT, diff --git a/tools/assimp_view/resource.h b/tools/assimp_view/resource.h index 5077f6ccf..754eb69bd 100644 --- a/tools/assimp_view/resource.h +++ b/tools/assimp_view/resource.h @@ -223,7 +223,7 @@ #define IDC_STATIC -1 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 From dfd95633a4dfc1fff397ed30a4ffda5edf97418f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 8 Sep 2021 08:31:33 +0200 Subject: [PATCH 158/232] Add patreon --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index e1ab13396..3f820ec7d 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ open_collective: assimp +patreon: assimp From f47479aba4797fc9c9b119cd21ab427748a9f839 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 8 Sep 2021 23:18:13 +0200 Subject: [PATCH 159/232] Rework format + introdule missing C++11 features --- code/Common/Bitmap.cpp | 57 ++++++++++++------------ include/assimp/Bitmap.h | 14 ++++-- include/assimp/BlobIOSystem.h | 75 +++++++++++++++++--------------- include/assimp/ByteSwapper.h | 4 +- include/assimp/ColladaMetaData.h | 1 - include/assimp/CreateAnimMesh.h | 1 - include/assimp/DefaultIOStream.h | 37 +++++++--------- include/assimp/DefaultIOSystem.h | 11 +++-- include/assimp/DefaultLogger.hpp | 34 ++++++++------- include/assimp/Exceptional.h | 30 +++++++++++-- include/assimp/IOSystem.hpp | 54 ++++++++++------------- include/assimp/Importer.hpp | 2 +- include/assimp/LineSplitter.h | 56 +++++++++--------------- include/assimp/LogStream.hpp | 9 ++-- include/assimp/Logger.hpp | 27 ++++-------- include/assimp/NullLogger.hpp | 5 +++ include/assimp/aabb.h | 24 +++++----- include/assimp/ai_assert.h | 26 +++++++---- include/assimp/camera.h | 34 +++++++-------- include/assimp/cfileio.h | 5 +-- include/assimp/color4.inl | 2 - include/assimp/commonMetaData.h | 2 - 22 files changed, 258 insertions(+), 252 deletions(-) diff --git a/code/Common/Bitmap.cpp b/code/Common/Bitmap.cpp index 23b96d42d..51f31625e 100644 --- a/code/Common/Bitmap.cpp +++ b/code/Common/Bitmap.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -54,33 +52,36 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { -void Bitmap::Save(aiTexture *texture, IOStream *file) { - if (file != nullptr) { - Header header; - DIB dib; - - dib.size = DIB::dib_size; - dib.width = texture->mWidth; - dib.height = texture->mHeight; - dib.planes = 1; - dib.bits_per_pixel = 8 * mBytesPerPixel; - dib.compression = 0; - dib.image_size = (((dib.width * mBytesPerPixel) + 3) & 0x0000FFFC) * dib.height; - dib.x_resolution = 0; - dib.y_resolution = 0; - dib.nb_colors = 0; - dib.nb_important_colors = 0; - - header.type = 0x4D42; // 'BM' - header.offset = Header::header_size + DIB::dib_size; - header.size = header.offset + dib.image_size; - header.reserved1 = 0; - header.reserved2 = 0; - - WriteHeader(header, file); - WriteDIB(dib, file); - WriteData(texture, file); +bool Bitmap::Save(aiTexture *texture, IOStream *file) { + if (file == nullptr) { + return false; } + + Header header; + DIB dib; + dib.size = DIB::dib_size; + dib.width = texture->mWidth; + dib.height = texture->mHeight; + dib.planes = 1; + dib.bits_per_pixel = 8 * mBytesPerPixel; + dib.compression = 0; + dib.image_size = (((dib.width * mBytesPerPixel) + 3) & 0x0000FFFC) * dib.height; + dib.x_resolution = 0; + dib.y_resolution = 0; + dib.nb_colors = 0; + dib.nb_important_colors = 0; + + header.type = 0x4D42; // 'BM' + header.offset = Header::header_size + DIB::dib_size; + header.size = header.offset + dib.image_size; + header.reserved1 = 0; + header.reserved2 = 0; + + WriteHeader(header, file); + WriteDIB(dib, file); + WriteData(texture, file); + + return true; } template diff --git a/include/assimp/Bitmap.h b/include/assimp/Bitmap.h index e4ce194d9..228f6e9ba 100644 --- a/include/assimp/Bitmap.h +++ b/include/assimp/Bitmap.h @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -55,7 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endif #include "defs.h" -#include +#include #include struct aiTexture; @@ -64,6 +62,10 @@ namespace Assimp { class IOStream; +// --------------------------------------------------------------------------- +/** + * This class is used to store and write bitmap information. + */ class ASSIMP_API Bitmap { protected: @@ -114,7 +116,11 @@ protected: static constexpr std::size_t mBytesPerPixel = 4; public: - static void Save(aiTexture* texture, IOStream* file); + /// @brief Will save an aiTexture instance as a bitmap. + /// @param texture The pointer to the texture instance + /// @param file The filename to save into. + /// @return true if successfully saved, false if not. + static bool Save(aiTexture* texture, IOStream* file); protected: static void WriteHeader(Header& header, IOStream* file); diff --git a/include/assimp/BlobIOSystem.h b/include/assimp/BlobIOSystem.h index 0abf166bc..90e806431 100644 --- a/include/assimp/BlobIOSystem.h +++ b/include/assimp/BlobIOSystem.h @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -44,14 +42,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file Provides cheat implementations for IOSystem and IOStream to * redirect exporter output to a blob chain.*/ +#pragma once #ifndef AI_BLOBIOSYSTEM_H_INCLUDED #define AI_BLOBIOSYSTEM_H_INCLUDED #include -#include #include #include #include +#include #include #include @@ -63,6 +62,10 @@ class BlobIOSystem; // -------------------------------------------------------------------------------------------- class BlobIOStream : public IOStream { public: + /// @brief The class constructor with all needed parameters + /// @param creator Pointer to the creator instance + /// @param file The filename + /// @param initial The initial size BlobIOStream(BlobIOSystem *creator, const std::string &file, size_t initial = 4096) : buffer(), cur_size(), @@ -74,7 +77,8 @@ public: // empty } - virtual ~BlobIOStream(); + /// @brief The class destructor. + ~BlobIOStream() override; public: // ------------------------------------------------------------------- @@ -89,16 +93,12 @@ public: } // ------------------------------------------------------------------- - virtual size_t Read(void *, - size_t, - size_t) { + size_t Read(void *, size_t, size_t) override { return 0; } // ------------------------------------------------------------------- - virtual size_t Write(const void *pvBuffer, - size_t pSize, - size_t pCount) { + size_t Write(const void *pvBuffer, size_t pSize, size_t pCount) override { pSize *= pCount; if (cursor + pSize > cur_size) { Grow(cursor + pSize); @@ -112,23 +112,22 @@ public: } // ------------------------------------------------------------------- - virtual aiReturn Seek(size_t pOffset, - aiOrigin pOrigin) { + aiReturn Seek(size_t pOffset, aiOrigin pOrigin) override { switch (pOrigin) { - case aiOrigin_CUR: - cursor += pOffset; - break; + case aiOrigin_CUR: + cursor += pOffset; + break; - case aiOrigin_END: - cursor = file_size - pOffset; - break; + case aiOrigin_END: + cursor = file_size - pOffset; + break; - case aiOrigin_SET: - cursor = pOffset; - break; + case aiOrigin_SET: + cursor = pOffset; + break; - default: - return AI_FAILURE; + default: + return AI_FAILURE; } if (cursor > file_size) { @@ -136,21 +135,22 @@ public: } file_size = std::max(cursor, file_size); + return AI_SUCCESS; } // ------------------------------------------------------------------- - virtual size_t Tell() const { + size_t Tell() const override { return cursor; } // ------------------------------------------------------------------- - virtual size_t FileSize() const { + size_t FileSize() const override { return file_size; } // ------------------------------------------------------------------- - virtual void Flush() { + void Flush() override { // ignore } @@ -196,15 +196,19 @@ class BlobIOSystem : public IOSystem { public: + /// @brief The default class constructor. BlobIOSystem() : baseName{AI_BLOBIO_MAGIC} { } + /// @brief The class constructor with the base name. + /// @param baseName The base name. BlobIOSystem(const std::string &baseName) : baseName(baseName) { + // empty } - virtual ~BlobIOSystem() { + ~BlobIOSystem() override { for (BlobEntry &blobby : blobs) { delete blobby.second; } @@ -263,18 +267,17 @@ public: public: // ------------------------------------------------------------------- - virtual bool Exists(const char *pFile) const { + bool Exists(const char *pFile) const override { return created.find(std::string(pFile)) != created.end(); } // ------------------------------------------------------------------- - virtual char getOsSeparator() const { + char getOsSeparator() const override { return '/'; } // ------------------------------------------------------------------- - virtual IOStream *Open(const char *pFile, - const char *pMode) { + IOStream *Open(const char *pFile, const char *pMode) override { if (pMode[0] != 'w') { return nullptr; } @@ -284,7 +287,7 @@ public: } // ------------------------------------------------------------------- - virtual void Close(IOStream *pFile) { + void Close(IOStream *pFile) override { delete pFile; } @@ -294,7 +297,7 @@ private: // we don't know in which the files are closed, so we // can't reliably say that the first must be the master // file ... - blobs.push_back(BlobEntry(filename, child->GetBlob())); + blobs.emplace_back(filename, child->GetBlob()); } private: @@ -304,8 +307,10 @@ private: }; // -------------------------------------------------------------------------------------------- -BlobIOStream ::~BlobIOStream() { - creator->OnDestruct(file, this); +BlobIOStream::~BlobIOStream() { + if (nullptr != creator) { + creator->OnDestruct(file, this); + } delete[] buffer; } diff --git a/include/assimp/ByteSwapper.h b/include/assimp/ByteSwapper.h index 98fdafc98..9e1eea10f 100644 --- a/include/assimp/ByteSwapper.h +++ b/include/assimp/ByteSwapper.h @@ -52,10 +52,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include +#include #if _MSC_VER >= 1400 -#include +#include #endif namespace Assimp { diff --git a/include/assimp/ColladaMetaData.h b/include/assimp/ColladaMetaData.h index 602f342f9..f8fd5e6b4 100644 --- a/include/assimp/ColladaMetaData.h +++ b/include/assimp/ColladaMetaData.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, diff --git a/include/assimp/CreateAnimMesh.h b/include/assimp/CreateAnimMesh.h index dda4dc63a..868a1f399 100644 --- a/include/assimp/CreateAnimMesh.h +++ b/include/assimp/CreateAnimMesh.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, diff --git a/include/assimp/DefaultIOStream.h b/include/assimp/DefaultIOStream.h index 6d0e13149..aae80a2a4 100644 --- a/include/assimp/DefaultIOStream.h +++ b/include/assimp/DefaultIOStream.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -82,32 +81,27 @@ public: // ------------------------------------------------------------------- /// Read from stream - size_t Read(void* pvBuffer, - size_t pSize, - size_t pCount); + size_t Read(void* pvBuffer, size_t pSize, size_t pCount) override; // ------------------------------------------------------------------- /// Write to stream - size_t Write(const void* pvBuffer, - size_t pSize, - size_t pCount); + size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) override; // ------------------------------------------------------------------- /// Seek specific position - aiReturn Seek(size_t pOffset, - aiOrigin pOrigin); + aiReturn Seek(size_t pOffset, aiOrigin pOrigin) override; // ------------------------------------------------------------------- /// Get current seek position - size_t Tell() const; + size_t Tell() const override; // ------------------------------------------------------------------- /// Get size of file - size_t FileSize() const; + size_t FileSize() const override; // ------------------------------------------------------------------- /// Flush file contents - void Flush(); + void Flush() override; private: FILE* mFile; @@ -116,22 +110,21 @@ private: }; // ---------------------------------------------------------------------------------- -AI_FORCE_INLINE -DefaultIOStream::DefaultIOStream() AI_NO_EXCEPT -: mFile(nullptr) -, mFilename() -, mCachedSize(SIZE_MAX) { +AI_FORCE_INLINE DefaultIOStream::DefaultIOStream() AI_NO_EXCEPT : + mFile(nullptr), + mFilename(), + mCachedSize(SIZE_MAX) { // empty } // ---------------------------------------------------------------------------------- -AI_FORCE_INLINE -DefaultIOStream::DefaultIOStream (FILE* pFile, const std::string &strFilename) -: mFile(pFile) -, mFilename(strFilename) -, mCachedSize(SIZE_MAX) { +AI_FORCE_INLINE DefaultIOStream::DefaultIOStream (FILE* pFile, const std::string &strFilename) : + mFile(pFile), + mFilename(strFilename), + mCachedSize(SIZE_MAX) { // empty } + // ---------------------------------------------------------------------------------- } // ns assimp diff --git a/include/assimp/DefaultIOSystem.h b/include/assimp/DefaultIOSystem.h index 3f7235f5b..c2f1aac61 100644 --- a/include/assimp/DefaultIOSystem.h +++ b/include/assimp/DefaultIOSystem.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -59,23 +58,23 @@ class ASSIMP_API DefaultIOSystem : public IOSystem { public: // ------------------------------------------------------------------- /** Tests for the existence of a file at the given path. */ - bool Exists( const char* pFile) const; + bool Exists( const char* pFile) const override; // ------------------------------------------------------------------- /** Returns the directory separator. */ - char getOsSeparator() const; + char getOsSeparator() const override; // ------------------------------------------------------------------- /** Open a new file with a given path. */ - IOStream* Open( const char* pFile, const char* pMode = "rb"); + IOStream* Open( const char* pFile, const char* pMode = "rb") override; // ------------------------------------------------------------------- /** Closes the given file and releases all resources associated with it. */ - void Close( IOStream* pFile); + void Close( IOStream* pFile) override; // ------------------------------------------------------------------- /** Compare two paths */ - bool ComparePaths (const char* one, const char* second) const; + bool ComparePaths (const char* one, const char* second) const override; /** @brief get the file name of a full filepath * example: /tmp/archive.tar.gz -> archive.tar.gz diff --git a/include/assimp/DefaultLogger.hpp b/include/assimp/DefaultLogger.hpp index f71bb07aa..e06c94f31 100644 --- a/include/assimp/DefaultLogger.hpp +++ b/include/assimp/DefaultLogger.hpp @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -42,9 +41,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file DefaultLogger.hpp */ +#pragma once #ifndef INCLUDED_AI_DEFAULTLOGGER #define INCLUDED_AI_DEFAULTLOGGER +#ifdef __GNUC__ +#pragma GCC system_header +#endif + #include "LogStream.hpp" #include "Logger.hpp" #include "NullLogger.hpp" @@ -55,7 +59,7 @@ namespace Assimp { class IOStream; struct LogStreamInfo; -/** default name of logfile */ +/** default name of log-file */ #define ASSIMP_DEFAULT_LOG_NAME "AssimpLog.txt" // ------------------------------------------------------------------------------------ @@ -72,7 +76,6 @@ struct LogStreamInfo; * 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. @@ -121,13 +124,11 @@ public: // ---------------------------------------------------------------------- /** @copydoc Logger::attachStream */ - bool attachStream(LogStream *pStream, - unsigned int severity); + bool attachStream(LogStream *pStream, unsigned int severity) override; // ---------------------------------------------------------------------- /** @copydoc Logger::detachStream */ - bool detachStream(LogStream *pStream, - unsigned int severity); + bool detachStream(LogStream *pStream, unsigned int severity) override; private: // ---------------------------------------------------------------------- @@ -137,22 +138,22 @@ private: // ---------------------------------------------------------------------- /** @briefDestructor */ - ~DefaultLogger(); + ~DefaultLogger() override; /** @brief Logs debug infos, only been written when severity level DEBUG or higher is set */ - void OnDebug(const char *message); + void OnDebug(const char *message) override; /** @brief Logs debug infos, only been written when severity level VERBOSE is set */ - void OnVerboseDebug(const char *message); + void OnVerboseDebug(const char *message) override; /** @brief Logs an info message */ - void OnInfo(const char *message); + void OnInfo(const char *message) override; /** @brief Logs a warning message */ - void OnWarn(const char *message); + void OnWarn(const char *message) override; /** @brief Logs an error message */ - void OnError(const char *message); + void OnError(const char *message) override; // ---------------------------------------------------------------------- /** @brief Writes a message to all streams */ @@ -167,9 +168,9 @@ private: private: // Aliases for stream container - typedef std::vector StreamArray; - typedef std::vector::iterator StreamIt; - typedef std::vector::const_iterator ConstStreamIt; + using StreamArray = std::vector; + using StreamIt = std::vector::iterator; + using ConstStreamIt = std::vector::const_iterator; //! only logging instance static Logger *m_pLogger; @@ -182,6 +183,7 @@ private: char lastMsg[MAX_LOG_MESSAGE_LENGTH * 2]; size_t lastLen; }; + // ------------------------------------------------------------------------------------ } // Namespace Assimp diff --git a/include/assimp/Exceptional.h b/include/assimp/Exceptional.h index 1bf399cbc..3078de9f4 100644 --- a/include/assimp/Exceptional.h +++ b/include/assimp/Exceptional.h @@ -56,10 +56,22 @@ using std::runtime_error; #pragma warning(disable : 4275) #endif +// --------------------------------------------------------------------------- +/** + * The base-class for all other exceptions + */ class ASSIMP_API DeadlyErrorBase : public runtime_error { protected: + /// @brief The class constructor with the formatter. + /// @param f The formatter. DeadlyErrorBase(Assimp::Formatter::format f); + /// @brief The class constructor with the parameter ellipse. + /// @tparam ...T The type for the ellipse + /// @tparam U The other type + /// @param f The formatter + /// @param u One parameter + /// @param ...args The rest template DeadlyErrorBase(Assimp::Formatter::format f, U&& u, T&&... args) : DeadlyErrorBase(std::move(f << std::forward(u)), std::forward(args)...) {} @@ -71,19 +83,31 @@ protected: * nullptr instead of a valid aiScene then. */ class ASSIMP_API DeadlyImportError : public DeadlyErrorBase { public: + /// @brief The class constructor with the message. + /// @param message The message DeadlyImportError(const char *message) : - DeadlyErrorBase(Assimp::Formatter::format(), std::forward(message)) {} + DeadlyErrorBase(Assimp::Formatter::format(), std::forward(message)) { + // empty + } - /** Constructor with arguments */ + /// @brief The class constructor with the parameter ellipse. + /// @tparam ...T The type for the ellipse + /// @param ...args The args template explicit DeadlyImportError(T&&... args) : - DeadlyErrorBase(Assimp::Formatter::format(), std::forward(args)...) {} + DeadlyErrorBase(Assimp::Formatter::format(), std::forward(args)...) { + // empty + } #if defined(_MSC_VER) && defined(__clang__) DeadlyImportError(DeadlyImportError& other) = delete; #endif }; +// --------------------------------------------------------------------------- +/** FOR EXPORTER PLUGINS ONLY: Simple exception class to be thrown if an + * unrecoverable error occurs while exporting. Exporting APIs return + * nullptr instead of a valid aiScene then. */ class ASSIMP_API DeadlyExportError : public DeadlyErrorBase { public: /** Constructor with arguments */ diff --git a/include/assimp/IOSystem.hpp b/include/assimp/IOSystem.hpp index 7be373cf1..dda0718e6 100644 --- a/include/assimp/IOSystem.hpp +++ b/include/assimp/IOSystem.hpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -63,8 +61,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifdef _WIN32 # include -# include -# include +# include +# include #else # include # include @@ -75,7 +73,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { - class IOStream; +class IOStream; // --------------------------------------------------------------------------- /** @brief CPP-API: Interface to the file system. @@ -226,22 +224,26 @@ public: */ virtual bool ChangeDirectory( const std::string &path ); - virtual bool DeleteFile( const std::string &file ); + // ------------------------------------------------------------------- + /** + * @brief Will delete the given file. + * @param file [in] The filename + * @return true, if the file wase deleted, false if not. + */ + virtual bool DeleteFile(const std::string &file); private: std::vector m_pathStack; }; // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -IOSystem::IOSystem() AI_NO_EXCEPT -: m_pathStack() { +AI_FORCE_INLINE IOSystem::IOSystem() AI_NO_EXCEPT : + m_pathStack() { // empty } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -IOSystem::~IOSystem() { +AI_FORCE_INLINE IOSystem::~IOSystem() { // empty } @@ -252,8 +254,7 @@ IOSystem::~IOSystem() { // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -IOStream* IOSystem::Open(const std::string& pFile, const std::string& pMode) { +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 @@ -261,8 +262,7 @@ IOStream* IOSystem::Open(const std::string& pFile, const std::string& pMode) { } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -bool IOSystem::Exists( const std::string& pFile) const { +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 @@ -270,8 +270,7 @@ bool IOSystem::Exists( const std::string& pFile) const { } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -bool IOSystem::ComparePaths (const std::string& one, const std::string& second) const { +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 @@ -279,8 +278,7 @@ bool IOSystem::ComparePaths (const std::string& one, const std::string& second) } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -bool IOSystem::PushDirectory( const std::string &path ) { +AI_FORCE_INLINE bool IOSystem::PushDirectory( const std::string &path ) { if ( path.empty() ) { return false; } @@ -291,8 +289,7 @@ bool IOSystem::PushDirectory( const std::string &path ) { } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -const std::string &IOSystem::CurrentDirectory() const { +AI_FORCE_INLINE const std::string &IOSystem::CurrentDirectory() const { if ( m_pathStack.empty() ) { static const std::string Dummy; return Dummy; @@ -301,14 +298,12 @@ const std::string &IOSystem::CurrentDirectory() const { } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -size_t IOSystem::StackSize() const { +AI_FORCE_INLINE size_t IOSystem::StackSize() const { return m_pathStack.size(); } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -bool IOSystem::PopDirectory() { +AI_FORCE_INLINE bool IOSystem::PopDirectory() { if ( m_pathStack.empty() ) { return false; } @@ -319,8 +314,7 @@ bool IOSystem::PopDirectory() { } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -bool IOSystem::CreateDirectory( const std::string &path ) { +AI_FORCE_INLINE bool IOSystem::CreateDirectory( const std::string &path ) { if ( path.empty() ) { return false; } @@ -333,8 +327,7 @@ bool IOSystem::CreateDirectory( const std::string &path ) { } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -bool IOSystem::ChangeDirectory( const std::string &path ) { +AI_FORCE_INLINE bool IOSystem::ChangeDirectory( const std::string &path ) { if ( path.empty() ) { return false; } @@ -348,8 +341,7 @@ bool IOSystem::ChangeDirectory( const std::string &path ) { // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -bool IOSystem::DeleteFile( const std::string &file ) { +AI_FORCE_INLINE bool IOSystem::DeleteFile( const std::string &file ) { if ( file.empty() ) { return false; } diff --git a/include/assimp/Importer.hpp b/include/assimp/Importer.hpp index 09b5b6883..97f34090e 100644 --- a/include/assimp/Importer.hpp +++ b/include/assimp/Importer.hpp @@ -59,7 +59,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Public ASSIMP data structures #include -#include +//#include namespace Assimp { // ======================================================================= diff --git a/include/assimp/LineSplitter.h b/include/assimp/LineSplitter.h index 44e6f0f1e..0a9d24437 100644 --- a/include/assimp/LineSplitter.h +++ b/include/assimp/LineSplitter.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -144,26 +143,23 @@ private: bool mSwallow, mSkip_empty_lines, mTrim; }; -AI_FORCE_INLINE -LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim ) -: mIdx(0) -, mCur() -, mStream(stream) -, mSwallow() -, mSkip_empty_lines(skip_empty_lines) -, mTrim(trim) { +AI_FORCE_INLINE LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim ) : + mIdx(0), + mCur(), + mStream(stream), + mSwallow(), + mSkip_empty_lines(skip_empty_lines), + mTrim(trim) { mCur.reserve(1024); operator++(); mIdx = 0; } -AI_FORCE_INLINE -LineSplitter::~LineSplitter() { +AI_FORCE_INLINE LineSplitter::~LineSplitter() { // empty } -AI_FORCE_INLINE -LineSplitter& LineSplitter::operator++() { +AI_FORCE_INLINE LineSplitter& LineSplitter::operator++() { if (mSwallow) { mSwallow = false; return *this; @@ -203,18 +199,15 @@ LineSplitter& LineSplitter::operator++() { return *this; } -AI_FORCE_INLINE -LineSplitter &LineSplitter::operator++(int) { +AI_FORCE_INLINE LineSplitter &LineSplitter::operator++(int) { return ++(*this); } -AI_FORCE_INLINE -const char *LineSplitter::operator[] (size_t idx) const { +AI_FORCE_INLINE const char *LineSplitter::operator[] (size_t idx) const { const char* s = operator->()->c_str(); SkipSpaces(&s); for (size_t i = 0; i < idx; ++i) { - for (; !IsSpace(*s); ++s) { if (IsLineEnd(*s)) { throw std::range_error("Token index out of range, EOL reached"); @@ -226,8 +219,7 @@ const char *LineSplitter::operator[] (size_t idx) const { } template -AI_FORCE_INLINE -void LineSplitter::get_tokens(const char* (&tokens)[N]) const { +AI_FORCE_INLINE void LineSplitter::get_tokens(const char* (&tokens)[N]) const { const char* s = operator->()->c_str(); SkipSpaces(&s); @@ -242,45 +234,37 @@ void LineSplitter::get_tokens(const char* (&tokens)[N]) const { } } -AI_FORCE_INLINE -const std::string* LineSplitter::operator -> () const { +AI_FORCE_INLINE const std::string* LineSplitter::operator -> () const { return &mCur; } -AI_FORCE_INLINE -std::string LineSplitter::operator* () const { +AI_FORCE_INLINE std::string LineSplitter::operator* () const { return mCur; } -AI_FORCE_INLINE -LineSplitter::operator bool() const { +AI_FORCE_INLINE LineSplitter::operator bool() const { return mStream.GetRemainingSize() > 0; } -AI_FORCE_INLINE -LineSplitter::operator line_idx() const { +AI_FORCE_INLINE LineSplitter::operator line_idx() const { return mIdx; } -AI_FORCE_INLINE -LineSplitter::line_idx LineSplitter::get_index() const { +AI_FORCE_INLINE LineSplitter::line_idx LineSplitter::get_index() const { return mIdx; } -AI_FORCE_INLINE -StreamReaderLE &LineSplitter::get_stream() { +AI_FORCE_INLINE StreamReaderLE &LineSplitter::get_stream() { return mStream; } -AI_FORCE_INLINE -bool LineSplitter::match_start(const char* check) { +AI_FORCE_INLINE bool LineSplitter::match_start(const char* check) { const size_t len = ::strlen(check); return len <= mCur.length() && std::equal(check, check + len, mCur.begin()); } -AI_FORCE_INLINE -void LineSplitter::swallow_next_increment() { +AI_FORCE_INLINE void LineSplitter::swallow_next_increment() { mSwallow = true; } diff --git a/include/assimp/LogStream.hpp b/include/assimp/LogStream.hpp index 3c2a587a2..64a7955fd 100644 --- a/include/assimp/LogStream.hpp +++ b/include/assimp/LogStream.hpp @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -43,9 +42,14 @@ 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. */ +#pragma once #ifndef INCLUDED_AI_LOGSTREAM_H #define INCLUDED_AI_LOGSTREAM_H +#ifdef __GNUC__ +#pragma GCC system_header +#endif + #include "types.h" namespace Assimp { @@ -103,7 +107,6 @@ inline LogStream::~LogStream() { // empty } -// ------------------------------------------------------------------------------------ } // Namespace Assimp -#endif +#endif // INCLUDED_AI_LOGSTREAM_H diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index aa7ffba7c..549b0fbc2 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -43,6 +42,7 @@ 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. */ +#pragma once #ifndef INCLUDED_AI_LOGGER_H #define INCLUDED_AI_LOGGER_H @@ -93,8 +93,6 @@ public: Err = 8 //!< Error log message }; -public: - /** @brief Virtual destructor */ virtual ~Logger(); @@ -259,39 +257,30 @@ protected: }; // ---------------------------------------------------------------------------------- -// Default constructor -inline -Logger::Logger() AI_NO_EXCEPT -: m_Severity(NORMAL) { +inline Logger::Logger() AI_NO_EXCEPT : + m_Severity(NORMAL) { // empty } // ---------------------------------------------------------------------------------- -// Virtual destructor -inline -Logger::~Logger() { +inline Logger::~Logger() { // empty } // ---------------------------------------------------------------------------------- -// Construction with given logging severity -inline -Logger::Logger(LogSeverity severity) -: m_Severity(severity) { +inline Logger::Logger(LogSeverity severity) : + m_Severity(severity) { // empty } // ---------------------------------------------------------------------------------- -// Log severity setter -inline -void Logger::setLogSeverity(LogSeverity log_severity){ +inline void Logger::setLogSeverity(LogSeverity log_severity){ m_Severity = log_severity; } // ---------------------------------------------------------------------------------- // Log severity getter -inline -Logger::LogSeverity Logger::getLogSeverity() const { +inline Logger::LogSeverity Logger::getLogSeverity() const { return m_Severity; } diff --git a/include/assimp/NullLogger.hpp b/include/assimp/NullLogger.hpp index 8281da4f7..8277b014a 100644 --- a/include/assimp/NullLogger.hpp +++ b/include/assimp/NullLogger.hpp @@ -44,9 +44,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @brief Dummy logger */ +#pragma once #ifndef INCLUDED_AI_NULLLOGGER_H #define INCLUDED_AI_NULLLOGGER_H +#ifdef __GNUC__ +#pragma GCC system_header +#endif + #include "Logger.hpp" namespace Assimp { diff --git a/include/assimp/aabb.h b/include/assimp/aabb.h index cbf6193cb..a6ccdd452 100644 --- a/include/assimp/aabb.h +++ b/include/assimp/aabb.h @@ -44,36 +44,40 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define AI_AABB_H_INC #ifdef __GNUC__ -# pragma GCC system_header +#pragma GCC system_header #endif #include +// --------------------------------------------------------------------------- +/** + * An axis-aligned bounding box. + */ struct aiAABB { C_STRUCT aiVector3D mMin; C_STRUCT aiVector3D mMax; #ifdef __cplusplus - - aiAABB() - : mMin() - , mMax() { + /// @brief The default class constructor. + aiAABB() : + mMin(), mMax() { // empty } - aiAABB(const aiVector3D &min, const aiVector3D &max ) - : mMin(min) - , mMax(max) { + /// @brief The class constructor with the minimum and maximum. + /// @param min The minimum dimension. + /// @param max The maximum dimension. + aiAABB(const aiVector3D &min, const aiVector3D &max) : + mMin(min), mMax(max) { // empty } + /// @brief The class destructor. ~aiAABB() { // empty } #endif // __cplusplus - }; - #endif // AI_AABB_H_INC diff --git a/include/assimp/ai_assert.h b/include/assimp/ai_assert.h index b377b6e8b..b4f1ef8b2 100644 --- a/include/assimp/ai_assert.h +++ b/include/assimp/ai_assert.h @@ -38,6 +38,11 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ + +/** @file ai_assert.h + * @brief Declares the assimp-specific assertion handler. + */ + #pragma once #ifndef AI_ASSERT_H_INC #define AI_ASSERT_H_INC @@ -46,19 +51,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #if defined(ASSIMP_BUILD_DEBUG) -namespace Assimp -{ - // Assert violation behavior can be customized: see AssertHandler.h. - ASSIMP_API void aiAssertViolation(const char* failedExpression, const char* file, int line); +namespace Assimp { + +/// @brief Assert violation behavior can be customized: see AssertHandler.h. +/// @param failedExpression The expression to validate. +/// @param file The file location +/// @param line The line number +ASSIMP_API void aiAssertViolation(const char* failedExpression, const char* file, int line); + } +#endif -# define ai_assert(expression) (void)((!!(expression)) || (Assimp::aiAssertViolation(#expression, __FILE__, __LINE__), 0)) -# define ai_assert_entry() ai_assert(false) - +// Define assertion resolinig +#if defined(ASSIMP_BUILD_DEBUG) +# define ai_assert(expression) (void)((!!(expression)) || (Assimp::aiAssertViolation(#expression, __FILE__, __LINE__), 0)) +# define ai_assert_entry() ai_assert(false) #else # define ai_assert(expression) # define ai_assert_entry() #endif // ASSIMP_BUILD_DEBUG #endif // AI_ASSERT_H_INC - diff --git a/include/assimp/camera.h b/include/assimp/camera.h index 6a7acadbb..56c4dbe3f 100644 --- a/include/assimp/camera.h +++ b/include/assimp/camera.h @@ -48,7 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define AI_CAMERA_H_INC #ifdef __GNUC__ -# pragma GCC system_header +#pragma GCC system_header #endif #include "types.h" @@ -100,8 +100,7 @@ extern "C" { * camera already look in the right direction. * */ -struct aiCamera -{ +struct aiCamera { /** The name of the camera. * * There must be a node in the scenegraph with the same name. @@ -127,7 +126,6 @@ struct aiCamera */ C_STRUCT aiVector3D mUp; - /** 'LookAt' - vector of the camera coordinate system relative to * the coordinate space defined by the corresponding node. * @@ -184,26 +182,27 @@ struct aiCamera #ifdef __cplusplus aiCamera() AI_NO_EXCEPT - : 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) - , mOrthographicWidth (0.f) - {} + : 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), + mOrthographicWidth(0.f) {} /** @brief Get a *right-handed* camera matrix from me * @param out Camera matrix to be filled */ - void GetCameraMatrix (aiMatrix4x4& out) const - { + 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(); + aiVector3D zaxis = mLookAt; + zaxis.Normalize(); + aiVector3D yaxis = mUp; + yaxis.Normalize(); + aiVector3D xaxis = mUp ^ mLookAt; + xaxis.Normalize(); out.a4 = -(xaxis * mPosition); out.b4 = -(yaxis * mPosition); @@ -228,7 +227,6 @@ struct aiCamera #endif }; - #ifdef __cplusplus } #endif diff --git a/include/assimp/cfileio.h b/include/assimp/cfileio.h index c1f8625b9..48a728a2a 100644 --- a/include/assimp/cfileio.h +++ b/include/assimp/cfileio.h @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -106,8 +104,7 @@ struct aiFileIO * 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 -{ +struct aiFile { /** Callback to read from a file */ aiFileReadProc ReadProc; diff --git a/include/assimp/color4.inl b/include/assimp/color4.inl index fb31afa27..e5bfdf356 100644 --- a/include/assimp/color4.inl +++ b/include/assimp/color4.inl @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, diff --git a/include/assimp/commonMetaData.h b/include/assimp/commonMetaData.h index 128281bfb..34a375bf3 100644 --- a/include/assimp/commonMetaData.h +++ b/include/assimp/commonMetaData.h @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, From 93edbe883f0e89cdd207d18f85ccaa0544ae1c6d Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Fri, 10 Sep 2021 11:00:21 -0700 Subject: [PATCH 160/232] Use Safe Constants Idioms for ObjFileParser::DEFAULT_MATERIAL. Reference: "A static Class Member" in https://abseil.io/tips/140 --- code/AssetLib/Obj/ObjFileParser.cpp | 3 +-- code/AssetLib/Obj/ObjFileParser.h | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/AssetLib/Obj/ObjFileParser.cpp b/code/AssetLib/Obj/ObjFileParser.cpp index 767805c10..2e998a815 100644 --- a/code/AssetLib/Obj/ObjFileParser.cpp +++ b/code/AssetLib/Obj/ObjFileParser.cpp @@ -47,7 +47,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #include #include #include @@ -56,7 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { -const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME; +constexpr char ObjFileParser::DEFAULT_MATERIAL[]; ObjFileParser::ObjFileParser() : m_DataIt(), diff --git a/code/AssetLib/Obj/ObjFileParser.h b/code/AssetLib/Obj/ObjFileParser.h index 5d7ce065a..05a3b8046 100644 --- a/code/AssetLib/Obj/ObjFileParser.h +++ b/code/AssetLib/Obj/ObjFileParser.h @@ -42,6 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define OBJ_FILEPARSER_H_INC #include +#include #include #include #include @@ -141,7 +142,7 @@ private: // because the class contains pointer to allocated memory /// Default material name - static const std::string DEFAULT_MATERIAL; + static constexpr char DEFAULT_MATERIAL[] = AI_DEFAULT_MATERIAL_NAME; //! Iterator to current position in buffer DataArrayIt m_DataIt; //! Iterator to end position of buffer From 4cf4e7454f883cb5b34a484111fd06d7a3c7951c Mon Sep 17 00:00:00 2001 From: Krishty Date: Sat, 11 Sep 2021 21:51:20 +0200 Subject: [PATCH 161/232] removed useless code In destructors, zeroing attributes or clearing containers is utterly useless. --- code/AssetLib/3MF/D3MFOpcPackage.cpp | 1 - code/AssetLib/OpenGEX/OpenGEXImporter.cpp | 1 - code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp | 4 ---- code/AssetLib/Q3BSP/Q3BSPFileParser.cpp | 1 - code/AssetLib/XGL/XGLLoader.cpp | 1 - code/CApi/CInterfaceIOWrapper.cpp | 1 - code/Common/DefaultIOStream.cpp | 1 - code/Common/ZipArchiveIOSystem.cpp | 1 - 8 files changed, 11 deletions(-) diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index c29cec368..37fb80111 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -186,7 +186,6 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : D3MFOpcPackage::~D3MFOpcPackage() { mZipArchive->Close(mRootStream); delete mZipArchive; - mZipArchive = nullptr; } IOStream *D3MFOpcPackage::RootStream() const { diff --git a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp index 05507944e..5b62594ef 100644 --- a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp +++ b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp @@ -290,7 +290,6 @@ OpenGEXImporter::OpenGEXImporter() : //------------------------------------------------------------------------------------------------ OpenGEXImporter::~OpenGEXImporter() { - m_ctx = nullptr; } //------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp index a1da8fd74..897bcb994 100644 --- a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp +++ b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp @@ -146,9 +146,6 @@ Q3BSPFileImporter::Q3BSPFileImporter() : // ------------------------------------------------------------------------------------------------ // Destructor. Q3BSPFileImporter::~Q3BSPFileImporter() { - m_pCurrentMesh = nullptr; - m_pCurrentFace = nullptr; - // Clear face-to-material map for (FaceMap::iterator it = m_MaterialLookupMap.begin(); it != m_MaterialLookupMap.end(); ++it) { const std::string &matName = it->first; @@ -156,7 +153,6 @@ Q3BSPFileImporter::~Q3BSPFileImporter() { delete it->second; } } - m_MaterialLookupMap.clear(); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/Q3BSP/Q3BSPFileParser.cpp b/code/AssetLib/Q3BSP/Q3BSPFileParser.cpp index 9269e1e2b..a647985b0 100644 --- a/code/AssetLib/Q3BSP/Q3BSPFileParser.cpp +++ b/code/AssetLib/Q3BSP/Q3BSPFileParser.cpp @@ -78,7 +78,6 @@ Q3BSPFileParser::Q3BSPFileParser( const std::string &mapName, ZipArchiveIOSystem // ------------------------------------------------------------------------------------------------ Q3BSPFileParser::~Q3BSPFileParser() { delete m_pModel; - m_pModel = nullptr; } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/XGL/XGLLoader.cpp b/code/AssetLib/XGL/XGLLoader.cpp index bbfa31829..df8eab5f7 100644 --- a/code/AssetLib/XGL/XGLLoader.cpp +++ b/code/AssetLib/XGL/XGLLoader.cpp @@ -100,7 +100,6 @@ XGLImporter::XGLImporter() : // Destructor, private as well XGLImporter::~XGLImporter() { delete mXmlParser; - mXmlParser = nullptr; } // ------------------------------------------------------------------------------------------------ diff --git a/code/CApi/CInterfaceIOWrapper.cpp b/code/CApi/CInterfaceIOWrapper.cpp index 8e2ac95c0..245f3bc7a 100644 --- a/code/CApi/CInterfaceIOWrapper.cpp +++ b/code/CApi/CInterfaceIOWrapper.cpp @@ -51,7 +51,6 @@ CIOStreamWrapper::~CIOStreamWrapper() { /* Various places depend on this destructor to close the file */ if (mFile) { mIO->mFileSystem->CloseProc(mIO->mFileSystem, mFile); - mFile = nullptr; } } diff --git a/code/Common/DefaultIOStream.cpp b/code/Common/DefaultIOStream.cpp index 557fbc78f..e18dcd3f5 100644 --- a/code/Common/DefaultIOStream.cpp +++ b/code/Common/DefaultIOStream.cpp @@ -82,7 +82,6 @@ inline int select_fseek<8>(FILE *file, int64_t offset, int origin) { DefaultIOStream::~DefaultIOStream() { if (mFile) { ::fclose(mFile); - mFile = nullptr; } } diff --git a/code/Common/ZipArchiveIOSystem.cpp b/code/Common/ZipArchiveIOSystem.cpp index 1f46ab0a9..7df51b8aa 100644 --- a/code/Common/ZipArchiveIOSystem.cpp +++ b/code/Common/ZipArchiveIOSystem.cpp @@ -352,7 +352,6 @@ ZipArchiveIOSystem::Implement::Implement(IOSystem *pIOHandler, const char *pFile ZipArchiveIOSystem::Implement::~Implement() { if (m_ZipFileHandle != nullptr) { unzClose(m_ZipFileHandle); - m_ZipFileHandle = nullptr; } } From 5895c0c22cc88763c016de821d2da2da7f4fd5dd Mon Sep 17 00:00:00 2001 From: Krishty Date: Sat, 11 Sep 2021 23:23:05 +0200 Subject: [PATCH 162/232] more const in format detection BaseImporter::SearchFileHeaderForToken() expected a pointer to a non-const token list. This was probably an oversight, as nobody would realistically expect the function to change the list. Furthermore, it prevented token lists from being compiled to read-only memory, in some cases even causing the compiler to generate thread-safe initialization. The list is now const and all callers declare their token lists static const, thus compiling them to read-only memory. --- code/AssetLib/AMF/AMFImporter.cpp | 2 +- code/AssetLib/ASE/ASELoader.cpp | 2 +- code/AssetLib/BVH/BVHLoader.cpp | 2 +- code/AssetLib/Blender/BlenderLoader.cpp | 4 ++-- code/AssetLib/COB/COBLoader.cpp | 2 +- code/AssetLib/CSM/CSMLoader.cpp | 2 +- code/AssetLib/Collada/ColladaLoader.cpp | 2 +- code/AssetLib/DXF/DXFLoader.cpp | 4 ++-- code/AssetLib/FBX/FBXImporter.cpp | 2 +- code/AssetLib/IFC/IFCLoader.cpp | 5 ++--- code/AssetLib/Irr/IRRLoader.cpp | 2 +- code/AssetLib/Irr/IRRMeshLoader.cpp | 2 +- code/AssetLib/MD5/MD5Loader.cpp | 2 +- code/AssetLib/MMD/MMDImporter.cpp | 4 ++-- code/AssetLib/MS3D/MS3DLoader.cpp | 2 +- code/AssetLib/NDO/NDOLoader.cpp | 2 +- code/AssetLib/OFF/OFFLoader.cpp | 2 +- code/AssetLib/Ogre/OgreImporter.cpp | 2 +- code/AssetLib/OpenGEX/OpenGEXImporter.cpp | 4 ++-- code/AssetLib/Ply/PlyLoader.cpp | 2 +- code/AssetLib/Q3D/Q3DLoader.cpp | 2 +- code/AssetLib/STL/STLLoader.cpp | 2 +- code/AssetLib/Terragen/TerragenLoader.cpp | 2 +- code/AssetLib/XGL/XGLLoader.cpp | 2 +- code/Common/BaseImporter.cpp | 2 +- include/assimp/BaseImporter.h | 2 +- 26 files changed, 31 insertions(+), 32 deletions(-) diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp index 88a38b827..9a9ab94ca 100644 --- a/code/AssetLib/AMF/AMFImporter.cpp +++ b/code/AssetLib/AMF/AMFImporter.cpp @@ -511,7 +511,7 @@ bool AMFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool p } if (extension.empty() || pCheckSig) { - const char *tokens[] = { "" }; + static const char * const tokens[] = { "" }; return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); } else { /// @todo Read and validate first header chunk? diff --git a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp index 05507944e..0f224ea28 100644 --- a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp +++ b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp @@ -299,8 +299,8 @@ bool OpenGEXImporter::CanRead(const std::string &file, IOSystem *pIOHandler, boo if (!checkSig) { canRead = SimpleExtensionCheck(file, "ogex"); } else { - static const char *token[] = { "Metric", "GeometryNode", "VertexArray (attrib", "IndexArray" }; - canRead = BaseImporter::SearchFileHeaderForToken(pIOHandler, file, token, 4); + static const char * const token[] = { "Metric", "GeometryNode", "VertexArray (attrib", "IndexArray" }; + canRead = SearchFileHeaderForToken(pIOHandler, file, token, 4); } return canRead; diff --git a/code/AssetLib/Ply/PlyLoader.cpp b/code/AssetLib/Ply/PlyLoader.cpp index 93d48bcbf..b58ef1ef2 100644 --- a/code/AssetLib/Ply/PlyLoader.cpp +++ b/code/AssetLib/Ply/PlyLoader.cpp @@ -111,7 +111,7 @@ bool PLYImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool c if (!pIOHandler) { return true; } - static const char *tokens[] = { + static const char * const tokens[] = { "ply" }; return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); diff --git a/code/AssetLib/Q3D/Q3DLoader.cpp b/code/AssetLib/Q3D/Q3DLoader.cpp index f81026547..fe8541a5d 100644 --- a/code/AssetLib/Q3D/Q3DLoader.cpp +++ b/code/AssetLib/Q3D/Q3DLoader.cpp @@ -92,7 +92,7 @@ bool Q3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool c else if (!extension.length() || checkSig) { if (!pIOHandler) return true; - const char *tokens[] = { "quick3Do", "quick3Ds" }; + static const char * const tokens[] = { "quick3Do", "quick3Ds" }; return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 2); } return false; diff --git a/code/AssetLib/STL/STLLoader.cpp b/code/AssetLib/STL/STLLoader.cpp index 8cfe63e0d..4d23b93e2 100644 --- a/code/AssetLib/STL/STLLoader.cpp +++ b/code/AssetLib/STL/STLLoader.cpp @@ -150,7 +150,7 @@ bool STLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool c if (!pIOHandler) { return true; } - const char *tokens[] = { "STL", "solid" }; + static const char * const tokens[] = { "STL", "solid" }; return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 2); } diff --git a/code/AssetLib/Terragen/TerragenLoader.cpp b/code/AssetLib/Terragen/TerragenLoader.cpp index e0bdbf026..9a6e7fa24 100644 --- a/code/AssetLib/Terragen/TerragenLoader.cpp +++ b/code/AssetLib/Terragen/TerragenLoader.cpp @@ -93,7 +93,7 @@ bool TerragenImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, b return true; } - const char *tokens[] = { "terragen" }; + static const char * const tokens[] = { "terragen" }; return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); } diff --git a/code/AssetLib/XGL/XGLLoader.cpp b/code/AssetLib/XGL/XGLLoader.cpp index bbfa31829..c3341af5e 100644 --- a/code/AssetLib/XGL/XGLLoader.cpp +++ b/code/AssetLib/XGL/XGLLoader.cpp @@ -118,7 +118,7 @@ bool XGLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool c } else if (extension == "xml" || checkSig) { ai_assert(pIOHandler != NULL); - const char *tokens[] = { "", "", "" }; + static const char * const tokens[] = { "", "", "" }; return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 3); } return false; diff --git a/code/Common/BaseImporter.cpp b/code/Common/BaseImporter.cpp index 0f41e9e65..fa6cac938 100644 --- a/code/Common/BaseImporter.cpp +++ b/code/Common/BaseImporter.cpp @@ -155,7 +155,7 @@ void BaseImporter::GetExtensionList(std::set &extensions) { // ------------------------------------------------------------------------------------------------ /*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem *pIOHandler, const std::string &pFile, - const char **tokens, + const char * const *tokens, unsigned int numTokens, unsigned int searchBytes /* = 200 */, bool tokensSol /* false */, diff --git a/include/assimp/BaseImporter.h b/include/assimp/BaseImporter.h index 54b5daac1..656e0f165 100644 --- a/include/assimp/BaseImporter.h +++ b/include/assimp/BaseImporter.h @@ -259,7 +259,7 @@ public: // static utilities static bool SearchFileHeaderForToken( IOSystem *pIOSystem, const std::string &file, - const char **tokens, + const char * const *tokens, unsigned int numTokens, unsigned int searchBytes = 200, bool tokensSol = false, From 3f6a371b648ab3dc4971859488473064cb0276a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Martin?= Date: Sun, 12 Sep 2021 11:37:33 +0200 Subject: [PATCH 163/232] migrated x3d importer to pugixml reader --- code/AssetLib/X3D/X3DGeoHelper.cpp | 2 +- code/AssetLib/X3D/X3DGeoHelper.h | 2 +- code/AssetLib/X3D/X3DImporter.cpp | 2702 +---------------- code/AssetLib/X3D/X3DImporter.hpp | 531 +--- code/AssetLib/X3D/X3DImporter_Geometry2D.cpp | 467 +++ code/AssetLib/X3D/X3DImporter_Geometry3D.cpp | 981 ++++++ code/AssetLib/X3D/X3DImporter_Group.cpp | 273 ++ code/AssetLib/X3D/X3DImporter_Light.cpp | 270 ++ code/AssetLib/X3D/X3DImporter_Macro.hpp | 110 + code/AssetLib/X3D/X3DImporter_Metadata.cpp | 255 ++ code/AssetLib/X3D/X3DImporter_Networking.cpp | 125 + code/AssetLib/X3D/X3DImporter_Node.hpp | 459 +++ code/AssetLib/X3D/X3DImporter_Postprocess.cpp | 731 +++++ code/AssetLib/X3D/X3DImporter_Rendering.cpp | 987 ++++++ code/AssetLib/X3D/X3DImporter_Shape.cpp | 221 ++ code/AssetLib/X3D/X3DImporter_Texturing.cpp | 179 ++ code/AssetLib/X3D/X3DXmlHelper.cpp | 294 ++ code/AssetLib/X3D/X3DXmlHelper.h | 30 + code/CMakeLists.txt | 14 + code/Common/ImporterRegistry.cpp | 4 +- 20 files changed, 5662 insertions(+), 2975 deletions(-) create mode 100644 code/AssetLib/X3D/X3DImporter_Geometry2D.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Geometry3D.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Group.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Light.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Macro.hpp create mode 100644 code/AssetLib/X3D/X3DImporter_Metadata.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Networking.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Node.hpp create mode 100644 code/AssetLib/X3D/X3DImporter_Postprocess.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Rendering.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Shape.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Texturing.cpp create mode 100644 code/AssetLib/X3D/X3DXmlHelper.cpp create mode 100644 code/AssetLib/X3D/X3DXmlHelper.h diff --git a/code/AssetLib/X3D/X3DGeoHelper.cpp b/code/AssetLib/X3D/X3DGeoHelper.cpp index be41cc012..a9ac57e06 100644 --- a/code/AssetLib/X3D/X3DGeoHelper.cpp +++ b/code/AssetLib/X3D/X3DGeoHelper.cpp @@ -116,7 +116,7 @@ void X3DGeoHelper::polylineIdx_to_lineIdx(const std::list &pPolylineCoo vert_set[6].Set(x1, y2, z1); \ vert_set[7].Set(x1, y1, z1) -void X3DGeoHelper::rect_parallele_piped(const aiVector3D &pSize, std::list &pVertices) { +void X3DGeoHelper::rect_parallel_epiped(const aiVector3D &pSize, std::list &pVertices) { MESH_RectParallelepiped_CREATE_VERT; MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 3, 2, 1, 0); // front MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 6, 7, 4, 5); // back diff --git a/code/AssetLib/X3D/X3DGeoHelper.h b/code/AssetLib/X3D/X3DGeoHelper.h index e730d3a9c..78e57f9da 100644 --- a/code/AssetLib/X3D/X3DGeoHelper.h +++ b/code/AssetLib/X3D/X3DGeoHelper.h @@ -19,7 +19,7 @@ public: static void make_arc2D(float pStartAngle, float pEndAngle, float pRadius, size_t numSegments, std::list &pVertices); static void extend_point_to_line(const std::list &pPoint, std::list &pLine); static void polylineIdx_to_lineIdx(const std::list &pPolylineCoordIdx, std::list &pLineCoordIdx); - static void rect_parallele_piped(const aiVector3D &pSize, std::list &pVertices); + static void rect_parallel_epiped(const aiVector3D &pSize, std::list &pVertices); static void coordIdx_str2faces_arr(const std::vector &pCoordIdx, std::vector &pFaces, unsigned int &pPrimitiveTypes); static void add_color(aiMesh &pMesh, const std::list &pColors, const bool pColorPerVertex); static void add_color(aiMesh &pMesh, const std::list &pColors, const bool pColorPerVertex); diff --git a/code/AssetLib/X3D/X3DImporter.cpp b/code/AssetLib/X3D/X3DImporter.cpp index eeb1a53d7..bfe83a49f 100644 --- a/code/AssetLib/X3D/X3DImporter.cpp +++ b/code/AssetLib/X3D/X3DImporter.cpp @@ -46,11 +46,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER #include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" -#include -#include #include -#include // Header files, stdlib. #include @@ -72,59 +70,15 @@ const aiImporterDesc X3DImporter::Description = { "x3d x3db" }; -struct WordIterator { - using iterator_category = std::input_iterator_tag; - using value_type = const char *; - using difference_type = ptrdiff_t; - using pointer = value_type *; - using reference = value_type &; +bool X3DImporter::isNodeEmpty(XmlNode &node) { + return node.first_child().empty(); +} - static const char *whitespace; - const char *mStart, *mEnd; +void X3DImporter::checkNodeMustBeEmpty(XmlNode &node) { + if (isNodeEmpty(node)) throw DeadlyImportError(std::string("Node <") + node.name() + "> must be empty."); +} - WordIterator(const char *start, const char *end) : - mStart(start), - mEnd(end) { - mStart = start + ::strspn(start, whitespace); - if (mStart >= mEnd) { - mStart = 0; - } - } - WordIterator() : - mStart(0), - mEnd(0) {} - WordIterator(const WordIterator &other) : - mStart(other.mStart), - mEnd(other.mEnd) {} - WordIterator &operator=(const WordIterator &other) { - mStart = other.mStart; - mEnd = other.mEnd; - return *this; - } - - bool operator==(const WordIterator &other) const { return mStart == other.mStart; } - - bool operator!=(const WordIterator &other) const { return mStart != other.mStart; } - - WordIterator &operator++() { - mStart += strcspn(mStart, whitespace); - mStart += strspn(mStart, whitespace); - if (mStart >= mEnd) { - mStart = 0; - } - return *this; - } - WordIterator operator++(int) { - WordIterator result(*this); - ++(*this); - return result; - } - const char *operator*() const { return mStart; } -}; - -const char *WordIterator::whitespace = ", \t\r\n"; - -void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) { +void X3DImporter::skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) { static const size_t Uns_Skip_Len = 192; static const char *Uns_Skip[Uns_Skip_Len] = { // CAD geometry component @@ -203,16 +157,20 @@ void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) { }; const std::string nn = node.name(); + + if (nn.empty()) { + const std::string nv = node.value(); + if (!nv.empty()) { + LogInfo("Ignoring comment \"" + nv + "\" in " + pParentNodeName + "."); + return; + } + } + bool found = false; - bool close_found = false; for (size_t i = 0; i < Uns_Skip_Len; i++) { if (nn == Uns_Skip[i]) { found = true; - if (node.empty()) { - close_found = true; - break; - } } } @@ -223,7 +181,8 @@ void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) { X3DImporter::X3DImporter() : mNodeElementCur(nullptr), - mScene(nullptr) { + mScene(nullptr), + mpIOHandler(nullptr) { // empty } @@ -265,9 +224,11 @@ void X3DImporter::ParseFile(const std::string &file, IOSystem *pIOHandler) { for (auto ¤tNode : node->children()) { const std::string ¤tName = currentNode.name(); if (currentName == "head") { - readMetadata(currentNode); + readHead(currentNode); } else if (currentName == "Scene") { readScene(currentNode); + } else { + skipUnsupportedNode("X3D", currentNode); } } } @@ -283,23 +244,25 @@ bool X3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, b return false; } -void X3DImporter::InternReadFile( const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler ) { +void X3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { + mpIOHandler = pIOHandler; + + Clear(); std::shared_ptr stream(pIOHandler->Open(pFile, "rb")); if (!stream) { throw DeadlyImportError("Could not open file for reading"); } std::string::size_type slashPos = pFile.find_last_of("\\/"); - pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1)); - ParseFile(pFile, pIOHandler); - pIOHandler->PopDirectory(); - - // mScene = pScene; pScene->mRootNode = new aiNode(pFile); pScene->mRootNode->mParent = nullptr; pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED; + pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1)); + ParseFile(pFile, pIOHandler); + pIOHandler->PopDirectory(); + //search for root node element mNodeElementCur = NodeElement_List.front(); @@ -342,7 +305,6 @@ void X3DImporter::InternReadFile( const std::string &pFile, aiScene *pScene, IOS pScene->mLights[i] = *it++; } } - } const aiImporterDesc *X3DImporter::GetInfo() const { @@ -354,2540 +316,162 @@ struct meta_entry { std::string value; }; -void X3DImporter::readMetadata(XmlNode &node) { +void X3DImporter::readHead(XmlNode &node) { std::vector metaArray; for (auto currentNode : node.children()) { const std::string ¤tName = currentNode.name(); if (currentName == "meta") { + checkNodeMustBeEmpty(node); meta_entry entry; if (XmlParser::getStdStrAttribute(currentNode, "name", entry.name)) { XmlParser::getStdStrAttribute(currentNode, "content", entry.value); metaArray.emplace_back(entry); } } + // TODO: check if other node types in head should be supported } mScene->mMetaData = aiMetadata::Alloc(static_cast(metaArray.size())); unsigned int i = 0; for (auto currentMeta : metaArray) { - mScene->mMetaData->Set(i, currentMeta.name, currentMeta.value); + mScene->mMetaData->Set(i, currentMeta.name, aiString(currentMeta.value)); ++i; } } -void X3DImporter::readScene(XmlNode &node) { +void X3DImporter::readChildNodes(XmlNode &node, const std::string &pParentNodeName) { for (auto currentNode : node.children()) { const std::string ¤tName = currentNode.name(); - if (currentName == "Viewpoint") { - readViewpoint(currentNode); + if (currentName == "Shape") + readShape(currentNode); + else if (currentName == "Group") { + startReadGroup(currentNode); + readChildNodes(currentNode, "Group"); + endReadGroup(); + } else if (currentName == "StaticGroup") { + startReadStaticGroup(currentNode); + readChildNodes(currentNode, "StaticGroup"); + endReadStaticGroup(); + } else if (currentName == "Transform") { + startReadTransform(currentNode); + readChildNodes(currentNode, "Transform"); + endReadTransform(); + } else if (currentName == "Switch") { + startReadSwitch(currentNode); + readChildNodes(currentNode, "Switch"); + endReadSwitch(); + } else if (currentName == "DirectionalLight") { + readDirectionalLight(currentNode); + } else if (currentName == "PointLight") { + readPointLight(currentNode); + } else if (currentName == "SpotLight") { + readSpotLight(currentNode); + } else if (currentName == "Inline") { + readInline(currentNode); + } else if (!checkForMetadataNode(currentNode)) { + skipUnsupportedNode(pParentNodeName, currentNode); } } } -void X3DImporter::readViewpoint(XmlNode &node) { - for (auto currentNode : node.children()) { - //const std::string ¤tName = currentNode.name(); - } +void X3DImporter::readScene(XmlNode &node) { + ParseHelper_Group_Begin(true); + readChildNodes(node, "Scene"); + ParseHelper_Node_Exit(); } -void readMetadataBoolean(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaBoolean *boolean = nullptr; - if (XmlParser::getStdStrAttribute(node, "value", val)) { - std::vector values; - tokenize(val, values, " "); - boolean = new X3DNodeElementMetaBoolean(parent); - for (size_t i = 0; i < values.size(); ++i) { - bool current_boolean = false; - if (values[i] == "true") { - current_boolean = true; - } - boolean->Value.emplace_back(current_boolean); +/*********************************************************************************************************************************************/ +/************************************************************ Functions: find set ************************************************************/ +/*********************************************************************************************************************************************/ + +bool X3DImporter::FindNodeElement_FromRoot(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement) { + for (std::list::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); ++it) { + if (((*it)->Type == pType) && ((*it)->ID == pID)) { + if (pElement != nullptr) *pElement = *it; + + return true; } - } -} + } // for(std::list::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); it++) -void readMetadataDouble(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaDouble *doubleNode = nullptr; - if (XmlParser::getStdStrAttribute(node, "value", val)) { - std::vector values; - tokenize(val, values, " "); - doubleNode = new X3DNodeElementMetaDouble(parent); - for (size_t i = 0; i < values.size(); ++i) { - double current_double = static_cast(fast_atof(values[i].c_str())); - doubleNode->Value.emplace_back(current_double); - } - } -} - -void readMetadataFloat(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaFloat *floatNode = nullptr; - if (XmlParser::getStdStrAttribute(node, "value", val)) { - std::vector values; - tokenize(val, values, " "); - floatNode = new X3DNodeElementMetaFloat(parent); - for (size_t i = 0; i < values.size(); ++i) { - float current_float = static_cast(fast_atof(values[i].c_str())); - floatNode->Value.emplace_back(current_float); - } - } -} - -void readMetadataInteger(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaInt *intNode = nullptr; - if (XmlParser::getStdStrAttribute(node, "value", val)) { - std::vector values; - tokenize(val, values, " "); - intNode = new X3DNodeElementMetaInt(parent); - for (size_t i = 0; i < values.size(); ++i) { - int current_int = static_cast(std::atoi(values[i].c_str())); - intNode->Value.emplace_back(current_int); - } - } -} - -void readMetadataSet(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaSet *setNode = new X3DNodeElementMetaSet(parent); - if (XmlParser::getStdStrAttribute(node, "name", val)) { - setNode->Name = val; - } - - if (XmlParser::getStdStrAttribute(node, "reference", val)) { - setNode->Reference = val; - } -} - -void readMetadataString(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaString *strNode = nullptr; - if (XmlParser::getStdStrAttribute(node, "value", val)) { - std::vector values; - tokenize(val, values, " "); - strNode = new X3DNodeElementMetaString(parent); - for (size_t i = 0; i < values.size(); ++i) { - strNode->Value.emplace_back(values[i]); - } - } -} - -void X3DImporter::ParseDirectionalLight(XmlNode &node) { - std::string def, use; - float ambientIntensity = 0; - aiColor3D color(1, 1, 1); - aiVector3D direction(0, 0, -1); - bool global = false; - float intensity = 1; - bool on = true; - X3DNodeElementBase *ne = nullptr; - - //MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); - //MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f); - MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_DirectionalLight, ne); - } else { - if (on) { - // create and if needed - define new geometry object. - ne = new X3DNodeNodeElementLight(CX3DImporter_NodeElement::ENET_DirectionalLight, NodeElement_Cur); - if (!def.empty()) - ne->ID = def; - else - ne->ID = "DirectionalLight_" + to_string((size_t)ne); // make random name - - ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; - ((X3DNodeNodeElementLight *)ne)->Color = color; - ((X3DNodeNodeElementLight *)ne)->Direction = direction; - ((X3DNodeNodeElementLight *)ne)->Global = global; - ((X3DNodeNodeElementLight *)ne)->Intensity = intensity; - // Assimp want a node with name similar to a light. "Why? I don't no." ) - ParseHelper_Group_Begin(false); - - mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. - ParseHelper_Node_Exit(); - // check for child nodes - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "DirectionalLight"); - else - mNodeElementCur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(on) - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Lighting_PointLight() { - std::string def, use; - float ambientIntensity = 0; - aiVector3D attenuation(1, 0, 0); - aiColor3D color(1, 1, 1); - bool global = true; - float intensity = 1; - aiVector3D location(0, 0, 0); - bool on = true; - float radius = 100; - X3DNodeElementBase *ne = nullptr; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f); - MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_PointLight, ne); - } else { - if (on) { - // create and if needed - define new geometry object. - ne = new X3DNodeNodeElementLight(X3DElemType::ENET_PointLight, mNodeElementCur); - if (!def.empty()) ne->ID = def; - - ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; - ((X3DNodeNodeElementLight *)ne)->Attenuation = attenuation; - ((X3DNodeNodeElementLight *)ne)->Color = color; - ((X3DNodeNodeElementLight *)ne)->Global = global; - ((X3DNodeNodeElementLight *)ne)->Intensity = intensity; - ((X3DNodeNodeElementLight *)ne)->Location = location; - ((X3DNodeNodeElementLight *)ne)->Radius = radius; - // Assimp want a node with name similar to a light. "Why? I don't no." ) - ParseHelper_Group_Begin(false); - // make random name - if (ne->ID.empty()) ne->ID = "PointLight_" + to_string((size_t)ne); - - mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. - ParseHelper_Node_Exit(); - // check for child nodes - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "PointLight"); - else - mNodeElementCur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(on) - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Lighting_SpotLight() { - std::string def, use; - float ambientIntensity = 0; - aiVector3D attenuation(1, 0, 0); - float beamWidth = 0.7854f; - aiColor3D color(1, 1, 1); - float cutOffAngle = 1.570796f; - aiVector3D direction(0, 0, -1); - bool global = true; - float intensity = 1; - aiVector3D location(0, 0, 0); - bool on = true; - float radius = 100; - X3DNodeElementBase *ne = nullptr; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("beamWidth", beamWidth, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f); - MACRO_ATTRREAD_CHECK_RET("cutOffAngle", cutOffAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_SpotLight, ne); - } else { - if (on) { - // create and if needed - define new geometry object. - ne = new X3DNodeNodeElementLight(X3DElemType::ENET_SpotLight, mNodeElementCur); - if (!def.empty()) - ne->ID = def; - - if (beamWidth > cutOffAngle) - beamWidth = cutOffAngle; - - ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; - ((X3DNodeNodeElementLight *)ne)->Attenuation = attenuation; - ((X3DNodeNodeElementLight *)ne)->BeamWidth = beamWidth; - ((X3DNodeNodeElementLight *)ne)->Color = color; - ((X3DNodeNodeElementLight *)ne)->CutOffAngle = cutOffAngle; - ((X3DNodeNodeElementLight *)ne)->Direction = direction; - ((X3DNodeNodeElementLight *)ne)->Global = global; - ((X3DNodeNodeElementLight *)ne)->Intensity = intensity; - ((X3DNodeNodeElementLight *)ne)->Location = location; - ((X3DNodeNodeElementLight *)ne)->Radius = radius; - - // Assimp want a node with name similar to a light. "Why? I don't no." ) - ParseHelper_Group_Begin(false); - // make random name - if (ne->ID.empty()) ne->ID = "SpotLight_" + to_string((size_t)ne); - - mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. - ParseHelper_Node_Exit(); - // check for child nodes - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "SpotLight"); - else - mNodeElementCur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(on) - } // if(!use.empty()) else -} - -void X3DImporter::ParseNode_Grouping_Group() { - std::string def, use; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - X3DNodeElementBase *ne = nullptr; - - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); - } else { - ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. - // at this place new group mode created and made current, so we can name it. - if (!def.empty()) mNodeElementCur->ID = def; - // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. - - // for empty element exit from node in that place - if (mReader->isEmptyElement()) ParseHelper_Node_Exit(); - } // if(!use.empty()) else -} - -void X3DImporter::ParseNode_Grouping_GroupEnd() { - ParseHelper_Node_Exit(); // go up in scene graph -} - -// -// -// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, -// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the -// precise palette of legal nodes that are available depends on assigned profile and components. -// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. -// -// The StaticGroup node contains children nodes which cannot be modified. StaticGroup children are guaranteed to not change, send events, receive events or -// contain any USE references outside the StaticGroup. -void X3DImporter::ParseNode_Grouping_StaticGroup() { - std::string def, use; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - X3DNodeElementBase *ne = nullptr; - - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); - } else { - ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children. - // at this place new group mode created and made current, so we can name it. - if (!def.empty()) mNodeElementCur->ID = def; - // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. - - // for empty element exit from node in that place - if (mReader->isEmptyElement()) ParseHelper_Node_Exit(); - } // if(!use.empty()) else -} - -void X3DImporter::ParseNode_Grouping_StaticGroupEnd() { - ParseHelper_Node_Exit(); // go up in scene graph -} - -// -// -// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, -// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the -// precise palette of legal nodes that are available depends on assigned profile and components. -// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. -// -// The Switch grouping node traverses zero or one of the nodes specified in the children field. The whichChoice field specifies the index of the child -// to traverse, with the first child having index 0. If whichChoice is less than zero or greater than the number of nodes in the children field, nothing -// is chosen. -void X3DImporter::ParseNode_Grouping_Switch() { - std::string def, use; - int32_t whichChoice = -1; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("whichChoice", whichChoice, XML_ReadNode_GetAttrVal_AsI32); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - CX3DImporter_NodeElement *ne; - - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); - } else { - ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. - // at this place new group mode created and made current, so we can name it. - if (!def.empty()) NodeElement_Cur->ID = def; - - // also set values specific to this type of group - ((CX3DNodeElementGroup *)NodeElement_Cur)->UseChoice = true; - ((CX3DNodeElementGroup *)NodeElement_Cur)->Choice = whichChoice; - // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. - - // for empty element exit from node in that place - if (mReader->isEmptyElement()) ParseHelper_Node_Exit(); - } // if(!use.empty()) else -} - -void X3DImporter::ParseNode_Grouping_SwitchEnd() { - // just exit from node. Defined choice will be accepted at postprocessing stage. - ParseHelper_Node_Exit(); // go up in scene graph -} - -// -// -// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, -// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the -// precise palette of legal nodes that are available depends on assigned profile and components. -// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. -// -// The Transform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors. -// Given a 3-dimensional point P and Transform node, P is transformed into point P' in its parent's coordinate system by a series of intermediate -// transformations. In matrix transformation notation, where C (center), SR (scaleOrientation), T (translation), R (rotation), and S (scale) are the -// equivalent transformation matrices, -// P' = T * C * R * SR * S * -SR * -C * P -void X3DImporter::ParseNode_Grouping_Transform() { - aiVector3D center(0, 0, 0); - float rotation[4] = { 0, 0, 1, 0 }; - aiVector3D scale(1, 1, 1); // A value of zero indicates that any child geometry shall not be displayed - float scale_orientation[4] = { 0, 0, 1, 0 }; - aiVector3D translation(0, 0, 0); - aiMatrix4x4 matr, tmatr; - std::string use, def; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("center", center, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_REF("translation", translation, XML_ReadNode_GetAttrVal_AsVec3f); - if (an == "rotation") { - std::vector tvec; - - XML_ReadNode_GetAttrVal_AsArrF(idx, tvec); - if (tvec.size() != 4) throw DeadlyImportError(": rotation vector must have 4 elements."); - - memcpy(rotation, tvec.data(), sizeof(rotation)); - - continue; - } - - if (an == "scaleOrientation") { - std::vector tvec; - XML_ReadNode_GetAttrVal_AsArrF(idx, tvec); - if (tvec.size() != 4) { - throw DeadlyImportError(": scaleOrientation vector must have 4 elements."); - } - - ::memcpy(scale_orientation, tvec.data(), sizeof(scale_orientation)); - - continue; - } - - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); - } else { - ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. - // at this place new group mode created and made current, so we can name it. - if (!def.empty()) { - NodeElement_Cur->ID = def; - } - - // - // also set values specific to this type of group - // - // calculate transformation matrix - aiMatrix4x4::Translation(translation, matr); // T - aiMatrix4x4::Translation(center, tmatr); // C - matr *= tmatr; - aiMatrix4x4::Rotation(rotation[3], aiVector3D(rotation[0], rotation[1], rotation[2]), tmatr); // R - matr *= tmatr; - aiMatrix4x4::Rotation(scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // SR - matr *= tmatr; - aiMatrix4x4::Scaling(scale, tmatr); // S - matr *= tmatr; - aiMatrix4x4::Rotation(-scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // -SR - matr *= tmatr; - aiMatrix4x4::Translation(-center, tmatr); // -C - matr *= tmatr; - // and assign it - ((CX3DNodeElementGroup *)mNodeElementCur)->Transformation = matr; - // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. - - // for empty element exit from node in that place - if (mReader->isEmptyElement()) { - ParseHelper_Node_Exit(); - } - } // if(!use.empty()) else -} - -void X3DImporter::ParseNode_Grouping_TransformEnd() { - ParseHelper_Node_Exit(); // go up in scene graph -} - -void X3DImporter::ParseNode_Geometry2D_Arc2D() { - std::string def, use; - float endAngle = AI_MATH_HALF_PI_F; - float radius = 1; - float startAngle = 0; - X3DNodeElementBase *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Arc2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Arc2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // create point list of geometry object and convert it to line set. - std::list tlist; - - GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg - GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Arc2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -// The ArcClose node specifies a portion of a circle whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping -// towards the positive y-axis. The end points of the arc specified are connected as defined by the closureType field. The radius field specifies the radius -// of the circle of which the arc is a portion. The arc extends from the startAngle counterclockwise to the endAngle. The value of radius shall be greater -// than zero. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different default angle base unit has -// been specified). If startAngle and endAngle have the same value, a circle is specified and closureType is ignored. If the absolute difference between -// startAngle and endAngle is greater than or equal to 2pi, a complete circle is produced with no chord or radial line(s) drawn from the center. -// A closureType of "PIE" connects the end point to the start point by defining two straight line segments first from the end point to the center and then -// the center to the start point. A closureType of "CHORD" connects the end point to the start point by defining a straight line segment from the end point -// to the start point. Textures are applied individually to each face of the ArcClose2D. On the front (+Z) and back (-Z) faces of the ArcClose2D, when -// viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. -void X3DImporter::ParseNode_Geometry2D_ArcClose2D() { - std::string def, use; - std::string closureType("PIE"); - float endAngle = AI_MATH_HALF_PI_F; - float radius = 1; - bool solid = false; - float startAngle = 0; - X3DNodeElementBase *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("closureType", closureType, mReader->getAttributeValue); - MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_ArcClose2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_ArcClose2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; - // create point list of geometry object. - GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); ///TODO: IME - AI_CONFIG for NumSeg - // add chord or two radiuses only if not a circle was defined - if (!((std::fabs(endAngle - startAngle) >= AI_MATH_TWO_PI_F) || (endAngle == startAngle))) { - std::list &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias. - - if ((closureType == "PIE") || (closureType == "\"PIE\"")) - vlist.push_back(aiVector3D(0, 0, 0)); // center point - first radial line - else if ((closureType != "CHORD") && (closureType != "\"CHORD\"")) - Throw_IncorrectAttrValue("closureType"); - - vlist.push_back(*vlist.begin()); // arc first point - chord from first to last point of arc(if CHORD) or second radial line(if PIE). - } - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.size(); - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "ArcClose2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry2D_Circle2D() { - std::string def, use; - float radius = 1; - X3DNodeElementBase *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Circle2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Circle2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // create point list of geometry object and convert it to line set. - std::list tlist; - - GeometryHelper_Make_Arc2D(0, 0, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg - GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Circle2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -// The Disk2D node specifies a circular disk which is centred at (0, 0) in the local coordinate system. The outerRadius field specifies the radius of the -// outer dimension of the Disk2D. The innerRadius field specifies the inner dimension of the Disk2D. The value of outerRadius shall be greater than zero. -// The value of innerRadius shall be greater than or equal to zero and less than or equal to outerRadius. If innerRadius is zero, the Disk2D is completely -// filled. Otherwise, the area within the innerRadius forms a hole in the Disk2D. If innerRadius is equal to outerRadius, a solid circular line shall -// be drawn using the current line properties. Textures are applied individually to each face of the Disk2D. On the front (+Z) and back (-Z) faces of -// the Disk2D, when viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. -void X3DImporter::ParseNode_Geometry2D_Disk2D() { - std::string def, use; - float innerRadius = 0; - float outerRadius = 1; - bool solid = false; - X3DNodeElementBase *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("innerRadius", innerRadius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("outerRadius", outerRadius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Disk2D, ne); - } else { - std::list tlist_o, tlist_i; - - if (innerRadius > outerRadius) Throw_IncorrectAttrValue("innerRadius"); - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Disk2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // create point list of geometry object. - ///TODO: IME - AI_CONFIG for NumSeg - GeometryHelper_Make_Arc2D(0, 0, outerRadius, 10, tlist_o); // outer circle - if (innerRadius == 0.0f) { // make filled disk - // in tlist_o we already have points of circle. just copy it and sign as polygon. - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices = tlist_o; - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = tlist_o.size(); - } else if (innerRadius == outerRadius) { // make circle - // in tlist_o we already have points of circle. convert it to line set. - GeometryHelper_Extend_PointToLine(tlist_o, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; - } else { // make disk - std::list &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias. - - GeometryHelper_Make_Arc2D(0, 0, innerRadius, 10, tlist_i); // inner circle - // - // create quad list from two point lists - // - if (tlist_i.size() < 2) throw DeadlyImportError("Disk2D. Not enough points for creating quad list."); // tlist_i and tlist_o has equal size. - - // add all quads except last - for (std::list::iterator it_i = tlist_i.begin(), it_o = tlist_o.begin(); it_i != tlist_i.end();) { - // do not forget - CCW direction - vlist.push_back(*it_i++); // 1st point - vlist.push_back(*it_o++); // 2nd point - vlist.push_back(*it_o); // 3rd point - vlist.push_back(*it_i); // 4th point - } - - // add last quad - vlist.push_back(*tlist_i.end()); // 1st point - vlist.push_back(*tlist_o.end()); // 2nd point - vlist.push_back(*tlist_o.begin()); // 3rd point - vlist.push_back(*tlist_o.begin()); // 4th point - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 4; - } - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Disk2D"); - else - mNodeElementCur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry2D_Polyline2D() { - std::string def, use; - std::list lineSegments; - X3DNodeElementBase *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("lineSegments", lineSegments, XML_ReadNode_GetAttrVal_AsListVec2f); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polyline2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polyline2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // - // convert read point list of geometry object to line set. - // - std::list tlist; - - // convert vec2 to vec3 - for (std::list::iterator it2 = lineSegments.begin(); it2 != lineSegments.end(); ++it2) - tlist.push_back(aiVector3D(it2->x, it2->y, 0)); - - // convert point set to line set - GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Polyline2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry2D_Polypoint2D() { - std::string def, use; - std::list point; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("point", point, XML_ReadNode_GetAttrVal_AsListVec2f); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polypoint2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polypoint2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // convert vec2 to vec3 - for (std::list::iterator it2 = point.begin(); it2 != point.end(); ++it2) { - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); - } - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 1; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Polypoint2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry2D_Rectangle2D() { - std::string def, use; - aiVector2D size(2, 2); - bool solid = false; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec2f); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Rectangle2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Rectangle2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - float x1 = -size.x / 2.0f; - float x2 = size.x / 2.0f; - float y1 = -size.y / 2.0f; - float y2 = size.y / 2.0f; - std::list &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias. - - vlist.push_back(aiVector3D(x2, y1, 0)); // 1st point - vlist.push_back(aiVector3D(x2, y2, 0)); // 2nd point - vlist.push_back(aiVector3D(x1, y2, 0)); // 3rd point - vlist.push_back(aiVector3D(x1, y1, 0)); // 4th point - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 4; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Rectangle2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry2D_TriangleSet2D() { - std::string def, use; - bool solid = false; - std::list vertices; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("vertices", vertices, XML_ReadNode_GetAttrVal_AsListVec2f); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_TriangleSet2D, ne); - } else { - if (vertices.size() % 3) throw DeadlyImportError("TriangleSet2D. Not enough points for defining triangle."); - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_TriangleSet2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // convert vec2 to vec3 - for (std::list::iterator it2 = vertices.begin(); it2 != vertices.end(); ++it2) { - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); - } - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 3; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "TriangleSet2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -// The Box node specifies a rectangular parallelepiped box centred at (0, 0, 0) in the local coordinate system and aligned with the local coordinate axes. -// By default, the box measures 2 units in each dimension, from -1 to +1. The size field specifies the extents of the box along the X-, Y-, and Z-axes -// respectively and each component value shall be greater than zero. -void X3DImporter::ParseNode_Geometry3D_Box() { - std::string def, use; - bool solid = true; - aiVector3D size(2, 2, 2); - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Box, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Box, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - GeometryHelper_MakeQL_RectParallelepiped(size, ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices); // get quad list - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 4; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Box"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry3D_Cone() { - std::string use, def; - bool bottom = true; - float bottomRadius = 1; - float height = 2; - bool side = true; - bool solid = true; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("bottomRadius", bottomRadius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cone, ne); - } else { - const unsigned int tess = 30; ///TODO: IME tessellation factor through ai_property - - std::vector tvec; // temp array for vertices. - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cone, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // make cone or parts according to flags. - if (side) { - StandardShapes::MakeCone(height, 0, bottomRadius, tess, tvec, !bottom); - } else if (bottom) { - StandardShapes::MakeCircle(bottomRadius, tess, tvec); - height = -(height / 2); - for (std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) - it->y = height; // y - because circle made in oXZ. - } - - // copy data from temp array - for (std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices.push_back(*it); - - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Cone"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry3D_Cylinder() { - std::string use, def; - bool bottom = true; - float height = 2; - float radius = 1; - bool side = true; - bool solid = true; - bool top = true; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("top", top, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cylinder, ne); - } else { - const unsigned int tess = 30; ///TODO: IME tessellation factor through ai_property - - std::vector tside; // temp array for vertices of side. - std::vector tcir; // temp array for vertices of circle. - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cylinder, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // make cilynder or parts according to flags. - if (side) StandardShapes::MakeCone(height, radius, radius, tess, tside, true); - - height /= 2; // height defined for whole cylinder, when creating top and bottom circle we are using just half of height. - if (top || bottom) StandardShapes::MakeCircle(radius, tess, tcir); - // copy data from temp arrays - std::list &vlist = ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices; // just short alias. - - for (std::vector::iterator it = tside.begin(); it != tside.end(); ++it) - vlist.push_back(*it); - - if (top) { - for (std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) { - (*it).y = height; // y - because circle made in oXZ. - vlist.push_back(*it); - } - } // if(top) - - if (bottom) { - for (std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) { - (*it).y = -height; // y - because circle made in oXZ. - vlist.push_back(*it); - } - } // if(top) - - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Cylinder"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -// -// ColorNormalTexCoordContentModel can contain Color (or ColorRGBA), Normal and TextureCoordinate, in any order. No more than one instance of any single -// node type is allowed. A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. -// -// The ElevationGrid node specifies a uniform rectangular grid of varying height in the Y=0 plane of the local coordinate system. The geometry is described -// by a scalar array of height values that specify the height of a surface above each point of the grid. The xDimension and zDimension fields indicate -// the number of elements of the grid height array in the X and Z directions. Both xDimension and zDimension shall be greater than or equal to zero. -// If either the xDimension or the zDimension is less than two, the ElevationGrid contains no quadrilaterals. -void X3DImporter::ParseNode_Geometry3D_ElevationGrid() { - std::string use, def; - bool ccw = true; - bool colorPerVertex = true; - float creaseAngle = 0; - std::vector height; - bool normalPerVertex = true; - bool solid = true; - int32_t xDimension = 0; - float xSpacing = 1; - int32_t zDimension = 0; - float zSpacing = 1; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("height", height, XML_ReadNode_GetAttrVal_AsArrF); - MACRO_ATTRREAD_CHECK_RET("xDimension", xDimension, XML_ReadNode_GetAttrVal_AsI32); - MACRO_ATTRREAD_CHECK_RET("xSpacing", xSpacing, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("zDimension", zDimension, XML_ReadNode_GetAttrVal_AsI32); - MACRO_ATTRREAD_CHECK_RET("zSpacing", zSpacing, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_ElevationGrid, ne); - } else { - if ((xSpacing == 0.0f) || (zSpacing == 0.0f)) throw DeadlyImportError("Spacing in must be grater than zero."); - if ((xDimension <= 0) || (zDimension <= 0)) throw DeadlyImportError("Dimension in must be grater than zero."); - if ((size_t)(xDimension * zDimension) != height.size()) Throw_IncorrectAttrValue("Heights count must be equal to \"xDimension * zDimension\""); - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_ElevationGrid(CX3DImporter_NodeElement::ENET_ElevationGrid, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - CX3DImporter_NodeElement_ElevationGrid &grid_alias = *((CX3DImporter_NodeElement_ElevationGrid *)ne); // create alias for conveience - - { // create grid vertices list - std::vector::const_iterator he_it = height.begin(); - - for (int32_t zi = 0; zi < zDimension; zi++) // rows - { - for (int32_t xi = 0; xi < xDimension; xi++) // columns - { - aiVector3D tvec(xSpacing * xi, *he_it, zSpacing * zi); - - grid_alias.Vertices.push_back(tvec); - ++he_it; - } - } - } // END: create grid vertices list - // - // create faces list. In "coordIdx" format - // - // check if we have quads - if ((xDimension < 2) || (zDimension < 2)) // only one element in dimension is set, create line set. - { - ((CX3DImporter_NodeElement_ElevationGrid *)ne)->NumIndices = 2; // will be holded as line set. - for (size_t i = 0, i_e = (grid_alias.Vertices.size() - 1); i < i_e; i++) { - grid_alias.CoordIdx.push_back(static_cast(i)); - grid_alias.CoordIdx.push_back(static_cast(i + 1)); - grid_alias.CoordIdx.push_back(-1); - } - } else // two or more elements in every dimension is set. create quad set. - { - ((CX3DImporter_NodeElement_ElevationGrid *)ne)->NumIndices = 4; - for (int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) // rows - { - for (int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) // columns - { - // points direction in face. - if (ccw) { - // CCW: - // 3 2 - // 0 1 - grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); - grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); - grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); - grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); - } else { - // CW: - // 0 1 - // 3 2 - grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); - grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); - grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); - grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); - } // if(ccw) else - - grid_alias.CoordIdx.push_back(-1); - } // for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) - } // for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) - } // if((xDimension < 2) || (zDimension < 2)) else - - grid_alias.ColorPerVertex = colorPerVertex; - grid_alias.NormalPerVertex = normalPerVertex; - grid_alias.CreaseAngle = creaseAngle; - grid_alias.Solid = solid; - // check for child nodes - if (!mReader->isEmptyElement()) { - ParseHelper_Node_Enter(ne); - MACRO_NODECHECK_LOOPBEGIN("ElevationGrid"); - // check for X3DComposedGeometryNodes - if (XML_CheckNode_NameEqual("Color")) { - ParseNode_Rendering_Color(); - continue; - } - if (XML_CheckNode_NameEqual("ColorRGBA")) { - ParseNode_Rendering_ColorRGBA(); - continue; - } - if (XML_CheckNode_NameEqual("Normal")) { - ParseNode_Rendering_Normal(); - continue; - } - if (XML_CheckNode_NameEqual("TextureCoordinate")) { - ParseNode_Texturing_TextureCoordinate(); - continue; - } - // check for X3DMetadataObject - if (!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("ElevationGrid"); - - MACRO_NODECHECK_LOOPEND("ElevationGrid"); - ParseHelper_Node_Exit(); - } // if(!mReader->isEmptyElement()) - else { - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - } // if(!mReader->isEmptyElement()) else - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -template -static void GeometryHelper_Extrusion_CurveIsClosed(std::vector &pCurve, const bool pDropTail, const bool pRemoveLastPoint, bool &pCurveIsClosed) { - size_t cur_sz = pCurve.size(); - - pCurveIsClosed = false; - // for curve with less than four points checking is have no sense, - if (cur_sz < 4) return; - - for (size_t s = 3, s_e = cur_sz; s < s_e; s++) { - // search for first point of duplicated part. - if (pCurve[0] == pCurve[s]) { - bool found = true; - - // check if tail(indexed by b2) is duplicate of head(indexed by b1). - for (size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) { - if (pCurve[b1] != pCurve[b2]) { // points not match: clear flag and break loop. - found = false; - - break; - } - } // for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) - - // if duplicate tail is found then drop or not it depending on flags. - if (found) { - pCurveIsClosed = true; - if (pDropTail) { - if (!pRemoveLastPoint) s++; // prepare value for iterator's arithmetics. - - pCurve.erase(pCurve.begin() + s, pCurve.end()); // remove tail - } - - break; - } // if(found) - } // if(pCurve[0] == pCurve[s]) - } // for(size_t s = 3, s_e = (cur_sz - 1); s < s_e; s++) -} - -static aiVector3D GeometryHelper_Extrusion_GetNextY(const size_t pSpine_PointIdx, const std::vector &pSpine, const bool pSpine_Closed) { - const size_t spine_idx_last = pSpine.size() - 1; - aiVector3D tvec; - - if ((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) // at first special cases - { - if (pSpine_Closed) { // If the spine curve is closed: The SCP for the first and last points is the same and is found using (spine[1] - spine[n - 2]) to compute the Y-axis. - // As we even for closed spine curve last and first point in pSpine are not the same: duplicates(spine[n - 1] which are equivalent to spine[0]) - // in tail are removed. - // So, last point in pSpine is a spine[n - 2] - tvec = pSpine[1] - pSpine[spine_idx_last]; - } else if (pSpine_PointIdx == 0) { // The Y-axis used for the first point is the vector from spine[0] to spine[1] - tvec = pSpine[1] - pSpine[0]; - } else { // The Y-axis used for the last point it is the vector from spine[n-2] to spine[n-1]. In our case(see above about dropping tail) spine[n - 1] is - // the spine[0]. - tvec = pSpine[spine_idx_last] - pSpine[spine_idx_last - 1]; - } - } // if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) - else { // For all points other than the first or last: The Y-axis for spine[i] is found by normalizing the vector defined by (spine[i+1] - spine[i-1]). - tvec = pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx - 1]; - } // if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) else - - return tvec.Normalize(); -} - -static aiVector3D GeometryHelper_Extrusion_GetNextZ(const size_t pSpine_PointIdx, const std::vector &pSpine, const bool pSpine_Closed, - const aiVector3D pVecZ_Prev) { - const aiVector3D zero_vec(0); - const size_t spine_idx_last = pSpine.size() - 1; - - aiVector3D tvec; - - // at first special cases - if (pSpine.size() < 3) // spine have not enough points for vector calculations. - { - tvec.Set(0, 0, 1); - } else if (pSpine_PointIdx == 0) // special case: first point - { - if (pSpine_Closed) // for calculating use previous point in curve s[n - 2]. In list it's a last point, because point s[n - 1] was removed as duplicate. - { - tvec = (pSpine[1] - pSpine[0]) ^ (pSpine[spine_idx_last] - pSpine[0]); - } else // for not closed curve first and next point(s[0] and s[1]) has the same vector Z. - { - bool found = false; - - // As said: "If the Z-axis of the first point is undefined (because the spine is not closed and the first two spine segments are collinear) - // then the Z-axis for the first spine point with a defined Z-axis is used." - // Walk through spine and find Z. - for (size_t next_point = 2; (next_point <= spine_idx_last) && !found; next_point++) { - // (pSpine[2] - pSpine[1]) ^ (pSpine[0] - pSpine[1]) - tvec = (pSpine[next_point] - pSpine[next_point - 1]) ^ (pSpine[next_point - 2] - pSpine[next_point - 1]); - found = !tvec.Equal(zero_vec); - } - - // if entire spine are collinear then use OZ axis. - if (!found) tvec.Set(0, 0, 1); - } // if(pSpine_Closed) else - } // else if(pSpine_PointIdx == 0) - else if (pSpine_PointIdx == spine_idx_last) // special case: last point - { - if (pSpine_Closed) { // do not forget that real last point s[n - 1] is removed as duplicated. And in this case we are calculating vector Z for point s[n - 2]. - tvec = (pSpine[0] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); - // if taken spine vectors are collinear then use previous vector Z. - if (tvec.Equal(zero_vec)) tvec = pVecZ_Prev; - } else { // vector Z for last point of not closed curve is previous vector Z. - tvec = pVecZ_Prev; - } - } else // regular point - { - tvec = (pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); - // if taken spine vectors are collinear then use previous vector Z. - if (tvec.Equal(zero_vec)) tvec = pVecZ_Prev; - } - - // After determining the Z-axis, its dot product with the Z-axis of the previous spine point is computed. If this value is negative, the Z-axis - // is flipped (multiplied by -1). - if ((tvec * pVecZ_Prev) < 0) tvec = -tvec; - - return tvec.Normalize(); -} - -// -void X3DImporter::ParseNode_Geometry3D_Extrusion() { - std::string use, def; - bool beginCap = true; - bool ccw = true; - bool convex = true; - float creaseAngle = 0; - std::vector crossSection; - bool endCap = true; - std::vector orientation; - std::vector scale; - bool solid = true; - std::vector spine; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("beginCap", beginCap, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("crossSection", crossSection, XML_ReadNode_GetAttrVal_AsArrVec2f); - MACRO_ATTRREAD_CHECK_RET("endCap", endCap, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_REF("orientation", orientation, XML_ReadNode_GetAttrVal_AsArrF); - MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsArrVec2f); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_REF("spine", spine, XML_ReadNode_GetAttrVal_AsArrVec3f); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Extrusion, ne); - } else { - // - // check if default values must be assigned - // - if (spine.size() == 0) { - spine.resize(2); - spine[0].Set(0, 0, 0), spine[1].Set(0, 1, 0); - } else if (spine.size() == 1) { - throw DeadlyImportError("ParseNode_Geometry3D_Extrusion. Spine must have at least two points."); - } - - if (crossSection.size() == 0) { - crossSection.resize(5); - crossSection[0].Set(1, 1), crossSection[1].Set(1, -1), crossSection[2].Set(-1, -1), crossSection[3].Set(-1, 1), crossSection[4].Set(1, 1); - } - - { // orientation - size_t ori_size = orientation.size() / 4; - - if (ori_size < spine.size()) { - float add_ori[4]; // values that will be added - - if (ori_size == 1) // if "orientation" has one element(means one MFRotation with four components) then use it value for all spine points. - { - add_ori[0] = orientation[0], add_ori[1] = orientation[1], add_ori[2] = orientation[2], add_ori[3] = orientation[3]; - } else // else - use default values - { - add_ori[0] = 0, add_ori[1] = 0, add_ori[2] = 1, add_ori[3] = 0; - } - - orientation.reserve(spine.size() * 4); - for (size_t i = 0, i_e = (spine.size() - ori_size); i < i_e; i++) - orientation.push_back(add_ori[0]), orientation.push_back(add_ori[1]), orientation.push_back(add_ori[2]), orientation.push_back(add_ori[3]); - } - - if (orientation.size() % 4) throw DeadlyImportError("Attribute \"orientation\" in must has multiple four quantity of numbers."); - } // END: orientation - - { // scale - if (scale.size() < spine.size()) { - aiVector2D add_sc; - - if (scale.size() == 1) // if "scale" has one element then use it value for all spine points. - add_sc = scale[0]; - else // else - use default values - add_sc.Set(1, 1); - - scale.reserve(spine.size()); - for (size_t i = 0, i_e = (spine.size() - scale.size()); i < i_e; i++) - scale.push_back(add_sc); - } - } // END: scale - // - // create and if needed - define new geometry object. - // - ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_Extrusion, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - CX3DImporter_NodeElement_IndexedSet &ext_alias = *((CX3DImporter_NodeElement_IndexedSet *)ne); // create alias for conveience - // assign part of input data - ext_alias.CCW = ccw; - ext_alias.Convex = convex; - ext_alias.CreaseAngle = creaseAngle; - ext_alias.Solid = solid; - - // - // How we done it at all? - // 1. At first we will calculate array of basises for every point in spine(look SCP in ISO-dic). Also "orientation" vector - // are applied vor every basis. - // 2. After that we can create array of point sets: which are scaled, transferred to basis of relative basis and at final translated to real position - // using relative spine point. - // 3. Next step is creating CoordIdx array(do not forget "-1" delimiter). While creating CoordIdx also created faces for begin and end caps, if - // needed. While createing CootdIdx is taking in account CCW flag. - // 4. The last step: create Vertices list. - // - bool spine_closed; // flag: true if spine curve is closed. - bool cross_closed; // flag: true if cross curve is closed. - std::vector basis_arr; // array of basises. ROW_a - X, ROW_b - Y, ROW_c - Z. - std::vector> pointset_arr; // array of point sets: cross curves. - - // detect closed curves - GeometryHelper_Extrusion_CurveIsClosed(crossSection, true, true, cross_closed); // true - drop tail, true - remove duplicate end. - GeometryHelper_Extrusion_CurveIsClosed(spine, true, true, spine_closed); // true - drop tail, true - remove duplicate end. - // If both cap are requested and spine curve is closed then we can make only one cap. Because second cap will be the same surface. - if (spine_closed) { - beginCap |= endCap; - endCap = false; - } - - { // 1. Calculate array of basises. - aiMatrix4x4 rotmat; - aiVector3D vecX(0), vecY(0), vecZ(0); - - basis_arr.resize(spine.size()); - for (size_t i = 0, i_e = spine.size(); i < i_e; i++) { - aiVector3D tvec; - - // get axises of basis. - vecY = GeometryHelper_Extrusion_GetNextY(i, spine, spine_closed); - vecZ = GeometryHelper_Extrusion_GetNextZ(i, spine, spine_closed, vecZ); - vecX = (vecY ^ vecZ).Normalize(); - // get rotation matrix and apply "orientation" to basis - aiMatrix4x4::Rotation(orientation[i * 4 + 3], aiVector3D(orientation[i * 4], orientation[i * 4 + 1], orientation[i * 4 + 2]), rotmat); - tvec = vecX, tvec *= rotmat, basis_arr[i].a1 = tvec.x, basis_arr[i].a2 = tvec.y, basis_arr[i].a3 = tvec.z; - tvec = vecY, tvec *= rotmat, basis_arr[i].b1 = tvec.x, basis_arr[i].b2 = tvec.y, basis_arr[i].b3 = tvec.z; - tvec = vecZ, tvec *= rotmat, basis_arr[i].c1 = tvec.x, basis_arr[i].c2 = tvec.y, basis_arr[i].c3 = tvec.z; - } // for(size_t i = 0, i_e = spine.size(); i < i_e; i++) - } // END: 1. Calculate array of basises - - { // 2. Create array of point sets. - aiMatrix4x4 scmat; - std::vector tcross(crossSection.size()); - - pointset_arr.resize(spine.size()); - for (size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) { - aiVector3D tc23vec; - - tc23vec.Set(scale[spi].x, 0, scale[spi].y); - aiMatrix4x4::Scaling(tc23vec, scmat); - for (size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) { - aiVector3D tvecX, tvecY, tvecZ; - - tc23vec.Set(crossSection[cri].x, 0, crossSection[cri].y); - // apply scaling to point - tcross[cri] = scmat * tc23vec; - // - // transfer point to new basis - // calculate coordinate in new basis - tvecX.Set(basis_arr[spi].a1, basis_arr[spi].a2, basis_arr[spi].a3), tvecX *= tcross[cri].x; - tvecY.Set(basis_arr[spi].b1, basis_arr[spi].b2, basis_arr[spi].b3), tvecY *= tcross[cri].y; - tvecZ.Set(basis_arr[spi].c1, basis_arr[spi].c2, basis_arr[spi].c3), tvecZ *= tcross[cri].z; - // apply new coordinates and translate it to spine point. - tcross[cri] = tvecX + tvecY + tvecZ + spine[spi]; - } // for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; i++) - - pointset_arr[spi] = tcross; // store transferred point set - } // for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; i++) - } // END: 2. Create array of point sets. - - { // 3. Create CoordIdx. - // add caps if needed - if (beginCap) { - // add cap as polygon. vertices of cap are places at begin, so just add numbers from zero. - for (size_t i = 0, i_e = crossSection.size(); i < i_e; i++) - ext_alias.CoordIndex.push_back(static_cast(i)); - - // add delimiter - ext_alias.CoordIndex.push_back(-1); - } // if(beginCap) - - if (endCap) { - // add cap as polygon. vertices of cap are places at end, as for beginCap use just sequence of numbers but with offset. - size_t beg = (pointset_arr.size() - 1) * crossSection.size(); - - for (size_t i = beg, i_e = (beg + crossSection.size()); i < i_e; i++) - ext_alias.CoordIndex.push_back(static_cast(i)); - - // add delimiter - ext_alias.CoordIndex.push_back(-1); - } // if(beginCap) - - // add quads - for (size_t spi = 0, spi_e = (spine.size() - 1); spi <= spi_e; spi++) { - const size_t cr_sz = crossSection.size(); - const size_t cr_last = crossSection.size() - 1; - - size_t right_col; // hold index basis for points of quad placed in right column; - - if (spi != spi_e) - right_col = spi + 1; - else if (spine_closed) // if spine curve is closed then one more quad is needed: between first and last points of curve. - right_col = 0; - else - break; // if spine curve is not closed then break the loop, because spi is out of range for that type of spine. - - for (size_t cri = 0; cri < cr_sz; cri++) { - if (cri != cr_last) { - MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, - static_cast(spi * cr_sz + cri), - static_cast(right_col * cr_sz + cri), - static_cast(right_col * cr_sz + cri + 1), - static_cast(spi * cr_sz + cri + 1)); - // add delimiter - ext_alias.CoordIndex.push_back(-1); - } else if (cross_closed) // if cross curve is closed then one more quad is needed: between first and last points of curve. - { - MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, - static_cast(spi * cr_sz + cri), - static_cast(right_col * cr_sz + cri), - static_cast(right_col * cr_sz + 0), - static_cast(spi * cr_sz + 0)); - // add delimiter - ext_alias.CoordIndex.push_back(-1); - } - } // for(size_t cri = 0; cri < cr_sz; cri++) - } // for(size_t spi = 0, spi_e = (spine.size() - 2); spi < spi_e; spi++) - } // END: 3. Create CoordIdx. - - { // 4. Create vertices list. - // just copy all vertices - for (size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) { - for (size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) { - ext_alias.Vertices.push_back(pointset_arr[spi][cri]); - } - } - } // END: 4. Create vertices list. - //PrintVectorSet("Ext. CoordIdx", ext_alias.CoordIndex); - //PrintVectorSet("Ext. Vertices", ext_alias.Vertices); - // check for child nodes - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Extrusion"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -// -// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, -// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, -// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. -// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. -// -void X3DImporter::ParseNode_Geometry3D_IndexedFaceSet() { - std::string use, def; - bool ccw = true; - std::vector colorIndex; - bool colorPerVertex = true; - bool convex = true; - std::vector coordIndex; - float creaseAngle = 0; - std::vector normalIndex; - bool normalPerVertex = true; - bool solid = true; - std::vector texCoordIndex; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_REF("colorIndex", colorIndex, XML_ReadNode_GetAttrVal_AsArrI32); - MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_REF("coordIndex", coordIndex, XML_ReadNode_GetAttrVal_AsArrI32); - MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("normalIndex", normalIndex, XML_ReadNode_GetAttrVal_AsArrI32); - MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_REF("texCoordIndex", texCoordIndex, XML_ReadNode_GetAttrVal_AsArrI32); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedFaceSet, ne); - } else { - // check data - if (coordIndex.size() == 0) throw DeadlyImportError("IndexedFaceSet must contain not empty \"coordIndex\" attribute."); - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedFaceSet, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - CX3DImporter_NodeElement_IndexedSet &ne_alias = *((CX3DImporter_NodeElement_IndexedSet *)ne); - - ne_alias.CCW = ccw; - ne_alias.ColorIndex = colorIndex; - ne_alias.ColorPerVertex = colorPerVertex; - ne_alias.Convex = convex; - ne_alias.CoordIndex = coordIndex; - ne_alias.CreaseAngle = creaseAngle; - ne_alias.NormalIndex = normalIndex; - ne_alias.NormalPerVertex = normalPerVertex; - ne_alias.Solid = solid; - ne_alias.TexCoordIndex = texCoordIndex; - // check for child nodes - if (!mReader->isEmptyElement()) { - ParseHelper_Node_Enter(ne); - MACRO_NODECHECK_LOOPBEGIN("IndexedFaceSet"); - // check for X3DComposedGeometryNodes - if (XML_CheckNode_NameEqual("Color")) { - ParseNode_Rendering_Color(); - continue; - } - if (XML_CheckNode_NameEqual("ColorRGBA")) { - ParseNode_Rendering_ColorRGBA(); - continue; - } - if (XML_CheckNode_NameEqual("Coordinate")) { - ParseNode_Rendering_Coordinate(); - continue; - } - if (XML_CheckNode_NameEqual("Normal")) { - ParseNode_Rendering_Normal(); - continue; - } - if (XML_CheckNode_NameEqual("TextureCoordinate")) { - ParseNode_Texturing_TextureCoordinate(); - continue; - } - // check for X3DMetadataObject - if (!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedFaceSet"); - - MACRO_NODECHECK_LOOPEND("IndexedFaceSet"); - ParseHelper_Node_Exit(); - } // if(!mReader->isEmptyElement()) - else { - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - } - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry3D_Sphere() { - std::string use, def; - ai_real radius = 1; - bool solid = true; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Sphere, ne); - } else { - const unsigned int tess = 3; ///TODO: IME tessellation factor through ai_property - - std::vector tlist; - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Sphere, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - StandardShapes::MakeSphere(tess, tlist); - // copy data from temp array and apply scale - for (std::vector::iterator it = tlist.begin(); it != tlist.end(); ++it) { - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices.push_back(*it * radius); - } - - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Sphere"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - - - -void X3DImporter::readMetadataObject(XmlNode &node) { - const std::string &name = node.name(); - if (name == "MetadataBoolean") { - readMetadataBoolean(node, mNodeElementCur); - } else if (name == "MetadataDouble") { - readMetadataDouble(node, mNodeElementCur); - } else if (name == "MetadataFloat") { - readMetadataFloat(node, mNodeElementCur); - } else if (name == "MetadataInteger") { - readMetadataInteger(node, mNodeElementCur); - } else if (name == "MetadataSet") { - readMetadataSet(node, mNodeElementCur); - } else if (name == "MetadataString") { - readMetadataString(node, mNodeElementCur); - } -} - - -aiMatrix4x4 PostprocessHelper_Matrix_GlobalToCurrent() { - X3DNodeElementBase *cur_node = nullptr; - std::list matr; - aiMatrix4x4 out_matr; - - // starting walk from current element to root - cur_node = cur_node; - if (cur_node != nullptr) { - do { - // if cur_node is group then store group transformation matrix in list. - if (cur_node->Type == X3DNodeElementBase::ENET_Group) matr.push_back(((X3DNodeElementBase *)cur_node)->Transformation); - - cur_node = cur_node->Parent; - } while (cur_node != nullptr); - } - - // multiplicate all matrices in reverse order - for (std::list::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); ++rit) - out_matr = out_matr * (*rit); - - return out_matr; -} - -void X3DImporter::PostprocessHelper_CollectMetadata(const CX3DImporter_NodeElement &pNodeElement, std::list &pList) const { - // walk through childs and find for metadata. - for (std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it) { - if (((*el_it)->Type == X3DElemType::ENET_MetaBoolean) || ((*el_it)->Type == X3DElemType::ENET_MetaDouble) || - ((*el_it)->Type == X3DElemType::ENET_MetaFloat) || ((*el_it)->Type == X3DElemType::ENET_MetaInteger) || - ((*el_it)->Type == X3DElemType::ENET_MetaString)) { - pList.push_back(*el_it); - } else if ((*el_it)->Type == X3DElemType::ENET_MetaSet) { - PostprocessHelper_CollectMetadata(**el_it, pList); - } - } // for(std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++) -} - -bool X3DImporter::PostprocessHelper_ElementIsMetadata(const CX3DImporter_NodeElement::EType pType) const { - if ((pType == X3DNodeElementBase::ENET_MetaBoolean) || (pType == X3DElemType::ENET_MetaDouble) || - (pType == X3DElemType::ENET_MetaFloat) || (pType == X3DElemType::ENET_MetaInteger) || - (pType == X3DElemType::ENET_MetaString) || (pType == X3DElemType::ENET_MetaSet)) { - return true; - } return false; } -bool X3DImporter::PostprocessHelper_ElementIsMesh(const CX3DImporter_NodeElement::EType pType) const { - if ((pType == X3DElemType::ENET_Arc2D) || (pType == X3DElemType::ENET_ArcClose2D) || - (pType == X3DElemType::ENET_Box) || (pType == X3DElemType::ENET_Circle2D) || - (pType == X3DElemType::ENET_Cone) || (pType == X3DElemType::ENET_Cylinder) || - (pType == X3DElemType::ENET_Disk2D) || (pType == X3DElemType::ENET_ElevationGrid) || - (pType == X3DElemType::ENET_Extrusion) || (pType == X3DElemType::ENET_IndexedFaceSet) || - (pType == X3DElemType::ENET_IndexedLineSet) || (pType == X3DElemType::ENET_IndexedTriangleFanSet) || - (pType == X3DElemType::ENET_IndexedTriangleSet) || (pType == X3DElemType::ENET_IndexedTriangleStripSet) || - (pType == X3DElemType::ENET_PointSet) || (pType == X3DElemType::ENET_LineSet) || - (pType == X3DElemType::ENET_Polyline2D) || (pType == X3DElemType::ENET_Polypoint2D) || - (pType == X3DElemType::ENET_Rectangle2D) || (pType == X3DElemType::ENET_Sphere) || - (pType == X3DElemType::ENET_TriangleFanSet) || (pType == X3DElemType::ENET_TriangleSet) || - (pType == X3DElemType::ENET_TriangleSet2D) || (pType == X3DElemType::ENET_TriangleStripSet)) { - return true; +bool X3DImporter::FindNodeElement_FromNode(X3DNodeElementBase *pStartNode, const std::string &pID, + const X3DElemType pType, X3DNodeElementBase **pElement) { + bool found = false; // flag: true - if requested element is found. + + // Check if pStartNode - this is the element, we are looking for. + if ((pStartNode->Type == pType) && (pStartNode->ID == pID)) { + found = true; + if (pElement != nullptr) { + *pElement = pStartNode; + } + + goto fne_fn_end; + } // if((pStartNode->Type() == pType) && (pStartNode->ID() == pID)) + + // Check childs of pStartNode. + for (std::list::iterator ch_it = pStartNode->Children.begin(); ch_it != pStartNode->Children.end(); ++ch_it) { + found = FindNodeElement_FromNode(*ch_it, pID, pType, pElement); + if (found) { + break; + } + } // for(std::list::iterator ch_it = it->Children.begin(); ch_it != it->Children.end(); ch_it++) + +fne_fn_end: + + return found; +} + +bool X3DImporter::FindNodeElement(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement) { + X3DNodeElementBase *tnd = mNodeElementCur; // temporary pointer to node. + bool static_search = false; // flag: true if searching in static node. + + // At first check if we have deal with static node. Go up through parent nodes and check flag. + while (tnd != nullptr) { + if (tnd->Type == X3DElemType::ENET_Group) { + if (((X3DNodeElementGroup *)tnd)->Static) { + static_search = true; // Flag found, stop walking up. Node with static flag will holded in tnd variable. + break; + } + } + + tnd = tnd->Parent; // go up in graph. + } // while (tnd != nullptr) + + // at now call appropriate search function. + if (static_search) { + return FindNodeElement_FromNode(tnd, pID, pType, pElement); } else { - return false; + return FindNodeElement_FromRoot(pID, pType, pElement); } } -void X3DImporter::Postprocess_BuildLight(const CX3DImporter_NodeElement &pNodeElement, std::list &pSceneLightList) const { - const CX3DImporter_NodeElement_Light &ne = *((CX3DImporter_NodeElement_Light *)&pNodeElement); - aiMatrix4x4 transform_matr = PostprocessHelper_Matrix_GlobalToCurrent(); - aiLight *new_light = new aiLight; +/*********************************************************************************************************************************************/ +/************************************************************ Functions: parse set ***********************************************************/ +/*********************************************************************************************************************************************/ - new_light->mName = ne.ID; - new_light->mColorAmbient = ne.Color * ne.AmbientIntensity; - new_light->mColorDiffuse = ne.Color * ne.Intensity; - new_light->mColorSpecular = ne.Color * ne.Intensity; - switch (pNodeElement.Type) { - case CX3DImporter_NodeElement::ENET_DirectionalLight: - new_light->mType = aiLightSource_DIRECTIONAL; - new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; +void X3DImporter::ParseHelper_Group_Begin(const bool pStatic) { + X3DNodeElementGroup *new_group = new X3DNodeElementGroup(mNodeElementCur, pStatic); // create new node with current node as parent. - break; - case CX3DImporter_NodeElement::ENET_PointLight: - new_light->mType = aiLightSource_POINT; - new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; - new_light->mAttenuationConstant = ne.Attenuation.x; - new_light->mAttenuationLinear = ne.Attenuation.y; - new_light->mAttenuationQuadratic = ne.Attenuation.z; - - break; - case CX3DImporter_NodeElement::ENET_SpotLight: - new_light->mType = aiLightSource_SPOT; - new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; - new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; - new_light->mAttenuationConstant = ne.Attenuation.x; - new_light->mAttenuationLinear = ne.Attenuation.y; - new_light->mAttenuationQuadratic = ne.Attenuation.z; - new_light->mAngleInnerCone = ne.BeamWidth; - new_light->mAngleOuterCone = ne.CutOffAngle; - - break; - default: - throw DeadlyImportError("Postprocess_BuildLight. Unknown type of light: " + to_string(pNodeElement.Type) + "."); + // if we are adding not the root element then add new element to current element child list. + if (mNodeElementCur != nullptr) { + mNodeElementCur->Children.push_back(new_group); } - pSceneLightList.push_back(new_light); + NodeElement_List.push_back(new_group); // it's a new element - add it to list. + mNodeElementCur = new_group; // switch current element to new one. } -void X3DImporter::Postprocess_BuildMaterial(const CX3DImporter_NodeElement &pNodeElement, aiMaterial **pMaterial) const { - // check argument - if (pMaterial == nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. pMaterial is nullptr."); - if (*pMaterial != nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. *pMaterial must be nullptr."); +void X3DImporter::ParseHelper_Node_Enter(X3DNodeElementBase *pNode) { + mNodeElementCur->Children.push_back(pNode); // add new element to current element child list. + mNodeElementCur = pNode; // switch current element to new one. +} - *pMaterial = new aiMaterial; - aiMaterial &taimat = **pMaterial; // creating alias for convenience. - - // at this point pNodeElement point to node. Walk through childs and add all stored data. - for (std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it) { - if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material) { - aiColor3D tcol3; - float tvalf; - CX3DImporter_NodeElement_Material &tnemat = *((CX3DImporter_NodeElement_Material *)*el_it); - - tcol3.r = tnemat.AmbientIntensity, tcol3.g = tnemat.AmbientIntensity, tcol3.b = tnemat.AmbientIntensity; - taimat.AddProperty(&tcol3, 1, AI_MATKEY_COLOR_AMBIENT); - taimat.AddProperty(&tnemat.DiffuseColor, 1, AI_MATKEY_COLOR_DIFFUSE); - taimat.AddProperty(&tnemat.EmissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE); - taimat.AddProperty(&tnemat.SpecularColor, 1, AI_MATKEY_COLOR_SPECULAR); - tvalf = 1; - taimat.AddProperty(&tvalf, 1, AI_MATKEY_SHININESS_STRENGTH); - taimat.AddProperty(&tnemat.Shininess, 1, AI_MATKEY_SHININESS); - tvalf = 1.0f - tnemat.Transparency; - taimat.AddProperty(&tvalf, 1, AI_MATKEY_OPACITY); - } // if((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material) - else if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture) { - CX3DImporter_NodeElement_ImageTexture &tnetex = *((CX3DImporter_NodeElement_ImageTexture *)*el_it); - aiString url_str(tnetex.URL.c_str()); - int mode = aiTextureOp_Multiply; - - taimat.AddProperty(&url_str, AI_MATKEY_TEXTURE_DIFFUSE(0)); - taimat.AddProperty(&tnetex.RepeatS, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0)); - taimat.AddProperty(&tnetex.RepeatT, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0)); - taimat.AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0)); - } // else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture) - else if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform) { - aiUVTransform trans; - CX3DImporter_NodeElement_TextureTransform &tnetextr = *((CX3DImporter_NodeElement_TextureTransform *)*el_it); - - trans.mTranslation = tnetextr.Translation - tnetextr.Center; - trans.mScaling = tnetextr.Scale; - trans.mRotation = tnetextr.Rotation; - taimat.AddProperty(&trans, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); - } // else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform) - } // for(std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++) -} - -void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement &pNodeElement, aiMesh **pMesh) const { - // check argument - if (pMesh == nullptr) throw DeadlyImportError("Postprocess_BuildMesh. pMesh is nullptr."); - if (*pMesh != nullptr) throw DeadlyImportError("Postprocess_BuildMesh. *pMesh must be nullptr."); - - /************************************************************************************************************************************/ - /************************************************************ Geometry2D ************************************************************/ - /************************************************************************************************************************************/ - if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Arc2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_ArcClose2D) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Circle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Disk2D) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polyline2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polypoint2D) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Rectangle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet2D)) { - CX3DImporter_NodeElement_Geometry2D &tnemesh = *((CX3DImporter_NodeElement_Geometry2D *)&pNodeElement); // create alias for convenience - std::vector tarr; - - tarr.reserve(tnemesh.Vertices.size()); - for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) - tarr.push_back(*it); - *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. - - return; // mesh is build, nothing to do anymore. +void X3DImporter::ParseHelper_Node_Exit() { + // check if we can walk up. + if (mNodeElementCur != nullptr) { + mNodeElementCur = mNodeElementCur->Parent; } - /************************************************************************************************************************************/ - /************************************************************ Geometry3D ************************************************************/ - /************************************************************************************************************************************/ - // - // Predefined figures - // - if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Box) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cone) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cylinder) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Sphere)) { - CX3DImporter_NodeElement_Geometry3D &tnemesh = *((CX3DImporter_NodeElement_Geometry3D *)&pNodeElement); // create alias for convenience - std::vector tarr; - - tarr.reserve(tnemesh.Vertices.size()); - for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) - tarr.push_back(*it); - - *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. - - return; // mesh is build, nothing to do anymore. - } - // - // Parametric figures - // - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid) { - CX3DImporter_NodeElement_ElevationGrid &tnemesh = *((CX3DImporter_NodeElement_ElevationGrid *)&pNodeElement); // create alias for convenience - - // at first create mesh from existing vertices. - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIdx, tnemesh.Vertices); - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid) - // - // Indexed primitives sets - // - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet) { - CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, - tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, - tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet) { - CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, - tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet) - - if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleSet) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet)) { - CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, - tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, - tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedTriangleSet or IndexedTriangleFanSet, or \ - IndexedTriangleStripSet: " + - to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet)) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion) { - CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience - - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, tnemesh.Vertices); - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion) - - // - // Primitives sets - // - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet) { - CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - std::vector vec_copy; - - vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.size()); - for (std::list::const_iterator it = ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.begin(); - it != ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.end(); ++it) { - vec_copy.push_back(*it); - } - - *pMesh = StandardShapes::MakeMesh(vec_copy, 1); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, true); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, true); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet) { - CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, true); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, true); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet) { - CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if (nullptr == *pMesh) { - break; - } - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, - tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet) { - CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - std::vector vec_copy; - - vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.size()); - for (std::list::const_iterator it = ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.begin(); - it != ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.end(); ++it) { - vec_copy.push_back(*it); - } - - *pMesh = StandardShapes::MakeMesh(vec_copy, 3); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, - tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet) { - CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, - tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet) - - throw DeadlyImportError("Postprocess_BuildMesh. Unknown mesh type: " + to_string(pNodeElement.Type) + "."); -} - -void X3DImporter::Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, - std::list &pSceneMaterialList, std::list &pSceneLightList) const { - X3DElementList::const_iterator chit_begin = pNodeElement.Children.begin(); - X3DElementList::const_iterator chit_end = pNodeElement.Children.end(); - std::list SceneNode_Child; - std::list SceneNode_Mesh; - - // At first read all metadata - Postprocess_CollectMetadata(pNodeElement, pSceneNode); - // check if we have deal with grouping node. Which can contain transformation or switch - if (pNodeElement.Type == X3DElemType::ENET_Group) { - const CX3DNodeElementGroup &tne_group = *((CX3DNodeElementGroup*)&pNodeElement); // create alias for convenience - - pSceneNode.mTransformation = tne_group.Transformation; - if (tne_group.UseChoice) { - // If Choice is less than zero or greater than the number of nodes in the children field, nothing is chosen. - if ((tne_group.Choice < 0) || ((size_t)tne_group.Choice >= pNodeElement.Children.size())) { - chit_begin = pNodeElement.Children.end(); - chit_end = pNodeElement.Children.end(); - } else { - for (size_t i = 0; i < (size_t)tne_group.Choice; i++) - ++chit_begin; // forward iterator to chosen node. - - chit_end = chit_begin; - ++chit_end; // point end iterator to next element after chosen node. - } - } // if(tne_group.UseChoice) - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Group) - - // Reserve memory for fast access and check children. - for (std::list::const_iterator it = chit_begin; it != chit_end; ++it) { // in this loop we do not read metadata because it's already read at begin. - if ((*it)->Type == X3DElemType::ENET_Group) { - // if child is group then create new node and do recursive call. - aiNode *new_node = new aiNode; - - new_node->mName = (*it)->ID; - new_node->mParent = &pSceneNode; - SceneNode_Child.push_back(new_node); - Postprocess_BuildNode(**it, *new_node, pSceneMeshList, pSceneMaterialList, pSceneLightList); - } else if ((*it)->Type == X3DElemType::ENET_Shape) { - // shape can contain only one geometry and one appearance nodes. - Postprocess_BuildShape(*((CX3DImporter_NodeElement_Shape *)*it), SceneNode_Mesh, pSceneMeshList, pSceneMaterialList); - } else if (((*it)->Type == X3DElemType::ENET_DirectionalLight) || ((*it)->Type == X3DElemType::ENET_PointLight) || - ((*it)->Type == X3DElemType::ENET_SpotLight)) { - Postprocess_BuildLight(*((X3DElemType *)*it), pSceneLightList); - } else if (!PostprocessHelper_ElementIsMetadata((*it)->Type)) // skip metadata - { - throw DeadlyImportError("Postprocess_BuildNode. Unknown type: " + to_string((*it)->Type) + "."); - } - } // for(std::list::const_iterator it = chit_begin; it != chit_end; it++) - - // copy data about children and meshes to aiNode. - if (!SceneNode_Child.empty()) { - std::list::const_iterator it = SceneNode_Child.begin(); - - pSceneNode.mNumChildren = static_cast(SceneNode_Child.size()); - pSceneNode.mChildren = new aiNode *[pSceneNode.mNumChildren]; - for (size_t i = 0; i < pSceneNode.mNumChildren; i++) - pSceneNode.mChildren[i] = *it++; - } - - if (!SceneNode_Mesh.empty()) { - std::list::const_iterator it = SceneNode_Mesh.begin(); - - pSceneNode.mNumMeshes = static_cast(SceneNode_Mesh.size()); - pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes]; - for (size_t i = 0; i < pSceneNode.mNumMeshes; i++) - pSceneNode.mMeshes[i] = *it++; - } - - // that's all. return to previous deals -} - -void X3DImporter::Postprocess_BuildShape(const CX3DImporter_NodeElement_Shape &pShapeNodeElement, std::list &pNodeMeshInd, - std::list &pSceneMeshList, std::list &pSceneMaterialList) const { - aiMaterial *tmat = nullptr; - aiMesh *tmesh = nullptr; - X3DElemType mesh_type = X3DElemType::ENET_Invalid; - unsigned int mat_ind = 0; - - for (X3DElementList::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); ++it) { - if (PostprocessHelper_ElementIsMesh((*it)->Type)) { - Postprocess_BuildMesh(**it, &tmesh); - if (tmesh != nullptr) { - // if mesh successfully built then add data about it to arrays - pNodeMeshInd.push_back(static_cast(pSceneMeshList.size())); - pSceneMeshList.push_back(tmesh); - // keep mesh type. Need above for texture coordinate generation. - mesh_type = (*it)->Type; - } - } else if ((*it)->Type == X3DElemType::ENET_Appearance) { - Postprocess_BuildMaterial(**it, &tmat); - if (tmat != nullptr) { - // if material successfully built then add data about it to array - mat_ind = static_cast(pSceneMaterialList.size()); - pSceneMaterialList.push_back(tmat); - } - } - } // for(std::list::const_iterator it = pShapeNodeElement.Child.begin(); it != pShapeNodeElement.Child.end(); it++) - - // associate read material with read mesh. - if ((tmesh != nullptr) && (tmat != nullptr)) { - tmesh->mMaterialIndex = mat_ind; - // Check texture mapping. If material has texture but mesh has no texture coordinate then try to ask Assimp to generate texture coordinates. - if ((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) { - int32_t tm; - aiVector3D tvec3; - - switch (mesh_type) { - case X3DElemType::ENET_Box: - tm = aiTextureMapping_BOX; - break; - case X3DElemType::ENET_Cone: - case X3DElemType::ENET_Cylinder: - tm = aiTextureMapping_CYLINDER; - break; - case X3DElemType::ENET_Sphere: - tm = aiTextureMapping_SPHERE; - break; - default: - tm = aiTextureMapping_PLANE; - break; - } // switch(mesh_type) - - tmat->AddProperty(&tm, 1, AI_MATKEY_MAPPING_DIFFUSE(0)); - } // if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) - } // if((tmesh != nullptr) && (tmat != nullptr)) -} - -void X3DImporter::Postprocess_CollectMetadata(const CX3DImporter_NodeElement &pNodeElement, aiNode &pSceneNode) const { - X3DElementList meta_list; - size_t meta_idx; - - PostprocessHelper_CollectMetadata(pNodeElement, meta_list); // find metadata in current node element. - if (!meta_list.empty()) { - if (pSceneNode.mMetaData != nullptr) { - throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong."); - } - - // copy collected metadata to output node. - pSceneNode.mMetaData = aiMetadata::Alloc(static_cast(meta_list.size())); - meta_idx = 0; - for (X3DElementList::const_iterator it = meta_list.begin(); it != meta_list.end(); ++it, ++meta_idx) { - CX3DImporter_NodeElement_Meta *cur_meta = (CX3DImporter_NodeElement_Meta *)*it; - - // due to limitations we can add only first element of value list. - // Add an element according to its type. - if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaBoolean) { - if (((CX3DImporter_NodeElement_MetaBoolean *)cur_meta)->Value.size() > 0) - pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaBoolean *)cur_meta)->Value.begin())); - } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaDouble) { - if (((CX3DImporter_NodeElement_MetaDouble *)cur_meta)->Value.size() > 0) - pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, (float)*(((CX3DImporter_NodeElement_MetaDouble *)cur_meta)->Value.begin())); - } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaFloat) { - if (((CX3DImporter_NodeElement_MetaFloat *)cur_meta)->Value.size() > 0) - pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaFloat *)cur_meta)->Value.begin())); - } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaInteger) { - if (((CX3DImporter_NodeElement_MetaInteger *)cur_meta)->Value.size() > 0) - pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaInteger *)cur_meta)->Value.begin())); - } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaString) { - if (((CX3DImporter_NodeElement_MetaString *)cur_meta)->Value.size() > 0) { - aiString tstr(((CX3DImporter_NodeElement_MetaString *)cur_meta)->Value.begin()->data()); - - pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, tstr); - } - } else { - throw DeadlyImportError("Postprocess. Unknown metadata type."); - } // if((*it)->Type == CX3DImporter_NodeElement::ENET_Meta*) else - } // for(std::list::const_iterator it = meta_list.begin(); it != meta_list.end(); it++) - } // if( !meta_list.empty() ) } #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter.hpp b/code/AssetLib/X3D/X3DImporter.hpp index 559e4932f..c9509a035 100644 --- a/code/AssetLib/X3D/X3DImporter.hpp +++ b/code/AssetLib/X3D/X3DImporter.hpp @@ -41,6 +41,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef INCLUDED_AI_X3D_IMPORTER_H #define INCLUDED_AI_X3D_IMPORTER_H +#include "X3DImporter_Node.hpp" + #include #include #include @@ -51,7 +53,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include namespace Assimp { @@ -68,6 +69,21 @@ inline void Throw_ConvertFail_Str2ArrF(const std::string &nodeName, const std::s "\" from string to array of floats."); } +inline void Throw_ConvertFail_Str2ArrD(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue + + "\" from string to array of doubles."); +} + +inline void Throw_ConvertFail_Str2ArrB(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue + + "\" from string to array of booleans."); +} + +inline void Throw_ConvertFail_Str2ArrI(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue + + "\" from string to array of integers."); +} + inline void Throw_DEF_And_USE(const std::string &nodeName) { throw DeadlyImportError("\"DEF\" and \"USE\" can not be defined both in <" + nodeName + ">."); } @@ -225,410 +241,8 @@ inline void LogInfo(const std::string &message) { /// /// That's all for now. Enjoy /// -enum class X3DElemType { - ENET_Group, ///< Element has type "Group". - ENET_MetaBoolean, ///< Element has type "Metadata boolean". - ENET_MetaDouble, ///< Element has type "Metadata double". - ENET_MetaFloat, ///< Element has type "Metadata float". - ENET_MetaInteger, ///< Element has type "Metadata integer". - ENET_MetaSet, ///< Element has type "Metadata set". - ENET_MetaString, ///< Element has type "Metadata string". - ENET_Arc2D, ///< Element has type "Arc2D". - ENET_ArcClose2D, ///< Element has type "ArcClose2D". - ENET_Circle2D, ///< Element has type "Circle2D". - ENET_Disk2D, ///< Element has type "Disk2D". - ENET_Polyline2D, ///< Element has type "Polyline2D". - ENET_Polypoint2D, ///< Element has type "Polypoint2D". - ENET_Rectangle2D, ///< Element has type "Rectangle2D". - ENET_TriangleSet2D, ///< Element has type "TriangleSet2D". - ENET_Box, ///< Element has type "Box". - ENET_Cone, ///< Element has type "Cone". - ENET_Cylinder, ///< Element has type "Cylinder". - ENET_Sphere, ///< Element has type "Sphere". - ENET_ElevationGrid, ///< Element has type "ElevationGrid". - ENET_Extrusion, ///< Element has type "Extrusion". - ENET_Coordinate, ///< Element has type "Coordinate". - ENET_Normal, ///< Element has type "Normal". - ENET_TextureCoordinate, ///< Element has type "TextureCoordinate". - ENET_IndexedFaceSet, ///< Element has type "IndexedFaceSet". - ENET_IndexedLineSet, ///< Element has type "IndexedLineSet". - ENET_IndexedTriangleSet, ///< Element has type "IndexedTriangleSet". - ENET_IndexedTriangleFanSet, ///< Element has type "IndexedTriangleFanSet". - ENET_IndexedTriangleStripSet, ///< Element has type "IndexedTriangleStripSet". - ENET_LineSet, ///< Element has type "LineSet". - ENET_PointSet, ///< Element has type "PointSet". - ENET_TriangleSet, ///< Element has type "TriangleSet". - ENET_TriangleFanSet, ///< Element has type "TriangleFanSet". - ENET_TriangleStripSet, ///< Element has type "TriangleStripSet". - ENET_Color, ///< Element has type "Color". - ENET_ColorRGBA, ///< Element has type "ColorRGBA". - ENET_Shape, ///< Element has type "Shape". - ENET_Appearance, ///< Element has type "Appearance". - ENET_Material, ///< Element has type "Material". - ENET_ImageTexture, ///< Element has type "ImageTexture". - ENET_TextureTransform, ///< Element has type "TextureTransform". - ENET_DirectionalLight, ///< Element has type "DirectionalLight". - ENET_PointLight, ///< Element has type "PointLight". - ENET_SpotLight, ///< Element has type "SpotLight". - ENET_Invalid ///< Element has invalid type and possible contain invalid data. -}; - -struct X3DNodeElementBase { - X3DNodeElementBase *Parent; - std::string ID; - std::list Children; - X3DElemType Type; - -protected: - X3DNodeElementBase(X3DElemType type, X3DNodeElementBase *pParent) : - Type(type), Parent(pParent) { - // empty - } -}; - -/// This struct hold value. -struct CX3DImporter_NodeElement_Color : X3DNodeElementBase { - std::list Value; ///< Stored value. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_Color(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Color, pParent) {} - -}; // struct CX3DImporter_NodeElement_Color - -/// This struct hold value. -struct CX3DImporter_NodeElement_ColorRGBA : X3DNodeElementBase { - std::list Value; ///< Stored value. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_ColorRGBA(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_ColorRGBA, pParent) {} - -}; // struct CX3DImporter_NodeElement_ColorRGBA - -/// This struct hold value. -struct CX3DImporter_NodeElement_Coordinate : public X3DNodeElementBase { - std::list Value; ///< Stored value. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_Coordinate(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Coordinate, pParent) {} - -}; // struct CX3DImporter_NodeElement_Coordinate - -/// This struct hold value. -struct CX3DImporter_NodeElement_Normal : X3DNodeElementBase { - std::list Value; ///< Stored value. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_Normal(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Normal, pParent) {} - -}; // struct CX3DImporter_NodeElement_Normal - -/// This struct hold value. -struct CX3DImporter_NodeElement_TextureCoordinate : X3DNodeElementBase { - std::list Value; ///< Stored value. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_TextureCoordinate(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_TextureCoordinate, pParent) {} - -}; // struct CX3DImporter_NodeElement_TextureCoordinate - -/// Two-dimensional figure. -struct CX3DImporter_NodeElement_Geometry2D : X3DNodeElementBase { - std::list Vertices; ///< Vertices list. - size_t NumIndices; ///< Number of indices in one face. - bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_Geometry2D(X3DElemType pType, X3DNodeElementBase *pParent) : - X3DNodeElementBase(pType, pParent), Solid(true) {} - -}; // class CX3DImporter_NodeElement_Geometry2D - -/// Three-dimensional body. -struct CX3DImporter_NodeElement_Geometry3D : X3DNodeElementBase { - std::list Vertices; ///< Vertices list. - size_t NumIndices; ///< Number of indices in one face. - bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_Geometry3D(X3DElemType pType, X3DNodeElementBase *pParent) : - X3DNodeElementBase(pType, pParent), Vertices(), NumIndices(0), Solid(true) { - // empty - } -}; // class CX3DImporter_NodeElement_Geometry3D - -/// Uniform rectangular grid of varying height. -struct CX3DImporter_NodeElement_ElevationGrid : CX3DImporter_NodeElement_Geometry3D { - bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). - bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). - /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are - /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. - float CreaseAngle; - std::vector CoordIdx; ///< Coordinates list by faces. In X3D format: "-1" - delimiter for faces. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_ElevationGrid(X3DElemType pType, X3DNodeElementBase *pParent) : - CX3DImporter_NodeElement_Geometry3D(pType, pParent) {} -}; // class CX3DImporter_NodeElement_IndexedSet - -/// Shape with indexed vertices. -struct CX3DImporter_NodeElement_IndexedSet : public CX3DImporter_NodeElement_Geometry3D { - /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors - /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to - /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the - /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite - /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the - /// ccw field, results are undefined. - bool CCW; - std::vector ColorIndex; ///< Field to specify the polygonal faces by indexing into the or . - bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). - /// The convex field indicates whether all polygons in the shape are convex (TRUE). A polygon is convex if it is planar, does not intersect itself, - /// and all of the interior angles at its vertices are less than 180 degrees. Non planar and self intersecting polygons may produce undefined results - /// even if the convex field is FALSE. - bool Convex; - std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . - /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are - /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. - float CreaseAngle; - std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . - bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). - std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_IndexedSet(X3DElemType pType, X3DNodeElementBase *pParent) : - CX3DImporter_NodeElement_Geometry3D(pType, pParent) {} -}; // class CX3DImporter_NodeElement_IndexedSet - -/// Shape with set of vertices. -struct CX3DImporter_NodeElement_Set : CX3DImporter_NodeElement_Geometry3D { - /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors - /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to - /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the - /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite - /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the - /// ccw field, results are undefined. - bool CCW; - bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). - bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). - std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . - std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . - std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . - std::vector VertexCount; ///< Field describes how many vertices are to be used in each polyline(polygon) from the field. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_Set(X3DElemType type, X3DNodeElementBase *pParent) : - CX3DImporter_NodeElement_Geometry3D(type, pParent) {} - -}; // class CX3DImporter_NodeElement_Set - -/// This struct hold value. -struct CX3DImporter_NodeElement_Shape : X3DNodeElementBase { - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_Shape(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Shape, pParent) {} -}; // struct CX3DImporter_NodeElement_Shape - -/// This struct hold value. -struct CX3DImporter_NodeElement_Appearance : public X3DNodeElementBase { - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_Appearance(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Appearance, pParent) {} - -}; // struct CX3DImporter_NodeElement_Appearance - -struct CX3DImporter_NodeElement_Material : public X3DNodeElementBase { - float AmbientIntensity; ///< Specifies how much ambient light from light sources this surface shall reflect. - aiColor3D DiffuseColor; ///< Reflects all X3D light sources depending on the angle of the surface with respect to the light source. - aiColor3D EmissiveColor; ///< Models "glowing" objects. This can be useful for displaying pre-lit models. - float Shininess; ///< Lower shininess values produce soft glows, while higher values result in sharper, smaller highlights. - aiColor3D SpecularColor; ///< The specularColor and shininess fields determine the specular highlights. - float Transparency; ///< Specifies how "clear" an object is, with 1.0 being completely transparent, and 0.0 completely opaque. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_Material(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Material, pParent), - AmbientIntensity(0.0f), - DiffuseColor(), - EmissiveColor(), - Shininess(0.0f), - SpecularColor(), - Transparency(1.0f) { - // empty - } -}; // class CX3DImporter_NodeElement_Material - -/// This struct hold value. -struct CX3DImporter_NodeElement_ImageTexture : X3DNodeElementBase { - /// RepeatS and RepeatT, that specify how the texture wraps in the S and T directions. If repeatS is TRUE (the default), the texture map is repeated - /// outside the [0.0, 1.0] texture coordinate range in the S direction so that it fills the shape. If repeatS is FALSE, the texture coordinates are - /// clamped in the S direction to lie within the [0.0, 1.0] range. The repeatT field is analogous to the repeatS field. - bool RepeatS; - bool RepeatT; ///< See \ref RepeatS. - std::string URL; ///< URL of the texture. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_ImageTexture(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_ImageTexture, pParent) {} - -}; // struct CX3DImporter_NodeElement_ImageTexture - -/// This struct hold value. -struct CX3DImporter_NodeElement_TextureTransform : X3DNodeElementBase { - aiVector2D Center; ///< Specifies a translation offset in texture coordinate space about which the rotation and scale fields are applied. - float Rotation; ///< Specifies a rotation in angle base units of the texture coordinates about the center point after the scale has been applied. - aiVector2D Scale; ///< Specifies a scaling factor in S and T of the texture coordinates about the center point. - aiVector2D Translation; ///< Specifies a translation of the texture coordinates. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_TextureTransform(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_TextureTransform, pParent) {} - -}; // struct CX3DImporter_NodeElement_TextureTransform - -struct CX3DNodeElementGroup : X3DNodeElementBase { - aiMatrix4x4 Transformation; ///< Transformation matrix. - - /// As you know node elements can use already defined node elements when attribute "USE" is defined. - /// Standard search when looking for an element in the whole scene graph, existing at this moment. - /// If a node is marked as static, the children(or lower) can not search for elements in the nodes upper then static. - bool Static; - - bool UseChoice; ///< Flag: if true then use number from \ref Choice to choose what the child will be kept. - int32_t Choice; ///< Number of the child which will be kept. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pStatic - static node flag. - CX3DNodeElementGroup(X3DNodeElementBase *pParent, const bool pStatic = false) : - X3DNodeElementBase(X3DElemType::ENET_Group, pParent), Static(pStatic), UseChoice(false) {} -}; - -struct X3DNodeElementMeta : X3DNodeElementBase { - std::string Name; ///< Name of metadata object. - std::string Reference; - - virtual ~X3DNodeElementMeta() { - // empty - } - -protected: - X3DNodeElementMeta(X3DElemType type, X3DNodeElementBase *parent) : - X3DNodeElementBase(type, parent) { - // empty - } -}; - -struct X3DNodeElementMetaBoolean : X3DNodeElementMeta { - std::vector Value; ///< Stored value. - - explicit X3DNodeElementMetaBoolean(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaBoolean, pParent) { - // empty - } -}; - -struct X3DNodeElementMetaDouble : X3DNodeElementMeta { - std::vector Value; ///< Stored value. - - explicit X3DNodeElementMetaDouble(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaDouble, pParent) { - // empty - } -}; - -struct X3DNodeElementMetaFloat : public X3DNodeElementMeta { - std::vector Value; ///< Stored value. - - explicit X3DNodeElementMetaFloat(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaFloat, pParent) { - // empty - } -}; - -struct X3DNodeElementMetaInt : public X3DNodeElementMeta { - std::vector Value; ///< Stored value. - - explicit X3DNodeElementMetaInt(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaInteger, pParent) { - // empty - } -}; - -struct X3DNodeElementMetaSet : public X3DNodeElementMeta { - std::list Value; ///< Stored value. - - explicit X3DNodeElementMetaSet(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaSet, pParent) { - // empty - } -}; - -struct X3DNodeElementMetaString : X3DNodeElementMeta { - std::list Value; ///< Stored value. - - explicit X3DNodeElementMetaString(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaString, pParent) { - // empty - } -}; - -/// \struct CX3DImporter_NodeElement_Light -/// This struct hold value. -struct X3DNodeNodeElementLight : X3DNodeElementBase { - float AmbientIntensity; ///< Specifies the intensity of the ambient emission from the light. - aiColor3D Color; ///< specifies the spectral colour properties of both the direct and ambient light emission as an RGB value. - aiVector3D Direction; ///< Specifies the direction vector of the illumination emanating from the light source in the local coordinate system. - /// \var Global - /// Field that determines whether the light is global or scoped. Global lights illuminate all objects that fall within their volume of lighting influence. - /// Scoped lights only illuminate objects that are in the same transformation hierarchy as the light. - bool Global; - float Intensity; ///< Specifies the brightness of the direct emission from the light. - /// \var Attenuation - /// PointLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor - /// is: "1 / max(attenuation[0] + attenuation[1] * r + attenuation[2] * r2, 1)", where r is the distance from the light to the surface being illuminated. - aiVector3D Attenuation; - aiVector3D Location; ///< Specifies a translation offset of the centre point of the light source from the light's local coordinate system origin. - float Radius; ///< Specifies the radial extent of the solid angle and the maximum distance from location that may be illuminated by the light source. - float BeamWidth; ///< Specifies an inner solid angle in which the light source emits light at uniform full intensity. - float CutOffAngle; ///< The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle). - - /// Constructor - /// \param [in] pParent - pointer to parent node. - /// \param [in] pLightType - type of the light source. - X3DNodeNodeElementLight(X3DElemType pLightType, X3DNodeElementBase *pParent) : - X3DNodeElementBase(pLightType, pParent) {} - -}; // struct CX3DImporter_NodeElement_Light - -using X3DElementList = std::list; +using X3DElementList = std::list; class X3DImporter : public BaseImporter { public: @@ -654,18 +268,113 @@ public: void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); const aiImporterDesc *GetInfo() const; void Clear(); - void readMetadata(XmlNode &node); - void readScene(XmlNode &node); - void readViewpoint(XmlNode &node); - void readMetadataObject(XmlNode &node); - void ParseDirectionalLight(XmlNode &node); - void Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, - std::list &pSceneMaterialList, std::list &pSceneLightList) const; private: + bool isNodeEmpty(XmlNode &node); + void checkNodeMustBeEmpty(XmlNode &node); + void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node); + void readHead(XmlNode &node); + void readChildNodes(XmlNode &node, const std::string &pParentNodeName); + void readScene(XmlNode &node); + + bool FindNodeElement_FromRoot(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement); + bool FindNodeElement_FromNode(X3DNodeElementBase *pStartNode, const std::string &pID, + const X3DElemType pType, X3DNodeElementBase **pElement); + bool FindNodeElement(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement); + void ParseHelper_Group_Begin(const bool pStatic = false); + void ParseHelper_Node_Enter(X3DNodeElementBase *pNode); + void ParseHelper_Node_Exit(); + + // 2D geometry + void readArc2D(XmlNode &node); + void readArcClose2D(XmlNode &node); + void readCircle2D(XmlNode &node); + void readDisk2D(XmlNode &node); + void readPolyline2D(XmlNode &node); + void readPolypoint2D(XmlNode &node); + void readRectangle2D(XmlNode &node); + void readTriangleSet2D(XmlNode &node); + + // 3D geometry + void readBox(XmlNode &node); + void readCone(XmlNode &node); + void readCylinder(XmlNode &node); + void readElevationGrid(XmlNode &node); + void readExtrusion(XmlNode &node); + void readIndexedFaceSet(XmlNode &node); + void readSphere(XmlNode &node); + + // group + void startReadGroup(XmlNode &node); + void endReadGroup(); + void startReadStaticGroup(XmlNode &node); + void endReadStaticGroup(); + void startReadSwitch(XmlNode &node); + void endReadSwitch(); + void startReadTransform(XmlNode &node); + void endReadTransform(); + + // light + void readDirectionalLight(XmlNode &node); + void readPointLight(XmlNode &node); + void readSpotLight(XmlNode &node); + + // metadata + bool checkForMetadataNode(XmlNode &node); + void childrenReadMetadata(XmlNode &node, X3DNodeElementBase *pParentElement, const std::string &pNodeName); + void readMetadataBoolean(XmlNode &node); + void readMetadataDouble(XmlNode &node); + void readMetadataFloat(XmlNode &node); + void readMetadataInteger(XmlNode &node); + void readMetadataSet(XmlNode &node); + void readMetadataString(XmlNode &node); + + // networking + void readInline(XmlNode &node); + + // postprocessing + aiMatrix4x4 PostprocessHelper_Matrix_GlobalToCurrent() const; + void PostprocessHelper_CollectMetadata(const X3DNodeElementBase &pNodeElement, std::list &pList) const; + bool PostprocessHelper_ElementIsMetadata(const X3DElemType pType) const; + bool PostprocessHelper_ElementIsMesh(const X3DElemType pType) const; + void Postprocess_BuildLight(const X3DNodeElementBase &pNodeElement, std::list &pSceneLightList) const; + void Postprocess_BuildMaterial(const X3DNodeElementBase &pNodeElement, aiMaterial **pMaterial) const; + void Postprocess_BuildMesh(const X3DNodeElementBase &pNodeElement, aiMesh **pMesh) const; + void Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, + std::list &pSceneMaterialList, std::list &pSceneLightList) const; + void Postprocess_BuildShape(const X3DNodeElementShape &pShapeNodeElement, std::list &pNodeMeshInd, + std::list &pSceneMeshList, std::list &pSceneMaterialList) const; + void Postprocess_CollectMetadata(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode) const; + + // rendering + void readColor(XmlNode &node); + void readColorRGBA(XmlNode &node); + void readCoordinate(XmlNode &node); + void readIndexedLineSet(XmlNode &node); + void readIndexedTriangleFanSet(XmlNode &node); + void readIndexedTriangleSet(XmlNode &node); + void readIndexedTriangleStripSet(XmlNode &node); + void readLineSet(XmlNode &node); + void readPointSet(XmlNode &node); + void readTriangleFanSet(XmlNode &node); + void readTriangleSet(XmlNode &node); + void readTriangleStripSet(XmlNode &node); + void readNormal(XmlNode &node); + + // shape + void readShape(XmlNode &node); + void readAppearance(XmlNode &node); + void readMaterial(XmlNode &node); + + // texturing + void readImageTexture(XmlNode &node); + void readTextureCoordinate(XmlNode &node); + void readTextureTransform(XmlNode &node); + static const aiImporterDesc Description; X3DNodeElementBase *mNodeElementCur; aiScene *mScene; + IOSystem *mpIOHandler; }; // class X3DImporter } // namespace Assimp diff --git a/code/AssetLib/X3D/X3DImporter_Geometry2D.cpp b/code/AssetLib/X3D/X3DImporter_Geometry2D.cpp new file mode 100644 index 000000000..171075556 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Geometry2D.cpp @@ -0,0 +1,467 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Geometry2D.cpp +/// \brief Parsing data from nodes of "Geometry2D" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" +#include "X3DGeoHelper.h" + +namespace Assimp { + +// +// The Arc2D node specifies a linear circular arc whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping +// towards the positive y-axis. The radius field specifies the radius of the circle of which the arc is a portion. The arc extends from the startAngle +// counterclockwise to the endAngle. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different +// angle base unit has been specified). If startAngle and endAngle have the same value, a circle is specified. +void X3DImporter::readArc2D(XmlNode &node) { + std::string def, use; + float endAngle = AI_MATH_HALF_PI_F; + float radius = 1; + float startAngle = 0; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "endAngle", endAngle); + XmlParser::getFloatAttribute(node, "radius", radius); + XmlParser::getFloatAttribute(node, "startAngle", startAngle); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Arc2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Arc2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object and convert it to line set. + std::list tlist; + + X3DGeoHelper::make_arc2D(startAngle, endAngle, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg + X3DGeoHelper::extend_point_to_line(tlist, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Arc2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// The ArcClose node specifies a portion of a circle whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping +// towards the positive y-axis. The end points of the arc specified are connected as defined by the closureType field. The radius field specifies the radius +// of the circle of which the arc is a portion. The arc extends from the startAngle counterclockwise to the endAngle. The value of radius shall be greater +// than zero. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different default angle base unit has +// been specified). If startAngle and endAngle have the same value, a circle is specified and closureType is ignored. If the absolute difference between +// startAngle and endAngle is greater than or equal to 2pi, a complete circle is produced with no chord or radial line(s) drawn from the center. +// A closureType of "PIE" connects the end point to the start point by defining two straight line segments first from the end point to the center and then +// the center to the start point. A closureType of "CHORD" connects the end point to the start point by defining a straight line segment from the end point +// to the start point. Textures are applied individually to each face of the ArcClose2D. On the front (+Z) and back (-Z) faces of the ArcClose2D, when +// viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. +void X3DImporter::readArcClose2D(XmlNode &node) { + std::string def, use; + std::string closureType("PIE"); + float endAngle = AI_MATH_HALF_PI_F; + float radius = 1; + bool solid = false; + float startAngle = 0; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "closureType", closureType); + XmlParser::getFloatAttribute(node, "endAngle", endAngle); + XmlParser::getFloatAttribute(node, "endAngle", endAngle); + XmlParser::getFloatAttribute(node, "radius", radius); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getFloatAttribute(node, "startAngle", startAngle); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ArcClose2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_ArcClose2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + // create point list of geometry object. + X3DGeoHelper::make_arc2D(startAngle, endAngle, radius, 10, ((X3DNodeElementGeometry2D *)ne)->Vertices); ///TODO: IME - AI_CONFIG for NumSeg + // add chord or two radiuses only if not a circle was defined + if (!((std::fabs(endAngle - startAngle) >= AI_MATH_TWO_PI_F) || (endAngle == startAngle))) { + std::list &vlist = ((X3DNodeElementGeometry2D *)ne)->Vertices; // just short alias. + + if ((closureType == "PIE") || (closureType == "\"PIE\"")) + vlist.push_back(aiVector3D(0, 0, 0)); // center point - first radial line + else if ((closureType != "CHORD") && (closureType != "\"CHORD\"")) + Throw_IncorrectAttrValue("ArcClose2D", "closureType"); + + vlist.push_back(*vlist.begin()); // arc first point - chord from first to last point of arc(if CHORD) or second radial line(if PIE). + } + + ((X3DNodeElementGeometry2D *)ne)->NumIndices = ((X3DNodeElementGeometry2D *)ne)->Vertices.size(); + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "ArcClose2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readCircle2D(XmlNode &node) { + std::string def, use; + float radius = 1; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "radius", radius); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Circle2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Circle2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object and convert it to line set. + std::list tlist; + + X3DGeoHelper::make_arc2D(0, 0, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg + X3DGeoHelper::extend_point_to_line(tlist, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Circle2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// The Disk2D node specifies a circular disk which is centred at (0, 0) in the local coordinate system. The outerRadius field specifies the radius of the +// outer dimension of the Disk2D. The innerRadius field specifies the inner dimension of the Disk2D. The value of outerRadius shall be greater than zero. +// The value of innerRadius shall be greater than or equal to zero and less than or equal to outerRadius. If innerRadius is zero, the Disk2D is completely +// filled. Otherwise, the area within the innerRadius forms a hole in the Disk2D. If innerRadius is equal to outerRadius, a solid circular line shall +// be drawn using the current line properties. Textures are applied individually to each face of the Disk2D. On the front (+Z) and back (-Z) faces of +// the Disk2D, when viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. +void X3DImporter::readDisk2D(XmlNode &node) { + std::string def, use; + float innerRadius = 0; + float outerRadius = 1; + bool solid = false; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "innerRadius", innerRadius); + XmlParser::getFloatAttribute(node, "outerRadius", outerRadius); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Disk2D, ne); + } else { + std::list tlist_o, tlist_i; + + if (innerRadius > outerRadius) Throw_IncorrectAttrValue("Disk2D", "innerRadius"); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Disk2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object. + ///TODO: IME - AI_CONFIG for NumSeg + X3DGeoHelper::make_arc2D(0, 0, outerRadius, 10, tlist_o); // outer circle + if (innerRadius == 0.0f) { // make filled disk + // in tlist_o we already have points of circle. just copy it and sign as polygon. + ((X3DNodeElementGeometry2D *)ne)->Vertices = tlist_o; + ((X3DNodeElementGeometry2D *)ne)->NumIndices = tlist_o.size(); + } else if (innerRadius == outerRadius) { // make circle + // in tlist_o we already have points of circle. convert it to line set. + X3DGeoHelper::extend_point_to_line(tlist_o, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + } else { // make disk + std::list &vlist = ((X3DNodeElementGeometry2D *)ne)->Vertices; // just short alias. + + X3DGeoHelper::make_arc2D(0, 0, innerRadius, 10, tlist_i); // inner circle + // + // create quad list from two point lists + // + if (tlist_i.size() < 2) throw DeadlyImportError("Disk2D. Not enough points for creating quad list."); // tlist_i and tlist_o has equal size. + + // add all quads except last + for (std::list::iterator it_i = tlist_i.begin(), it_o = tlist_o.begin(); it_i != tlist_i.end();) { + // do not forget - CCW direction + vlist.push_back(*it_i++); // 1st point + vlist.push_back(*it_o++); // 2nd point + vlist.push_back(*it_o); // 3rd point + vlist.push_back(*it_i); // 4th point + } + + // add last quad + vlist.push_back(*tlist_i.end()); // 1st point + vlist.push_back(*tlist_o.end()); // 2nd point + vlist.push_back(*tlist_o.begin()); // 3rd point + vlist.push_back(*tlist_o.begin()); // 4th point + + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 4; + } + + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Disk2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readPolyline2D(XmlNode &node) { + std::string def, use; + std::list lineSegments; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "lineSegments", lineSegments); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Polyline2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Polyline2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // + // convert read point list of geometry object to line set. + // + std::list tlist; + + // convert vec2 to vec3 + for (std::list::iterator it2 = lineSegments.begin(); it2 != lineSegments.end(); ++it2) + tlist.push_back(aiVector3D(it2->x, it2->y, 0)); + + // convert point set to line set + X3DGeoHelper::extend_point_to_line(tlist, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Polyline2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readPolypoint2D(XmlNode &node) { + std::string def, use; + std::list point; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "point", point); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Polypoint2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Polypoint2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // convert vec2 to vec3 + for (std::list::iterator it2 = point.begin(); it2 != point.end(); ++it2) { + ((X3DNodeElementGeometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); + } + + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 1; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Polypoint2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readRectangle2D(XmlNode &node) { + std::string def, use; + aiVector2D size(2, 2); + bool solid = false; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DAttribute(node, "size", size); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Rectangle2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Rectangle2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + float x1 = -size.x / 2.0f; + float x2 = size.x / 2.0f; + float y1 = -size.y / 2.0f; + float y2 = size.y / 2.0f; + std::list &vlist = ((X3DNodeElementGeometry2D *)ne)->Vertices; // just short alias. + + vlist.push_back(aiVector3D(x2, y1, 0)); // 1st point + vlist.push_back(aiVector3D(x2, y2, 0)); // 2nd point + vlist.push_back(aiVector3D(x1, y2, 0)); // 3rd point + vlist.push_back(aiVector3D(x1, y1, 0)); // 4th point + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 4; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Rectangle2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readTriangleSet2D(XmlNode &node) { + std::string def, use; + bool solid = false; + std::list vertices; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "vertices", vertices); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleSet2D, ne); + } else { + if (vertices.size() % 3) throw DeadlyImportError("TriangleSet2D. Not enough points for defining triangle."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_TriangleSet2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // convert vec2 to vec3 + for (std::list::iterator it2 = vertices.begin(); it2 != vertices.end(); ++it2) { + ((X3DNodeElementGeometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); + } + + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "TriangleSet2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Geometry3D.cpp b/code/AssetLib/X3D/X3DImporter_Geometry3D.cpp new file mode 100644 index 000000000..4250bfb00 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Geometry3D.cpp @@ -0,0 +1,981 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Geometry3D.cpp +/// \brief Parsing data from nodes of "Geometry3D" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" +#include "X3DGeoHelper.h" + +// Header files, Assimp. +#include + +namespace Assimp +{ + +// +// The Box node specifies a rectangular parallelepiped box centred at (0, 0, 0) in the local coordinate system and aligned with the local coordinate axes. +// By default, the box measures 2 units in each dimension, from -1 to +1. The size field specifies the extents of the box along the X-, Y-, and Z-axes +// respectively and each component value shall be greater than zero. +void X3DImporter::readBox(XmlNode &node) { + std::string def, use; + bool solid = true; + aiVector3D size(2, 2, 2); + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DAttribute(node, "size", size); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Box, ne); + } + else + { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Box, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + X3DGeoHelper::rect_parallel_epiped(size, ((X3DNodeElementGeometry3D *)ne)->Vertices); // get quad list + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 4; + // check for X3DMetadataObject childs. + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Box"); + else + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +void X3DImporter::readCone(XmlNode &node) { + std::string use, def; + bool bottom = true; + float bottomRadius = 1; + float height = 2; + bool side = true; + bool solid = true; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getBoolAttribute(node, "side", side); + XmlParser::getBoolAttribute(node, "bottom", bottom); + XmlParser::getFloatAttribute(node, "height", height); + XmlParser::getFloatAttribute(node, "bottomRadius", bottomRadius); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Cone, ne); + } + else + { + const unsigned int tess = 30;///TODO: IME tessellation factor through ai_property + + std::vector tvec;// temp array for vertices. + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Cone, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + // make cone or parts according to flags. + if(side) + { + StandardShapes::MakeCone(height, 0, bottomRadius, tess, tvec, !bottom); + } + else if(bottom) + { + StandardShapes::MakeCircle(bottomRadius, tess, tvec); + height = -(height / 2); + for(std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) it->y = height;// y - because circle made in oXZ. + } + + // copy data from temp array + for (std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) + ((X3DNodeElementGeometry3D*)ne)->Vertices.push_back(*it); + + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Cone"); + else + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +void X3DImporter::readCylinder(XmlNode &node) { + std::string use, def; + bool bottom = true; + float height = 2; + float radius = 1; + bool side = true; + bool solid = true; + bool top = true; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "radius", radius); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getBoolAttribute(node, "bottom", bottom); + XmlParser::getBoolAttribute(node, "top", top); + XmlParser::getBoolAttribute(node, "side", side); + XmlParser::getFloatAttribute(node, "height", height); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Cylinder, ne); + } + else + { + const unsigned int tess = 30;///TODO: IME tessellation factor through ai_property + + std::vector tside;// temp array for vertices of side. + std::vector tcir;// temp array for vertices of circle. + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Cylinder, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + // make cilynder or parts according to flags. + if(side) StandardShapes::MakeCone(height, radius, radius, tess, tside, true); + + height /= 2;// height defined for whole cylinder, when creating top and bottom circle we are using just half of height. + if(top || bottom) StandardShapes::MakeCircle(radius, tess, tcir); + // copy data from temp arrays + std::list &vlist = ((X3DNodeElementGeometry3D *)ne)->Vertices; // just short alias. + + for(std::vector::iterator it = tside.begin(); it != tside.end(); ++it) vlist.push_back(*it); + + if(top) + { + for(std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) + { + (*it).y = height;// y - because circle made in oXZ. + vlist.push_back(*it); + } + }// if(top) + + if(bottom) + { + for(std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) + { + (*it).y = -height;// y - because circle made in oXZ. + vlist.push_back(*it); + } + }// if(top) + + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Cylinder"); + else + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +// +// ColorNormalTexCoordContentModel can contain Color (or ColorRGBA), Normal and TextureCoordinate, in any order. No more than one instance of any single +// node type is allowed. A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The ElevationGrid node specifies a uniform rectangular grid of varying height in the Y=0 plane of the local coordinate system. The geometry is described +// by a scalar array of height values that specify the height of a surface above each point of the grid. The xDimension and zDimension fields indicate +// the number of elements of the grid height array in the X and Z directions. Both xDimension and zDimension shall be greater than or equal to zero. +// If either the xDimension or the zDimension is less than two, the ElevationGrid contains no quadrilaterals. +void X3DImporter::readElevationGrid(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + float creaseAngle = 0; + std::vector height; + bool normalPerVertex = true; + bool solid = true; + int32_t xDimension = 0; + float xSpacing = 1; + int32_t zDimension = 0; + float zSpacing = 1; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getFloatAttribute(node, "creaseAngle", creaseAngle); + X3DXmlHelper::getFloatArrayAttribute(node, "height", height); + XmlParser::getIntAttribute(node, "xDimension", xDimension); + XmlParser::getFloatAttribute(node, "xSpacing", xSpacing); + XmlParser::getIntAttribute(node, "zDimension", zDimension); + XmlParser::getFloatAttribute(node, "zSpacing", zSpacing); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ElevationGrid, ne); + } + else + { + if((xSpacing == 0.0f) || (zSpacing == 0.0f)) throw DeadlyImportError("Spacing in must be grater than zero."); + if((xDimension <= 0) || (zDimension <= 0)) throw DeadlyImportError("Dimension in must be grater than zero."); + if ((size_t)(xDimension * zDimension) != height.size()) DeadlyImportError("Heights count must be equal to \"xDimension * zDimension\" in "); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementElevationGrid(X3DElemType::ENET_ElevationGrid, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + X3DNodeElementElevationGrid &grid_alias = *((X3DNodeElementElevationGrid *)ne); // create alias for conveience + + {// create grid vertices list + std::vector::const_iterator he_it = height.begin(); + + for(int32_t zi = 0; zi < zDimension; zi++)// rows + { + for(int32_t xi = 0; xi < xDimension; xi++)// columns + { + aiVector3D tvec(xSpacing * xi, *he_it, zSpacing * zi); + + grid_alias.Vertices.push_back(tvec); + ++he_it; + } + } + }// END: create grid vertices list + // + // create faces list. In "coordIdx" format + // + // check if we have quads + if((xDimension < 2) || (zDimension < 2))// only one element in dimension is set, create line set. + { + ((X3DNodeElementElevationGrid *)ne)->NumIndices = 2; // will be holded as line set. + for(size_t i = 0, i_e = (grid_alias.Vertices.size() - 1); i < i_e; i++) + { + grid_alias.CoordIdx.push_back(static_cast(i)); + grid_alias.CoordIdx.push_back(static_cast(i + 1)); + grid_alias.CoordIdx.push_back(-1); + } + } + else// two or more elements in every dimension is set. create quad set. + { + ((X3DNodeElementElevationGrid *)ne)->NumIndices = 4; + for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++)// rows + { + for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++)// columns + { + // points direction in face. + if(ccw) + { + // CCW: + // 3 2 + // 0 1 + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); + } + else + { + // CW: + // 0 1 + // 3 2 + grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); + grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); + }// if(ccw) else + + grid_alias.CoordIdx.push_back(-1); + }// for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) + }// for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) + }// if((xDimension < 2) || (zDimension < 2)) else + + grid_alias.ColorPerVertex = colorPerVertex; + grid_alias.NormalPerVertex = normalPerVertex; + grid_alias.CreaseAngle = creaseAngle; + grid_alias.Solid = solid; + // check for child nodes + if(!isNodeEmpty(node)) + { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") readColorRGBA(currentChildNode); + else if (currentChildName == "Normal") readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("ElevationGrid", currentChildNode); + } + ParseHelper_Node_Exit(); + }// if(!mReader->isEmptyElement()) + else + { + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + }// if(!mReader->isEmptyElement()) else + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +template +static void GeometryHelper_Extrusion_CurveIsClosed(std::vector& pCurve, const bool pDropTail, const bool pRemoveLastPoint, bool& pCurveIsClosed) +{ + size_t cur_sz = pCurve.size(); + + pCurveIsClosed = false; + // for curve with less than four points checking is have no sense, + if(cur_sz < 4) return; + + for(size_t s = 3, s_e = cur_sz; s < s_e; s++) + { + // search for first point of duplicated part. + if(pCurve[0] == pCurve[s]) + { + bool found = true; + + // check if tail(indexed by b2) is duplicate of head(indexed by b1). + for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) + { + if(pCurve[b1] != pCurve[b2]) + {// points not match: clear flag and break loop. + found = false; + + break; + } + }// for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) + + // if duplicate tail is found then drop or not it depending on flags. + if(found) + { + pCurveIsClosed = true; + if(pDropTail) + { + if(!pRemoveLastPoint) s++;// prepare value for iterator's arithmetics. + + pCurve.erase(pCurve.begin() + s, pCurve.end());// remove tail + } + + break; + }// if(found) + }// if(pCurve[0] == pCurve[s]) + }// for(size_t s = 3, s_e = (cur_sz - 1); s < s_e; s++) +} + +static aiVector3D GeometryHelper_Extrusion_GetNextY(const size_t pSpine_PointIdx, const std::vector& pSpine, const bool pSpine_Closed) +{ + const size_t spine_idx_last = pSpine.size() - 1; + aiVector3D tvec; + + if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last))// at first special cases + { + if(pSpine_Closed) + {// If the spine curve is closed: The SCP for the first and last points is the same and is found using (spine[1] - spine[n - 2]) to compute the Y-axis. + // As we even for closed spine curve last and first point in pSpine are not the same: duplicates(spine[n - 1] which are equivalent to spine[0]) + // in tail are removed. + // So, last point in pSpine is a spine[n - 2] + tvec = pSpine[1] - pSpine[spine_idx_last]; + } + else if(pSpine_PointIdx == 0) + {// The Y-axis used for the first point is the vector from spine[0] to spine[1] + tvec = pSpine[1] - pSpine[0]; + } + else + {// The Y-axis used for the last point it is the vector from spine[n-2] to spine[n-1]. In our case(see above about dropping tail) spine[n - 1] is + // the spine[0]. + tvec = pSpine[spine_idx_last] - pSpine[spine_idx_last - 1]; + } + }// if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) + else + {// For all points other than the first or last: The Y-axis for spine[i] is found by normalizing the vector defined by (spine[i+1] - spine[i-1]). + tvec = pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx - 1]; + }// if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) else + + return tvec.Normalize(); +} + +static aiVector3D GeometryHelper_Extrusion_GetNextZ(const size_t pSpine_PointIdx, const std::vector& pSpine, const bool pSpine_Closed, + const aiVector3D pVecZ_Prev) +{ + const aiVector3D zero_vec(0); + const size_t spine_idx_last = pSpine.size() - 1; + + aiVector3D tvec; + + // at first special cases + if(pSpine.size() < 3)// spine have not enough points for vector calculations. + { + tvec.Set(0, 0, 1); + } + else if(pSpine_PointIdx == 0)// special case: first point + { + if(pSpine_Closed)// for calculating use previous point in curve s[n - 2]. In list it's a last point, because point s[n - 1] was removed as duplicate. + { + tvec = (pSpine[1] - pSpine[0]) ^ (pSpine[spine_idx_last] - pSpine[0]); + } + else // for not closed curve first and next point(s[0] and s[1]) has the same vector Z. + { + bool found = false; + + // As said: "If the Z-axis of the first point is undefined (because the spine is not closed and the first two spine segments are collinear) + // then the Z-axis for the first spine point with a defined Z-axis is used." + // Walk through spine and find Z. + for(size_t next_point = 2; (next_point <= spine_idx_last) && !found; next_point++) + { + // (pSpine[2] - pSpine[1]) ^ (pSpine[0] - pSpine[1]) + tvec = (pSpine[next_point] - pSpine[next_point - 1]) ^ (pSpine[next_point - 2] - pSpine[next_point - 1]); + found = !tvec.Equal(zero_vec); + } + + // if entire spine are collinear then use OZ axis. + if(!found) tvec.Set(0, 0, 1); + }// if(pSpine_Closed) else + }// else if(pSpine_PointIdx == 0) + else if(pSpine_PointIdx == spine_idx_last)// special case: last point + { + if(pSpine_Closed) + {// do not forget that real last point s[n - 1] is removed as duplicated. And in this case we are calculating vector Z for point s[n - 2]. + tvec = (pSpine[0] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); + // if taken spine vectors are collinear then use previous vector Z. + if(tvec.Equal(zero_vec)) tvec = pVecZ_Prev; + } + else + {// vector Z for last point of not closed curve is previous vector Z. + tvec = pVecZ_Prev; + } + } + else// regular point + { + tvec = (pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); + // if taken spine vectors are collinear then use previous vector Z. + if(tvec.Equal(zero_vec)) tvec = pVecZ_Prev; + } + + // After determining the Z-axis, its dot product with the Z-axis of the previous spine point is computed. If this value is negative, the Z-axis + // is flipped (multiplied by -1). + if((tvec * pVecZ_Prev) < 0) tvec = -tvec; + + return tvec.Normalize(); +} + +// +void X3DImporter::readExtrusion(XmlNode &node) { + std::string use, def; + bool beginCap = true; + bool ccw = true; + bool convex = true; + float creaseAngle = 0; + std::vector crossSection; + bool endCap = true; + std::vector orientation; + std::vector scale; + bool solid = true; + std::vector spine; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "beginCap", beginCap); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "convex", convex); + XmlParser::getFloatAttribute(node, "creaseAngle", creaseAngle); + X3DXmlHelper::getVector2DArrayAttribute(node, "crossSection", crossSection); + XmlParser::getBoolAttribute(node, "endCap", endCap); + X3DXmlHelper::getFloatArrayAttribute(node, "orientation", orientation); + X3DXmlHelper::getVector2DArrayAttribute(node, "scale", scale); + XmlParser::getBoolAttribute(node, "solid", solid); + X3DXmlHelper::getVector3DArrayAttribute(node, "spine", spine); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Extrusion, ne); + } + else + { + // + // check if default values must be assigned + // + if(spine.size() == 0) + { + spine.resize(2); + spine[0].Set(0, 0, 0), spine[1].Set(0, 1, 0); + } + else if(spine.size() == 1) + { + throw DeadlyImportError("ParseNode_Geometry3D_Extrusion. Spine must have at least two points."); + } + + if(crossSection.size() == 0) + { + crossSection.resize(5); + crossSection[0].Set(1, 1), crossSection[1].Set(1, -1), crossSection[2].Set(-1, -1), crossSection[3].Set(-1, 1), crossSection[4].Set(1, 1); + } + + {// orientation + size_t ori_size = orientation.size() / 4; + + if(ori_size < spine.size()) + { + float add_ori[4];// values that will be added + + if(ori_size == 1)// if "orientation" has one element(means one MFRotation with four components) then use it value for all spine points. + { + add_ori[0] = orientation[0], add_ori[1] = orientation[1], add_ori[2] = orientation[2], add_ori[3] = orientation[3]; + } + else// else - use default values + { + add_ori[0] = 0, add_ori[1] = 0, add_ori[2] = 1, add_ori[3] = 0; + } + + orientation.reserve(spine.size() * 4); + for(size_t i = 0, i_e = (spine.size() - ori_size); i < i_e; i++) + orientation.push_back(add_ori[0]), orientation.push_back(add_ori[1]), orientation.push_back(add_ori[2]), orientation.push_back(add_ori[3]); + } + + if(orientation.size() % 4) throw DeadlyImportError("Attribute \"orientation\" in must has multiple four quantity of numbers."); + }// END: orientation + + {// scale + if(scale.size() < spine.size()) + { + aiVector2D add_sc; + + if(scale.size() == 1)// if "scale" has one element then use it value for all spine points. + add_sc = scale[0]; + else// else - use default values + add_sc.Set(1, 1); + + scale.reserve(spine.size()); + for(size_t i = 0, i_e = (spine.size() - scale.size()); i < i_e; i++) scale.push_back(add_sc); + } + }// END: scale + // + // create and if needed - define new geometry object. + // + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_Extrusion, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ext_alias = *((X3DNodeElementIndexedSet *)ne); // create alias for conveience + // assign part of input data + ext_alias.CCW = ccw; + ext_alias.Convex = convex; + ext_alias.CreaseAngle = creaseAngle; + ext_alias.Solid = solid; + + // + // How we done it at all? + // 1. At first we will calculate array of basises for every point in spine(look SCP in ISO-dic). Also "orientation" vector + // are applied vor every basis. + // 2. After that we can create array of point sets: which are scaled, transferred to basis of relative basis and at final translated to real position + // using relative spine point. + // 3. Next step is creating CoordIdx array(do not forget "-1" delimiter). While creating CoordIdx also created faces for begin and end caps, if + // needed. While createing CootdIdx is taking in account CCW flag. + // 4. The last step: create Vertices list. + // + bool spine_closed;// flag: true if spine curve is closed. + bool cross_closed;// flag: true if cross curve is closed. + std::vector basis_arr;// array of basises. ROW_a - X, ROW_b - Y, ROW_c - Z. + std::vector > pointset_arr;// array of point sets: cross curves. + + // detect closed curves + GeometryHelper_Extrusion_CurveIsClosed(crossSection, true, true, cross_closed);// true - drop tail, true - remove duplicate end. + GeometryHelper_Extrusion_CurveIsClosed(spine, true, true, spine_closed);// true - drop tail, true - remove duplicate end. + // If both cap are requested and spine curve is closed then we can make only one cap. Because second cap will be the same surface. + if(spine_closed) + { + beginCap |= endCap; + endCap = false; + } + + {// 1. Calculate array of basises. + aiMatrix4x4 rotmat; + aiVector3D vecX(0), vecY(0), vecZ(0); + + basis_arr.resize(spine.size()); + for(size_t i = 0, i_e = spine.size(); i < i_e; i++) + { + aiVector3D tvec; + + // get axises of basis. + vecY = GeometryHelper_Extrusion_GetNextY(i, spine, spine_closed); + vecZ = GeometryHelper_Extrusion_GetNextZ(i, spine, spine_closed, vecZ); + vecX = (vecY ^ vecZ).Normalize(); + // get rotation matrix and apply "orientation" to basis + aiMatrix4x4::Rotation(orientation[i * 4 + 3], aiVector3D(orientation[i * 4], orientation[i * 4 + 1], orientation[i * 4 + 2]), rotmat); + tvec = vecX, tvec *= rotmat, basis_arr[i].a1 = tvec.x, basis_arr[i].a2 = tvec.y, basis_arr[i].a3 = tvec.z; + tvec = vecY, tvec *= rotmat, basis_arr[i].b1 = tvec.x, basis_arr[i].b2 = tvec.y, basis_arr[i].b3 = tvec.z; + tvec = vecZ, tvec *= rotmat, basis_arr[i].c1 = tvec.x, basis_arr[i].c2 = tvec.y, basis_arr[i].c3 = tvec.z; + }// for(size_t i = 0, i_e = spine.size(); i < i_e; i++) + }// END: 1. Calculate array of basises + + {// 2. Create array of point sets. + aiMatrix4x4 scmat; + std::vector tcross(crossSection.size()); + + pointset_arr.resize(spine.size()); + for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) + { + aiVector3D tc23vec; + + tc23vec.Set(scale[spi].x, 0, scale[spi].y); + aiMatrix4x4::Scaling(tc23vec, scmat); + for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) + { + aiVector3D tvecX, tvecY, tvecZ; + + tc23vec.Set(crossSection[cri].x, 0, crossSection[cri].y); + // apply scaling to point + tcross[cri] = scmat * tc23vec; + // + // transfer point to new basis + // calculate coordinate in new basis + tvecX.Set(basis_arr[spi].a1, basis_arr[spi].a2, basis_arr[spi].a3), tvecX *= tcross[cri].x; + tvecY.Set(basis_arr[spi].b1, basis_arr[spi].b2, basis_arr[spi].b3), tvecY *= tcross[cri].y; + tvecZ.Set(basis_arr[spi].c1, basis_arr[spi].c2, basis_arr[spi].c3), tvecZ *= tcross[cri].z; + // apply new coordinates and translate it to spine point. + tcross[cri] = tvecX + tvecY + tvecZ + spine[spi]; + }// for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; i++) + + pointset_arr[spi] = tcross;// store transferred point set + }// for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; i++) + }// END: 2. Create array of point sets. + + {// 3. Create CoordIdx. + // add caps if needed + if(beginCap) + { + // add cap as polygon. vertices of cap are places at begin, so just add numbers from zero. + for(size_t i = 0, i_e = crossSection.size(); i < i_e; i++) ext_alias.CoordIndex.push_back(static_cast(i)); + + // add delimiter + ext_alias.CoordIndex.push_back(-1); + }// if(beginCap) + + if(endCap) + { + // add cap as polygon. vertices of cap are places at end, as for beginCap use just sequence of numbers but with offset. + size_t beg = (pointset_arr.size() - 1) * crossSection.size(); + + for(size_t i = beg, i_e = (beg + crossSection.size()); i < i_e; i++) ext_alias.CoordIndex.push_back(static_cast(i)); + + // add delimiter + ext_alias.CoordIndex.push_back(-1); + }// if(beginCap) + + // add quads + for(size_t spi = 0, spi_e = (spine.size() - 1); spi <= spi_e; spi++) + { + const size_t cr_sz = crossSection.size(); + const size_t cr_last = crossSection.size() - 1; + + size_t right_col;// hold index basis for points of quad placed in right column; + + if(spi != spi_e) + right_col = spi + 1; + else if(spine_closed)// if spine curve is closed then one more quad is needed: between first and last points of curve. + right_col = 0; + else + break;// if spine curve is not closed then break the loop, because spi is out of range for that type of spine. + + for(size_t cri = 0; cri < cr_sz; cri++) + { + if(cri != cr_last) + { + MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, + static_cast(spi * cr_sz + cri), + static_cast(right_col * cr_sz + cri), + static_cast(right_col * cr_sz + cri + 1), + static_cast(spi * cr_sz + cri + 1)); + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } + else if(cross_closed)// if cross curve is closed then one more quad is needed: between first and last points of curve. + { + MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, + static_cast(spi * cr_sz + cri), + static_cast(right_col * cr_sz + cri), + static_cast(right_col * cr_sz + 0), + static_cast(spi * cr_sz + 0)); + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } + }// for(size_t cri = 0; cri < cr_sz; cri++) + }// for(size_t spi = 0, spi_e = (spine.size() - 2); spi < spi_e; spi++) + }// END: 3. Create CoordIdx. + + {// 4. Create vertices list. + // just copy all vertices + for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) + { + for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) + { + ext_alias.Vertices.push_back(pointset_arr[spi][cri]); + } + } + }// END: 4. Create vertices list. +//PrintVectorSet("Ext. CoordIdx", ext_alias.CoordIndex); +//PrintVectorSet("Ext. Vertices", ext_alias.Vertices); + // check for child nodes + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Extrusion"); + else + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readIndexedFaceSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + std::vector colorIndex; + bool colorPerVertex = true; + bool convex = true; + std::vector coordIndex; + float creaseAngle = 0; + std::vector normalIndex; + bool normalPerVertex = true; + bool solid = true; + std::vector texCoordIndex; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + X3DXmlHelper::getInt32ArrayAttribute(node, "colorIndex", colorIndex); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + XmlParser::getBoolAttribute(node, "convex", convex); + X3DXmlHelper::getInt32ArrayAttribute(node, "coordIndex", coordIndex); + XmlParser::getFloatAttribute(node, "creaseAngle", creaseAngle); + X3DXmlHelper::getInt32ArrayAttribute(node, "normalIndex", normalIndex); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + X3DXmlHelper::getInt32ArrayAttribute(node, "texCoordIndex", texCoordIndex); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedFaceSet, ne); + } + else + { + // check data + if(coordIndex.size() == 0) throw DeadlyImportError("IndexedFaceSet must contain not empty \"coordIndex\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedFaceSet, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorIndex = colorIndex; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.Convex = convex; + ne_alias.CoordIndex = coordIndex; + ne_alias.CreaseAngle = creaseAngle; + ne_alias.NormalIndex = normalIndex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + ne_alias.TexCoordIndex = texCoordIndex; + // check for child nodes + if(!isNodeEmpty(node)) + { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") readCoordinate(currentChildNode); + else if (currentChildName == "Normal") readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("IndexedFaceSet", currentChildNode); + } + ParseHelper_Node_Exit(); + }// if(!isNodeEmpty(node)) + else + { + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + } + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +void X3DImporter::readSphere(XmlNode &node) { + std::string use, def; + ai_real radius = 1; + bool solid = true; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "radius", radius); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Sphere, ne); + } + else + { + const unsigned int tess = 3;///TODO: IME tessellation factor through ai_property + + std::vector tlist; + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Sphere, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + StandardShapes::MakeSphere(tess, tlist); + // copy data from temp array and apply scale + for(std::vector::iterator it = tlist.begin(); it != tlist.end(); ++it) + { + ((X3DNodeElementGeometry3D *)ne)->Vertices.push_back(*it * radius); + } + + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Sphere"); + else + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +}// namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Group.cpp b/code/AssetLib/X3D/X3DImporter_Group.cpp new file mode 100644 index 000000000..d672d741b --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Group.cpp @@ -0,0 +1,273 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Group.cpp +/// \brief Parsing data from nodes of "Grouping" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +// +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// A Group node contains children nodes without introducing a new transformation. It is equivalent to a Transform node containing an identity transform. +void X3DImporter::startReadGroup(XmlNode &node) { + std::string def, use; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne; + + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::endReadGroup() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +// +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The StaticGroup node contains children nodes which cannot be modified. StaticGroup children are guaranteed to not change, send events, receive events or +// contain any USE references outside the StaticGroup. +void X3DImporter::startReadStaticGroup(XmlNode &node) { + std::string def, use; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne; + + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::endReadStaticGroup() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +// +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The Switch grouping node traverses zero or one of the nodes specified in the children field. The whichChoice field specifies the index of the child +// to traverse, with the first child having index 0. If whichChoice is less than zero or greater than the number of nodes in the children field, nothing +// is chosen. +void X3DImporter::startReadSwitch(XmlNode &node) { + std::string def, use; + int32_t whichChoice = -1; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getIntAttribute(node, "whichChoice", whichChoice); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne; + + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + + // also set values specific to this type of group + ((X3DNodeElementGroup *)mNodeElementCur)->UseChoice = true; + ((X3DNodeElementGroup *)mNodeElementCur)->Choice = whichChoice; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::endReadSwitch() { + // just exit from node. Defined choice will be accepted at postprocessing stage. + ParseHelper_Node_Exit(); // go up in scene graph +} + +// +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The Transform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors. +// Given a 3-dimensional point P and Transform node, P is transformed into point P' in its parent's coordinate system by a series of intermediate +// transformations. In matrix transformation notation, where C (center), SR (scaleOrientation), T (translation), R (rotation), and S (scale) are the +// equivalent transformation matrices, +// P' = T * C * R * SR * S * -SR * -C * P +void X3DImporter::startReadTransform(XmlNode &node) { + aiVector3D center(0, 0, 0); + float rotation[4] = { 0, 0, 1, 0 }; + aiVector3D scale(1, 1, 1); // A value of zero indicates that any child geometry shall not be displayed + float scale_orientation[4] = { 0, 0, 1, 0 }; + aiVector3D translation(0, 0, 0); + aiMatrix4x4 matr, tmatr; + std::string use, def; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DAttribute(node, "center", center); + X3DXmlHelper::getVector3DAttribute(node, "scale", scale); + X3DXmlHelper::getVector3DAttribute(node, "translation", translation); + std::vector tvec; + if (X3DXmlHelper::getFloatArrayAttribute(node, "rotation", tvec)) { + if (tvec.size() != 4) throw DeadlyImportError(": rotation vector must have 4 elements."); + memcpy(rotation, tvec.data(), sizeof(rotation)); + tvec.clear(); + } + if (X3DXmlHelper::getFloatArrayAttribute(node, "scaleOrientation", tvec)) { + if (tvec.size() != 4) throw DeadlyImportError(": scaleOrientation vector must have 4 elements."); + memcpy(scale_orientation, tvec.data(), sizeof(scale_orientation)); + tvec.clear(); + } + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne(nullptr); + + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) { + mNodeElementCur->ID = def; + } + + // + // also set values specific to this type of group + // + // calculate transformation matrix + aiMatrix4x4::Translation(translation, matr); // T + aiMatrix4x4::Translation(center, tmatr); // C + matr *= tmatr; + aiMatrix4x4::Rotation(rotation[3], aiVector3D(rotation[0], rotation[1], rotation[2]), tmatr); // R + matr *= tmatr; + aiMatrix4x4::Rotation(scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // SR + matr *= tmatr; + aiMatrix4x4::Scaling(scale, tmatr); // S + matr *= tmatr; + aiMatrix4x4::Rotation(-scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // -SR + matr *= tmatr; + aiMatrix4x4::Translation(-center, tmatr); // -C + matr *= tmatr; + // and assign it + ((X3DNodeElementGroup *)mNodeElementCur)->Transformation = matr; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) { + ParseHelper_Node_Exit(); + } + } // if(!use.empty()) else +} + +void X3DImporter::endReadTransform() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Light.cpp b/code/AssetLib/X3D/X3DImporter_Light.cpp new file mode 100644 index 000000000..b9bd17164 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Light.cpp @@ -0,0 +1,270 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Light.cpp +/// \brief Parsing data from nodes of "Lighting" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" +#include + +namespace Assimp { + +// +void X3DImporter::readDirectionalLight(XmlNode &node) { + std::string def, use; + float ambientIntensity = 0; + aiColor3D color(1, 1, 1); + aiVector3D direction(0, 0, -1); + bool global = false; + float intensity = 1; + bool on = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + X3DXmlHelper::getColor3DAttribute(node, "color", color); + X3DXmlHelper::getVector3DAttribute(node, "direction", direction); + XmlParser::getBoolAttribute(node, "global", global); + XmlParser::getFloatAttribute(node, "intensity", intensity); + XmlParser::getBoolAttribute(node, "on", on); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_DirectionalLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeElementLight(X3DElemType::ENET_DirectionalLight, mNodeElementCur); + if (!def.empty()) + ne->ID = def; + else + ne->ID = "DirectionalLight_" + ai_to_string((size_t)ne); // make random name + + ((X3DNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementLight *)ne)->Color = color; + ((X3DNodeElementLight *)ne)->Direction = direction; + ((X3DNodeElementLight *)ne)->Global = global; + ((X3DNodeElementLight *)ne)->Intensity = intensity; + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "DirectionalLight"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +// +void X3DImporter::readPointLight(XmlNode &node) { + std::string def, use; + float ambientIntensity = 0; + aiVector3D attenuation(1, 0, 0); + aiColor3D color(1, 1, 1); + bool global = true; + float intensity = 1; + aiVector3D location(0, 0, 0); + bool on = true; + float radius = 100; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + X3DXmlHelper::getVector3DAttribute(node, "attenuation", attenuation); + X3DXmlHelper::getColor3DAttribute(node, "color", color); + XmlParser::getBoolAttribute(node, "global", global); + XmlParser::getFloatAttribute(node, "intensity", intensity); + X3DXmlHelper::getVector3DAttribute(node, "location", location); + XmlParser::getBoolAttribute(node, "on", on); + XmlParser::getFloatAttribute(node, "radius", radius); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_PointLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeElementLight(X3DElemType::ENET_PointLight, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementLight *)ne)->Attenuation = attenuation; + ((X3DNodeElementLight *)ne)->Color = color; + ((X3DNodeElementLight *)ne)->Global = global; + ((X3DNodeElementLight *)ne)->Intensity = intensity; + ((X3DNodeElementLight *)ne)->Location = location; + ((X3DNodeElementLight *)ne)->Radius = radius; + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + // make random name + if (ne->ID.empty()) ne->ID = "PointLight_" + ai_to_string((size_t)ne); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "PointLight"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +// +void X3DImporter::readSpotLight(XmlNode &node) { + std::string def, use; + float ambientIntensity = 0; + aiVector3D attenuation(1, 0, 0); + float beamWidth = 0.7854f; + aiColor3D color(1, 1, 1); + float cutOffAngle = 1.570796f; + aiVector3D direction(0, 0, -1); + bool global = true; + float intensity = 1; + aiVector3D location(0, 0, 0); + bool on = true; + float radius = 100; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + X3DXmlHelper::getVector3DAttribute(node, "attenuation", attenuation); + XmlParser::getFloatAttribute(node, "beamWidth", beamWidth); + X3DXmlHelper::getColor3DAttribute(node, "color", color); + XmlParser::getFloatAttribute(node, "cutOffAngle", cutOffAngle); + X3DXmlHelper::getVector3DAttribute(node, "direction", direction); + XmlParser::getBoolAttribute(node, "global", global); + XmlParser::getFloatAttribute(node, "intensity", intensity); + X3DXmlHelper::getVector3DAttribute(node, "location", location); + XmlParser::getBoolAttribute(node, "on", on); + XmlParser::getFloatAttribute(node, "radius", radius); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_SpotLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeElementLight(X3DElemType::ENET_SpotLight, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + if (beamWidth > cutOffAngle) beamWidth = cutOffAngle; + + ((X3DNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementLight *)ne)->Attenuation = attenuation; + ((X3DNodeElementLight *)ne)->BeamWidth = beamWidth; + ((X3DNodeElementLight *)ne)->Color = color; + ((X3DNodeElementLight *)ne)->CutOffAngle = cutOffAngle; + ((X3DNodeElementLight *)ne)->Direction = direction; + ((X3DNodeElementLight *)ne)->Global = global; + ((X3DNodeElementLight *)ne)->Intensity = intensity; + ((X3DNodeElementLight *)ne)->Location = location; + ((X3DNodeElementLight *)ne)->Radius = radius; + + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + // make random name + if (ne->ID.empty()) ne->ID = "SpotLight_" + ai_to_string((size_t)ne); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "SpotLight"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Macro.hpp b/code/AssetLib/X3D/X3DImporter_Macro.hpp new file mode 100644 index 000000000..8d902b346 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Macro.hpp @@ -0,0 +1,110 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Macro.hpp +/// \brief Useful macrodefines. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef X3DIMPORTER_MACRO_HPP_INCLUDED +#define X3DIMPORTER_MACRO_HPP_INCLUDED + +/// \def MACRO_USE_CHECKANDAPPLY(pDEF, pUSE, pNE) +/// Used for regular checking while attribute "USE" is defined. +/// \param [in] pNode - pugi xml node to read. +/// \param [in] pDEF - string holding "DEF" value. +/// \param [in] pUSE - string holding "USE" value. +/// \param [in] pType - type of element to find. +/// \param [out] pNE - pointer to found node element. +#define MACRO_USE_CHECKANDAPPLY(pNode, pDEF, pUSE, pType, pNE) \ + do { \ + checkNodeMustBeEmpty(pNode); \ + if (!pDEF.empty()) Throw_DEF_And_USE(pNode.name()); \ + if (!FindNodeElement(pUSE, X3DElemType::pType, &pNE)) Throw_USE_NotFound(pNode.name(), pUSE); \ + mNodeElementCur->Children.push_back(pNE); /* add found object as child to current element */ \ + } while (false) + +/// \def MACRO_ATTRREAD_CHECKUSEDEF_RET +/// Compact variant for checking "USE" and "DEF". +/// \param [in] pNode - pugi xml node to read. +/// \param [out] pDEF_Var - output variable name for "DEF" value. +/// \param [out] pUSE_Var - output variable name for "USE" value. +#define MACRO_ATTRREAD_CHECKUSEDEF_RET(pNode, pDEF_Var, pUSE_Var) \ + do { \ + XmlParser::getStdStrAttribute(pNode, "def", pDEF_Var); \ + XmlParser::getStdStrAttribute(pNode, "use", pUSE_Var); \ + } while (false) + +/// \def MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4) +/// Add points as quad. Means that pP1..pP4 set in CCW order. +#define MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4) \ + do { \ + if (pCCW) { \ + pOut.push_back(pIn[pP1]); \ + pOut.push_back(pIn[pP2]); \ + pOut.push_back(pIn[pP3]); \ + pOut.push_back(pIn[pP4]); \ + } else { \ + pOut.push_back(pIn[pP4]); \ + pOut.push_back(pIn[pP3]); \ + pOut.push_back(pIn[pP2]); \ + pOut.push_back(pIn[pP1]); \ + } \ + } while (false) + +/// \def MACRO_FACE_ADD_QUAD(pCCW, pOut, pP1, pP2, pP3, pP4) +/// Add points as quad. Means that pP1..pP4 set in CCW order. +#define MACRO_FACE_ADD_QUAD(pCCW, pOut, pP1, pP2, pP3, pP4) \ + do { \ + if (pCCW) { \ + pOut.push_back(pP1); \ + pOut.push_back(pP2); \ + pOut.push_back(pP3); \ + pOut.push_back(pP4); \ + } else { \ + pOut.push_back(pP4); \ + pOut.push_back(pP3); \ + pOut.push_back(pP2); \ + pOut.push_back(pP1); \ + } \ + } while (false) + +#endif // X3DIMPORTER_MACRO_HPP_INCLUDED diff --git a/code/AssetLib/X3D/X3DImporter_Metadata.cpp b/code/AssetLib/X3D/X3DImporter_Metadata.cpp new file mode 100644 index 000000000..bb1ba9a9d --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Metadata.cpp @@ -0,0 +1,255 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Metadata.cpp +/// \brief Parsing data from nodes of "Metadata" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +bool X3DImporter::checkForMetadataNode(XmlNode &node) { + const std::string &name = node.name(); + if (name == "MetadataBoolean") { + readMetadataBoolean(node); + } else if (name == "MetadataDouble") { + readMetadataDouble(node); + } else if (name == "MetadataFloat") { + readMetadataFloat(node); + } else if (name == "MetadataInteger") { + readMetadataInteger(node); + } else if (name == "MetadataSet") { + readMetadataSet(node); + } else if (name == "MetadataString") { + readMetadataString(node); + } else + return false; + return true; +} + +void X3DImporter::childrenReadMetadata(XmlNode &node, X3DNodeElementBase *pParentElement, const std::string &pNodeName) { + ParseHelper_Node_Enter(pParentElement); + for (auto childNode : node.children()) { + if (!checkForMetadataNode(childNode)) skipUnsupportedNode(pNodeName, childNode); + } + ParseHelper_Node_Exit(); +} + +/// \def MACRO_METADATA_FINDCREATE(pDEF_Var, pUSE_Var, pReference, pValue, pNE, pMetaName) +/// Find element by "USE" or create new one. +/// \param [in] pNode - pugi xml node to read. +/// \param [in] pDEF_Var - variable name with "DEF" value. +/// \param [in] pUSE_Var - variable name with "USE" value. +/// \param [in] pReference - variable name with "reference" value. +/// \param [in] pValue - variable name with "value" value. +/// \param [in, out] pNE - pointer to node element. +/// \param [in] pMetaClass - Class of node. +/// \param [in] pMetaName - Name of node. +/// \param [in] pType - type of element to find. +#define MACRO_METADATA_FINDCREATE(pNode, pDEF_Var, pUSE_Var, pReference, pValue, pNE, pMetaClass, pMetaName, pType) \ + /* if "USE" defined then find already defined element. */ \ + if (!pUSE_Var.empty()) { \ + MACRO_USE_CHECKANDAPPLY(pNode, pDEF_Var, pUSE_Var, pType, pNE); \ + } else { \ + pNE = new pMetaClass(mNodeElementCur); \ + if (!pDEF_Var.empty()) pNE->ID = pDEF_Var; \ + \ + ((pMetaClass *)pNE)->Reference = pReference; \ + ((pMetaClass *)pNE)->Value = pValue; \ + /* also metadata node can contain childs */ \ + if (!isNodeEmpty(pNode)) \ + childrenReadMetadata(pNode, pNE, pMetaName); /* in that case node element will be added to child elements list of current node. */ \ + else \ + mNodeElementCur->Children.push_back(pNE); /* else - add element to child list manually */ \ + \ + NodeElement_List.push_back(pNE); /* add new element to elements list. */ \ + } /* if(!pUSE_Var.empty()) else */ \ + \ + do { \ + } while (false) + +// +void X3DImporter::readMetadataBoolean(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getBooleanArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaBoolean, "MetadataBoolean", ENET_MetaBoolean); +} + +// +void X3DImporter::readMetadataDouble(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getDoubleArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaDouble, "MetadataDouble", ENET_MetaDouble); +} + +// +void X3DImporter::readMetadataFloat(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getFloatArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaFloat, "MetadataFloat", ENET_MetaFloat); +} + +// +void X3DImporter::readMetadataInteger(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getInt32ArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaInt, "MetadataInteger", ENET_MetaInteger); +} + +// +void X3DImporter::readMetadataSet(XmlNode &node) { + std::string def, use; + std::string name, reference; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_MetaSet, ne); + } else { + ne = new X3DNodeElementMetaSet(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementMetaSet *)ne)->Reference = reference; + // also metadata node can contain childs + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "MetadataSet"); + else + mNodeElementCur->Children.push_back(ne); // made object as child to current element + + NodeElement_List.push_back(ne); // add new element to elements list. + } // if(!use.empty()) else +} + +// +void X3DImporter::readMetadataString(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getStringArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaString, "MetadataString", ENET_MetaString); +} + +}// namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Networking.cpp b/code/AssetLib/X3D/X3DImporter_Networking.cpp new file mode 100644 index 000000000..d5ef58397 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Networking.cpp @@ -0,0 +1,125 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Networking.cpp +/// \brief Parsing data from nodes of "Networking" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +// Header files, Assimp. +#include + +//#include + +namespace Assimp { + +//static std::regex pattern_parentDir(R"((^|/)[^/]+/../)"); +static std::string parentDir("/../"); + +// +void X3DImporter::readInline(XmlNode &node) { + std::string def, use; + bool load = true; + std::list url; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "load", load); + X3DXmlHelper::getStringListAttribute(node, "url", url); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne; + + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + + if (load && !url.empty()) { + std::string full_path = mpIOHandler->CurrentDirectory() + url.front(); + + //full_path = std::regex_replace(full_path, pattern_parentDir, "$1"); + for (std::string::size_type pos = full_path.find(parentDir); pos != std::string::npos; pos = full_path.find(parentDir, pos)) { + if (pos > 0) { + std::string::size_type pos2 = full_path.rfind('/', pos - 1); + if (pos2 != std::string::npos) { + full_path.erase(pos2, pos - pos2 + 3); + pos = pos2; + } else { + full_path.erase(0, pos + 4); + pos = 0; + } + } else { + pos += 3; + } + } + // Attribute "url" can contain list of strings. But we need only one - first. + std::string::size_type slashPos = full_path.find_last_of("\\/"); + mpIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : full_path.substr(0, slashPos + 1)); + ParseFile(full_path, mpIOHandler); + mpIOHandler->PopDirectory(); + } + + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) childrenReadMetadata(node, mNodeElementCur, "Inline"); + + // exit from node in that place + ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Node.hpp b/code/AssetLib/X3D/X3DImporter_Node.hpp new file mode 100644 index 000000000..8079cbf11 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Node.hpp @@ -0,0 +1,459 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Node.hpp +/// \brief Elements of scene graph. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef INCLUDED_AI_X3D_IMPORTER_NODE_H +#define INCLUDED_AI_X3D_IMPORTER_NODE_H + +// Header files, Assimp. +#include + +#include +#include + +enum X3DElemType { + ENET_Group, ///< Element has type "Group". + ENET_MetaBoolean, ///< Element has type "Metadata boolean". + ENET_MetaDouble, ///< Element has type "Metadata double". + ENET_MetaFloat, ///< Element has type "Metadata float". + ENET_MetaInteger, ///< Element has type "Metadata integer". + ENET_MetaSet, ///< Element has type "Metadata set". + ENET_MetaString, ///< Element has type "Metadata string". + ENET_Arc2D, ///< Element has type "Arc2D". + ENET_ArcClose2D, ///< Element has type "ArcClose2D". + ENET_Circle2D, ///< Element has type "Circle2D". + ENET_Disk2D, ///< Element has type "Disk2D". + ENET_Polyline2D, ///< Element has type "Polyline2D". + ENET_Polypoint2D, ///< Element has type "Polypoint2D". + ENET_Rectangle2D, ///< Element has type "Rectangle2D". + ENET_TriangleSet2D, ///< Element has type "TriangleSet2D". + ENET_Box, ///< Element has type "Box". + ENET_Cone, ///< Element has type "Cone". + ENET_Cylinder, ///< Element has type "Cylinder". + ENET_Sphere, ///< Element has type "Sphere". + ENET_ElevationGrid, ///< Element has type "ElevationGrid". + ENET_Extrusion, ///< Element has type "Extrusion". + ENET_Coordinate, ///< Element has type "Coordinate". + ENET_Normal, ///< Element has type "Normal". + ENET_TextureCoordinate, ///< Element has type "TextureCoordinate". + ENET_IndexedFaceSet, ///< Element has type "IndexedFaceSet". + ENET_IndexedLineSet, ///< Element has type "IndexedLineSet". + ENET_IndexedTriangleSet, ///< Element has type "IndexedTriangleSet". + ENET_IndexedTriangleFanSet, ///< Element has type "IndexedTriangleFanSet". + ENET_IndexedTriangleStripSet, ///< Element has type "IndexedTriangleStripSet". + ENET_LineSet, ///< Element has type "LineSet". + ENET_PointSet, ///< Element has type "PointSet". + ENET_TriangleSet, ///< Element has type "TriangleSet". + ENET_TriangleFanSet, ///< Element has type "TriangleFanSet". + ENET_TriangleStripSet, ///< Element has type "TriangleStripSet". + ENET_Color, ///< Element has type "Color". + ENET_ColorRGBA, ///< Element has type "ColorRGBA". + ENET_Shape, ///< Element has type "Shape". + ENET_Appearance, ///< Element has type "Appearance". + ENET_Material, ///< Element has type "Material". + ENET_ImageTexture, ///< Element has type "ImageTexture". + ENET_TextureTransform, ///< Element has type "TextureTransform". + ENET_DirectionalLight, ///< Element has type "DirectionalLight". + ENET_PointLight, ///< Element has type "PointLight". + ENET_SpotLight, ///< Element has type "SpotLight". + + ENET_Invalid ///< Element has invalid type and possible contain invalid data. +}; + +struct X3DNodeElementBase { + X3DNodeElementBase *Parent; + std::string ID; + std::list Children; + X3DElemType Type; + +protected: + X3DNodeElementBase(X3DElemType type, X3DNodeElementBase *pParent) : + Type(type), Parent(pParent) { + // empty + } +}; + +/// This struct hold value. +struct X3DNodeElementColor : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementColor(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Color, pParent) {} + +}; // struct X3DNodeElementColor + +/// This struct hold value. +struct X3DNodeElementColorRGBA : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementColorRGBA(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_ColorRGBA, pParent) {} + +}; // struct X3DNodeElementColorRGBA + +/// This struct hold value. +struct X3DNodeElementCoordinate : public X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementCoordinate(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Coordinate, pParent) {} + +}; // struct X3DNodeElementCoordinate + +/// This struct hold value. +struct X3DNodeElementNormal : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementNormal(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Normal, pParent) {} + +}; // struct X3DNodeElementNormal + +/// This struct hold value. +struct X3DNodeElementTextureCoordinate : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementTextureCoordinate(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_TextureCoordinate, pParent) {} + +}; // struct X3DNodeElementTextureCoordinate + +/// Two-dimensional figure. +struct X3DNodeElementGeometry2D : X3DNodeElementBase { + std::list Vertices; ///< Vertices list. + size_t NumIndices; ///< Number of indices in one face. + bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementGeometry2D(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pType, pParent), Solid(true) {} + +}; // class X3DNodeElementGeometry2D + +/// Three-dimensional body. +struct X3DNodeElementGeometry3D : X3DNodeElementBase { + std::list Vertices; ///< Vertices list. + size_t NumIndices; ///< Number of indices in one face. + bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementGeometry3D(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pType, pParent), Vertices(), NumIndices(0), Solid(true) { + // empty + } +}; // class X3DNodeElementGeometry3D + +/// Uniform rectangular grid of varying height. +struct X3DNodeElementElevationGrid : X3DNodeElementGeometry3D { + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are + /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. + float CreaseAngle; + std::vector CoordIdx; ///< Coordinates list by faces. In X3D format: "-1" - delimiter for faces. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementElevationGrid(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementGeometry3D(pType, pParent) {} +}; // class X3DNodeElementIndexedSet + +/// Shape with indexed vertices. +struct X3DNodeElementIndexedSet : public X3DNodeElementGeometry3D { + /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors + /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to + /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the + /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite + /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the + /// ccw field, results are undefined. + bool CCW; + std::vector ColorIndex; ///< Field to specify the polygonal faces by indexing into the or . + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + /// The convex field indicates whether all polygons in the shape are convex (TRUE). A polygon is convex if it is planar, does not intersect itself, + /// and all of the interior angles at its vertices are less than 180 degrees. Non planar and self intersecting polygons may produce undefined results + /// even if the convex field is FALSE. + bool Convex; + std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . + /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are + /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. + float CreaseAngle; + std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementIndexedSet(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementGeometry3D(pType, pParent) {} +}; // class X3DNodeElementIndexedSet + +/// Shape with set of vertices. +struct X3DNodeElementSet : X3DNodeElementGeometry3D { + /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors + /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to + /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the + /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite + /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the + /// ccw field, results are undefined. + bool CCW; + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . + std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . + std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . + std::vector VertexCount; ///< Field describes how many vertices are to be used in each polyline(polygon) from the field. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementSet(X3DElemType type, X3DNodeElementBase *pParent) : + X3DNodeElementGeometry3D(type, pParent) {} + +}; // class X3DNodeElementSet + +/// This struct hold value. +struct X3DNodeElementShape : X3DNodeElementBase { + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementShape(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Shape, pParent) {} +}; // struct X3DNodeElementShape + +/// This struct hold value. +struct X3DNodeElementAppearance : public X3DNodeElementBase { + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementAppearance(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Appearance, pParent) {} + +}; // struct X3DNodeElementAppearance + +struct X3DNodeElementMaterial : public X3DNodeElementBase { + float AmbientIntensity; ///< Specifies how much ambient light from light sources this surface shall reflect. + aiColor3D DiffuseColor; ///< Reflects all X3D light sources depending on the angle of the surface with respect to the light source. + aiColor3D EmissiveColor; ///< Models "glowing" objects. This can be useful for displaying pre-lit models. + float Shininess; ///< Lower shininess values produce soft glows, while higher values result in sharper, smaller highlights. + aiColor3D SpecularColor; ///< The specularColor and shininess fields determine the specular highlights. + float Transparency; ///< Specifies how "clear" an object is, with 1.0 being completely transparent, and 0.0 completely opaque. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementMaterial(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Material, pParent), + AmbientIntensity(0.0f), + DiffuseColor(), + EmissiveColor(), + Shininess(0.0f), + SpecularColor(), + Transparency(1.0f) { + // empty + } +}; // class X3DNodeElementMaterial + +/// This struct hold value. +struct X3DNodeElementImageTexture : X3DNodeElementBase { + /// RepeatS and RepeatT, that specify how the texture wraps in the S and T directions. If repeatS is TRUE (the default), the texture map is repeated + /// outside the [0.0, 1.0] texture coordinate range in the S direction so that it fills the shape. If repeatS is FALSE, the texture coordinates are + /// clamped in the S direction to lie within the [0.0, 1.0] range. The repeatT field is analogous to the repeatS field. + bool RepeatS; + bool RepeatT; ///< See \ref RepeatS. + std::string URL; ///< URL of the texture. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementImageTexture(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_ImageTexture, pParent) {} + +}; // struct X3DNodeElementImageTexture + +/// This struct hold value. +struct X3DNodeElementTextureTransform : X3DNodeElementBase { + aiVector2D Center; ///< Specifies a translation offset in texture coordinate space about which the rotation and scale fields are applied. + float Rotation; ///< Specifies a rotation in angle base units of the texture coordinates about the center point after the scale has been applied. + aiVector2D Scale; ///< Specifies a scaling factor in S and T of the texture coordinates about the center point. + aiVector2D Translation; ///< Specifies a translation of the texture coordinates. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementTextureTransform(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_TextureTransform, pParent) {} + +}; // struct X3DNodeElementTextureTransform + +struct X3DNodeElementGroup : X3DNodeElementBase { + aiMatrix4x4 Transformation; ///< Transformation matrix. + + /// As you know node elements can use already defined node elements when attribute "USE" is defined. + /// Standard search when looking for an element in the whole scene graph, existing at this moment. + /// If a node is marked as static, the children(or lower) can not search for elements in the nodes upper then static. + bool Static; + + bool UseChoice; ///< Flag: if true then use number from \ref Choice to choose what the child will be kept. + int32_t Choice; ///< Number of the child which will be kept. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pStatic - static node flag. + X3DNodeElementGroup(X3DNodeElementBase *pParent, const bool pStatic = false) : + X3DNodeElementBase(X3DElemType::ENET_Group, pParent), Static(pStatic), UseChoice(false) {} +}; + +struct X3DNodeElementMeta : X3DNodeElementBase { + std::string Name; ///< Name of metadata object. + std::string Reference; + + virtual ~X3DNodeElementMeta() { + // empty + } + +protected: + X3DNodeElementMeta(X3DElemType type, X3DNodeElementBase *parent) : + X3DNodeElementBase(type, parent) { + // empty + } +}; + +struct X3DNodeElementMetaBoolean : X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + explicit X3DNodeElementMetaBoolean(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaBoolean, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaDouble : X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + explicit X3DNodeElementMetaDouble(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaDouble, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaFloat : public X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + explicit X3DNodeElementMetaFloat(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaFloat, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaInt : public X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + explicit X3DNodeElementMetaInt(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaInteger, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaSet : public X3DNodeElementMeta { + std::list Value; ///< Stored value. + + explicit X3DNodeElementMetaSet(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaSet, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaString : X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + explicit X3DNodeElementMetaString(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaString, pParent) { + // empty + } +}; + +/// \struct X3DNodeElementLight +/// This struct hold value. +struct X3DNodeElementLight : X3DNodeElementBase { + float AmbientIntensity; ///< Specifies the intensity of the ambient emission from the light. + aiColor3D Color; ///< specifies the spectral colour properties of both the direct and ambient light emission as an RGB value. + aiVector3D Direction; ///< Specifies the direction vector of the illumination emanating from the light source in the local coordinate system. + /// \var Global + /// Field that determines whether the light is global or scoped. Global lights illuminate all objects that fall within their volume of lighting influence. + /// Scoped lights only illuminate objects that are in the same transformation hierarchy as the light. + bool Global; + float Intensity; ///< Specifies the brightness of the direct emission from the light. + /// \var Attenuation + /// PointLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor + /// is: "1 / max(attenuation[0] + attenuation[1] * r + attenuation[2] * r2, 1)", where r is the distance from the light to the surface being illuminated. + aiVector3D Attenuation; + aiVector3D Location; ///< Specifies a translation offset of the centre point of the light source from the light's local coordinate system origin. + float Radius; ///< Specifies the radial extent of the solid angle and the maximum distance from location that may be illuminated by the light source. + float BeamWidth; ///< Specifies an inner solid angle in which the light source emits light at uniform full intensity. + float CutOffAngle; ///< The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle). + + /// Constructor + /// \param [in] pParent - pointer to parent node. + /// \param [in] pLightType - type of the light source. + X3DNodeElementLight(X3DElemType pLightType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pLightType, pParent) {} + +}; // struct X3DNodeElementLight + +#endif // INCLUDED_AI_X3D_IMPORTER_NODE_H diff --git a/code/AssetLib/X3D/X3DImporter_Postprocess.cpp b/code/AssetLib/X3D/X3DImporter_Postprocess.cpp new file mode 100644 index 000000000..24e03ca16 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Postprocess.cpp @@ -0,0 +1,731 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Postprocess.cpp +/// \brief Convert built scenegraph and objects to Assimp scenegraph. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DGeoHelper.h" +#include "X3DImporter.hpp" + +// Header files, Assimp. +#include +#include +#include + +// Header files, stdlib. +#include +#include +#include + +namespace Assimp { + +aiMatrix4x4 X3DImporter::PostprocessHelper_Matrix_GlobalToCurrent() const { + X3DNodeElementBase *cur_node; + std::list matr; + aiMatrix4x4 out_matr; + + // starting walk from current element to root + cur_node = mNodeElementCur; + if (cur_node != nullptr) { + do { + // if cur_node is group then store group transformation matrix in list. + if (cur_node->Type == X3DElemType::ENET_Group) matr.push_back(((X3DNodeElementGroup *)cur_node)->Transformation); + + cur_node = cur_node->Parent; + } while (cur_node != nullptr); + } + + // multiplicate all matrices in reverse order + for (std::list::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); ++rit) + out_matr = out_matr * (*rit); + + return out_matr; +} + +void X3DImporter::PostprocessHelper_CollectMetadata(const X3DNodeElementBase &pNodeElement, std::list &pList) const { + // walk through childs and find for metadata. + for (std::list::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); ++el_it) { + if (((*el_it)->Type == X3DElemType::ENET_MetaBoolean) || ((*el_it)->Type == X3DElemType::ENET_MetaDouble) || + ((*el_it)->Type == X3DElemType::ENET_MetaFloat) || ((*el_it)->Type == X3DElemType::ENET_MetaInteger) || + ((*el_it)->Type == X3DElemType::ENET_MetaString)) { + pList.push_back(*el_it); + } else if ((*el_it)->Type == X3DElemType::ENET_MetaSet) { + PostprocessHelper_CollectMetadata(**el_it, pList); + } + } // for(std::list::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); el_it++) +} + +bool X3DImporter::PostprocessHelper_ElementIsMetadata(const X3DElemType pType) const { + if ((pType == X3DElemType::ENET_MetaBoolean) || (pType == X3DElemType::ENET_MetaDouble) || + (pType == X3DElemType::ENET_MetaFloat) || (pType == X3DElemType::ENET_MetaInteger) || + (pType == X3DElemType::ENET_MetaString) || (pType == X3DElemType::ENET_MetaSet)) { + return true; + } else { + return false; + } +} + +bool X3DImporter::PostprocessHelper_ElementIsMesh(const X3DElemType pType) const { + if ((pType == X3DElemType::ENET_Arc2D) || (pType == X3DElemType::ENET_ArcClose2D) || + (pType == X3DElemType::ENET_Box) || (pType == X3DElemType::ENET_Circle2D) || + (pType == X3DElemType::ENET_Cone) || (pType == X3DElemType::ENET_Cylinder) || + (pType == X3DElemType::ENET_Disk2D) || (pType == X3DElemType::ENET_ElevationGrid) || + (pType == X3DElemType::ENET_Extrusion) || (pType == X3DElemType::ENET_IndexedFaceSet) || + (pType == X3DElemType::ENET_IndexedLineSet) || (pType == X3DElemType::ENET_IndexedTriangleFanSet) || + (pType == X3DElemType::ENET_IndexedTriangleSet) || (pType == X3DElemType::ENET_IndexedTriangleStripSet) || + (pType == X3DElemType::ENET_PointSet) || (pType == X3DElemType::ENET_LineSet) || + (pType == X3DElemType::ENET_Polyline2D) || (pType == X3DElemType::ENET_Polypoint2D) || + (pType == X3DElemType::ENET_Rectangle2D) || (pType == X3DElemType::ENET_Sphere) || + (pType == X3DElemType::ENET_TriangleFanSet) || (pType == X3DElemType::ENET_TriangleSet) || + (pType == X3DElemType::ENET_TriangleSet2D) || (pType == X3DElemType::ENET_TriangleStripSet)) { + return true; + } else { + return false; + } +} + +void X3DImporter::Postprocess_BuildLight(const X3DNodeElementBase &pNodeElement, std::list &pSceneLightList) const { + const X3DNodeElementLight &ne = *((X3DNodeElementLight *)&pNodeElement); + aiMatrix4x4 transform_matr = PostprocessHelper_Matrix_GlobalToCurrent(); + aiLight *new_light = new aiLight; + + new_light->mName = ne.ID; + new_light->mColorAmbient = ne.Color * ne.AmbientIntensity; + new_light->mColorDiffuse = ne.Color * ne.Intensity; + new_light->mColorSpecular = ne.Color * ne.Intensity; + switch (pNodeElement.Type) { + case X3DElemType::ENET_DirectionalLight: + new_light->mType = aiLightSource_DIRECTIONAL; + new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; + + break; + case X3DElemType::ENET_PointLight: + new_light->mType = aiLightSource_POINT; + new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; + new_light->mAttenuationConstant = ne.Attenuation.x; + new_light->mAttenuationLinear = ne.Attenuation.y; + new_light->mAttenuationQuadratic = ne.Attenuation.z; + + break; + case X3DElemType::ENET_SpotLight: + new_light->mType = aiLightSource_SPOT; + new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; + new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; + new_light->mAttenuationConstant = ne.Attenuation.x; + new_light->mAttenuationLinear = ne.Attenuation.y; + new_light->mAttenuationQuadratic = ne.Attenuation.z; + new_light->mAngleInnerCone = ne.BeamWidth; + new_light->mAngleOuterCone = ne.CutOffAngle; + + break; + default: + throw DeadlyImportError("Postprocess_BuildLight. Unknown type of light: " + ai_to_string(pNodeElement.Type) + "."); + } + + pSceneLightList.push_back(new_light); +} + +void X3DImporter::Postprocess_BuildMaterial(const X3DNodeElementBase &pNodeElement, aiMaterial **pMaterial) const { + // check argument + if (pMaterial == nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. pMaterial is nullptr."); + if (*pMaterial != nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. *pMaterial must be nullptr."); + + *pMaterial = new aiMaterial; + aiMaterial &taimat = **pMaterial; // creating alias for convenience. + + // at this point pNodeElement point to node. Walk through childs and add all stored data. + for (std::list::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); ++el_it) { + if ((*el_it)->Type == X3DElemType::ENET_Material) { + aiColor3D tcol3; + float tvalf; + X3DNodeElementMaterial &tnemat = *((X3DNodeElementMaterial *)*el_it); + + tcol3.r = tnemat.AmbientIntensity, tcol3.g = tnemat.AmbientIntensity, tcol3.b = tnemat.AmbientIntensity; + taimat.AddProperty(&tcol3, 1, AI_MATKEY_COLOR_AMBIENT); + taimat.AddProperty(&tnemat.DiffuseColor, 1, AI_MATKEY_COLOR_DIFFUSE); + taimat.AddProperty(&tnemat.EmissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE); + taimat.AddProperty(&tnemat.SpecularColor, 1, AI_MATKEY_COLOR_SPECULAR); + tvalf = 1; + taimat.AddProperty(&tvalf, 1, AI_MATKEY_SHININESS_STRENGTH); + taimat.AddProperty(&tnemat.Shininess, 1, AI_MATKEY_SHININESS); + tvalf = 1.0f - tnemat.Transparency; + taimat.AddProperty(&tvalf, 1, AI_MATKEY_OPACITY); + } // if((*el_it)->Type == X3DElemType::ENET_Material) + else if ((*el_it)->Type == X3DElemType::ENET_ImageTexture) { + X3DNodeElementImageTexture &tnetex = *((X3DNodeElementImageTexture *)*el_it); + aiString url_str(tnetex.URL.c_str()); + int mode = aiTextureOp_Multiply; + + taimat.AddProperty(&url_str, AI_MATKEY_TEXTURE_DIFFUSE(0)); + taimat.AddProperty(&tnetex.RepeatS, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0)); + taimat.AddProperty(&tnetex.RepeatT, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0)); + taimat.AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0)); + } // else if((*el_it)->Type == X3DElemType::ENET_ImageTexture) + else if ((*el_it)->Type == X3DElemType::ENET_TextureTransform) { + aiUVTransform trans; + X3DNodeElementTextureTransform &tnetextr = *((X3DNodeElementTextureTransform *)*el_it); + + trans.mTranslation = tnetextr.Translation - tnetextr.Center; + trans.mScaling = tnetextr.Scale; + trans.mRotation = tnetextr.Rotation; + taimat.AddProperty(&trans, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); + } // else if((*el_it)->Type == X3DElemType::ENET_TextureTransform) + } // for(std::list::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); el_it++) +} + +void X3DImporter::Postprocess_BuildMesh(const X3DNodeElementBase &pNodeElement, aiMesh **pMesh) const { + // check argument + if (pMesh == nullptr) throw DeadlyImportError("Postprocess_BuildMesh. pMesh is nullptr."); + if (*pMesh != nullptr) throw DeadlyImportError("Postprocess_BuildMesh. *pMesh must be nullptr."); + + /************************************************************************************************************************************/ + /************************************************************ Geometry2D ************************************************************/ + /************************************************************************************************************************************/ + if ((pNodeElement.Type == X3DElemType::ENET_Arc2D) || (pNodeElement.Type == X3DElemType::ENET_ArcClose2D) || + (pNodeElement.Type == X3DElemType::ENET_Circle2D) || (pNodeElement.Type == X3DElemType::ENET_Disk2D) || + (pNodeElement.Type == X3DElemType::ENET_Polyline2D) || (pNodeElement.Type == X3DElemType::ENET_Polypoint2D) || + (pNodeElement.Type == X3DElemType::ENET_Rectangle2D) || (pNodeElement.Type == X3DElemType::ENET_TriangleSet2D)) { + X3DNodeElementGeometry2D &tnemesh = *((X3DNodeElementGeometry2D *)&pNodeElement); // create alias for convenience + std::vector tarr; + + tarr.reserve(tnemesh.Vertices.size()); + for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) + tarr.push_back(*it); + *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. + + return; // mesh is build, nothing to do anymore. + } + /************************************************************************************************************************************/ + /************************************************************ Geometry3D ************************************************************/ + /************************************************************************************************************************************/ + // + // Predefined figures + // + if ((pNodeElement.Type == X3DElemType::ENET_Box) || (pNodeElement.Type == X3DElemType::ENET_Cone) || + (pNodeElement.Type == X3DElemType::ENET_Cylinder) || (pNodeElement.Type == X3DElemType::ENET_Sphere)) { + X3DNodeElementGeometry3D &tnemesh = *((X3DNodeElementGeometry3D *)&pNodeElement); // create alias for convenience + std::vector tarr; + + tarr.reserve(tnemesh.Vertices.size()); + for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) + tarr.push_back(*it); + + *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. + + return; // mesh is build, nothing to do anymore. + } + // + // Parametric figures + // + if (pNodeElement.Type == X3DElemType::ENET_ElevationGrid) { + X3DNodeElementElevationGrid &tnemesh = *((X3DNodeElementElevationGrid *)&pNodeElement); // create alias for convenience + + // at first create mesh from existing vertices. + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIdx, tnemesh.Vertices); + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, ((X3DNodeElementNormal *)*ch_it)->Value, tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_ElevationGrid) + // + // Indexed primitives sets + // + if (pNodeElement.Type == X3DElemType::ENET_IndexedFaceSet) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_IndexedFaceSet) + + if (pNodeElement.Type == X3DElemType::ENET_IndexedLineSet) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_IndexedLineSet) + + if ((pNodeElement.Type == X3DElemType::ENET_IndexedTriangleSet) || + (pNodeElement.Type == X3DElemType::ENET_IndexedTriangleFanSet) || + (pNodeElement.Type == X3DElemType::ENET_IndexedTriangleStripSet)) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedTriangleSet or IndexedTriangleFanSet, or \ + IndexedTriangleStripSet: " + + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if((pNodeElement.Type == X3DElemType::ENET_IndexedTriangleFanSet) || (pNodeElement.Type == X3DElemType::ENET_IndexedTriangleStripSet)) + + if (pNodeElement.Type == X3DElemType::ENET_Extrusion) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, tnemesh.Vertices); + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_Extrusion) + + // + // Primitives sets + // + if (pNodeElement.Type == X3DElemType::ENET_PointSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + std::vector vec_copy; + + vec_copy.reserve(((X3DNodeElementCoordinate *)*ch_it)->Value.size()); + for (std::list::const_iterator it = ((X3DNodeElementCoordinate *)*ch_it)->Value.begin(); + it != ((X3DNodeElementCoordinate *)*ch_it)->Value.end(); ++it) { + vec_copy.push_back(*it); + } + + *pMesh = StandardShapes::MakeMesh(vec_copy, 1); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_PointSet) + + if (pNodeElement.Type == X3DElemType::ENET_LineSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_LineSet) + + if (pNodeElement.Type == X3DElemType::ENET_TriangleFanSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if (nullptr == *pMesh) { + break; + } + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_TriangleFanSet) + + if (pNodeElement.Type == X3DElemType::ENET_TriangleSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + std::vector vec_copy; + + vec_copy.reserve(((X3DNodeElementCoordinate *)*ch_it)->Value.size()); + for (std::list::const_iterator it = ((X3DNodeElementCoordinate *)*ch_it)->Value.begin(); + it != ((X3DNodeElementCoordinate *)*ch_it)->Value.end(); ++it) { + vec_copy.push_back(*it); + } + + *pMesh = StandardShapes::MakeMesh(vec_copy, 3); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_TriangleSet) + + if (pNodeElement.Type == X3DElemType::ENET_TriangleStripSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_TriangleStripSet) + + throw DeadlyImportError("Postprocess_BuildMesh. Unknown mesh type: " + ai_to_string(pNodeElement.Type) + "."); +} + +void X3DImporter::Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, + std::list &pSceneMaterialList, std::list &pSceneLightList) const { + std::list::const_iterator chit_begin = pNodeElement.Children.begin(); + std::list::const_iterator chit_end = pNodeElement.Children.end(); + std::list SceneNode_Child; + std::list SceneNode_Mesh; + + // At first read all metadata + Postprocess_CollectMetadata(pNodeElement, pSceneNode); + // check if we have deal with grouping node. Which can contain transformation or switch + if (pNodeElement.Type == X3DElemType::ENET_Group) { + const X3DNodeElementGroup &tne_group = *((X3DNodeElementGroup *)&pNodeElement); // create alias for convenience + + pSceneNode.mTransformation = tne_group.Transformation; + if (tne_group.UseChoice) { + // If Choice is less than zero or greater than the number of nodes in the children field, nothing is chosen. + if ((tne_group.Choice < 0) || ((size_t)tne_group.Choice >= pNodeElement.Children.size())) { + chit_begin = pNodeElement.Children.end(); + chit_end = pNodeElement.Children.end(); + } else { + for (size_t i = 0; i < (size_t)tne_group.Choice; i++) + ++chit_begin; // forward iterator to chosen node. + + chit_end = chit_begin; + ++chit_end; // point end iterator to next element after chosen node. + } + } // if(tne_group.UseChoice) + } // if(pNodeElement.Type == X3DElemType::ENET_Group) + + // Reserve memory for fast access and check children. + for (std::list::const_iterator it = chit_begin; it != chit_end; ++it) { // in this loop we do not read metadata because it's already read at begin. + if ((*it)->Type == X3DElemType::ENET_Group) { + // if child is group then create new node and do recursive call. + aiNode *new_node = new aiNode; + + new_node->mName = (*it)->ID; + new_node->mParent = &pSceneNode; + SceneNode_Child.push_back(new_node); + Postprocess_BuildNode(**it, *new_node, pSceneMeshList, pSceneMaterialList, pSceneLightList); + } else if ((*it)->Type == X3DElemType::ENET_Shape) { + // shape can contain only one geometry and one appearance nodes. + Postprocess_BuildShape(*((X3DNodeElementShape *)*it), SceneNode_Mesh, pSceneMeshList, pSceneMaterialList); + } else if (((*it)->Type == X3DElemType::ENET_DirectionalLight) || ((*it)->Type == X3DElemType::ENET_PointLight) || + ((*it)->Type == X3DElemType::ENET_SpotLight)) { + Postprocess_BuildLight(*((X3DNodeElementLight *)*it), pSceneLightList); + } else if (!PostprocessHelper_ElementIsMetadata((*it)->Type)) // skip metadata + { + throw DeadlyImportError("Postprocess_BuildNode. Unknown type: " + ai_to_string((*it)->Type) + "."); + } + } // for(std::list::const_iterator it = chit_begin; it != chit_end; it++) + + // copy data about children and meshes to aiNode. + if (!SceneNode_Child.empty()) { + std::list::const_iterator it = SceneNode_Child.begin(); + + pSceneNode.mNumChildren = static_cast(SceneNode_Child.size()); + pSceneNode.mChildren = new aiNode *[pSceneNode.mNumChildren]; + for (size_t i = 0; i < pSceneNode.mNumChildren; i++) + pSceneNode.mChildren[i] = *it++; + } + + if (!SceneNode_Mesh.empty()) { + std::list::const_iterator it = SceneNode_Mesh.begin(); + + pSceneNode.mNumMeshes = static_cast(SceneNode_Mesh.size()); + pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes]; + for (size_t i = 0; i < pSceneNode.mNumMeshes; i++) + pSceneNode.mMeshes[i] = *it++; + } + + // that's all. return to previous deals +} + +void X3DImporter::Postprocess_BuildShape(const X3DNodeElementShape &pShapeNodeElement, std::list &pNodeMeshInd, + std::list &pSceneMeshList, std::list &pSceneMaterialList) const { + aiMaterial *tmat = nullptr; + aiMesh *tmesh = nullptr; + X3DElemType mesh_type = X3DElemType::ENET_Invalid; + unsigned int mat_ind = 0; + + for (std::list::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); ++it) { + if (PostprocessHelper_ElementIsMesh((*it)->Type)) { + Postprocess_BuildMesh(**it, &tmesh); + if (tmesh != nullptr) { + // if mesh successfully built then add data about it to arrays + pNodeMeshInd.push_back(static_cast(pSceneMeshList.size())); + pSceneMeshList.push_back(tmesh); + // keep mesh type. Need above for texture coordinate generation. + mesh_type = (*it)->Type; + } + } else if ((*it)->Type == X3DElemType::ENET_Appearance) { + Postprocess_BuildMaterial(**it, &tmat); + if (tmat != nullptr) { + // if material successfully built then add data about it to array + mat_ind = static_cast(pSceneMaterialList.size()); + pSceneMaterialList.push_back(tmat); + } + } + } // for(std::list::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); it++) + + // associate read material with read mesh. + if ((tmesh != nullptr) && (tmat != nullptr)) { + tmesh->mMaterialIndex = mat_ind; + // Check texture mapping. If material has texture but mesh has no texture coordinate then try to ask Assimp to generate texture coordinates. + if ((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) { + int32_t tm; + aiVector3D tvec3; + + switch (mesh_type) { + case X3DElemType::ENET_Box: + tm = aiTextureMapping_BOX; + break; + case X3DElemType::ENET_Cone: + case X3DElemType::ENET_Cylinder: + tm = aiTextureMapping_CYLINDER; + break; + case X3DElemType::ENET_Sphere: + tm = aiTextureMapping_SPHERE; + break; + default: + tm = aiTextureMapping_PLANE; + break; + } // switch(mesh_type) + + tmat->AddProperty(&tm, 1, AI_MATKEY_MAPPING_DIFFUSE(0)); + } // if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) + } // if((tmesh != nullptr) && (tmat != nullptr)) +} + +void X3DImporter::Postprocess_CollectMetadata(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode) const { + std::list meta_list; + size_t meta_idx; + + PostprocessHelper_CollectMetadata(pNodeElement, meta_list); // find metadata in current node element. + if (!meta_list.empty()) { + if (pSceneNode.mMetaData != nullptr) { + throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong."); + } + + // copy collected metadata to output node. + pSceneNode.mMetaData = aiMetadata::Alloc(static_cast(meta_list.size())); + meta_idx = 0; + for (std::list::const_iterator it = meta_list.begin(); it != meta_list.end(); ++it, ++meta_idx) { + X3DNodeElementMeta *cur_meta = (X3DNodeElementMeta *)*it; + + // due to limitations we can add only first element of value list. + // Add an element according to its type. + if ((*it)->Type == X3DElemType::ENET_MetaBoolean) { + if (((X3DNodeElementMetaBoolean *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((X3DNodeElementMetaBoolean *)cur_meta)->Value.begin())); + } else if ((*it)->Type == X3DElemType::ENET_MetaDouble) { + if (((X3DNodeElementMetaDouble *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, (float)*(((X3DNodeElementMetaDouble *)cur_meta)->Value.begin())); + } else if ((*it)->Type == X3DElemType::ENET_MetaFloat) { + if (((X3DNodeElementMetaFloat *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((X3DNodeElementMetaFloat *)cur_meta)->Value.begin())); + } else if ((*it)->Type == X3DElemType::ENET_MetaInteger) { + if (((X3DNodeElementMetaInt *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((X3DNodeElementMetaInt *)cur_meta)->Value.begin())); + } else if ((*it)->Type == X3DElemType::ENET_MetaString) { + if (((X3DNodeElementMetaString *)cur_meta)->Value.size() > 0) { + aiString tstr(((X3DNodeElementMetaString *)cur_meta)->Value.begin()->data()); + + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, tstr); + } + } else { + throw DeadlyImportError("Postprocess. Unknown metadata type."); + } // if((*it)->Type == X3DElemType::ENET_Meta*) else + } // for(std::list::const_iterator it = meta_list.begin(); it != meta_list.end(); it++) + } // if( !meta_list.empty() ) +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Rendering.cpp b/code/AssetLib/X3D/X3DImporter_Rendering.cpp new file mode 100644 index 000000000..fe7e68081 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Rendering.cpp @@ -0,0 +1,987 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Rendering.cpp +/// \brief Parsing data from nodes of "Rendering" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +// +void X3DImporter::readColor(XmlNode &node) { + std::string use, def; + std::list color; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getColor3DListAttribute(node, "color", color); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Color, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementColor(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementColor *)ne)->Value = color; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Color"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readColorRGBA(XmlNode &node) { + std::string use, def; + std::list color; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getColor4DListAttribute(node, "color", color); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ColorRGBA, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementColorRGBA(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementColorRGBA *)ne)->Value = color; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "ColorRGBA"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readCoordinate(XmlNode &node) { + std::string use, def; + std::list point; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DListAttribute(node, "point", point); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Coordinate, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementCoordinate(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementCoordinate *)ne)->Value = point; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Coordinate"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can +// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readIndexedLineSet(XmlNode &node) { + std::string use, def; + std::vector colorIndex; + bool colorPerVertex = true; + std::vector coordIndex; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getInt32ArrayAttribute(node, "colorIndex", colorIndex); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "coordIndex", coordIndex); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedLineSet, ne); + } else { + // check data + if ((coordIndex.size() < 2) || ((coordIndex.back() == (-1)) && (coordIndex.size() < 3))) + throw DeadlyImportError("IndexedLineSet must contain not empty \"coordIndex\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedLineSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.ColorIndex = colorIndex; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.CoordIndex = coordIndex; + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for Color and Coordinate nodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedLineSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readIndexedTriangleFanSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector index; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "index", index); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleFanSet, ne); + } else { + // check data + if (index.size() == 0) throw DeadlyImportError("IndexedTriangleFanSet must contain not empty \"index\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleFanSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + + ne_alias.CoordIndex.clear(); + int counter = 0; + int32_t idx[3]; + for (std::vector::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) { + idx[2] = *idx_it; + if (idx[2] < 0) { + counter = 0; + } else { + if (counter >= 2) { + if (ccw) { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[1]); + ne_alias.CoordIndex.push_back(idx[2]); + } else { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[2]); + ne_alias.CoordIndex.push_back(idx[1]); + } + ne_alias.CoordIndex.push_back(-1); + idx[1] = idx[2]; + } else { + idx[counter] = idx[2]; + } + ++counter; + } + } // for(std::list::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++) + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedTriangleFanSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readIndexedTriangleSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector index; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "index", index); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleSet, ne); + } else { + // check data + if (index.size() == 0) throw DeadlyImportError("IndexedTriangleSet must contain not empty \"index\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + + ne_alias.CoordIndex.clear(); + int counter = 0; + int32_t idx[3]; + for (std::vector::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) { + idx[counter++] = *idx_it; + if (counter > 2) { + counter = 0; + if (ccw) { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[1]); + ne_alias.CoordIndex.push_back(idx[2]); + } else { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[2]); + ne_alias.CoordIndex.push_back(idx[1]); + } + ne_alias.CoordIndex.push_back(-1); + } + } // for(std::list::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++) + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedTriangleSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readIndexedTriangleStripSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector index; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "index", index); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleStripSet, ne); + } else { + // check data + if (index.size() == 0) throw DeadlyImportError("IndexedTriangleStripSet must contain not empty \"index\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleStripSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + + ne_alias.CoordIndex.clear(); + int counter = 0; + int32_t idx[3]; + for (std::vector::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) { + idx[2] = *idx_it; + if (idx[2] < 0) { + counter = 0; + } else { + if (counter >= 2) { + if (ccw) { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[1]); + ne_alias.CoordIndex.push_back(idx[2]); + } else { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[2]); + ne_alias.CoordIndex.push_back(idx[1]); + } + ne_alias.CoordIndex.push_back(-1); + } + idx[counter & 1] = idx[2]; + ++counter; + } + } // for(std::list::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++) + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedTriangleStripSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can +// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readLineSet(XmlNode &node) { + std::string use, def; + std::vector vertexCount; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getInt32ArrayAttribute(node, "vertexCount", vertexCount); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_LineSet, ne); + } else { + // check data + if (vertexCount.size() == 0) throw DeadlyImportError("LineSet must contain not empty \"vertexCount\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementSet(X3DElemType::ENET_LineSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.VertexCount = vertexCount; + // create CoordIdx + size_t coord_num = 0; + + ne_alias.CoordIndex.clear(); + for (std::vector::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) { + if (*vc_it < 2) throw DeadlyImportError("LineSet. vertexCount shall be greater than or equal to two."); + + for (int32_t i = 0; i < *vc_it; i++) + ne_alias.CoordIndex.push_back(static_cast(coord_num++)); // add vertices indices + + ne_alias.CoordIndex.push_back(-1); // add face delimiter. + } + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("LineSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can +// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readPointSet(XmlNode &node) { + std::string use, def; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_PointSet, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_PointSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("PointSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readTriangleFanSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector fanCount; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "fanCount", fanCount); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleFanSet, ne); + } else { + // check data + if (fanCount.size() == 0) throw DeadlyImportError("TriangleFanSet must contain not empty \"fanCount\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementSet(X3DElemType::ENET_TriangleFanSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.VertexCount = fanCount; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + // create CoordIdx + size_t coord_num_first, coord_num_prev; + + ne_alias.CoordIndex.clear(); + // assign indices for first triangle + coord_num_first = 0; + coord_num_prev = 1; + for (std::vector::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) { + if (*vc_it < 3) throw DeadlyImportError("TriangleFanSet. fanCount shall be greater than or equal to three."); + + for (int32_t vc = 2; vc < *vc_it; vc++) { + if (ccw) { + // 2 1 + // 0 + ne_alias.CoordIndex.push_back(static_cast(coord_num_first)); // first vertex is a center and always is [0]. + ne_alias.CoordIndex.push_back(static_cast(coord_num_prev++)); + ne_alias.CoordIndex.push_back(static_cast(coord_num_prev)); + } else { + // 1 2 + // 0 + ne_alias.CoordIndex.push_back(static_cast(coord_num_first)); // first vertex is a center and always is [0]. + ne_alias.CoordIndex.push_back(static_cast(coord_num_prev + 1)); + ne_alias.CoordIndex.push_back(static_cast(coord_num_prev++)); + } // if(ccw) else + + ne_alias.CoordIndex.push_back(-1); // add face delimiter. + } // for(int32_t vc = 2; vc < *vc_it; vc++) + + coord_num_prev++; // that index will be center of next fan + coord_num_first = coord_num_prev++; // forward to next point - second point of fan + } // for(std::list::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++) + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("TriangleFanSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readTriangleSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleSet, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_TriangleSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("TriangleSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readTriangleStripSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector stripCount; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "stripCount", stripCount); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleStripSet, ne); + } else { + // check data + if (stripCount.size() == 0) throw DeadlyImportError("TriangleStripSet must contain not empty \"stripCount\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementSet(X3DElemType::ENET_TriangleStripSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.VertexCount = stripCount; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + // create CoordIdx + size_t coord_num0, coord_num1, coord_num2; // indices of current triangle + bool odd_tri; // sequence of current triangle + size_t coord_num_sb; // index of first point of strip + + ne_alias.CoordIndex.clear(); + coord_num_sb = 0; + for (std::vector::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) { + if (*vc_it < 3) throw DeadlyImportError("TriangleStripSet. stripCount shall be greater than or equal to three."); + + // set initial values for first triangle + coord_num0 = coord_num_sb; + coord_num1 = coord_num_sb + 1; + coord_num2 = coord_num_sb + 2; + odd_tri = true; + + for (int32_t vc = 2; vc < *vc_it; vc++) { + if (ccw) { + // 0 2 + // 1 + ne_alias.CoordIndex.push_back(static_cast(coord_num0)); + ne_alias.CoordIndex.push_back(static_cast(coord_num1)); + ne_alias.CoordIndex.push_back(static_cast(coord_num2)); + } else { + // 0 1 + // 2 + ne_alias.CoordIndex.push_back(static_cast(coord_num0)); + ne_alias.CoordIndex.push_back(static_cast(coord_num2)); + ne_alias.CoordIndex.push_back(static_cast(coord_num1)); + } // if(ccw) else + + ne_alias.CoordIndex.push_back(-1); // add face delimiter. + // prepare values for next triangle + if (odd_tri) { + coord_num0 = coord_num2; + coord_num2++; + } else { + coord_num1 = coord_num2; + coord_num2 = coord_num1 + 1; + } + + odd_tri = !odd_tri; + coord_num_sb = coord_num2; // that index will be start of next strip + } // for(int32_t vc = 2; vc < *vc_it; vc++) + } // for(std::list::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++) + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("TriangleStripSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readNormal(XmlNode &node) { + std::string use, def; + std::list vector; + X3DNodeElementBase *ne; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DListAttribute(node, "vector", vector); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Normal, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementNormal(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementNormal *)ne)->Value = vector; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Normal"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Shape.cpp b/code/AssetLib/X3D/X3DImporter_Shape.cpp new file mode 100644 index 000000000..7a292be5c --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Shape.cpp @@ -0,0 +1,221 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Shape.cpp +/// \brief Parsing data from nodes of "Shape" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +void X3DImporter::readShape(XmlNode &node) { + std::string use, def; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Shape, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementShape(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for appearance node + if (currentChildName == "Appearance") readAppearance(currentChildNode); + // check for X3DGeometryNodes + else if (currentChildName == "Arc2D") readArc2D(currentChildNode); + else if (currentChildName == "ArcClose2D") readArcClose2D(currentChildNode); + else if (currentChildName == "Circle2D") readCircle2D(currentChildNode); + else if (currentChildName == "Disk2D") readDisk2D(currentChildNode); + else if (currentChildName == "Polyline2D") readPolyline2D(currentChildNode); + else if (currentChildName == "Polypoint2D") readPolypoint2D(currentChildNode); + else if (currentChildName == "Rectangle2D") readRectangle2D(currentChildNode); + else if (currentChildName == "TriangleSet2D") readTriangleSet2D(currentChildNode); + else if (currentChildName == "Box") readBox(currentChildNode); + else if (currentChildName == "Cone") readCone(currentChildNode); + else if (currentChildName == "Cylinder") readCylinder(currentChildNode); + else if (currentChildName == "ElevationGrid") readElevationGrid(currentChildNode); + else if (currentChildName == "Extrusion") readExtrusion(currentChildNode); + else if (currentChildName == "IndexedFaceSet") readIndexedFaceSet(currentChildNode); + else if (currentChildName == "Sphere") readSphere(currentChildNode); + else if (currentChildName == "IndexedLineSet") readIndexedLineSet(currentChildNode); + else if (currentChildName == "LineSet") readLineSet(currentChildNode); + else if (currentChildName == "PointSet") readPointSet(currentChildNode); + else if (currentChildName == "IndexedTriangleFanSet") readIndexedTriangleFanSet(currentChildNode); + else if (currentChildName == "IndexedTriangleSet") readIndexedTriangleSet(currentChildNode); + else if (currentChildName == "IndexedTriangleStripSet") readIndexedTriangleStripSet(currentChildNode); + else if (currentChildName == "TriangleFanSet") readTriangleFanSet(currentChildNode); + else if (currentChildName == "TriangleSet") readTriangleSet(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("Shape", currentChildNode); + } + + ParseHelper_Node_Exit(); + } // if (!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// "Child-node content model corresponding to X3DAppearanceChildNode. Appearance can contain FillProperties, LineProperties, Material, any Texture node and +// any TextureTransform node, in any order. No more than one instance of these nodes is allowed. Appearance may also contain multiple shaders (ComposedShader, +// PackagedShader, ProgramShader). +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model." +// +void X3DImporter::readAppearance(XmlNode &node) { + std::string use, def; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Appearance, ne); + } + else + { + // create and if needed - define new geometry object. + ne = new X3DNodeElementAppearance(mNodeElementCur); + if(!def.empty()) ne->ID = def; + + // check for child nodes + if(!isNodeEmpty(node)) + { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + if (currentChildName == "Material") readMaterial(currentChildNode); + else if (currentChildName == "ImageTexture") readImageTexture(currentChildNode); + else if (currentChildName == "TextureTransform") readTextureTransform(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("Appearance", currentChildNode); + } + ParseHelper_Node_Exit(); + }// if(!isNodeEmpty(node)) + else + { + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + } + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +void X3DImporter::readMaterial(XmlNode &node) { + std::string use, def; + float ambientIntensity = 0.2f; + float shininess = 0.2f; + float transparency = 0; + aiColor3D diffuseColor(0.8f, 0.8f, 0.8f); + aiColor3D emissiveColor(0, 0, 0); + aiColor3D specularColor(0, 0, 0); + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + XmlParser::getFloatAttribute(node, "shininess", shininess); + XmlParser::getFloatAttribute(node, "transparency", transparency); + X3DXmlHelper::getColor3DAttribute(node, "diffuseColor", diffuseColor); + X3DXmlHelper::getColor3DAttribute(node, "emissiveColor", emissiveColor); + X3DXmlHelper::getColor3DAttribute(node, "specularColor", specularColor); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Material, ne); + } + else + { + // create and if needed - define new geometry object. + ne = new X3DNodeElementMaterial(mNodeElementCur); + if(!def.empty()) ne->ID = def; + + ((X3DNodeElementMaterial *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementMaterial *)ne)->Shininess = shininess; + ((X3DNodeElementMaterial *)ne)->Transparency = transparency; + ((X3DNodeElementMaterial *)ne)->DiffuseColor = diffuseColor; + ((X3DNodeElementMaterial *)ne)->EmissiveColor = emissiveColor; + ((X3DNodeElementMaterial *)ne)->SpecularColor = specularColor; + // check for child nodes + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Material"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +}// namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Texturing.cpp b/code/AssetLib/X3D/X3DImporter_Texturing.cpp new file mode 100644 index 000000000..6463e2808 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Texturing.cpp @@ -0,0 +1,179 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Texturing.cpp +/// \brief Parsing data from nodes of "Texturing" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +// +// When the url field contains no values ([]), texturing is disabled. +void X3DImporter::readImageTexture(XmlNode &node) { + std::string use, def; + bool repeatS = true; + bool repeatT = true; + std::list url; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "repeatS", repeatS); + XmlParser::getBoolAttribute(node, "repeatT", repeatT); + X3DXmlHelper::getStringListAttribute(node, "url", url); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ImageTexture, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementImageTexture(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementImageTexture *)ne)->RepeatS = repeatS; + ((X3DNodeElementImageTexture *)ne)->RepeatT = repeatT; + // Attribute "url" can contain list of strings. But we need only one - first. + if (!url.empty()) + ((X3DNodeElementImageTexture *)ne)->URL = url.front(); + else + ((X3DNodeElementImageTexture *)ne)->URL = ""; + + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "ImageTexture"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readTextureCoordinate(XmlNode &node) { + std::string use, def; + std::list point; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "point", point); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TextureCoordinate, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementTextureCoordinate(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementTextureCoordinate *)ne)->Value = point; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "TextureCoordinate"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readTextureTransform(XmlNode &node) { + std::string use, def; + aiVector2D center(0, 0); + float rotation = 0; + aiVector2D scale(1, 1); + aiVector2D translation(0, 0); + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DAttribute(node, "center", center); + XmlParser::getFloatAttribute(node, "rotation", rotation); + X3DXmlHelper::getVector2DAttribute(node, "scale", scale); + X3DXmlHelper::getVector2DAttribute(node, "translation", translation); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TextureTransform, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementTextureTransform(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementTextureTransform *)ne)->Center = center; + ((X3DNodeElementTextureTransform *)ne)->Rotation = rotation; + ((X3DNodeElementTextureTransform *)ne)->Scale = scale; + ((X3DNodeElementTextureTransform *)ne)->Translation = translation; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "TextureTransform"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DXmlHelper.cpp b/code/AssetLib/X3D/X3DXmlHelper.cpp new file mode 100644 index 000000000..ff24b74b3 --- /dev/null +++ b/code/AssetLib/X3D/X3DXmlHelper.cpp @@ -0,0 +1,294 @@ +#include "X3DXmlHelper.h" +#include "X3DImporter.hpp" + +#include + +namespace Assimp { + +bool X3DXmlHelper::getColor3DAttribute(XmlNode &node, const char *attributeName, aiColor3D &color) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() != 3) { + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + return false; + } + auto it = values.begin(); + color.r = stof(*it++); + color.g = stof(*it++); + color.b = stof(*it); + return true; + } + return false; +} + +bool X3DXmlHelper::getVector2DAttribute(XmlNode &node, const char *attributeName, aiVector2D &color) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() != 2) { + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + return false; + } + auto it = values.begin(); + color.x = stof(*it++); + color.y = stof(*it); + return true; + } + return false; +} + +bool X3DXmlHelper::getVector3DAttribute(XmlNode &node, const char *attributeName, aiVector3D &color) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() != 3) { + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + return false; + } + auto it = values.begin(); + color.x = stof(*it++); + color.y = stof(*it++); + color.z = stof(*it); + return true; + } + return false; +} + +bool X3DXmlHelper::getBooleanArrayAttribute(XmlNode &node, const char *attributeName, std::vector &boolArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + boolArray.push_back(s[0] == 't' || s[0] == '1'); + else + Throw_ConvertFail_Str2ArrB(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getDoubleArrayAttribute(XmlNode &node, const char *attributeName, std::vector &doubleArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + doubleArray.push_back(atof(s.c_str())); + else + Throw_ConvertFail_Str2ArrD(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getFloatArrayAttribute(XmlNode &node, const char *attributeName, std::vector &floatArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + floatArray.push_back((float)atof(s.c_str())); + else + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getInt32ArrayAttribute(XmlNode &node, const char *attributeName, std::vector &intArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + intArray.push_back((int32_t)atof(s.c_str())); + else + Throw_ConvertFail_Str2ArrI(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getStringListAttribute(XmlNode &node, const char *attributeName, std::list &stringList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + auto it = values.begin(); + std::string currentConcat = ""; + bool inQuotes = false; + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) { + if (inQuotes) { + if (*(s.rbegin()) == '"') { + stringList.push_back(currentConcat + s.substr(0, s.length() - 1)); + currentConcat = ""; + inQuotes = false; + } else { + currentConcat += " " + s; + } + } else { + if (s[0] == '"') { + currentConcat = s.substr(1); + inQuotes = true; + } else { + stringList.push_back(s); + } + } + } else if (!inQuotes) + Throw_ConvertFail_Str2ArrI(node.name(), attributeName); + } + if (inQuotes) Throw_ConvertFail_Str2ArrI(node.name(), attributeName); + return true; + } + return false; +} + +bool X3DXmlHelper::getStringArrayAttribute(XmlNode &node, const char *attributeName, std::vector &stringArray) { + std::list tlist; + + if (getStringListAttribute(node, attributeName, tlist)) { + if (!tlist.empty()) { + stringArray.reserve(tlist.size()); + for (std::list::iterator it = tlist.begin(); it != tlist.end(); ++it) { + stringArray.push_back(*it); + } + return true; + } + } + return false; +} + +bool X3DXmlHelper::getVector2DListAttribute(XmlNode &node, const char *attributeName, std::list &vectorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() % 2) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiVector2D tvec; + + tvec.x = (float)atof((*it++).c_str()); + tvec.y = (float)atof((*it++).c_str()); + vectorList.push_back(tvec); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getVector2DArrayAttribute(XmlNode &node, const char *attributeName, std::vector &vectorArray) { + std::list tlist; + + if (getVector2DListAttribute(node, attributeName, tlist)) { + if (!tlist.empty()) { + vectorArray.reserve(tlist.size()); + for (std::list::iterator it = tlist.begin(); it != tlist.end(); ++it) { + vectorArray.push_back(*it); + } + return true; + } + } + return false; +} + +bool X3DXmlHelper::getVector3DListAttribute(XmlNode &node, const char *attributeName, std::list &vectorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() % 3 != 0) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiVector3D tvec; + + tvec.x = (float)atof((*it++).c_str()); + tvec.y = (float)atof((*it++).c_str()); + tvec.z = (float)atof((*it++).c_str()); + vectorList.push_back(tvec); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getVector3DArrayAttribute(XmlNode &node, const char *attributeName, std::vector &vectorArray) { + std::list tlist; + + if (getVector3DListAttribute(node, attributeName, tlist)) { + if (!tlist.empty()) { + vectorArray.reserve(tlist.size()); + for (std::list::iterator it = tlist.begin(); it != tlist.end(); ++it) { + vectorArray.push_back(*it); + } + return true; + } + } + return false; +} + +bool X3DXmlHelper::getColor3DListAttribute(XmlNode &node, const char *attributeName, std::list &colorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() % 3 != 0) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiColor3D tvec; + + tvec.r = (float)atof((*it++).c_str()); + tvec.g = (float)atof((*it++).c_str()); + tvec.b = (float)atof((*it++).c_str()); + colorList.push_back(tvec); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getColor4DListAttribute(XmlNode &node, const char *attributeName, std::list &colorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() % 4 != 0) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiColor4D tvec; + + tvec.r = (float)atof((*it++).c_str()); + tvec.g = (float)atof((*it++).c_str()); + tvec.b = (float)atof((*it++).c_str()); + tvec.a = (float)atof((*it++).c_str()); + colorList.push_back(tvec); + } + return true; + } + return false; +} + +} // namespace Assimp diff --git a/code/AssetLib/X3D/X3DXmlHelper.h b/code/AssetLib/X3D/X3DXmlHelper.h new file mode 100644 index 000000000..dd305f883 --- /dev/null +++ b/code/AssetLib/X3D/X3DXmlHelper.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +namespace Assimp { + +class X3DXmlHelper { +public: + static bool getColor3DAttribute(XmlNode &node, const char *attributeName, aiColor3D &color); + static bool getVector2DAttribute(XmlNode &node, const char *attributeName, aiVector2D &vector); + static bool getVector3DAttribute(XmlNode &node, const char *attributeName, aiVector3D &vector); + + static bool getBooleanArrayAttribute(XmlNode &node, const char *attributeName, std::vector &boolArray); + static bool getDoubleArrayAttribute(XmlNode &node, const char *attributeName, std::vector &doubleArray); + static bool getFloatArrayAttribute(XmlNode &node, const char *attributeName, std::vector &floatArray); + static bool getInt32ArrayAttribute(XmlNode &node, const char *attributeName, std::vector &intArray); + static bool getStringListAttribute(XmlNode &node, const char *attributeName, std::list &stringArray); + static bool getStringArrayAttribute(XmlNode &node, const char *attributeName, std::vector &stringArray); + + static bool getVector2DListAttribute(XmlNode &node, const char *attributeName, std::list &vectorList); + static bool getVector2DArrayAttribute(XmlNode &node, const char *attributeName, std::vector &vectorArray); + static bool getVector3DListAttribute(XmlNode &node, const char *attributeName, std::list &vectorList); + static bool getVector3DArrayAttribute(XmlNode &node, const char *attributeName, std::vector &vectorArray); + static bool getColor3DListAttribute(XmlNode &node, const char *attributeName, std::list &colorList); + static bool getColor4DListAttribute(XmlNode &node, const char *attributeName, std::list &colorList); +}; + +} // namespace Assimp diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 933b5488c..c99b81558 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -800,9 +800,23 @@ ADD_ASSIMP_IMPORTER( X ADD_ASSIMP_IMPORTER( X3D AssetLib/X3D/X3DImporter.cpp + AssetLib/X3D/X3DImporter_Geometry2D.cpp + AssetLib/X3D/X3DImporter_Geometry3D.cpp + AssetLib/X3D/X3DImporter_Group.cpp + AssetLib/X3D/X3DImporter_Light.cpp + AssetLib/X3D/X3DImporter_Metadata.cpp + AssetLib/X3D/X3DImporter_Networking.cpp + AssetLib/X3D/X3DImporter_Postprocess.cpp + AssetLib/X3D/X3DImporter_Rendering.cpp + AssetLib/X3D/X3DImporter_Shape.cpp + AssetLib/X3D/X3DImporter_Texturing.cpp AssetLib/X3D/X3DImporter.hpp + AssetLib/X3D/X3DImporter_Macro.hpp + AssetLib/X3D/X3DImporter_Node.hpp AssetLib/X3D/X3DGeoHelper.cpp AssetLib/X3D/X3DGeoHelper.h + AssetLib/X3D/X3DXmlHelper.cpp + AssetLib/X3D/X3DXmlHelper.h ) ADD_ASSIMP_IMPORTER( GLTF diff --git a/code/Common/ImporterRegistry.cpp b/code/Common/ImporterRegistry.cpp index 5df096166..7500d2610 100644 --- a/code/Common/ImporterRegistry.cpp +++ b/code/Common/ImporterRegistry.cpp @@ -365,9 +365,7 @@ void GetImporterInstanceList(std::vector &out) { out.push_back(new D3MFImporter()); #endif #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER - if (devImportersEnabled) { // https://github.com/assimp/assimp/issues/3647 - out.push_back(new X3DImporter()); - } + out.push_back(new X3DImporter()); #endif #ifndef ASSIMP_BUILD_NO_MMD_IMPORTER out.push_back(new MMDImporter()); From 18531e3677e8c8f78d6f7c56bfd642e748c08e3a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 13 Sep 2021 22:38:20 +0200 Subject: [PATCH 164/232] Next iteration for c++11 features. --- code/AssetLib/3DS/3DSExporter.cpp | 2 +- code/AssetLib/3DS/3DSHelper.h | 2 +- code/AssetLib/3DS/3DSLoader.h | 10 +- code/AssetLib/3MF/D3MFExporter.h | 5 +- code/AssetLib/3MF/D3MFImporter.h | 4 + code/AssetLib/3MF/D3MFOpcPackage.cpp | 2 +- code/AssetLib/3MF/D3MFOpcPackage.h | 2 +- code/AssetLib/AC/ACLoader.h | 10 +- code/AssetLib/AMF/AMFImporter.hpp | 8 +- code/AssetLib/AMF/AMFImporter_Node.hpp | 9 +- code/AssetLib/ASE/ASELoader.cpp | 2 - code/AssetLib/ASE/ASELoader.h | 25 +- code/AssetLib/ASE/ASEParser.cpp | 2 - code/AssetLib/Assbin/AssbinExporter.h | 2 +- code/AssetLib/Assbin/AssbinLoader.h | 17 +- code/AssetLib/Assjson/mesh_splitter.cpp | 13 +- code/AssetLib/Assjson/mesh_splitter.h | 35 +- code/AssetLib/Assxml/AssxmlExporter.h | 1 + code/AssetLib/Assxml/AssxmlFileWriter.h | 3 +- code/AssetLib/B3D/B3DImporter.h | 18 +- code/AssetLib/Blender/BlenderLoader.h | 186 ++++--- code/AssetLib/COB/COBLoader.h | 106 ++-- code/AssetLib/COB/COBScene.h | 9 +- code/AssetLib/CSM/CSMLoader.h | 28 +- code/AssetLib/Collada/ColladaLoader.cpp | 1 - code/AssetLib/Collada/ColladaParser.h | 1 + code/AssetLib/DXF/DXFLoader.h | 15 +- code/AssetLib/FBX/FBXImporter.h | 11 +- code/AssetLib/HMP/HMPLoader.h | 53 +- code/AssetLib/IFC/IFCBoolean.cpp | 1 - code/AssetLib/IFC/IFCLoader.h | 92 ++-- code/AssetLib/IFC/IFCUtil.cpp | 1 - code/AssetLib/Irr/IRRLoader.h | 11 +- code/AssetLib/Irr/IRRMeshLoader.h | 8 +- code/AssetLib/Irr/IRRShared.h | 2 +- code/AssetLib/LWO/LWOLoader.h | 208 ++++---- code/AssetLib/LWS/LWSLoader.h | 14 +- code/AssetLib/M3D/M3DImporter.h | 22 +- code/AssetLib/MD2/MD2Loader.h | 38 +- code/AssetLib/MD3/MD3Loader.h | 108 ++-- code/AssetLib/MD5/MD5Loader.h | 12 +- code/AssetLib/MDC/MDCLoader.h | 34 +- code/AssetLib/MDL/MDLLoader.h | 14 +- code/AssetLib/MDL/MDLMaterialLoader.cpp | 3 +- code/AssetLib/MMD/MMDImporter.h | 4 +- code/AssetLib/MS3D/MS3DLoader.h | 64 +-- code/AssetLib/NDO/NDOLoader.h | 50 +- code/AssetLib/NFF/NFFLoader.h | 118 ++--- code/AssetLib/OFF/OFFLoader.cpp | 2 - code/AssetLib/OFF/OFFLoader.h | 26 +- code/AssetLib/Obj/ObjFileData.h | 39 +- code/AssetLib/Obj/ObjFileImporter.h | 12 +- code/AssetLib/Obj/ObjFileMtlImporter.h | 6 +- code/AssetLib/Obj/ObjFileParser.h | 1 - code/AssetLib/Ogre/OgreImporter.cpp | 37 +- code/AssetLib/Ogre/OgreImporter.h | 42 +- code/AssetLib/OpenGEX/OpenGEXExporter.h | 2 +- code/AssetLib/OpenGEX/OpenGEXImporter.cpp | 3 - code/AssetLib/OpenGEX/OpenGEXImporter.h | 29 +- code/AssetLib/Ply/PlyLoader.h | 9 +- code/AssetLib/Ply/PlyParser.h | 3 +- code/AssetLib/Q3BSP/Q3BSPFileImporter.h | 15 +- code/AssetLib/Q3BSP/Q3BSPFileParser.cpp | 1 - code/AssetLib/Q3BSP/Q3BSPFileParser.h | 5 +- code/AssetLib/Q3D/Q3DLoader.h | 48 +- code/AssetLib/Raw/RawLoader.h | 33 +- code/AssetLib/SIB/SIBImporter.h | 31 +- code/AssetLib/SMD/SMDLoader.h | 57 +- code/AssetLib/STL/STLExporter.h | 19 +- code/AssetLib/STL/STLLoader.cpp | 5 +- code/AssetLib/STL/STLLoader.h | 9 +- code/AssetLib/Terragen/TerragenLoader.cpp | 8 +- code/AssetLib/Terragen/TerragenLoader.h | 13 +- code/AssetLib/Unreal/UnrealLoader.cpp | 2 +- code/AssetLib/Unreal/UnrealLoader.h | 16 +- code/AssetLib/X/XFileHelper.h | 115 ++--- code/AssetLib/X/XFileImporter.cpp | 1 - code/AssetLib/X/XFileImporter.h | 13 +- code/AssetLib/XGL/XGLLoader.cpp | 18 +- code/AssetLib/XGL/XGLLoader.h | 8 +- code/AssetLib/glTF/glTFAsset.h | 30 +- code/AssetLib/glTF/glTFImporter.h | 39 +- code/AssetLib/glTF2/glTF2Exporter.cpp | 603 +++++++++++----------- code/AssetLib/glTF2/glTF2Importer.cpp | 107 ++-- code/AssetLib/glTF2/glTF2Importer.h | 41 +- code/CMakeLists.txt | 1 - code/Common/StandardShapes.cpp | 1 - include/assimp/BlobIOSystem.h | 4 + include/assimp/ColladaMetaData.h | 4 + include/assimp/DefaultIOStream.h | 13 +- include/assimp/DefaultIOSystem.h | 4 +- include/assimp/DefaultLogger.hpp | 8 +- include/assimp/Defines.h | 58 --- include/assimp/IOStream.hpp | 2 - include/assimp/IOStreamBuffer.h | 183 +++---- include/assimp/LogAux.h | 7 +- include/assimp/SceneCombiner.h | 5 +- include/assimp/StreamReader.h | 3 +- include/assimp/commonMetaData.h | 4 + include/assimp/defs.h | 39 +- include/assimp/light.h | 12 +- include/assimp/matrix3x3.h | 2 - include/assimp/mesh.h | 11 +- 103 files changed, 1410 insertions(+), 1785 deletions(-) delete mode 100644 include/assimp/Defines.h diff --git a/code/AssetLib/3DS/3DSExporter.cpp b/code/AssetLib/3DS/3DSExporter.cpp index 0beecd563..d1059ae43 100644 --- a/code/AssetLib/3DS/3DSExporter.cpp +++ b/code/AssetLib/3DS/3DSExporter.cpp @@ -56,8 +56,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -using namespace Assimp; namespace Assimp { + using namespace D3DS; namespace { diff --git a/code/AssetLib/3DS/3DSHelper.h b/code/AssetLib/3DS/3DSHelper.h index e8efbf949..933795f65 100644 --- a/code/AssetLib/3DS/3DSHelper.h +++ b/code/AssetLib/3DS/3DSHelper.h @@ -53,7 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include //sprintf +#include //sprintf namespace Assimp { namespace D3DS { diff --git a/code/AssetLib/3DS/3DSLoader.h b/code/AssetLib/3DS/3DSLoader.h index 04dcac237..bd167872f 100644 --- a/code/AssetLib/3DS/3DSLoader.h +++ b/code/AssetLib/3DS/3DSLoader.h @@ -46,11 +46,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef AI_3DSIMPORTER_H_INC #define AI_3DSIMPORTER_H_INC +#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER #include #include -#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER #include "3DSHelper.h" #include @@ -75,14 +75,14 @@ public: * See BaseImporter::CanRead() for details. */ bool CanRead( const std::string& pFile, IOSystem* pIOHandler, - bool checkSig) const; + bool checkSig) const override; // ------------------------------------------------------------------- /** Called prior to ReadFile(). * The function is a request to the importer to update its configuration * basing on the Importer's configuration property list. */ - void SetupProperties(const Importer* pImp); + void SetupProperties(const Importer* pImp) override; protected: @@ -90,14 +90,14 @@ protected: /** Return importer meta information. * See #BaseImporter::GetInfo for the details */ - const aiImporterDesc* GetInfo () const; + const aiImporterDesc* GetInfo () const override; // ------------------------------------------------------------------- /** Imports the given file into the given scene structure. * See BaseImporter::InternReadFile() for details */ void InternReadFile( const std::string& pFile, aiScene* pScene, - IOSystem* pIOHandler); + IOSystem* pIOHandler) override; // ------------------------------------------------------------------- /** Converts a temporary material to the outer representation diff --git a/code/AssetLib/3MF/D3MFExporter.h b/code/AssetLib/3MF/D3MFExporter.h index d2fc82483..3da74fb37 100644 --- a/code/AssetLib/3MF/D3MFExporter.h +++ b/code/AssetLib/3MF/D3MFExporter.h @@ -40,6 +40,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER + #include #include #include @@ -58,8 +61,6 @@ class IOStream; namespace D3MF { -#ifndef ASSIMP_BUILD_NO_EXPORT -#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER struct OpcPackageRelationship; diff --git a/code/AssetLib/3MF/D3MFImporter.h b/code/AssetLib/3MF/D3MFImporter.h index 2b37010d9..7219fa39d 100644 --- a/code/AssetLib/3MF/D3MFImporter.h +++ b/code/AssetLib/3MF/D3MFImporter.h @@ -42,6 +42,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef AI_D3MFLOADER_H_INCLUDED #define AI_D3MFLOADER_H_INCLUDED +#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER + #include namespace Assimp { @@ -84,4 +86,6 @@ protected: } // Namespace Assimp +#endif // #ifndef ASSIMP_BUILD_NO_3MF_IMPORTER + #endif // AI_D3MFLOADER_H_INCLUDED diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index c29cec368..33428b124 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -51,11 +51,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include "3MFXmlTags.h" + #include #include #include #include -#include #include namespace Assimp { diff --git a/code/AssetLib/3MF/D3MFOpcPackage.h b/code/AssetLib/3MF/D3MFOpcPackage.h index fda74a879..27bf44ff2 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.h +++ b/code/AssetLib/3MF/D3MFOpcPackage.h @@ -42,9 +42,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef D3MFOPCPACKAGE_H #define D3MFOPCPACKAGE_H +#include #include #include -#include struct aiTexture; diff --git a/code/AssetLib/AC/ACLoader.h b/code/AssetLib/AC/ACLoader.h index 5ae2e80bb..5b706a4a0 100644 --- a/code/AssetLib/AC/ACLoader.h +++ b/code/AssetLib/AC/ACLoader.h @@ -63,7 +63,7 @@ namespace Assimp { class AC3DImporter : public BaseImporter { public: AC3DImporter(); - ~AC3DImporter(); + ~AC3DImporter() override; // Represents an AC3D material struct Material { @@ -185,25 +185,25 @@ public: * See BaseImporter::CanRead() for details. */ bool CanRead(const std::string &pFile, IOSystem *pIOHandler, - bool checkSig) const; + bool checkSig) const override; protected: // ------------------------------------------------------------------- /** Return importer meta information. * See #BaseImporter::GetInfo for the details */ - const aiImporterDesc *GetInfo() const; + const aiImporterDesc *GetInfo() const override; // ------------------------------------------------------------------- /** Imports the given file into the given scene structure. * See BaseImporter::InternReadFile() for details*/ void InternReadFile(const std::string &pFile, aiScene *pScene, - IOSystem *pIOHandler); + IOSystem *pIOHandler) override; // ------------------------------------------------------------------- /** Called prior to ReadFile(). * The function is a request to the importer to update its configuration * basing on the Importer's configuration property list.*/ - void SetupProperties(const Importer *pImp); + void SetupProperties(const Importer *pImp) override; private: // ------------------------------------------------------------------- diff --git a/code/AssetLib/AMF/AMFImporter.hpp b/code/AssetLib/AMF/AMFImporter.hpp index 18ef83c24..a71b6671e 100644 --- a/code/AssetLib/AMF/AMFImporter.hpp +++ b/code/AssetLib/AMF/AMFImporter.hpp @@ -267,7 +267,7 @@ public: AMFImporter() AI_NO_EXCEPT; /// Default destructor. - ~AMFImporter(); + ~AMFImporter() override; /// Parse AMF file and fill scene graph. The function has no return value. Result can be found by analyzing the generated graph. /// Also exception can be thrown if trouble will found. @@ -276,9 +276,9 @@ public: void ParseFile(const std::string &pFile, IOSystem *pIOHandler); void ParseHelper_Node_Enter(AMFNodeElementBase *child); void ParseHelper_Node_Exit(); - bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const; - void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); - const aiImporterDesc *GetInfo() const; + bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const override; + void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override; + const aiImporterDesc *GetInfo() const override; bool Find_NodeElement(const std::string &pID, const AMFNodeElementBase::EType pType, AMFNodeElementBase **pNodeElement) const; bool Find_ConvertedNode(const std::string &pID, NodeArray &nodeArray, aiNode **pNode) const; bool Find_ConvertedMaterial(const std::string &pID, const SPP_Material **pConvertedMaterial) const; diff --git a/code/AssetLib/AMF/AMFImporter_Node.hpp b/code/AssetLib/AMF/AMFImporter_Node.hpp index 9051b2871..b466e339b 100644 --- a/code/AssetLib/AMF/AMFImporter_Node.hpp +++ b/code/AssetLib/AMF/AMFImporter_Node.hpp @@ -48,15 +48,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef INCLUDED_AI_AMF_IMPORTER_NODE_H #define INCLUDED_AI_AMF_IMPORTER_NODE_H -// Header files, stdlib. +// Header files, Assimp. +#include +#include + #include #include #include -// Header files, Assimp. -#include "assimp/scene.h" -#include "assimp/types.h" - /// \class CAMFImporter_NodeElement /// Base class for elements of nodes. class AMFNodeElementBase { diff --git a/code/AssetLib/ASE/ASELoader.cpp b/code/AssetLib/ASE/ASELoader.cpp index 1a42c2f52..a9c7314c1 100644 --- a/code/AssetLib/ASE/ASELoader.cpp +++ b/code/AssetLib/ASE/ASELoader.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, diff --git a/code/AssetLib/ASE/ASELoader.h b/code/AssetLib/ASE/ASELoader.h index 1f30d28e7..9ec9d439f 100644 --- a/code/AssetLib/ASE/ASELoader.h +++ b/code/AssetLib/ASE/ASELoader.h @@ -62,42 +62,37 @@ namespace Assimp { class ASEImporter : public BaseImporter { public: ASEImporter(); - ~ASEImporter(); + ~ASEImporter() override; // ------------------------------------------------------------------- /** Returns whether the class can handle the format of the given file. * See BaseImporter::CanRead() for details. */ bool CanRead( const std::string& pFile, IOSystem* pIOHandler, - bool checkSig) const; + bool checkSig) const override; protected: - // ------------------------------------------------------------------- /** Return importer meta information. * See #BaseImporter::GetInfo for the details */ - const aiImporterDesc* GetInfo () const; - + const aiImporterDesc* GetInfo () const override; // ------------------------------------------------------------------- /** Imports the given file into the given scene structure. * See BaseImporter::InternReadFile() for details */ void InternReadFile( const std::string& pFile, aiScene* pScene, - IOSystem* pIOHandler); - + IOSystem* pIOHandler) override; // ------------------------------------------------------------------- /** Called prior to ReadFile(). * The function is a request to the importer to update its configuration * basing on the Importer's configuration property list. */ - void SetupProperties(const Importer* pImp); - + void SetupProperties(const Importer* pImp) override; private: - // ------------------------------------------------------------------- /** Generate normal vectors basing on smoothing groups * (in some cases the normal are already contained in the file) @@ -106,7 +101,6 @@ private: */ bool GenerateNormals(ASE::Mesh& mesh); - // ------------------------------------------------------------------- /** Create valid vertex/normal/UV/color/face lists. * All elements are unique, faces have only one set of indices @@ -115,51 +109,43 @@ private: */ void BuildUniqueRepresentation(ASE::Mesh& mesh); - /** Create one-material-per-mesh meshes ;-) * \param mesh Mesh to work with * \param Receives the list of all created meshes */ void ConvertMeshes(ASE::Mesh& mesh, std::vector& avOut); - // ------------------------------------------------------------------- /** Convert a material to a aiMaterial object * \param mat Input material */ void ConvertMaterial(ASE::Material& mat); - // ------------------------------------------------------------------- /** Setup the final material indices for each mesh */ void BuildMaterialIndices(); - // ------------------------------------------------------------------- /** Build the node graph */ void BuildNodes(std::vector& nodes); - // ------------------------------------------------------------------- /** Build output cameras */ void BuildCameras(); - // ------------------------------------------------------------------- /** Build output lights */ void BuildLights(); - // ------------------------------------------------------------------- /** Build output animations */ void BuildAnimations(const std::vector& nodes); - // ------------------------------------------------------------------- /** Add sub nodes to a node * \param pcParent parent node to be filled @@ -183,7 +169,6 @@ private: void GenerateDefaultMaterial(); protected: - /** Parser instance */ ASE::Parser* mParser; diff --git a/code/AssetLib/ASE/ASEParser.cpp b/code/AssetLib/ASE/ASEParser.cpp index 1f7018b11..a2884b8dd 100644 --- a/code/AssetLib/ASE/ASEParser.cpp +++ b/code/AssetLib/ASE/ASEParser.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, diff --git a/code/AssetLib/Assbin/AssbinExporter.h b/code/AssetLib/Assbin/AssbinExporter.h index 96dfdd1c2..4871b5021 100644 --- a/code/AssetLib/Assbin/AssbinExporter.h +++ b/code/AssetLib/Assbin/AssbinExporter.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -43,6 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file AssbinExporter.h * ASSBIN Exporter Main Header */ +#pragma once #ifndef AI_ASSBINEXPORTER_H_INC #define AI_ASSBINEXPORTER_H_INC diff --git a/code/AssetLib/Assbin/AssbinLoader.h b/code/AssetLib/Assbin/AssbinLoader.h index 59d799a3d..47f38db2a 100644 --- a/code/AssetLib/Assbin/AssbinLoader.h +++ b/code/AssetLib/Assbin/AssbinLoader.h @@ -5,7 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -74,17 +73,11 @@ private: bool compressed; public: - virtual bool CanRead( - const std::string& pFile, - IOSystem* pIOHandler, - bool checkSig - ) const; - virtual const aiImporterDesc* GetInfo() const; - virtual void InternReadFile( - const std::string& pFile, - aiScene* pScene, - IOSystem* pIOHandler - ); + bool CanRead(const std::string& pFile, + IOSystem* pIOHandler, bool checkSig) const override; + const aiImporterDesc* GetInfo() const override; + void InternReadFile( + const std::string& pFile,aiScene* pScene,IOSystem* pIOHandler) override; void ReadHeader(); void ReadBinaryScene( IOStream * stream, aiScene* pScene ); void ReadBinaryNode( IOStream * stream, aiNode** mRootNode, aiNode* parent ); diff --git a/code/AssetLib/Assjson/mesh_splitter.cpp b/code/AssetLib/Assjson/mesh_splitter.cpp index 9301cc27e..978437c41 100644 --- a/code/AssetLib/Assjson/mesh_splitter.cpp +++ b/code/AssetLib/Assjson/mesh_splitter.cpp @@ -69,13 +69,12 @@ void MeshSplitter::UpdateNode(aiNode* pcNode, const std::vectormNumChildren; i < end;++i) { UpdateNode ( pcNode->mChildren[i], source_mesh_map ); } - return; } -#define WAS_NOT_COPIED 0xffffffff +static const unsigned int WAS_NOT_COPIED = 0xffffffff; -typedef std::pair PerVertexWeight; -typedef std::vector VertexWeightTable; +using PerVertexWeight = std::pair ; +using VertexWeightTable = std::vector ; // ------------------------------------------------------------------------------------------------ VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) { @@ -89,7 +88,7 @@ VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) { aiBone* bone = pMesh->mBones[i]; for (unsigned int a = 0; a < bone->mNumWeights;++a) { const aiVertexWeight& weight = bone->mWeights[a]; - avPerVertexWeights[weight.mVertexId].push_back( std::make_pair(i,weight.mWeight) ); + avPerVertexWeights[weight.mVertexId].emplace_back(i,weight.mWeight); } } return avPerVertexWeights; @@ -100,7 +99,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormNumVertices <= LIMIT) { - source_mesh_map.push_back(std::make_pair(in_mesh,a)); + source_mesh_map.emplace_back(in_mesh,a); return; } @@ -187,7 +186,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vector> &source_mesh_map); + void SplitMesh(unsigned int index, aiMesh *mesh, std::vector> &source_mesh_map); - void UpdateNode(aiNode* pcNode, const std::vector >& source_mesh_map); - void SplitMesh (unsigned int index, aiMesh* mesh, std::vector >& source_mesh_map); - -public: - - unsigned int LIMIT; }; #endif // INCLUDED_MESH_SPLITTER - diff --git a/code/AssetLib/Assxml/AssxmlExporter.h b/code/AssetLib/Assxml/AssxmlExporter.h index 5c7f155d1..9a3d1f9d5 100644 --- a/code/AssetLib/Assxml/AssxmlExporter.h +++ b/code/AssetLib/Assxml/AssxmlExporter.h @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file AssxmlExporter.h * ASSXML Exporter Main Header */ +#pragma once #ifndef AI_ASSXMLEXPORTER_H_INC #define AI_ASSXMLEXPORTER_H_INC diff --git a/code/AssetLib/Assxml/AssxmlFileWriter.h b/code/AssetLib/Assxml/AssxmlFileWriter.h index e5f27095e..125869d1a 100644 --- a/code/AssetLib/Assxml/AssxmlFileWriter.h +++ b/code/AssetLib/Assxml/AssxmlFileWriter.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -43,7 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file AssxmlFileWriter.h * @brief Declaration of Assxml file writer. */ - +#pragma once #ifndef AI_ASSXMLFILEWRITER_H_INC #define AI_ASSXMLFILEWRITER_H_INC diff --git a/code/AssetLib/B3D/B3DImporter.h b/code/AssetLib/B3D/B3DImporter.h index a7ed65c3b..bb535afd1 100644 --- a/code/AssetLib/B3D/B3DImporter.h +++ b/code/AssetLib/B3D/B3DImporter.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -40,8 +39,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/** @file Definition of the .b3d importer class. */ - +/** + * @file Definition of the .b3d importer class. + */ +#pragma once #ifndef AI_B3DIMPORTER_H_INC #define AI_B3DIMPORTER_H_INC @@ -62,14 +63,12 @@ namespace Assimp{ class B3DImporter : public BaseImporter{ public: B3DImporter() = default; - virtual ~B3DImporter(); - - virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; + ~B3DImporter() override; + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override; protected: - - virtual const aiImporterDesc* GetInfo () const; - virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + const aiImporterDesc* GetInfo () const override; + void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override; private: @@ -113,7 +112,6 @@ private: void ReadBB3D( aiScene *scene ); size_t _pos; -// unsigned _size; std::vector _buf; std::vector _stack; diff --git a/code/AssetLib/Blender/BlenderLoader.h b/code/AssetLib/Blender/BlenderLoader.h index 42da76514..1df0fcf90 100644 --- a/code/AssetLib/Blender/BlenderLoader.h +++ b/code/AssetLib/Blender/BlenderLoader.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -43,6 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file BlenderLoader.h * @brief Declaration of the Blender 3D (*.blend) importer class. */ +#pragma once #ifndef INCLUDED_AI_BLEND_LOADER_H #define INCLUDED_AI_BLEND_LOADER_H @@ -56,149 +56,139 @@ struct aiLight; struct aiCamera; struct aiMaterial; -namespace Assimp { +namespace Assimp { - // TinyFormatter.h - namespace Formatter { - template class basic_formatter; - typedef class basic_formatter< char, std::char_traits, std::allocator > format; - } +// TinyFormatter.h +namespace Formatter { - // BlenderDNA.h - namespace Blender { - class FileDatabase; - struct ElemBase; - } +template +class basic_formatter; - // BlenderScene.h - namespace Blender { - struct Scene; - struct Object; - struct Mesh; - struct Camera; - struct Lamp; - struct MTex; - struct Image; - struct Material; - } +typedef class basic_formatter, std::allocator> format; - // BlenderIntermediate.h - namespace Blender { - struct ConversionData; - template

    Ao$|V)IS!XrjDofR+cp% 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