From 8126dce94dc12f504964e74047386372c3f07ecc Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 27 Aug 2014 23:00:19 +0200 Subject: [PATCH 01/74] update: add sceleton for OpenGEX importer. Signed-off-by: Kim Kulling --- code/CMakeLists.txt | 9 +++- code/ImporterRegistry.cpp | 6 +++ code/OgreImporter.h | 100 +++++++++++++++++++------------------- code/OpenGEXImporter.cpp | 96 ++++++++++++++++++++++++++++++++++++ code/OpenGEXImporter.h | 80 ++++++++++++++++++++++++++++++ 5 files changed, 239 insertions(+), 52 deletions(-) create mode 100644 code/OpenGEXImporter.cpp create mode 100644 code/OpenGEXImporter.h diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 224ae7f57..527e72511 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -251,8 +251,6 @@ SET( LWS_SRCS ) SOURCE_GROUP( LWS FILES ${LWS_SRCS}) - - SET( MD2_SRCS MD2FileData.h MD2Loader.cpp @@ -346,6 +344,12 @@ SET( Ogre_SRCS ) SOURCE_GROUP( Ogre FILES ${Ogre_SRCS}) +SET( OpenGEX_SRCS + OpenGEXImporter.cpp + OpenGEXImporter.h +) +SOURCE_GROUP( OpenGEX FILES ${OpenGEX_SRCS}) + SET( Ply_SRCS PlyLoader.cpp PlyLoader.h @@ -675,6 +679,7 @@ SET( assimp_src ${OFFFormat_SRCS} ${Obj_SRCS} ${Ogre_SRCS} + ${OpenGEX_SRCS} ${Ply_SRCS} ${Q3D_SRCS} ${Q3BSP_SRCS} diff --git a/code/ImporterRegistry.cpp b/code/ImporterRegistry.cpp index dff195854..ec804d141 100644 --- a/code/ImporterRegistry.cpp +++ b/code/ImporterRegistry.cpp @@ -142,6 +142,9 @@ corresponding preprocessor flag to selectively disable formats. #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER # include "OgreImporter.h" #endif +#ifndef ASSIMP_BUILD_NO_OPEMGEX_IMPORTER +# include "OpenGEXImporter.h" +#endif #ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER # include "MS3DLoader.h" #endif @@ -270,6 +273,9 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out) #if (!defined ASSIMP_BUILD_NO_OGRE_IMPORTER) out.push_back( new Ogre::OgreImporter()); #endif +#if (!defined ASSIMP_BUILD_NO_OPEMGEX_IMPORTER ) + out.push_back( new OpenGEX::OpenGEXImporter() ); +#endif #if (!defined ASSIMP_BUILD_NO_MS3D_IMPORTER) out.push_back( new MS3DImporter()); #endif diff --git a/code/OgreImporter.h b/code/OgreImporter.h index 892696407..1185eeeb0 100644 --- a/code/OgreImporter.h +++ b/code/OgreImporter.h @@ -36,63 +36,63 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- -*/ - +*/ + #ifndef AI_OGREIMPORTER_H_INC #define AI_OGREIMPORTER_H_INC #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER - -#include "BaseImporter.h" - -#include "OgreStructs.h" -#include "OgreParsingUtils.h" - -namespace Assimp -{ -namespace Ogre -{ - + +#include "BaseImporter.h" + +#include "OgreStructs.h" +#include "OgreParsingUtils.h" + +namespace Assimp +{ +namespace Ogre +{ + /** Importer for Ogre mesh, skeleton and material formats. - @todo Support vertex colors. - @todo Support poses/animations from the mesh file. - Currently only skeleton file animations are supported. */ -class OgreImporter : public BaseImporter -{ -public: - /// BaseImporter override. - virtual bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const; - - /// BaseImporter override. + @todo Support vertex colors. + @todo Support poses/animations from the mesh file. + Currently only skeleton file animations are supported. */ +class OgreImporter : public BaseImporter +{ +public: + /// BaseImporter override. + virtual bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const; + + /// BaseImporter override. virtual void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); /// BaseImporter override. - virtual const aiImporterDesc *GetInfo() const; - - /// BaseImporter override. - virtual void SetupProperties(const Importer *pImp); - -private: - /// Read materials referenced by the @c mesh to @c pScene. - void ReadMaterials(const std::string &pFile, Assimp::IOSystem *pIOHandler, aiScene *pScene, Mesh *mesh); - void ReadMaterials(const std::string &pFile, Assimp::IOSystem *pIOHandler, aiScene *pScene, MeshXml *mesh); - void AssignMaterials(aiScene *pScene, std::vector &materials); - - /// Reads material - aiMaterial* ReadMaterial(const std::string &pFile, Assimp::IOSystem *pIOHandler, const std::string MaterialName); - - // These functions parse blocks from a material file from @c ss. Starting parsing from "{" and ending it to "}". - bool ReadTechnique(const std::string &techniqueName, std::stringstream &ss, aiMaterial *material); - bool ReadPass(const std::string &passName, std::stringstream &ss, aiMaterial *material); - bool ReadTextureUnit(const std::string &textureUnitName, std::stringstream &ss, aiMaterial *material); - - std::string m_userDefinedMaterialLibFile; - bool m_detectTextureTypeFromFilename; - - std::map m_textures; -}; -} // Ogre -} // Assimp - + virtual const aiImporterDesc *GetInfo() const; + + /// BaseImporter override. + virtual void SetupProperties(const Importer *pImp); + +private: + /// Read materials referenced by the @c mesh to @c pScene. + void ReadMaterials(const std::string &pFile, Assimp::IOSystem *pIOHandler, aiScene *pScene, Mesh *mesh); + void ReadMaterials(const std::string &pFile, Assimp::IOSystem *pIOHandler, aiScene *pScene, MeshXml *mesh); + void AssignMaterials(aiScene *pScene, std::vector &materials); + + /// Reads material + aiMaterial* ReadMaterial(const std::string &pFile, Assimp::IOSystem *pIOHandler, const std::string MaterialName); + + // These functions parse blocks from a material file from @c ss. Starting parsing from "{" and ending it to "}". + bool ReadTechnique(const std::string &techniqueName, std::stringstream &ss, aiMaterial *material); + bool ReadPass(const std::string &passName, std::stringstream &ss, aiMaterial *material); + bool ReadTextureUnit(const std::string &textureUnitName, std::stringstream &ss, aiMaterial *material); + + std::string m_userDefinedMaterialLibFile; + bool m_detectTextureTypeFromFilename; + + std::map m_textures; +}; +} // Ogre +} // Assimp + #endif // ASSIMP_BUILD_NO_OGRE_IMPORTER #endif // AI_OGREIMPORTER_H_INC diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp new file mode 100644 index 000000000..361ef4c4e --- /dev/null +++ b/code/OpenGEXImporter.cpp @@ -0,0 +1,96 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2014, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef ASSIMP_BUILD_NO_OPEMGEX_IMPORTER + +#include "AssimpPCH.h" +#include "OpenGEXImporter.h" + +static const aiImporterDesc desc = { + "Open Game Engine Exchange", + "", + "", + "", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "ogex" +}; + +namespace Assimp { +namespace OpenGEX { + +//------------------------------------------------------------------------------------------------ +OpenGEXImporter::OpenGEXImporter() { + +} + +//------------------------------------------------------------------------------------------------ +OpenGEXImporter::~OpenGEXImporter() { + +} + +//------------------------------------------------------------------------------------------------ +bool OpenGEXImporter::CanRead( const std::string &file, IOSystem *pIOHandler, bool checkSig ) const { + return false; +} + +//------------------------------------------------------------------------------------------------ +void OpenGEXImporter::InternReadFile( const std::string &file, aiScene *pScene, IOSystem *pIOHandler ) { + +} + +//------------------------------------------------------------------------------------------------ +const aiImporterDesc *OpenGEXImporter::GetInfo() const { + return &desc; +} + +//------------------------------------------------------------------------------------------------ +void OpenGEXImporter::SetupProperties( const Importer *pImp ) { + +} + +//------------------------------------------------------------------------------------------------ + +} // Namespace OpenGEX +} // Namespace Assimp + +#endif // ASSIMP_BUILD_NO_OPEMGEX_IMPORTER diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h new file mode 100644 index 000000000..3cf55ff01 --- /dev/null +++ b/code/OpenGEXImporter.h @@ -0,0 +1,80 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef AI_OPENGEX_IMPORTER_H +#define AI_OPENGEX_IMPORTER_H + +#ifndef ASSIMP_BUILD_NO_OPEMGEX_IMPORTER + +#include "BaseImporter.h" + +namespace Assimp { +namespace OpenGEX { + +/** @brief This class is used to implement the OpenGEX importer + * + * See http://opengex.org/OpenGEX.pdf for spec. + */ +class OpenGEXImporter : public BaseImporter { +public: + /// The class constructor. + OpenGEXImporter(); + + /// The class destructor. + virtual ~OpenGEXImporter(); + + /// BaseImporter override. + virtual bool CanRead( const std::string &file, IOSystem *pIOHandler, bool checkSig ) const; + + /// BaseImporter override. + virtual void InternReadFile( const std::string &file, aiScene *pScene, IOSystem *pIOHandler ); + + /// BaseImporter override. + virtual const aiImporterDesc *GetInfo() const; + + /// BaseImporter override. + virtual void SetupProperties( const Importer *pImp ); +}; + +} // Namespace OpenGEX +} // Namespace Assimp + +#endif // ASSIMP_BUILD_NO_OPEMGEX_IMPORTER + +#endif // AI_OPENGEX_IMPORTER_H From 7f45f5fc954322110862d1a720cafa8dfa61df23 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 27 Aug 2014 23:01:35 +0200 Subject: [PATCH 02/74] bugfix: fix license. Signed-off-by: Kim Kulling --- code/OpenGEXImporter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index 3cf55ff01..f658c9fa1 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -2,7 +2,7 @@ Open Asset Import Library (assimp) ---------------------------------------------------------------------- -Copyright (c) 2006-2012, assimp team +Copyright (c) 2006-2014, assimp team All rights reserved. Redistribution and use of this software in source and binary forms, From ed9a466c30968aeb6ec6244ebfee2d073fc4e3c6 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 4 Sep 2014 19:41:14 +0200 Subject: [PATCH 03/74] update: - add OpenGEX parser sceleton - add base datatypes for OpenGEX support. --- code/CMakeLists.txt | 3 + code/OpenGEXParser.cpp | 59 +++++++ code/OpenGEXParser.h | 66 ++++++++ code/OpenGEXStructs.h | 262 +++++++++++++++++++++++++++++++ test/models/OpenGEX/Example.ogex | 95 +++++++++++ 5 files changed, 485 insertions(+) create mode 100644 code/OpenGEXParser.cpp create mode 100644 code/OpenGEXParser.h create mode 100644 code/OpenGEXStructs.h create mode 100644 test/models/OpenGEX/Example.ogex diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 527e72511..aad2c04a7 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -347,6 +347,9 @@ SOURCE_GROUP( Ogre FILES ${Ogre_SRCS}) SET( OpenGEX_SRCS OpenGEXImporter.cpp OpenGEXImporter.h + OpenGEXParser.cpp + OpenGEXParser.h + OpenGEXStructs.h ) SOURCE_GROUP( OpenGEX FILES ${OpenGEX_SRCS}) diff --git a/code/OpenGEXParser.cpp b/code/OpenGEXParser.cpp new file mode 100644 index 000000000..96b19f048 --- /dev/null +++ b/code/OpenGEXParser.cpp @@ -0,0 +1,59 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2014, 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 "AssimpPCH.h" +#ifndef ASSIMP_BUILD_NO_OPEMGEX_IMPORTER + +#include "OpenGEXParser.h" + +namespace Assimp { +namespace OpenGEX { + +OpenGEXParser::OpenGEXParser() { + +} + +OpenGEXParser::~OpenGEXParser() { + +} + +} // Namespace openGEX +} // Namespace Assimp + +#endif ASSIMP_BUILD_NO_OPEMGEX_IMPORTER \ No newline at end of file diff --git a/code/OpenGEXParser.h b/code/OpenGEXParser.h new file mode 100644 index 000000000..34b48e561 --- /dev/null +++ b/code/OpenGEXParser.h @@ -0,0 +1,66 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2014, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef ASSIMP_OPENGEX_OPENGEXPARSER_H_INC +#define ASSIMP_OPENGEX_OPENGEXPARSER_H_INC + +#ifndef ASSIMP_BUILD_NO_OPEMGEX_IMPORTER + +namespace Assimp { +namespace OpenGEX { + +class OpenGEXParser { +public: + OpenGEXParser(); + ~OpenGEXParser(); + +private: + OpenGEXParser( const OpenGEXParser & ); + OpenGEXParser &operator = ( const OpenGEXParser & ); + +private: + +}; + +} // Namespace openGEX +} // Namespace Assimp + +#endif // ASSIMP_BUILD_NO_OPEMGEX_IMPORTER + +#endif // ASSIMP_OPENGEX_OPENGEXPARSER_H_INC diff --git a/code/OpenGEXStructs.h b/code/OpenGEXStructs.h new file mode 100644 index 000000000..5a0899710 --- /dev/null +++ b/code/OpenGEXStructs.h @@ -0,0 +1,262 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2014, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef AI_OPENGEXSTRUCTS_H_INC +#define AI_OPENGEXSTRUCTS_H_INC + +#include +#include + +namespace Assimp { +namespace OpenGEX { + +struct Skin; +struct Object; +struct LightObject; +struct CameraObject; +struct Material; +struct BoneNode; +struct BoneCountArray; +struct BoneIndexArray; +struct BoneWeightArray; + +struct Metric { + std::string metricKey; +}; + +struct VertexArray { + std::string arrayAttrib; + unsigned int morphIndex; +}; + +struct IndexArray { + unsigned int materialIndex; + unsigned int restartIndex; + std::string frontFace; +}; + +struct Mesh { + unsigned int meshLevel; + std::string meshPrimitive; + Skin *skinStructure; +}; + +struct Node { + std::string nodeName; +}; + +struct GeometryNode { + bool visibleFlag[ 2 ]; + bool shadowFlag[ 2 ]; + bool motionBlurFlag[ 2 ]; +}; + +struct LightNode { + bool shadowFlag[ 2 ]; + const LightObject *lightObjectStructure; +}; + +struct CameraNode { + const CameraObject *cameraObjectStructure; +}; + +struct GeometryObject { + Object *object; + bool visibleFlag; + bool shadowFlag; + bool motionBlurFlag; + std::map meshMap; +}; + +struct LightObject { + Object *object; + std::string typeString; + bool shadowFlag; +}; + + +struct CameraObject { + float focalLength; + float nearDepth; + float farDepth; +}; + +struct Matrix { + bool objectFlag; +}; + +struct Transform { + Matrix *matrix; + int transformCount; + const float *transformArray; +}; + +struct Translation { + std::string translationKind; +}; + +struct Rotation { + std::string rotationKind; +}; + +struct Scale { + std::string scaleKind; +}; + +struct Name { + std::string name; +}; + + +struct ObjectRef { + Object *targetStructure; +}; + +struct MaterialRef { + unsigned int materialIndex; + const Material *targetStructure; +}; + +struct BoneRefArray { + int boneCount; + const BoneNode **boneNodeArray; +}; + +struct BoneCount { + int vertexCount; + const unsigned short *boneCountArray; + unsigned short *arrayStorage; +}; + +struct BoneIndex { + int boneIndexCount; + const unsigned short *boneIndexArray; + unsigned short *arrayStorage; +}; + + +struct BoneWeight { + int boneWeightCount; + const float *boneWeightArray; +}; + +struct Skeleton { + const BoneRefArray *boneRefArrayStructure; + const Transform *transformStructure; +}; + +struct Skin { + const Skeleton *skeletonStructure; + const BoneCountArray *boneCountArrayStructure; + const BoneIndexArray *boneIndexArrayStructure; + const BoneWeightArray *boneWeightArrayStructure; +}; + +struct Material { + bool twoSidedFlag; + const char *materialName; +}; + +struct Attrib { + std::string attribString; +}; + +struct Param { + float param; +}; + +struct Color { + float color[ 4 ]; +}; + +struct Texture { + std::string textureName; + unsigned int texcoordIndex; +}; + +struct Atten { + std::string attenKind; + std::string curveType; + + float beginParam; + float endParam; + + float scaleParam; + float offsetParam; + + float constantParam; + float linearParam; + float quadraticParam; + + float powerParam; +}; + +struct Key { + std::string keyKind; + bool scalarFlag; +}; + +struct Curve { + std::string curveType; + const Key *keyValueStructure; + const Key *keyControlStructure[ 2 ]; + const Key *keyTensionStructure; + const Key *keyContinuityStructure; + const Key *keyBiasStructure; +}; + +struct Animation { + int clipIndex; + bool beginFlag; + bool endFlag; + float beginTime; + float endTime; +}; + +struct OpenGexDataDescription { + float distanceScale; + float angleScale; + float timeScale; + int upDirection; +}; + +} // Namespace OpenGEX +} // Namespace Assimp + +#endif // AI_OPENGEXSTRUCTS_H_INC diff --git a/test/models/OpenGEX/Example.ogex b/test/models/OpenGEX/Example.ogex new file mode 100644 index 000000000..8aa1f9f4f --- /dev/null +++ b/test/models/OpenGEX/Example.ogex @@ -0,0 +1,95 @@ +Metric (key = "distance") {float {1}} +Metric (key = "angle") {float {1}} +Metric (key = "time") {float {1}} +Metric (key = "up") {string {"z"}} + +GeometryNode $node1 +{ + Name {string {"Box001"}} + ObjectRef {ref {$geometry1}} + MaterialRef {ref {$material1}} + + Transform + { + float[16] + { + {0x3F800000, 0x00000000, 0x00000000, 0x00000000, // {1, 0, 0, 0 + 0x00000000, 0x3F800000, 0x00000000, 0x00000000, // 0, 1, 0, 0 + 0x00000000, 0x00000000, 0x3F800000, 0x00000000, // 0, 0, 1, 0 + 0xBEF33B00, 0x411804DE, 0x00000000, 0x3F800000} // -0.47506, 9.50119, 0, 1} + } + } +} + +GeometryNode $node2 +{ + Name {string {"Box002"}} + ObjectRef {ref {$geometry1}} + MaterialRef {ref {$material1}} + + Transform + { + float[16] + { + {0x3F800000, 0x00000000, 0x00000000, 0x00000000, // {1, 0, 0, 0 + 0x00000000, 0x3F800000, 0x00000000, 0x00000000, // 0, 1, 0, 0 + 0x00000000, 0x00000000, 0x3F800000, 0x00000000, // 0, 0, 1, 0 + 0x43041438, 0x411804DE, 0x00000000, 0x3F800000} // 132.079, 9.50119, 0, 1} + } + } +} + +GeometryObject $geometry1 // Box001, Box002 +{ + Mesh (primitive = "triangles") + { + VertexArray (attrib = "position") + { + float[3] // 24 + { + {0xC2501375, 0xC24C468A, 0x00000000}, {0xC2501375, 0x424C468A, 0x00000000}, {0x42501375, 0x424C468A, 0x00000000}, {0x42501375, 0xC24C468A, 0x00000000}, {0xC2501375, 0xC24C468A, 0x42BA3928}, {0x42501375, 0xC24C468A, 0x42BA3928}, {0x42501375, 0x424C468A, 0x42BA3928}, {0xC2501375, 0x424C468A, 0x42BA3928}, + {0xC2501375, 0xC24C468A, 0x00000000}, {0x42501375, 0xC24C468A, 0x00000000}, {0x42501375, 0xC24C468A, 0x42BA3928}, {0xC2501375, 0xC24C468A, 0x42BA3928}, {0x42501375, 0xC24C468A, 0x00000000}, {0x42501375, 0x424C468A, 0x00000000}, {0x42501375, 0x424C468A, 0x42BA3928}, {0x42501375, 0xC24C468A, 0x42BA3928}, + {0x42501375, 0x424C468A, 0x00000000}, {0xC2501375, 0x424C468A, 0x00000000}, {0xC2501375, 0x424C468A, 0x42BA3928}, {0x42501375, 0x424C468A, 0x42BA3928}, {0xC2501375, 0x424C468A, 0x00000000}, {0xC2501375, 0xC24C468A, 0x00000000}, {0xC2501375, 0xC24C468A, 0x42BA3928}, {0xC2501375, 0x424C468A, 0x42BA3928} + } + } + + VertexArray (attrib = "normal") + { + float[3] // 24 + { + {0x00000000, 0x00000000, 0xBF800000}, {0x00000000, 0x00000000, 0xBF800000}, {0x00000000, 0x00000000, 0xBF800000}, {0x00000000, 0x00000000, 0xBF800000}, {0x00000000, 0x00000000, 0x3F800000}, {0x00000000, 0x00000000, 0x3F800000}, {0x00000000, 0x00000000, 0x3F800000}, {0x00000000, 0x00000000, 0x3F800000}, + {0x00000000, 0xBF800000, 0x00000000}, {0x00000000, 0xBF800000, 0x00000000}, {0x00000000, 0xBF800000, 0x00000000}, {0x80000000, 0xBF800000, 0x00000000}, {0x3F800000, 0x00000000, 0x00000000}, {0x3F800000, 0x00000000, 0x00000000}, {0x3F800000, 0x00000000, 0x00000000}, {0x3F800000, 0x00000000, 0x00000000}, + {0x00000000, 0x3F800000, 0x00000000}, {0x00000000, 0x3F800000, 0x00000000}, {0x00000000, 0x3F800000, 0x00000000}, {0x80000000, 0x3F800000, 0x00000000}, {0xBF800000, 0x00000000, 0x00000000}, {0xBF800000, 0x00000000, 0x00000000}, {0xBF800000, 0x00000000, 0x00000000}, {0xBF800000, 0x00000000, 0x00000000} + } + } + + VertexArray (attrib = "texcoord") + { + float[2] // 24 + { + {0x3F800000, 0x00000000}, {0x3F800000, 0x3F800000}, {0x00000000, 0x3F800000}, {0x00000000, 0x00000000}, {0x00000000, 0x00000000}, {0x3F800000, 0x00000000}, {0x3F800000, 0x3F800000}, {0x00000000, 0x3F800000}, + {0x00000000, 0x00000000}, {0x3F800000, 0x00000000}, {0x3F800000, 0x3F800000}, {0x00000000, 0x3F800000}, {0x00000000, 0x00000000}, {0x3F800000, 0x00000000}, {0x3F800000, 0x3F800000}, {0x00000000, 0x3F800000}, + {0x00000000, 0x00000000}, {0x3F800000, 0x00000000}, {0x3F800000, 0x3F800000}, {0x00000000, 0x3F800000}, {0x00000000, 0x00000000}, {0x3F800000, 0x00000000}, {0x3F800000, 0x3F800000}, {0x00000000, 0x3F800000} + } + } + + IndexArray + { + unsigned_int32[3] // 12 + { + {0, 1, 2}, {2, 3, 0}, {4, 5, 6}, {6, 7, 4}, {8, 9, 10}, {10, 11, 8}, {12, 13, 14}, {14, 15, 12}, {16, 17, 18}, {18, 19, 16}, {20, 21, 22}, {22, 23, 20} + } + } + } +} + +Material $material1 +{ + Name {string {"03 - Default"}} + + Color (attrib = "diffuse") {float[3] {{0.588235, 0.588235, 0.588235}}} + Texture (attrib = "diffuse") + { + string {"texture/Concrete.tga"} + } +} From c3b35f3933db2734fd3d0ab8dbb7323e656f4b0a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 6 Sep 2014 09:19:28 +0200 Subject: [PATCH 04/74] update: add endline. --- code/OpenGEXParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/OpenGEXParser.cpp b/code/OpenGEXParser.cpp index 96b19f048..dd7006e1e 100644 --- a/code/OpenGEXParser.cpp +++ b/code/OpenGEXParser.cpp @@ -56,4 +56,4 @@ OpenGEXParser::~OpenGEXParser() { } // Namespace openGEX } // Namespace Assimp -#endif ASSIMP_BUILD_NO_OPEMGEX_IMPORTER \ No newline at end of file +#endif ASSIMP_BUILD_NO_OPEMGEX_IMPORTER From 75d3c8e9f22d7be44c3e3811bcaf110d482d0d96 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 12 Sep 2014 12:00:03 +0200 Subject: [PATCH 05/74] update: metric parsing ongoing. Signed-off-by: Kim Kulling --- code/OpenGEXImporter.cpp | 27 ++++- code/OpenGEXImporter.h | 5 + code/OpenGEXParser.cpp | 223 ++++++++++++++++++++++++++++++++++++++- code/OpenGEXParser.h | 21 +++- code/OpenGEXStructs.h | 5 +- 5 files changed, 274 insertions(+), 7 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 361ef4c4e..f385cc11d 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -41,6 +41,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AssimpPCH.h" #include "OpenGEXImporter.h" +#include "OpenGEXParser.h" +#include "DefaultIOSystem.h" + +#include static const aiImporterDesc desc = { "Open Game Engine Exchange", @@ -70,12 +74,29 @@ OpenGEXImporter::~OpenGEXImporter() { //------------------------------------------------------------------------------------------------ bool OpenGEXImporter::CanRead( const std::string &file, IOSystem *pIOHandler, bool checkSig ) const { - return false; + bool canRead( false ); + if( !checkSig ) { + canRead = SimpleExtensionCheck( file, "ogex" ); + } else { + static const char *token[] = { "Metric", "GeometryNode", "VertexArray (attrib", "IndexArray" }; + canRead = BaseImporter::SearchFileHeaderForToken( pIOHandler, file, token, 4 ); + } + + return canRead; } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::InternReadFile( const std::string &file, aiScene *pScene, IOSystem *pIOHandler ) { +void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pScene, IOSystem *pIOHandler ) { + // open source file + IOStream *file = pIOHandler->Open( filename, "rb" ); + if( !file ) { + throw DeadlyImportError( "Failed to open file " + filename ); + } + std::vector buffer; + TextFileToBuffer( file, buffer ); + OpenGEXParser myParser( buffer ); + myParser.parse(); } //------------------------------------------------------------------------------------------------ @@ -85,7 +106,7 @@ const aiImporterDesc *OpenGEXImporter::GetInfo() const { //------------------------------------------------------------------------------------------------ void OpenGEXImporter::SetupProperties( const Importer *pImp ) { - + } //------------------------------------------------------------------------------------------------ diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index f658c9fa1..821692c25 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -70,6 +70,11 @@ public: /// BaseImporter override. virtual void SetupProperties( const Importer *pImp ); + +protected: + void ParseMetric(); + void ParseGeoObject(); + void ParseMaterial(); }; } // Namespace OpenGEX diff --git a/code/OpenGEXParser.cpp b/code/OpenGEXParser.cpp index dd7006e1e..7e73520ac 100644 --- a/code/OpenGEXParser.cpp +++ b/code/OpenGEXParser.cpp @@ -41,18 +41,239 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_OPEMGEX_IMPORTER #include "OpenGEXParser.h" +#include "OpenGEXStructs.h" +#include "ParsingUtils.h" +#include "fast_atof.h" + +#include namespace Assimp { namespace OpenGEX { -OpenGEXParser::OpenGEXParser() { +//------------------------------------------------------------------------------------------------ +static const std::string Metric = "Metric"; +static const std::string GeometryNode = "GeometryNode"; +static const std::string GeometryObject = "GeometryObject"; +static const std::string Material = "Material"; +static const size_t NumObjects = 4; + +static const std::string RootNodes[ NumObjects ] = { + Metric, + GeometryNode, + GeometryObject, + Material +}; + +static bool containsNode( const char *bufferPtr, size_t size, const std::string *nodes, size_t numNodes, + const std::string *tokenFound ) { + tokenFound = NULL; + if( 0 == numNodes ) { + return false; + } + + bool found( false ); + for( size_t i = 0; i < numNodes; ++i ) { + if( TokenMatch( bufferPtr, nodes[ i ].c_str(), nodes[ i ].size() ) ) { + tokenFound = &nodes[ i ]; + found = true; + break; + } + } + + return found; +} + +//------------------------------------------------------------------------------------------------ +OpenGEXParser::OpenGEXParser( const std::vector &buffer ) +: m_buffer( buffer ) +, m_index( 0 ) +, m_buffersize( buffer.size() ) { } +//------------------------------------------------------------------------------------------------ OpenGEXParser::~OpenGEXParser() { } +//------------------------------------------------------------------------------------------------ +void OpenGEXParser::parse() { + while( parseNextNode() ) { + + } +} + +//------------------------------------------------------------------------------------------------ +std::string OpenGEXParser::getNextToken() { + std::string token; + while( m_index < m_buffersize && IsSpace( m_buffer[ m_index ] ) ) { + ++m_index; + } + + while( m_index < m_buffersize && !IsSpace( m_buffer[ m_index ] ) ) { + token += m_buffer[ m_index ]; + m_index++; + } + + if( token == "//" ) { + skipComments(); + token = getNextToken(); + } + + return token; +} + +//------------------------------------------------------------------------------------------------ +bool OpenGEXParser::skipComments() { + bool skipped( false ); + if( strncmp( &m_buffer[ m_index ], "//", 2 ) == 0) { + while( !IsLineEnd( m_buffer[ m_index ] ) ) { + ++m_index; + } + skipped = true; + } + + return skipped; +} + +//------------------------------------------------------------------------------------------------ +bool OpenGEXParser::parseNextNode() { + std::string token( getNextToken() ); + std::string rootNodeName; + if( containsNode( token.c_str(), token.size(), RootNodes, NumObjects, &rootNodeName ) ) { + if( !getNodeHeader( rootNodeName ) ) { + return false; + } + + if( !getNodeData() ) { + return false; + } + } + + return true; +} + +//------------------------------------------------------------------------------------------------ +bool OpenGEXParser::getNodeHeader( const std::string &name ) { + if( name == Metric ) { + std::string token( getNextToken() ); + + } + + return false; +} + +//------------------------------------------------------------------------------------------------ +bool OpenGEXParser::getBracketOpen() { + const std::string token( getNextToken() ); + if( "{" == token ) { + return true; + } + + return false; +} + +//------------------------------------------------------------------------------------------------ +bool OpenGEXParser::getBracketClose() { + const std::string token( getNextToken() ); + if( "}" == token ) { + return true; + } + + return false; +} + +//------------------------------------------------------------------------------------------------ +bool OpenGEXParser::getStringData( std::string &data ) { + if( !getBracketOpen() ) { + return false; + } + + if( !getBracketClose() ) { + return false; + } + return false; +} + +//------------------------------------------------------------------------------------------------ +bool OpenGEXParser::getFloatData( size_t num, float *data ) { + ai_assert( nullptr != data ); + + if( !getBracketOpen() ) { + return false; + } + + bool ok( true ); + size_t dataIdx( 0 ); + for( unsigned int i = 0; i < num; ++i ) { + data[ dataIdx ] = fast_atof( &m_buffer[ m_index ] ); + ++dataIdx; + std::string tk = getNextToken(); + if( tk == "," ) { + if( i >= ( num - 1 ) ) { + ok = false; + break; + } + } + } + + if( !getBracketClose() ) { + return false; + } + + return ok; +} + +//------------------------------------------------------------------------------------------------ +bool OpenGEXParser::getNodeData() { + if( !getBracketOpen() ) { + return false; + } + + if( !onMetricNode() ) { + return false; + } + + if( !getBracketClose() ) { + return false; + } + + return true; +} + +//------------------------------------------------------------------------------------------------ +bool OpenGEXParser::getMetricAttribute( std::string &attribName ) { + return false; +} + +//------------------------------------------------------------------------------------------------ +bool OpenGEXParser::onMetricNode() { + std::string attribName; + if( !getMetricAttribute( attribName ) ) { + return false; + } + + if( "distance" == attribName ) { + float distance( 0.0f ); + getFloatData( 1, &distance ); + } else if( "angle" == attribName ) { + float angle( 0.0f ); + getFloatData( 1, &angle ); + } else if( "time" == attribName ) { + float time( 0.0f ); + getFloatData( 1, &time ); + } else if( "up" == attribName ) { + std::string up; + getStringData( up ); + } else { + return false; + } + + return true; +} + +//------------------------------------------------------------------------------------------------ + } // Namespace openGEX } // Namespace Assimp diff --git a/code/OpenGEXParser.h b/code/OpenGEXParser.h index 34b48e561..0bdb7909a 100644 --- a/code/OpenGEXParser.h +++ b/code/OpenGEXParser.h @@ -41,21 +41,38 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define ASSIMP_OPENGEX_OPENGEXPARSER_H_INC #ifndef ASSIMP_BUILD_NO_OPEMGEX_IMPORTER +#include namespace Assimp { namespace OpenGEX { class OpenGEXParser { public: - OpenGEXParser(); + OpenGEXParser( const std::vector &buffer ); ~OpenGEXParser(); + void parse(); + +protected: + std::string getNextToken(); + bool skipComments(); + bool parseNextNode(); + bool getNodeHeader( const std::string &name ); + bool getBracketOpen(); + bool getBracketClose(); + bool getStringData( std::string &data ); + bool getFloatData( size_t num, float *data ); + bool getNodeData(); + bool getMetricAttribute( std::string &attribName ); + bool onMetricNode(); private: OpenGEXParser( const OpenGEXParser & ); OpenGEXParser &operator = ( const OpenGEXParser & ); private: - + const std::vector &m_buffer; + size_t m_index; + size_t m_buffersize; }; } // Namespace openGEX diff --git a/code/OpenGEXStructs.h b/code/OpenGEXStructs.h index 5a0899710..d54bf32d1 100644 --- a/code/OpenGEXStructs.h +++ b/code/OpenGEXStructs.h @@ -57,7 +57,10 @@ struct BoneIndexArray; struct BoneWeightArray; struct Metric { - std::string metricKey; + float m_distance; + float m_angle; + float m_time; + float m_up; }; struct VertexArray { From db582ee024bfbd37af9ca6a53917e1345fa5f84c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 12 Sep 2014 18:12:18 +0200 Subject: [PATCH 06/74] bugfix: replace nullptr with NULL: Signed-off-by: Kim Kulling --- code/OpenGEXParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/OpenGEXParser.cpp b/code/OpenGEXParser.cpp index 7e73520ac..19fdcb990 100644 --- a/code/OpenGEXParser.cpp +++ b/code/OpenGEXParser.cpp @@ -197,7 +197,7 @@ bool OpenGEXParser::getStringData( std::string &data ) { //------------------------------------------------------------------------------------------------ bool OpenGEXParser::getFloatData( size_t num, float *data ) { - ai_assert( nullptr != data ); + ai_assert( NULL != data ); if( !getBracketOpen() ) { return false; From 6843c42da10c58ff1fe6dcb9d035be04eebe7b69 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 28 Oct 2014 21:45:09 +0100 Subject: [PATCH 07/74] update: - merge master into branch - fix metrix header parsing. Signed-off-by: Kim Kulling --- code/OpenGEXParser.cpp | 117 +++++++++++++++++++++++++++++------------ code/OpenGEXParser.h | 18 ++++++- 2 files changed, 98 insertions(+), 37 deletions(-) diff --git a/code/OpenGEXParser.cpp b/code/OpenGEXParser.cpp index 19fdcb990..efb1ff7b0 100644 --- a/code/OpenGEXParser.cpp +++ b/code/OpenGEXParser.cpp @@ -55,8 +55,7 @@ static const std::string Metric = "Metric"; static const std::string GeometryNode = "GeometryNode"; static const std::string GeometryObject = "GeometryObject"; static const std::string Material = "Material"; -static const size_t NumObjects = 4; - +static const size_t NumObjects = 4; static const std::string RootNodes[ NumObjects ] = { Metric, GeometryNode, @@ -64,9 +63,31 @@ static const std::string RootNodes[ NumObjects ] = { Material }; -static bool containsNode( const char *bufferPtr, size_t size, const std::string *nodes, size_t numNodes, - const std::string *tokenFound ) { - tokenFound = NULL; +static const size_t NumSeparator = 4; +static const std::string Separator[ NumSeparator ] = { + "(", ")", "{", "}" +}; + + +static const size_t NumToken = 8; +static const std::string Token[ NumToken ] = { + RootNodes[ 0 ], + RootNodes[ 1 ], + RootNodes[ 2 ], + RootNodes[ 3 ], + Separator[ 0 ], + Separator[ 1 ], + Separator[ 2 ], + Separator[ 3 ] +}; + +static bool isSeparator( char in ) { + return ( in == '(' || in == ')' || in == '{' || in == '}' ); +} + +static bool containsNode( const char *bufferPtr, size_t size, const std::string *nodes, + size_t numNodes, std::string &tokenFound ) { + tokenFound = ""; if( 0 == numNodes ) { return false; } @@ -74,7 +95,7 @@ static bool containsNode( const char *bufferPtr, size_t size, const std::string bool found( false ); for( size_t i = 0; i < numNodes; ++i ) { if( TokenMatch( bufferPtr, nodes[ i ].c_str(), nodes[ i ].size() ) ) { - tokenFound = &nodes[ i ]; + tokenFound = nodes[ i ]; found = true; break; } @@ -83,12 +104,27 @@ static bool containsNode( const char *bufferPtr, size_t size, const std::string return found; } +static OpenGEXParser::TokenType getTokenTypeByName( const char *in ) { + ai_assert( NULL != in ); + + OpenGEXParser::TokenType type( OpenGEXParser::None ); + for( size_t i = 0; i < NumToken; ++i ) { + if( TokenMatch( in, Token[ i ].c_str(), Token[ i ].size() ) ) { + type = static_cast( i+1 ); + break; + } + } + + return type; +} + //------------------------------------------------------------------------------------------------ OpenGEXParser::OpenGEXParser( const std::vector &buffer ) : m_buffer( buffer ) , m_index( 0 ) -, m_buffersize( buffer.size() ) { - +, m_buffersize( buffer.size() ) +, m_nodeTypeStack() { + // empty } //------------------------------------------------------------------------------------------------ @@ -107,10 +143,10 @@ void OpenGEXParser::parse() { std::string OpenGEXParser::getNextToken() { std::string token; while( m_index < m_buffersize && IsSpace( m_buffer[ m_index ] ) ) { - ++m_index; + m_index++; } - while( m_index < m_buffersize && !IsSpace( m_buffer[ m_index ] ) ) { + while( m_index < m_buffersize && !IsSpace( m_buffer[ m_index ] ) && !isSeparator( m_buffer[ m_index ] ) ) { token += m_buffer[ m_index ]; m_index++; } @@ -120,6 +156,13 @@ std::string OpenGEXParser::getNextToken() { token = getNextToken(); } + if( token.empty() ) { + if( isSeparator( m_buffer[ m_index ] ) ) { + token += m_buffer[ m_index ]; + m_index++; + } + } + return token; } @@ -140,7 +183,8 @@ bool OpenGEXParser::skipComments() { bool OpenGEXParser::parseNextNode() { std::string token( getNextToken() ); std::string rootNodeName; - if( containsNode( token.c_str(), token.size(), RootNodes, NumObjects, &rootNodeName ) ) { + if( containsNode( token.c_str(), token.size(), RootNodes, NumObjects, rootNodeName ) ) { + m_nodeTypeStack.push_back( getTokenTypeByName( rootNodeName.c_str() ) ); if( !getNodeHeader( rootNodeName ) ) { return false; } @@ -148,6 +192,8 @@ bool OpenGEXParser::parseNextNode() { if( !getNodeData() ) { return false; } + + m_nodeTypeStack.pop_back(); } return true; @@ -155,12 +201,15 @@ bool OpenGEXParser::parseNextNode() { //------------------------------------------------------------------------------------------------ bool OpenGEXParser::getNodeHeader( const std::string &name ) { - if( name == Metric ) { - std::string token( getNextToken() ); - + bool success( false ); + if( m_nodeTypeStack.back() == MetricNode ) { + std::string attribName, value; + if( getMetricAttributeKey( attribName, value ) ) { + success = true; + } } - return false; + return success; } //------------------------------------------------------------------------------------------------ @@ -226,33 +275,31 @@ bool OpenGEXParser::getFloatData( size_t num, float *data ) { //------------------------------------------------------------------------------------------------ bool OpenGEXParser::getNodeData() { - if( !getBracketOpen() ) { - return false; - } - - if( !onMetricNode() ) { - return false; - } - - if( !getBracketClose() ) { - return false; - } - return true; } //------------------------------------------------------------------------------------------------ -bool OpenGEXParser::getMetricAttribute( std::string &attribName ) { - return false; +bool OpenGEXParser::getMetricAttributeKey( std::string &attribName, std::string &value ) { + bool ok( false ); + attribName = ""; + std::string token( getNextToken() ); + if( token[ 0 ] == '(' ) { + // get attribute + attribName = getNextToken(); + std::string equal = getNextToken(); + value = getNextToken(); + + token = getNextToken(); + if( token[ 0 ] == ')' ) { + ok = true; + } + } + + return ok; } //------------------------------------------------------------------------------------------------ -bool OpenGEXParser::onMetricNode() { - std::string attribName; - if( !getMetricAttribute( attribName ) ) { - return false; - } - +bool OpenGEXParser::onMetricNode( const std::string &attribName ) { if( "distance" == attribName ) { float distance( 0.0f ); getFloatData( 1, &distance ); diff --git a/code/OpenGEXParser.h b/code/OpenGEXParser.h index 0bdb7909a..1733366f7 100644 --- a/code/OpenGEXParser.h +++ b/code/OpenGEXParser.h @@ -47,6 +47,19 @@ namespace Assimp { namespace OpenGEX { class OpenGEXParser { +public: + enum TokenType { + None = 0, + MetricNode, + GeometryNode, + GeometryObject, + Material, + BracketIn, + BracketOut, + CurlyBracketIn, + CurlyBracketOut, + }; + public: OpenGEXParser( const std::vector &buffer ); ~OpenGEXParser(); @@ -62,8 +75,8 @@ protected: bool getStringData( std::string &data ); bool getFloatData( size_t num, float *data ); bool getNodeData(); - bool getMetricAttribute( std::string &attribName ); - bool onMetricNode(); + bool getMetricAttributeKey( std::string &attribName, std::string &value ); + bool onMetricNode( const std::string &attribName ); private: OpenGEXParser( const OpenGEXParser & ); @@ -71,6 +84,7 @@ private: private: const std::vector &m_buffer; + std::vector m_nodeTypeStack; size_t m_index; size_t m_buffersize; }; From 1a5695ff485ec28f50db62f6376bab1fdf5d584e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 2 Nov 2014 19:01:04 +0100 Subject: [PATCH 08/74] update: add parsing of metric nodes. Signed-off-by: Kim Kulling --- code/OpenGEXParser.cpp | 84 ++++++++++++++++++++++++++++++------------ code/OpenGEXParser.h | 26 +++++++++++-- 2 files changed, 84 insertions(+), 26 deletions(-) diff --git a/code/OpenGEXParser.cpp b/code/OpenGEXParser.cpp index efb1ff7b0..0acf209a6 100644 --- a/code/OpenGEXParser.cpp +++ b/code/OpenGEXParser.cpp @@ -118,6 +118,16 @@ static OpenGEXParser::TokenType getTokenTypeByName( const char *in ) { return type; } +static void removeQuotes( std::string &attribName ) { + std::string tmp; + for( unsigned int i = 0; i < attribName.size(); ++i ) { + if( attribName[ i ] != '\"' ) { + tmp += attribName[ i ]; + } + } + attribName = tmp; +} + //------------------------------------------------------------------------------------------------ OpenGEXParser::OpenGEXParser( const std::vector &buffer ) : m_buffer( buffer ) @@ -182,14 +192,14 @@ bool OpenGEXParser::skipComments() { //------------------------------------------------------------------------------------------------ bool OpenGEXParser::parseNextNode() { std::string token( getNextToken() ); - std::string rootNodeName; + std::string rootNodeName, nodeType; if( containsNode( token.c_str(), token.size(), RootNodes, NumObjects, rootNodeName ) ) { m_nodeTypeStack.push_back( getTokenTypeByName( rootNodeName.c_str() ) ); - if( !getNodeHeader( rootNodeName ) ) { + if( !getNodeHeader( nodeType ) ) { return false; } - if( !getNodeData() ) { + if( !getNodeData( nodeType ) ) { return false; } @@ -200,11 +210,10 @@ bool OpenGEXParser::parseNextNode() { } //------------------------------------------------------------------------------------------------ -bool OpenGEXParser::getNodeHeader( const std::string &name ) { +bool OpenGEXParser::getNodeHeader( std::string &name ) { bool success( false ); if( m_nodeTypeStack.back() == MetricNode ) { - std::string attribName, value; - if( getMetricAttributeKey( attribName, value ) ) { + if( getMetricAttributeKey( name ) ) { success = true; } } @@ -248,6 +257,9 @@ bool OpenGEXParser::getStringData( std::string &data ) { bool OpenGEXParser::getFloatData( size_t num, float *data ) { ai_assert( NULL != data ); + std::string tk; + tk = getNextToken(); + if( !getBracketOpen() ) { return false; } @@ -257,7 +269,7 @@ bool OpenGEXParser::getFloatData( size_t num, float *data ) { for( unsigned int i = 0; i < num; ++i ) { data[ dataIdx ] = fast_atof( &m_buffer[ m_index ] ); ++dataIdx; - std::string tk = getNextToken(); + tk = getNextToken(); if( tk == "," ) { if( i >= ( num - 1 ) ) { ok = false; @@ -274,24 +286,41 @@ bool OpenGEXParser::getFloatData( size_t num, float *data ) { } //------------------------------------------------------------------------------------------------ -bool OpenGEXParser::getNodeData() { - return true; +bool OpenGEXParser::getNodeData( const std::string &nodeType ) { + bool success( false ); + + if( !getBracketOpen() ) { + return false; + } + + TokenType type( m_nodeTypeStack.back() ); + if( type == MetricNode ) { + success = onMetricNode( nodeType ); + } + + if( !getBracketClose() ) { + return false; + } + + return success; } //------------------------------------------------------------------------------------------------ -bool OpenGEXParser::getMetricAttributeKey( std::string &attribName, std::string &value ) { +bool OpenGEXParser::getMetricAttributeKey( std::string &attribName ) { bool ok( false ); attribName = ""; std::string token( getNextToken() ); if( token[ 0 ] == '(' ) { // get attribute - attribName = getNextToken(); - std::string equal = getNextToken(); - value = getNextToken(); - token = getNextToken(); - if( token[ 0 ] == ')' ) { - ok = true; + if( "key" == token ) { + std::string equal = getNextToken(); + attribName = getNextToken(); + token = getNextToken(); + if( token[ 0 ] == ')' ) { + ok = true; + removeQuotes( attribName ); + } } } @@ -300,28 +329,37 @@ bool OpenGEXParser::getMetricAttributeKey( std::string &attribName, std::string //------------------------------------------------------------------------------------------------ bool OpenGEXParser::onMetricNode( const std::string &attribName ) { + bool success( true ); if( "distance" == attribName ) { float distance( 0.0f ); - getFloatData( 1, &distance ); + if( getFloatData( 1, &distance ) ) { + m_model.m_metrics.m_distance = distance; + } } else if( "angle" == attribName ) { float angle( 0.0f ); - getFloatData( 1, &angle ); + if( getFloatData( 1, &angle ) ) { + m_model.m_metrics.m_angle = angle; + } } else if( "time" == attribName ) { float time( 0.0f ); - getFloatData( 1, &time ); + if( getFloatData( 1, &time ) ) { + m_model.m_metrics.m_time = time; + } } else if( "up" == attribName ) { std::string up; - getStringData( up ); + if( getStringData( up ) ) { + m_model.m_metrics.m_up = up; + } } else { - return false; + success = false; } - return true; + return success; } //------------------------------------------------------------------------------------------------ -} // Namespace openGEX +} // Namespace OpenGEX } // Namespace Assimp #endif ASSIMP_BUILD_NO_OPEMGEX_IMPORTER diff --git a/code/OpenGEXParser.h b/code/OpenGEXParser.h index 1733366f7..f6ef2df14 100644 --- a/code/OpenGEXParser.h +++ b/code/OpenGEXParser.h @@ -41,11 +41,30 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define ASSIMP_OPENGEX_OPENGEXPARSER_H_INC #ifndef ASSIMP_BUILD_NO_OPEMGEX_IMPORTER + #include +#include namespace Assimp { namespace OpenGEX { +struct OpenGEXModel { + struct Metrics { + float m_distance; + float m_angle; + float m_time; + std::string m_up; + + Metrics() + : m_distance( 0.0f ) + , m_angle( 0.0f ) + , m_time( 0.0f ) + , m_up() { + // empty + } + } m_metrics; +}; + class OpenGEXParser { public: enum TokenType { @@ -69,13 +88,13 @@ protected: std::string getNextToken(); bool skipComments(); bool parseNextNode(); - bool getNodeHeader( const std::string &name ); + bool getNodeHeader( std::string &name ); bool getBracketOpen(); bool getBracketClose(); bool getStringData( std::string &data ); bool getFloatData( size_t num, float *data ); - bool getNodeData(); - bool getMetricAttributeKey( std::string &attribName, std::string &value ); + bool getNodeData( const std::string &nodeType ); + bool getMetricAttributeKey( std::string &attribName ); bool onMetricNode( const std::string &attribName ); private: @@ -85,6 +104,7 @@ private: private: const std::vector &m_buffer; std::vector m_nodeTypeStack; + OpenGEXModel m_model; size_t m_index; size_t m_buffersize; }; From dbf95362133a5ae4ab377f547f33679c1a7c3c1b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 2 Nov 2014 20:30:45 +0100 Subject: [PATCH 09/74] bugfix: add correct handling for metric line end for example file. Signed-off-by: Kim Kulling --- .gitignore | 1 + code/OpenGEXParser.cpp | 30 +++++++++++++++++++++--------- code/OpenGEXParser.h | 1 + 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index f4940f273..67299f752 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ test/gtest/src/gtest-stamp/gtest-build.cmake test/gtest/src/gtest-stamp/Debug/gtest-patch *.cache test/gtest/src/gtest-stamp/Debug/gtest-build +*.lib diff --git a/code/OpenGEXParser.cpp b/code/OpenGEXParser.cpp index 0acf209a6..03f222d1c 100644 --- a/code/OpenGEXParser.cpp +++ b/code/OpenGEXParser.cpp @@ -189,6 +189,17 @@ bool OpenGEXParser::skipComments() { return skipped; } +//------------------------------------------------------------------------------------------------ +void OpenGEXParser::readUntilEndOfLine() { + while( !IsLineEnd( m_buffer[ m_index ] ) ) { + ++m_index; + } + ++m_index; + if( IsLineEnd( m_buffer[ m_index ] ) ) { + ++m_index; + } +} + //------------------------------------------------------------------------------------------------ bool OpenGEXParser::parseNextNode() { std::string token( getNextToken() ); @@ -202,8 +213,11 @@ bool OpenGEXParser::parseNextNode() { if( !getNodeData( nodeType ) ) { return false; } + + readUntilEndOfLine(); m_nodeTypeStack.pop_back(); + } return true; @@ -329,21 +343,19 @@ bool OpenGEXParser::getMetricAttributeKey( std::string &attribName ) { //------------------------------------------------------------------------------------------------ bool OpenGEXParser::onMetricNode( const std::string &attribName ) { + float value( 0.0f ); bool success( true ); if( "distance" == attribName ) { - float distance( 0.0f ); - if( getFloatData( 1, &distance ) ) { - m_model.m_metrics.m_distance = distance; + if( getFloatData( 1, &value ) ) { + m_model.m_metrics.m_distance = value; } } else if( "angle" == attribName ) { - float angle( 0.0f ); - if( getFloatData( 1, &angle ) ) { - m_model.m_metrics.m_angle = angle; + if( getFloatData( 1, &value ) ) { + m_model.m_metrics.m_angle = value; } } else if( "time" == attribName ) { - float time( 0.0f ); - if( getFloatData( 1, &time ) ) { - m_model.m_metrics.m_time = time; + if( getFloatData( 1, &value ) ) { + m_model.m_metrics.m_time = value; } } else if( "up" == attribName ) { std::string up; diff --git a/code/OpenGEXParser.h b/code/OpenGEXParser.h index f6ef2df14..82b30e4ae 100644 --- a/code/OpenGEXParser.h +++ b/code/OpenGEXParser.h @@ -87,6 +87,7 @@ public: protected: std::string getNextToken(); bool skipComments(); + void readUntilEndOfLine(); bool parseNextNode(); bool getNodeHeader( std::string &name ); bool getBracketOpen(); From 9f80a2a2a9e51a688e44baf75cc235a0e9895e2f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 29 Nov 2014 11:33:49 +0100 Subject: [PATCH 10/74] update parser. Signed-off-by: Kim Kulling --- code/OpenGEXParser.cpp | 36 ++++++++++++++++++++++++++++++++++-- code/OpenGEXParser.h | 3 +++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/code/OpenGEXParser.cpp b/code/OpenGEXParser.cpp index 03f222d1c..3dcc83e44 100644 --- a/code/OpenGEXParser.cpp +++ b/code/OpenGEXParser.cpp @@ -226,10 +226,19 @@ bool OpenGEXParser::parseNextNode() { //------------------------------------------------------------------------------------------------ bool OpenGEXParser::getNodeHeader( std::string &name ) { bool success( false ); - if( m_nodeTypeStack.back() == MetricNode ) { + TokenType tokenType( m_nodeTypeStack.back() ); + if( tokenType == MetricNode ) { if( getMetricAttributeKey( name ) ) { success = true; } + } else if( tokenType == GeometryNode ) { + + } else if( tokenType == GeometryObject ) { + + } else if( tokenType == Material ) { + + } else { + DefaultLogger::get()->warn( "Unknown token type in file." ); } return success; @@ -307,9 +316,17 @@ bool OpenGEXParser::getNodeData( const std::string &nodeType ) { return false; } - TokenType type( m_nodeTypeStack.back() ); + const TokenType type( m_nodeTypeStack.back() ); if( type == MetricNode ) { success = onMetricNode( nodeType ); + } else if( type == GeometryNode ) { + success = onGeometryNode(); + } else if( type == GeometryObject ) { + success = onGeometryObject(); + } else if( type == Material ) { + success = onMaterial(); + } else { + DefaultLogger::get()->warn( "Unknown token type in file." ); } if( !getBracketClose() ) { @@ -369,6 +386,21 @@ bool OpenGEXParser::onMetricNode( const std::string &attribName ) { return success; } +//------------------------------------------------------------------------------------------------ +bool OpenGEXParser::onGeometryNode() { + return true; +} + +//------------------------------------------------------------------------------------------------ +bool OpenGEXParser::onGeometryObject() { + return true; +} + +//------------------------------------------------------------------------------------------------ +bool OpenGEXParser::onMaterial() { + return true; +} + //------------------------------------------------------------------------------------------------ } // Namespace OpenGEX diff --git a/code/OpenGEXParser.h b/code/OpenGEXParser.h index 82b30e4ae..64debe43c 100644 --- a/code/OpenGEXParser.h +++ b/code/OpenGEXParser.h @@ -97,6 +97,9 @@ protected: bool getNodeData( const std::string &nodeType ); bool getMetricAttributeKey( std::string &attribName ); bool onMetricNode( const std::string &attribName ); + bool onGeometryNode(); + bool onGeometryObject(); + bool onMaterial(); private: OpenGEXParser( const OpenGEXParser & ); From 1ee4c06e4bb06809e0ff374e40b8d7555d0d544a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 31 Jan 2015 11:07:48 +0100 Subject: [PATCH 11/74] add openddl-parser to contrib. Signed-off-by: Kim Kulling --- .gitignore | 2 +- CMakeLists.txt | 1 + code/CMakeLists.txt | 1 - contrib/openddlparser/CMakeLists.txt | 63 ++ contrib/openddlparser/README.md | 111 +++ contrib/openddlparser/code/DDLNode.cpp | 162 ++++ contrib/openddlparser/code/OpenDDLParser.cpp | 852 ++++++++++++++++++ contrib/openddlparser/code/Value.cpp | 243 +++++ .../include/openddlparser/DDLNode.h | 87 ++ .../include/openddlparser/OpenDDLCommon.h | 148 +++ .../openddlparser/OpenDDLParserUtils.h | 233 +++++ .../include/openddlparser/Value.h | 85 ++ 12 files changed, 1986 insertions(+), 2 deletions(-) create mode 100644 contrib/openddlparser/CMakeLists.txt create mode 100644 contrib/openddlparser/README.md create mode 100644 contrib/openddlparser/code/DDLNode.cpp create mode 100644 contrib/openddlparser/code/OpenDDLParser.cpp create mode 100644 contrib/openddlparser/code/Value.cpp create mode 100644 contrib/openddlparser/include/openddlparser/DDLNode.h create mode 100644 contrib/openddlparser/include/openddlparser/OpenDDLCommon.h create mode 100644 contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h create mode 100644 contrib/openddlparser/include/openddlparser/Value.h diff --git a/.gitignore b/.gitignore index 55995220d..cf0c2a94f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,7 @@ build # Output bin/ lib/ -contrib/ + # Generated assimp.pc diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c8661f55..7c9468b95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,6 +154,7 @@ else(NOT ZLIB_FOUND) endif(NOT ZLIB_FOUND) INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR}) +add_subdirectory( contrib/openddlparser ) # Search for unzip if (PKG_CONFIG_FOUND) PKG_CHECK_MODULES(UNZIP minizip) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 1edaa8725..70034ed66 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -634,7 +634,6 @@ SET( unzip_SRCS ) SOURCE_GROUP( unzip FILES ${unzip_SRCS}) - # VC2010 fixes if(MSVC10) option( VC10_STDINT_FIX "Fix for VC10 Compiler regarding pstdint.h redefinition errors" OFF ) diff --git a/contrib/openddlparser/CMakeLists.txt b/contrib/openddlparser/CMakeLists.txt new file mode 100644 index 000000000..d551c9857 --- /dev/null +++ b/contrib/openddlparser/CMakeLists.txt @@ -0,0 +1,63 @@ +CMAKE_MINIMUM_REQUIRED( VERSION 2.6 ) +PROJECT( OpenDDL-Parser ) +SET ( OPENDDL_PARSER_VERSION_MAJOR 0 ) +SET ( OPENDDL_PARSER_VERSION_MINOR 1 ) +SET ( OPENDDL_PARSER_VERSION_PATCH 0 ) +SET ( OPENDDL_PARSER_VERSION ${CPPCORE_VERSION_MAJOR}.${CPPCORE_VERSION_MINOR}.${CPPCORE_VERSION_PATCH} ) +SET ( PROJECT_VERSION "${OPENDDL_PARSER_VERSION}" ) + +if( CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX ) + find_package(Threads) +else() + add_definitions( -D_CRT_SECURE_NO_WARNINGS ) +endif() + +add_definitions( -DOPENDDLPARSER_BUILD ) +add_definitions( -D_VARIADIC_MAX=10 ) + +INCLUDE_DIRECTORIES( + ./ + include/ + contrib/gtest-1.7.0/include + contrib/gtest-1.7.0/ +) + +link_directories( + ./ +) + +SET( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_HOME_DIRECTORY}/lib ) +SET( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_HOME_DIRECTORY}/lib ) +SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_HOME_DIRECTORY}/bin ) + +if( WIN32 AND NOT CYGWIN ) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc" ) # Force to always compile with W4 + if( CMAKE_CXX_FLAGS MATCHES "/W[0-4]" ) + string( REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" ) + else() + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4" ) + endif() +elseif( CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX ) + # Update if necessary + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic -std=c++0x") +elseif ( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" ) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic -std=c++11") +endif() + +SET ( openddl_parser_src + code/OpenDDLParser.cpp + code/DDLNode.cpp + code/Value.cpp + include/openddlparser/OpenDDLParser.h + include/openddlparser/OpenDDLParserUtils.h + include/openddlparser/OpenDDLCommon.h + include/openddlparser/DDLNode.h + include/openddlparser/Value.h + README.md +) + +SOURCE_GROUP( code FILES ${openddl_parser_src} ) + +ADD_LIBRARY( openddl_parser SHARED + ${openddl_parser_src} +) diff --git a/contrib/openddlparser/README.md b/contrib/openddlparser/README.md new file mode 100644 index 000000000..195de6932 --- /dev/null +++ b/contrib/openddlparser/README.md @@ -0,0 +1,111 @@ +The OpenDDL-Parser +================== + +A simple and fast OpenDDL Parser +Current build status: [![Build Status](https://travis-ci.org/kimkulling/openddl-parser.png)](https://travis-ci.org/kimkulling/openddl-parser) + +Get the source code +=================== +You can get the code from our git repository, which is located at GitHub. You can clone the repository like: + +> git clone https://github.com/kimkulling/openddl-parser.git + +Build from repo +=============== +To build the library you need to install cmake first ( see http://www.cmake.org/ for more information ). Make also sure that a compiler toolchain is installed on your machine. +After installing it you can open a console and type: + +> cmake CMakeLists.txt + +This command will generate a build environment for your installed build enrironment ( for Visual Studio the project files will be generated, for gcc the makefiles will be generated ). +When using an IDE open the IDE and run the build. When using GNU-make type in your console: + +> make + +and that's all. + +Use the library +=============== +To use the OpenDDL-parser you need to build the lib first. Now add the +> /include + +to your include-path and the + +> /lib + +to your lib-folder. Link the openddl.lib to your application. + +Here is a small example how to use the lib: + +```cpp + +#include +#include +#include + +USE_ODDLPARSER_NS; + +int main( int argc, char *argv[] ) { + if( argc < 3 ) { + return 1; + } + + char *filename( nullptr ); + if( 0 == strncmp( FileOption, argv[ 1 ], strlen( FileOption ) ) ) { + filename = argv[ 2 ]; + } + std::cout << "file to import: " << filename << std::endl; + if( nullptr == filename ) { + std::cerr << "Invalid filename." << std::endl; + return Error; + } + + FILE *fileStream = fopen( filename, "r+" ); + if( NULL == filename ) { + std::cerr << "Cannot open file " << filename << std::endl; + return 1; + } + + // obtain file size: + fseek( fileStream, 0, SEEK_END ); + const size_t size( ftell( fileStream ) ); + rewind( fileStream ); + if( size > 0 ) { + char *buffer = new char[ size ]; + const size_t readSize( fread( buffer, sizeof( char ), size, fileStream ) ); + assert( readSize == size ); + OpenDDLParser theParser; + theParser.setBuffer( buffer, size ); + const bool result( theParser.parse() ); + if( !result ) { + std::cerr << "Error while parsing file " << filename << "." << std::endl; + } + } + return 0; +} + +``` + +How to access the imported data +=============================== +The data is organized as a tree. You can get the root tree with the following code: + +``` +OpenDDLParser theParser; +theParser.setBuffer( buffer, size ); +const bool result( theParser.parse() ); +if ( result ) { + DDLNode *root = theParser.getRoot(); + + DDLNode::DllNodeList childs = root->getChildNodeList(); + for ( size_t i=0; igetProperty(); // to get properties + std:.string type = child->getType(); // to get the node type + Value *values = child->getValue(); // to get the data; + } +} + +``` + +The instance called root contains the data. diff --git a/contrib/openddlparser/code/DDLNode.cpp b/contrib/openddlparser/code/DDLNode.cpp new file mode 100644 index 000000000..30977ca2e --- /dev/null +++ b/contrib/openddlparser/code/DDLNode.cpp @@ -0,0 +1,162 @@ +/*----------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2014 Kim Kulling + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-----------------------------------------------------------------------------------------------*/ +#include +#include + +#include + +BEGIN_ODDLPARSER_NS + +DDLNode::DllNodeList DDLNode::s_allocatedNodes; + +template +inline +static void releaseDataType( T *ptr ) { + if( nullptr == ptr ) { + return; + } + + T *current( nullptr ); + while( ptr ) { + current = ptr; + ptr = ptr->m_next; + delete current; + } +} + +DDLNode::DDLNode( const std::string &type, const std::string &name, size_t idx, DDLNode *parent ) +: m_type( type ) +, m_name( name ) +, m_parent( parent ) +, m_children() +, m_properties( nullptr ) +, m_value( nullptr ) +, m_idx( idx ) +, m_dtArrayList( nullptr ) { + if( m_parent ) { + m_parent->m_children.push_back( this ); + } +} + +DDLNode::~DDLNode() { + releaseDataType( m_properties ); + releaseDataType( m_value ); + + delete m_dtArrayList; + m_dtArrayList = nullptr; + if( s_allocatedNodes[ m_idx ] == this ) { + s_allocatedNodes[ m_idx ] = nullptr; + } +} + +void DDLNode::attachParent( DDLNode *parent ) { + if( m_parent == parent ) { + return; + } + + m_parent = parent; + if( nullptr != m_parent ) { + m_parent->m_children.push_back( this ); + } +} + +void DDLNode::detachParent() { + if( m_parent ) { + std::vector::iterator it; + it = std::find( m_parent->m_children.begin(), m_parent->m_children.end(), this ); + if( m_parent->m_children.end() != it ) { + m_parent->m_children.erase( it ); + } + m_parent = nullptr; + } +} + +DDLNode *DDLNode::getParent() const { + return m_parent; +} + +const DDLNode::DllNodeList &DDLNode::getChildNodeList() const { + return m_children; +} + +void DDLNode::setType( const std::string &type ) { + m_type = type; +} + +const std::string &DDLNode::getType() const { + return m_type; +} + + +void DDLNode::setName( const std::string &name ) { + m_name = name; +} + +const std::string &DDLNode::getName() const { + return m_name; +} + +void DDLNode::setProperties( Property *first ) { + m_properties = first; +} + +Property *DDLNode::getProperties() const { + return m_properties; +} + +void DDLNode::setValue( Value *val ) { + m_value = val; +} + +Value *DDLNode::getValue() const { + return m_value; +} + +void DDLNode::setDataArrayList( DataArrayList *dtArrayList ) { + m_dtArrayList = dtArrayList; +} + +DataArrayList *DDLNode::getDataArrayList() const { + return m_dtArrayList; +} + +DDLNode *DDLNode::create( const std::string &type, const std::string &name, DDLNode *parent ) { + const size_t idx( s_allocatedNodes.size() ); + DDLNode *node = new DDLNode( type, name, idx, parent ); + s_allocatedNodes.push_back( node ); + + return node; +} + +void DDLNode::releaseNodes() { + if( s_allocatedNodes.size() > 0 ) { + for( DllNodeList::iterator it = s_allocatedNodes.begin(); it != s_allocatedNodes.end(); it++ ) { + if( *it ) { + delete *it; + } + } + s_allocatedNodes.clear(); + } +} + +END_ODDLPARSER_NS diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp new file mode 100644 index 000000000..675d58511 --- /dev/null +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -0,0 +1,852 @@ +/*----------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2014 Kim Kulling + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-----------------------------------------------------------------------------------------------*/ +#include + +#include +#include +#include +#include + +#ifdef _WIN32 +# include +#endif // _WIN32 + +#define DEBUG_HEADER_NAME + +BEGIN_ODDLPARSER_NS + +static const char *Version = "0.1.0"; + +static const char* PrimitiveTypeToken[ Value::ddl_types_max ] = { + "bool", + "int8", + "int16", + "int32", + "int64", + "unsigned_int8", + "unsigned_int16", + "unsigned_int32", + "unsigned_int64", + "half", + "float", + "double", + "string", + "ref" +}; + +static const char *BoolTrue = "true"; +static const char *BoolFalse = "false"; +static const char *RefToken = "ref"; + +static void logInvalidTokenError( char *in, char *exp, OpenDDLParser::logCallback callback ) { + std::stringstream stream; + stream << "Invalid token " << *in << ", " << exp << " expected." << std::endl; + callback( ddl_error_msg, stream.str() ); +} + +static bool isIntegerType( Value::ValueType integerType ) { + if( integerType != Value::ddl_int8 && integerType != Value::ddl_int16 && integerType != Value::ddl_int32 && integerType != Value::ddl_int64 ) { + return false; + } + + return true; +} + +static DDLNode *createDDLNode( Identifier *id, Property *first, OpenDDLParser *parser ) { + if( nullptr == id || nullptr == parser ) { + return nullptr; + } + + const std::string type( id->m_buffer ); + DDLNode *parent( parser->top() ); + DDLNode *node = DDLNode::create( type, "", parent ); + if( nullptr != first ) { + node->setProperties( first ); + } + + return node; +} + +static void logMessage( LogSeverity severity, const std::string &msg ) { + std::string log; + if( ddl_debug_msg == severity ) { + log += "Debug:"; + } else if( ddl_info_msg == severity ) { + log += "Info :"; + } else if( ddl_warn_msg == severity ) { + log += "Warn :"; + } else if( ddl_error_msg == severity ) { + log += "Error:"; + } else { + log += "None :"; + } + + log += msg; + std::cout << log; +} + +OpenDDLParser::OpenDDLParser() +: m_logCallback( logMessage ) +, m_ownsBuffer( false ) +,m_buffer( nullptr ) +, m_len( 0 ) +, m_stack() +, m_context( nullptr ) { + // empty +} + +OpenDDLParser::OpenDDLParser( char *buffer, size_t len, bool ownsIt ) +: m_logCallback( &logMessage ) +, m_ownsBuffer( false ) +, m_buffer( nullptr ) +, m_len( 0 ) +, m_context( nullptr ) { + if( 0 != m_len ) { + setBuffer( buffer, len, ownsIt ); + } +} + +OpenDDLParser::~OpenDDLParser() { + clear(); +} + +void OpenDDLParser::setLogCallback( logCallback callback ) { + if( nullptr != callback ) { + // install user-specific log callback + m_logCallback = callback; + } else { + // install default log callback + m_logCallback = &logMessage; + } +} + +OpenDDLParser::logCallback OpenDDLParser::getLogCallback() const { + return m_logCallback; +} + +void OpenDDLParser::setBuffer( char *buffer, size_t len, bool ownsIt ) { + if( m_buffer && m_ownsBuffer ) { + delete[] m_buffer; + m_buffer = nullptr; + m_len = 0; + } + + m_ownsBuffer = ownsIt; + if( m_ownsBuffer ) { + // when we are owning the buffer we will do a deep copy + m_buffer = new char[ len ]; + m_len = len; + ::memcpy( m_buffer, buffer, len ); + } else { + // when we are not owning the buffer, we just do a shallow copy + m_buffer = buffer; + m_len = len; + } +} + +char *OpenDDLParser::getBuffer() const { + return m_buffer; +} + +size_t OpenDDLParser::getBufferSize() const { + return m_len; +} + +void OpenDDLParser::clear() { + if( m_ownsBuffer ) { + delete [] m_buffer; + } + m_buffer = nullptr; + m_len = 0; + + if( m_context ) { + m_context->m_root = nullptr; + } + + DDLNode::releaseNodes(); +} + +bool OpenDDLParser::parse() { + if( 0 == m_len ) { + return false; + } + + normalizeBuffer( m_buffer, m_len ); + + m_context = new Context; + m_context->m_root = DDLNode::create( "root", "", nullptr ); + pushNode( m_context->m_root ); + + // do the main parsing + char *current( &m_buffer[ 0 ] ); + char *end( &m_buffer[ m_len - 1 ] + 1 ); + while( current != end ) { + current = parseNextNode( current, end ); + } + return true; +} + +char *OpenDDLParser::parseNextNode( char *in, char *end ) { + in = parseHeader( in, end ); + in = parseStructure( in, end ); + + return in; +} + +char *OpenDDLParser::parseHeader( char *in, char *end ) { + if( nullptr == in || in == end ) { + return in; + } + + Identifier *id( nullptr ); + in = OpenDDLParser::parseIdentifier( in, end, &id ); + +#ifdef DEBUG_HEADER_NAME + if( id ) { + std::cout << id->m_buffer << std::endl; + } +#endif // DEBUG_HEADER_NAME + + in = getNextToken( in, end ); + Property *first( nullptr ); + if( nullptr != id ) { + if( *in == '(' ) { + in++; + Property *prop( nullptr ), *prev( nullptr ); + while( *in != ')' && in != end ) { + in = parseProperty( in, end, &prop ); + in = getNextToken( in, end ); + + if( *in != ',' && *in != ')' ) { + logInvalidTokenError( in, ")", m_logCallback ); + return in; + } + if( nullptr != prop && *in != ',' ) { + if( nullptr == first ) { + first = prop; + } + if( nullptr != prev ) { + prev->m_next = prop; + } + prev = prop; + } + } + in++; + } + + // store the node + DDLNode *node( createDDLNode( id, first, this ) ); + if( nullptr != node ) { + pushNode( node ); + } else { + std::cerr << "nullptr returned by creating DDLNode." << std::endl; + } + + Name *name( nullptr ); + in = OpenDDLParser::parseName( in, end, &name ); + if( nullptr != name ) { + const std::string nodeName( name->m_id->m_buffer ); + node->setName( nodeName ); + } + } + + return in; +} + +char *OpenDDLParser::parseStructure( char *in, char *end ) { + if( nullptr == in || in == end ) { + return in; + } + + in = getNextToken( in, end ); + if( *in == '{' ) { + in++; + in = getNextToken( in, end ); + Value::ValueType type( Value::ddl_none ); + size_t arrayLen( 0 ); + in = OpenDDLParser::parsePrimitiveDataType( in, end, type, arrayLen ); + if( Value::ddl_none != type ) { + in = getNextToken( in, end ); + if( *in == '{' ) { + DataArrayList *dtArrayList( nullptr ); + Value *values( nullptr ); + if( 1 == arrayLen ) { + in = parseDataList( in, end, &values ); + if( nullptr != values ){ + DDLNode *currentNode( top() ); + if( nullptr != currentNode ) { + currentNode->setValue( values ); + } + } + } else if( arrayLen > 1 ) { + in = parseDataArrayList( in, end, &dtArrayList ); + if( nullptr != dtArrayList ) { + DDLNode *currentNode( top() ); + if( nullptr != currentNode ) { + currentNode->setDataArrayList( dtArrayList ); + } + } + } else { + std::cerr << "0 for array is invalid." << std::endl; + } + } + + in = getNextToken( in, end ); + if( *in != '}' ) { + logInvalidTokenError( in, "}", m_logCallback ); + } + } else { + in = parseHeader( in, end ); + in = parseStructure( in, end ); + } + } else { + in++; + logInvalidTokenError( in, "{", m_logCallback ); + return in; + + } + + in++; + + return in; +} + +void OpenDDLParser::pushNode( DDLNode *node ) { + if( nullptr == node ) { + return; + } + + m_stack.push_back( node ); +} + +DDLNode *OpenDDLParser::popNode() { + if( m_stack.empty() ) { + return nullptr; + } + + DDLNode *topNode( top() ); + m_stack.pop_back(); + + return topNode; +} + +DDLNode *OpenDDLParser::top() { + if( m_stack.empty() ) { + return nullptr; + } + + DDLNode *top( m_stack.back() ); + return top; +} + +DDLNode *OpenDDLParser::getRoot() const { + if( nullptr == m_context ) { + return nullptr; + } + + return m_context->m_root; +} + +Context *OpenDDLParser::getContext() const { + return m_context; +} + +void OpenDDLParser::normalizeBuffer( char *buffer, size_t len ) { + if( nullptr == buffer || 0 == len ) { + return; + } + + size_t writeIdx( 0 ); + char *end( &buffer[ len ] + 1 ); + for( size_t readIdx = 0; readIdx( c, end ) ) { + buffer[ writeIdx ] = buffer[ readIdx ]; + writeIdx++; + } else { + readIdx++; + // skip the comment and the rest of the line + while( !isEndofLine( buffer[ readIdx ] ) ) { + readIdx++; + } + buffer[writeIdx] = '\n'; + writeIdx++; + } + } + + if( writeIdx < len ) { + buffer[ writeIdx ] = '\0'; + } +} + +char *OpenDDLParser::parseName( char *in, char *end, Name **name ) { + *name = nullptr; + if( nullptr == in || in == end ) { + return in; + } + + // ignore blanks + in = getNextToken( in, end ); + if( *in != '$' && *in != '%' ) { + return in; + } + + NameType ntype( GlobalName ); + if( *in == '%' ) { + ntype = LocalName; + } + + Name *currentName( nullptr ); + Identifier *id( nullptr ); + in = parseIdentifier( in, end, &id ); + if( id ) { + currentName = new Name( ntype, id ); + if( currentName ) { + *name = currentName; + } + } + + return in; +} + +char *OpenDDLParser::parseIdentifier( char *in, char *end, Identifier **id ) { + *id = nullptr; + if( nullptr == in || in == end ) { + return in; + } + + // ignore blanks + in = getNextToken( in, end ); + + // staring with a number is forbidden + if( isNumeric( *in ) ) { + return in; + } + + // get size of id + size_t idLen( 0 ); + char *start( in ); + while( !isSeparator( *in ) && ( in != end ) && *in != '(' && *in != ')' ) { + in++; + idLen++; + } + + const size_t len( idLen + 1 ); + Identifier *newId = new Identifier( len, new char[ len ] ); + ::strncpy( newId->m_buffer, start, newId->m_len-1 ); + newId->m_buffer[ newId->m_len - 1 ] = '\0'; + *id = newId; + + return in; +} + +char *OpenDDLParser::parsePrimitiveDataType( char *in, char *end, Value::ValueType &type, size_t &len ) { + type = Value::ddl_none; + len = 0; + if( nullptr == in || in == end ) { + return in; + } + + for( unsigned int i = 0; i < Value::ddl_types_max; i++ ) { + const size_t prim_len( strlen( PrimitiveTypeToken[ i ] ) ); + if( 0 == strncmp( in, PrimitiveTypeToken[ i ], prim_len ) ) { + type = ( Value::ValueType ) i; + break; + } + } + + if( Value::ddl_none == type ) { + in = getNextToken( in, end ); + return in; + } else { + in += strlen( PrimitiveTypeToken[ type ] ); + } + + bool ok( true ); + if( *in == '[' ) { + ok = false; + in++; + char *start( in ); + while ( in != end ) { + in++; + if( *in == ']' ) { + len = atoi( start ); + ok = true; + in++; + break; + } + } + } else { + len = 1; + } + if( !ok ) { + type = Value::ddl_none; + } + + return in; +} + +char *OpenDDLParser::parseReference( char *in, char *end, std::vector &names ) { + if( nullptr == in || in == end ) { + return in; + } + + if( 0 != strncmp( in, RefToken, strlen( RefToken ) ) ) { + return false; + } else { + const size_t refTokenLen( strlen( RefToken ) ); + in += refTokenLen; + } + + in = getNextToken( in, end ); + if( '{' != *in ) { + return in; + } else { + in++; + } + + in = getNextToken( in, end ); + Name *nextName( nullptr ); + in = parseName( in, end, &nextName ); + if( nextName ) { + names.push_back( nextName ); + } + while( '}' != *in ) { + in = getNextSeparator( in, end ); + if( ',' == *in ) { + in = parseName( in, end, &nextName ); + if( nextName ) { + names.push_back( nextName ); + } + } else { + break; + } + } + + return in; +} + +char *OpenDDLParser::parseBooleanLiteral( char *in, char *end, Value **boolean ) { + *boolean = nullptr; + if( nullptr == in || in == end ) { + return in; + } + + in = getNextToken( in, end ); + char *start( in ); + size_t len( 0 ); + while( !isSeparator( *in ) && in != end ) { + in++; + len++; + } + len++; + int res = ::strncmp( BoolTrue, start, strlen( BoolTrue ) ); + if( 0 != res ) { + res = ::strncmp( BoolFalse, start, strlen( BoolFalse ) ); + if( 0 != res ) { + *boolean = nullptr; + return in; + } + *boolean = ValueAllocator::allocPrimData( Value::ddl_bool ); + (*boolean)->setBool( false ); + } else { + *boolean = ValueAllocator::allocPrimData( Value::ddl_bool ); + (*boolean)->setBool( true ); + } + + return in; +} + +char *OpenDDLParser::parseIntegerLiteral( char *in, char *end, Value **integer, Value::ValueType integerType ) { + *integer = nullptr; + if( nullptr == in || in == end ) { + return in; + } + + if( !isIntegerType( integerType ) ) { + return in; + } + + in = getNextToken( in, end ); + char *start( in ); + while( !isSeparator( *in ) && in != end ) { + in++; + } + + if( isNumeric( *start ) ) { + const int value( atoi( start ) ); + *integer = ValueAllocator::allocPrimData( integerType ); + switch( integerType ) { + case Value::ddl_int8: + ( *integer )->setInt8( (int8) value ); + break; + case Value::ddl_int16: + ( *integer )->setInt16( ( int16 ) value ); + break; + case Value::ddl_int32: + ( *integer )->setInt32( ( int32 ) value ); + break; + case Value::ddl_int64: + ( *integer )->setInt64( ( int64 ) value ); + break; + default: + break; + } + } + + return in; +} + +char *OpenDDLParser::parseFloatingLiteral( char *in, char *end, Value **floating ) { + *floating = nullptr; + if( nullptr == in || in == end ) { + return in; + } + + in = getNextToken( in, end ); + char *start( in ); + while( !isSeparator( *in ) && in != end ) { + in++; + } + + // parse the float value + bool ok( false ); + if( isNumeric( *start ) ) { + ok = true; + } else { + if( *start == '-' ) { + if( isNumeric( *(start+1) ) ) { + ok = true; + } + } + } + + if( ok ) { + const float value( ( float ) atof( start ) ); + *floating = ValueAllocator::allocPrimData( Value::ddl_float ); + ( *floating )->setFloat( value ); + } + + return in; +} + +char *OpenDDLParser::parseStringLiteral( char *in, char *end, Value **stringData ) { + *stringData = nullptr; + if( nullptr == in || in == end ) { + return in; + } + + in = getNextToken( in, end ); + size_t len( 0 ); + char *start( in ); + if( *start == '\"' ) { + start++; + in++; + while( *in != '\"' && in != end ) { + in++; + len++; + } + + *stringData = ValueAllocator::allocPrimData( Value::ddl_string, len + 1 ); + ::strncpy( ( char* ) ( *stringData )->m_data, start, len ); + ( *stringData )->m_data[len] = '\0'; + in++; + } + + return in; +} + +static void createPropertyWithData( Identifier *id, Value *primData, Property **prop ) { + if( nullptr != primData ) { + ( *prop ) = new Property( id ); + ( *prop )->m_primData = primData; + } +} + +char *OpenDDLParser::parseHexaLiteral( char *in, char *end, Value **data ) { + *data = nullptr; + if( nullptr == in || in == end ) { + return in; + } + + in = getNextToken( in, end ); + if( *in != '0' ) { + return in; + } + + in++; + if( *in != 'x' && *in != 'X' ) { + return in; + } + + in++; + bool ok( true ); + char *start( in ); + int pos( 0 ); + while( !isSeparator( *in ) && in != end ) { + if( ( *in < '0' && *in > '9' ) || ( *in < 'a' && *in > 'f' ) || ( *in < 'A' && *in > 'F' ) ) { + ok = false; + break; + } + pos++; + in++; + } + + if( !ok ) { + return in; + } + + int value( 0 ); + while( pos > 0 ) { + pos--; + value += hex2Decimal( *start ) * static_cast( pow( 16.0, pos ) ); + start++; + } + + *data = ValueAllocator::allocPrimData( Value::ddl_int32 ); + (*data)->setInt32( value ); + + return in; +} + +char *OpenDDLParser::parseProperty( char *in, char *end, Property **prop ) { + *prop = nullptr; + if( nullptr == in || in == end ) { + return in; + } + + in = getNextToken( in, end ); + Identifier *id( nullptr ); + in = parseIdentifier( in, end, &id ); + if( nullptr != id ) { + in = getNextToken( in, end ); + if( *in == '=' ) { + in++; + in = getNextToken( in, end ); + Value *primData( nullptr ); + if( isInteger( in, end ) ) { + in = parseIntegerLiteral( in, end, &primData ); + createPropertyWithData( id, primData, prop ); + } else if( isFloat( in, end ) ) { + in = parseFloatingLiteral( in, end, &primData ); + createPropertyWithData( id, primData, prop ); + } else if( isStringLiteral( *in ) ) { // string data + in = parseStringLiteral( in, end, &primData ); + createPropertyWithData( id, primData, prop ); + } else { // reference data + std::vector names; + in = parseReference( in, end, names ); + if( !names.empty() ) { + Reference *ref = new Reference( names.size(), &names[ 0 ] ); + ( *prop ) = new Property( id ); + ( *prop )->m_ref = ref; + } + } + } + } + + return in; +} + +char *OpenDDLParser::parseDataList( char *in, char *end, Value **data ) { + *data = nullptr; + if( nullptr == in || in == end ) { + return in; + } + + in = getNextToken( in, end ); + if( *in == '{' ) { + in++; + Value *current( nullptr ), *prev( nullptr ); + while( '}' != *in ) { + current = nullptr; + in = getNextToken( in, end ); + if( isInteger( in, end ) ) { + in = parseIntegerLiteral( in, end, ¤t ); + } else if( isFloat( in, end ) ) { + in = parseFloatingLiteral( in, end, ¤t ); + } else if( isStringLiteral( *in ) ) { + in = parseStringLiteral( in, end, ¤t ); + } else if( isHexLiteral( in, end ) ) { + in = parseHexaLiteral( in, end, ¤t ); + } + + if( nullptr != current ) { + if( nullptr == *data ) { + *data = current; + prev = current; + } else { + prev->setNext( current ); + prev = current; + } + } + + in = getNextSeparator( in, end ); + if( ',' != *in && '}' != *in && !isSpace( *in ) ) { + break; + } + } + in++; + } + + return in; +} + +char *OpenDDLParser::parseDataArrayList( char *in, char *end, DataArrayList **dataList ) { + *dataList = nullptr; + if( nullptr == in || in == end ) { + return in; + } + + in = getNextToken( in, end ); + if( *in == '{' ) { + in++; + Value *current( nullptr ); + DataArrayList *prev( nullptr ), *currentDataList( nullptr ); + do { + in = parseDataList( in, end, ¤t ); + if( nullptr != current ) { + if( nullptr == prev ) { + *dataList = new DataArrayList; + (*dataList)->m_dataList = current; + prev = *dataList; + } else { + currentDataList = new DataArrayList; + if( nullptr != prev ) { + prev->m_next = currentDataList; + prev = currentDataList; + } + } + } + } while( ',' == *in && in != end ); + } + + return in; +} + +const char *OpenDDLParser::getVersion() { + return Version; +} + +END_ODDLPARSER_NS diff --git a/contrib/openddlparser/code/Value.cpp b/contrib/openddlparser/code/Value.cpp new file mode 100644 index 000000000..71e97a566 --- /dev/null +++ b/contrib/openddlparser/code/Value.cpp @@ -0,0 +1,243 @@ +/*----------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2014 Kim Kulling + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-----------------------------------------------------------------------------------------------*/ +#include +#include +#include + +BEGIN_ODDLPARSER_NS + +Value::Value() +: m_type( ddl_none ) +, m_size( 0 ) +, m_data( nullptr ) +, m_next( nullptr ) { + // empty +} + +Value::~Value() { + // empty +} + +void Value::setBool( bool value ) { + assert( ddl_bool == m_type ); + ::memcpy( m_data, &value, m_size ); +} + +bool Value::getBool() { + assert( ddl_bool == m_type ); + return ( bool ) ( *m_data ); +} + +void Value::setInt8( int8 value ) { + assert( ddl_int8 == m_type ); + ::memcpy( m_data, &value, m_size ); +} + +int8 Value::getInt8() { + assert( ddl_int8 == m_type ); + return ( int8 ) ( *m_data ); +} + +void Value::setInt16( int16 value ) { + assert( ddl_int16 == m_type ); + ::memcpy( m_data, &value, m_size ); +} + +int16 Value::getInt16() { + assert( ddl_int16 == m_type ); + return ( int16 ) ( *m_data ); +} + +void Value::setInt32( int32 value ) { + assert( ddl_int32 == m_type ); + ::memcpy( m_data, &value, m_size ); +} + +int32 Value::getInt32() { + assert( ddl_int32 == m_type ); + return ( int32 ) ( *m_data ); +} + +void Value::setInt64( int64 value ) { + assert( ddl_int32 == m_type ); + ::memcpy( m_data, &value, m_size ); +} + +int64 Value::getInt64() { + return ( int64 ) ( *m_data ); +} + +void Value::setFloat( float value ) { + assert( ddl_float == m_type ); + ::memcpy( m_data, &value, m_size ); +} + +float Value::getFloat() const { + float v; + ::memcpy( &v, m_data, m_size ); + return v; +} + +void Value::setDouble( double value ) { + assert( ddl_double == m_type ); + ::memcpy( m_data, &value, m_size ); +} + +double Value::getDouble() const { + double v; + ::memcpy( &v, m_data, m_size ); + return v; +} + +void Value::dump() { + switch( m_type ) { + case ddl_none: + std::cout << "None" << std::endl; + break; + case ddl_bool: + std::cout << getBool() << std::endl; + break; + case ddl_int8: + std::cout << getInt8() << std::endl; + break; + case ddl_int16: + std::cout << getInt16() << std::endl; + break; + case ddl_int32: + std::cout << getInt32() << std::endl; + break; + case ddl_int64: + std::cout << getInt64() << std::endl; + break; + case ddl_unsigned_int8: + std::cout << "Not supported" << std::endl; + break; + case ddl_unsigned_int16: + std::cout << "Not supported" << std::endl; + break; + case ddl_unsigned_int32: + std::cout << "Not supported" << std::endl; + break; + case ddl_unsigned_int64: + std::cout << "Not supported" << std::endl; + break; + case ddl_half: + std::cout << "Not supported" << std::endl; + break; + case ddl_float: + std::cout << getFloat() << std::endl; + break; + case ddl_double: + std::cout << getDouble() << std::endl; + break; + case ddl_string: + std::cout << "Not supported" << std::endl; + break; + case ddl_ref: + std::cout << "Not supported" << std::endl; + break; + default: + break; + } +} + +void Value::setNext( Value *next ) { + m_next = next; +} + +Value *Value::getNext() const { + return m_next; +} + +Value *ValueAllocator::allocPrimData( Value::ValueType type, size_t len ) { + if( type == Value::ddl_none || Value::ddl_types_max == type ) { + return nullptr; + } + + Value *data = new Value; + data->m_type = type; + switch( type ) { + case Value::ddl_bool: + data->m_size = sizeof( bool ); + break; + case Value::ddl_int8: + data->m_size = sizeof( char ); + break; + case Value::ddl_int16: + data->m_size = sizeof( short ); + break; + case Value::ddl_int32: + data->m_size = sizeof( int ); + break; + case Value::ddl_int64: + data->m_size = sizeof( long ); + break; + case Value::ddl_unsigned_int8: + data->m_size = sizeof( unsigned char ); + break; + case Value::ddl_unsigned_int32: + data->m_size = sizeof( unsigned int ); + break; + case Value::ddl_unsigned_int64: + data->m_size = sizeof( unsigned long ); + break; + case Value::ddl_half: + data->m_size = sizeof( short ); + break; + case Value::ddl_float: + data->m_size = sizeof( float ); + break; + case Value::ddl_double: + data->m_size = sizeof( double ); + break; + case Value::ddl_string: + data->m_size = sizeof( char ); + break; + case Value::ddl_ref: + data->m_size = sizeof( char ); + break; + case Value::ddl_none: + case Value::ddl_types_max: + default: + break; + } + + if( data->m_size ) { + data->m_size *= len; + data->m_data = new unsigned char[ data->m_size ]; + } + + return data; +} + +void ValueAllocator::releasePrimData( Value **data ) { + if( !data ) { + return; + } + + delete *data; + *data = nullptr; +} + + +END_ODDLPARSER_NS diff --git a/contrib/openddlparser/include/openddlparser/DDLNode.h b/contrib/openddlparser/include/openddlparser/DDLNode.h new file mode 100644 index 000000000..ceaff3d5e --- /dev/null +++ b/contrib/openddlparser/include/openddlparser/DDLNode.h @@ -0,0 +1,87 @@ +/*----------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2014 Kim Kulling + +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. +-----------------------------------------------------------------------------------------------*/ +#pragma once +#ifndef OPENDDLPARSER_DDLNODE_H_INC +#define OPENDDLPARSER_DDLNODE_H_INC + +#include + +#include +#include + +BEGIN_ODDLPARSER_NS + +class Value; +class OpenDDLParser; + +struct Identifier; +struct Reference; +struct Property; +struct DataArrayList; + +class DLL_ODDLPARSER_EXPORT DDLNode { +public: + friend class OpenDDLParser; + + typedef std::vector DllNodeList; + +public: + ~DDLNode(); + void attachParent( DDLNode *parent ); + void detachParent(); + DDLNode *getParent() const; + const DllNodeList &getChildNodeList() const; + void setType( const std::string &name ); + const std::string &getType() const; + void setName( const std::string &name ); + const std::string &getName() const; + void setProperties( Property *first ); + Property *getProperties() const; + void setValue( Value *val ); + Value *getValue() const; + void setDataArrayList( DataArrayList *dtArrayList ); + DataArrayList *getDataArrayList() const; + static DDLNode *create( const std::string &type, const std::string &name, DDLNode *parent = nullptr ); + +private: + DDLNode( const std::string &type, const std::string &name, size_t idx, DDLNode *parent = nullptr ); + DDLNode(); + DDLNode( const DDLNode & ); + DDLNode &operator = ( const DDLNode & ); + static void releaseNodes(); + +private: + std::string m_type; + std::string m_name; + DDLNode *m_parent; + std::vector m_children; + Property *m_properties; + Value *m_value; + DataArrayList *m_dtArrayList; + size_t m_idx; + static DllNodeList s_allocatedNodes; +}; + +END_ODDLPARSER_NS + +#endif // OPENDDLPARSER_DDLNODE_H_INC diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h new file mode 100644 index 000000000..490ea46ac --- /dev/null +++ b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h @@ -0,0 +1,148 @@ +/*----------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2014 Kim Kulling + +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. +-----------------------------------------------------------------------------------------------*/ +#pragma once +#ifndef OPENDDLPARSER_OPENDDLPARSERCOMMON_H_INC +#define OPENDDLPARSER_OPENDDLPARSERCOMMON_H_INC + +#include +#include + +#ifdef _WIN32 +# define TAG_DLL_EXPORT __declspec(dllexport) +# define TAG_DLL_IMPORT __declspec(dllimport ) +# ifdef OPENDDLPARSER_BUILD +# define DLL_ODDLPARSER_EXPORT TAG_DLL_EXPORT +# else +# define DLL_ODDLPARSER_EXPORT TAG_DLL_IMPORT +# endif // OPENDDLPARSER_BUILD +# pragma warning( disable : 4251 ) +#else +# define DLL_ODDLPARSER_EXPORT +#endif // _WIN32 + +#define BEGIN_ODDLPARSER_NS namespace ODDLParser { +#define END_ODDLPARSER_NS } +#define USE_ODDLPARSER_NS using namespace ODDLParser; + +BEGIN_ODDLPARSER_NS + +class DDLNode; +class Value; + +struct Name; +struct Identifier; +struct Reference; +struct Property; +struct DataArrayList; + +typedef char int8; +typedef short int16; +typedef int int32; +typedef long int64; + +enum NameType { + GlobalName, + LocalName +}; + +struct Name { + NameType m_type; + Identifier *m_id; + + Name( NameType type, Identifier *id ) + : m_type( type ) + , m_id( id ) { + // empty + } +}; + +struct Reference { + size_t m_numRefs; + Name **m_referencedName; + + Reference( size_t numrefs, Name **names ) + : m_numRefs( numrefs ) + , m_referencedName( names ) { + // empty + } +}; + +struct Identifier { + size_t m_len; + char *m_buffer; + + Identifier( size_t len, char *buffer ) + : m_len( len ) + , m_buffer( buffer ) { + // empty + } +}; + +struct Property { + Identifier *m_id; + Value *m_primData; + Reference *m_ref; + Property *m_next; + + Property( Identifier *id ) + : m_id( id ) + , m_primData( nullptr ) + , m_ref( nullptr ) + , m_next( nullptr ) { + // empty + } +}; + +struct DataArrayList { + size_t m_numItems; + Value *m_dataList; + DataArrayList *m_next; + + DataArrayList() + : m_numItems( 0 ) + , m_dataList( nullptr ) + , m_next( nullptr ) { + // empty + } +}; + +struct Context { + Property *m_properties; + DDLNode *m_root; + + Context() + : m_properties( nullptr ) + , m_root( nullptr ) { + // empty + } +}; + +END_ODDLPARSER_NS + +#define ODDL_NO_COPYING( classname ) \ +private: \ + classname( const classname & ); \ + classname &operator = ( const classname & ); + +#endif // OPENDDLPARSER_OPENDDLPARSERCOMMON_H_INC + diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h b/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h new file mode 100644 index 000000000..9e978ade2 --- /dev/null +++ b/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h @@ -0,0 +1,233 @@ +/*----------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2014 Kim Kulling + +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. +-----------------------------------------------------------------------------------------------*/ +#pragma once +#ifndef OPENDDLPARSER_OPENDDLPARSERUTILS_H_INC +#define OPENDDLPARSER_OPENDDLPARSERUTILS_H_INC + +#include + +BEGIN_ODDLPARSER_NS + +template +inline +bool isComment( T *in, T *end ) { + if( *in == '/' ) { + if( in + 1 != end ) { + if( *( in + 1 ) == '/' ) { + return true; + } + } + } + return false; +} + +template +inline +bool isUpperCase( T in ) { + return ( in >= 'A' && in <= 'Z' ); +} + +template +inline +bool isLowerCase( T in ) { + return ( in >= 'a' && in <= 'z' ); +} + +template +inline +bool isSpace( const T in ) { + return ( ' ' == in || '\t' == in ); +} + +template +inline +bool isNewLine( const T in ) { + return ( '\n' == in ); +} + +template +inline +bool isSeparator( T in ) { + if( isSpace( in ) || ',' == in || '{' == in || '}' == in || '[' == in ) { + return true; + } + return false; +} + +static const unsigned char chartype_table[ 256 ] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0-15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32-47 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 48-63 + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 64-79 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80-95 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 96-111 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 112-127 + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // > 127 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +template +inline +bool isNumeric( const T in ) { + return ( in >= '0' && in <= '9' ); + //return ( chartype_table[in] ); + /*if (in >= '0' && in <= '9' ) + return true; + + return false;*/ +} + +template +inline +bool isInteger( T *in, T *end ) { + if( in != end ) { + if( *in == '-' ) { + in++; + } + } + + bool result( false ); + while( '}' != *in && ',' != *in && !isSpace( *in ) && in != end ) { + result = isNumeric( *in ); + if( !result ) { + break; + } + in++; + } + + return result; +} + +template +inline +bool isFloat( T *in, T *end ) { + if( in != end ) { + if( *in == '-' ) { + in++; + } + } + + // check for <1>.0f + bool result( false ); + while( !isSpace( *in ) && in != end ) { + if( *in == '.' ) { + result = true; + break; + } + result = isNumeric( *in ); + if( !result ) { + return false; + } + in++; + } + + // check for 1<.>0f + if( *in == '.' ) { + in++; + } else { + return false; + } + + // check for 1.<0>f + while( !isSpace( *in ) && in != end && *in != ',' ) { + result = isNumeric( *in ); + if( !result ) { + return false; + } + in++; + } + + return result; +} + +template +inline +bool isCharacter( const T in ) { + return ( in >= 'a' && in <= 'z' || in >= 'A' && in <= 'Z' ); +} + +template +inline +bool isStringLiteral( const T in ) { + return ( in == '\"' ); +} + +template +inline +bool isHexLiteral( T *in, T *end ) { + if( *in == '0' ) { + if( in + 1 != end ) { + if( *( in + 1 ) == 'x' || *( in + 1 ) == 'X' ) { + return true; + } + } + } + + return false; +} + +template +inline +bool isEndofLine( const T in ) { + return ( '\n' == in ); +} + +template +inline +static T *getNextSeparator( T *in, T *end ) { + while( !isSeparator( *in ) || in == end ) { + in++; + } + return in; +} + +static const int ErrorHex2Decimal = 9999; + +inline +int hex2Decimal( char in ) { + if( isNumeric( in ) ) { + return (int) in-48; + } + char hexCodeLower( 'a' ), hexCodeUpper( 'A' ); + for( int i = 0; i<16; i++ ) { + if( in == hexCodeLower + i || in == hexCodeUpper + i ) { + return i+10; + } + } + + return ErrorHex2Decimal; +} + +END_ODDLPARSER_NS + +#endif // OPENDDLPARSER_OPENDDLPARSERUTILS_H_INC diff --git a/contrib/openddlparser/include/openddlparser/Value.h b/contrib/openddlparser/include/openddlparser/Value.h new file mode 100644 index 000000000..665c407ed --- /dev/null +++ b/contrib/openddlparser/include/openddlparser/Value.h @@ -0,0 +1,85 @@ +/*----------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2014 Kim Kulling + +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. +-----------------------------------------------------------------------------------------------*/ +#pragma once +#ifndef OPENDDLPARSER_VALUE_H_INC +#define OPENDDLPARSER_VALUE_H_INC + +#include + +BEGIN_ODDLPARSER_NS + +class DLL_ODDLPARSER_EXPORT Value { +public: + enum ValueType { + ddl_none = -1, + ddl_bool = 0, + ddl_int8, + ddl_int16, + ddl_int32, + ddl_int64, + ddl_unsigned_int8, + ddl_unsigned_int16, + ddl_unsigned_int32, + ddl_unsigned_int64, + ddl_half, + ddl_float, + ddl_double, + ddl_string, + ddl_ref, + ddl_types_max + }; + + Value(); + ~Value(); + void setBool( bool value ); + bool getBool(); + void setInt8( int8 value ); + int8 getInt8(); + void setInt16( int16 value ); + int16 getInt16(); + void setInt32( int32 value ); + int32 getInt32(); + void setInt64( int64 value ); + int64 getInt64(); + void setFloat( float value ); + float getFloat() const; + void setDouble( double value ); + double getDouble() const; + void dump(); + void setNext( Value *next ); + Value *getNext() const; + + ValueType m_type; + size_t m_size; + unsigned char *m_data; + Value *m_next; +}; + +struct DLL_ODDLPARSER_EXPORT ValueAllocator { + static Value *allocPrimData( Value::ValueType type, size_t len = 1 ); + static void releasePrimData( Value **data ); +}; + +END_ODDLPARSER_NS + +#endif // OPENDDLPARSER_VALUE_H_INC From 8e87613bb3f19d312e801381a6b0576797fcc0ed Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 31 Jan 2015 11:31:39 +0100 Subject: [PATCH 12/74] add mmissing header. Signed-off-by: Kim Kulling --- .../include/openddlparser/OpenDDLParser.h | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 contrib/openddlparser/include/openddlparser/OpenDDLParser.h diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h new file mode 100644 index 000000000..76c5ca7ef --- /dev/null +++ b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h @@ -0,0 +1,117 @@ +/*----------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2014 Kim Kulling + +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. +-----------------------------------------------------------------------------------------------*/ +#pragma once +#ifndef OPENDDLPARSER_OPENDDLPARSER_H_INC +#define OPENDDLPARSER_OPENDDLPARSER_H_INC + +#include +#include +#include +#include + +#include +#include + +BEGIN_ODDLPARSER_NS + +class DDLNode; +class Value; + +struct Identifier; +struct Reference; +struct Property; + +template +inline +T *getNextToken( T *in, T *end ) { + while( ( isSpace( *in ) || isNewLine( *in ) || ',' == *in ) && ( in != end ) ) { + in++; + } + return in; +} + +enum LogSeverity { + ddl_debug_msg = 0, + ddl_info_msg, + ddl_warn_msg, + ddl_error_msg, +}; + +class DLL_ODDLPARSER_EXPORT OpenDDLParser { +public: + typedef void( *logCallback )( LogSeverity severity, const std::string &msg ); + +public: + OpenDDLParser(); + OpenDDLParser( char *buffer, size_t len, bool ownsIt = false ); + ~OpenDDLParser(); + void setLogCallback( logCallback callback ); + logCallback getLogCallback() const; + void setBuffer( char *buffer, size_t len, bool ownsIt = false ); + char *getBuffer() const; + size_t getBufferSize() const; + void clear(); + bool parse(); + char *parseNextNode( char *current, char *end ); + char *parseHeader( char *in, char *end ); + char *parseStructure( char *in, char *end ); + void pushNode( DDLNode *node ); + DDLNode *popNode(); + DDLNode *top(); + DDLNode *getRoot() const; + Context *getContext() const; + +public: // static parser helpers + static void normalizeBuffer( char *buffer, size_t len ); + static char *parseName( char *in, char *end, Name **name ); + static char *parseIdentifier( char *in, char *end, Identifier **id ); + static char *parsePrimitiveDataType( char *in, char *end, Value::ValueType &type, size_t &len ); + static char *parseReference( char *in, char *end, std::vector &names ); + static char *parseBooleanLiteral( char *in, char *end, Value **boolean ); + static char *parseIntegerLiteral( char *in, char *end, Value **integer, Value::ValueType integerType = Value::ddl_int32 ); + static char *parseFloatingLiteral( char *in, char *end, Value **floating ); + static char *parseStringLiteral( char *in, char *end, Value **stringData ); + static char *parseHexaLiteral( char *in, char *end, Value **data ); + static char *parseProperty( char *in, char *end, Property **prop ); + static char *parseDataList( char *in, char *end, Value **data ); + static char *parseDataArrayList( char *in, char *end, DataArrayList **dataList ); + static const char *getVersion(); + +private: + OpenDDLParser( const OpenDDLParser & ); + OpenDDLParser &operator = ( const OpenDDLParser & ); + +private: + logCallback m_logCallback; + bool m_ownsBuffer; + char *m_buffer; + size_t m_len; + //DDLNode *m_root; + typedef std::vector DDLNodeStack; + DDLNodeStack m_stack; + Context *m_context; +}; + +END_ODDLPARSER_NS + +#endif // OPENDDLPARSER_OPENDDLPARSER_H_INC From e1c6a16b8527147f7ec9ae134b9ae5334da18707 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 31 Jan 2015 11:46:13 +0100 Subject: [PATCH 13/74] support of openddlparser lib without x++11. Signed-off-by: Kim Kulling --- contrib/openddlparser/CMakeLists.txt | 1 + contrib/openddlparser/code/DDLNode.cpp | 18 +-- contrib/openddlparser/code/OpenDDLParser.cpp | 130 +++++++++--------- contrib/openddlparser/code/Value.cpp | 9 +- .../include/openddlparser/OpenDDLCommon.h | 20 ++- .../include/openddlparser/OpenDDLParser.h | 1 - 6 files changed, 92 insertions(+), 87 deletions(-) diff --git a/contrib/openddlparser/CMakeLists.txt b/contrib/openddlparser/CMakeLists.txt index d551c9857..a39d0219f 100644 --- a/contrib/openddlparser/CMakeLists.txt +++ b/contrib/openddlparser/CMakeLists.txt @@ -13,6 +13,7 @@ else() endif() add_definitions( -DOPENDDLPARSER_BUILD ) +add_definitions( -DOPENDDL_NO_USE_CPP11 ) add_definitions( -D_VARIADIC_MAX=10 ) INCLUDE_DIRECTORIES( diff --git a/contrib/openddlparser/code/DDLNode.cpp b/contrib/openddlparser/code/DDLNode.cpp index 30977ca2e..94e8eb4d7 100644 --- a/contrib/openddlparser/code/DDLNode.cpp +++ b/contrib/openddlparser/code/DDLNode.cpp @@ -32,11 +32,11 @@ DDLNode::DllNodeList DDLNode::s_allocatedNodes; template inline static void releaseDataType( T *ptr ) { - if( nullptr == ptr ) { + if( ddl_nullptr == ptr ) { return; } - T *current( nullptr ); + T *current( ddl_nullptr ); while( ptr ) { current = ptr; ptr = ptr->m_next; @@ -49,10 +49,10 @@ DDLNode::DDLNode( const std::string &type, const std::string &name, size_t idx, , m_name( name ) , m_parent( parent ) , m_children() -, m_properties( nullptr ) -, m_value( nullptr ) +, m_properties( ddl_nullptr ) +, m_value( ddl_nullptr ) , m_idx( idx ) -, m_dtArrayList( nullptr ) { +, m_dtArrayList( ddl_nullptr ) { if( m_parent ) { m_parent->m_children.push_back( this ); } @@ -63,9 +63,9 @@ DDLNode::~DDLNode() { releaseDataType( m_value ); delete m_dtArrayList; - m_dtArrayList = nullptr; + m_dtArrayList = ddl_nullptr; if( s_allocatedNodes[ m_idx ] == this ) { - s_allocatedNodes[ m_idx ] = nullptr; + s_allocatedNodes[ m_idx ] = ddl_nullptr; } } @@ -75,7 +75,7 @@ void DDLNode::attachParent( DDLNode *parent ) { } m_parent = parent; - if( nullptr != m_parent ) { + if( ddl_nullptr != m_parent ) { m_parent->m_children.push_back( this ); } } @@ -87,7 +87,7 @@ void DDLNode::detachParent() { if( m_parent->m_children.end() != it ) { m_parent->m_children.erase( it ); } - m_parent = nullptr; + m_parent = ddl_nullptr; } } diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index 675d58511..3ae26fdf2 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -73,14 +73,14 @@ static bool isIntegerType( Value::ValueType integerType ) { } static DDLNode *createDDLNode( Identifier *id, Property *first, OpenDDLParser *parser ) { - if( nullptr == id || nullptr == parser ) { - return nullptr; + if( nullptr == id || ddl_nullptr == parser ) { + return ddl_nullptr; } const std::string type( id->m_buffer ); DDLNode *parent( parser->top() ); DDLNode *node = DDLNode::create( type, "", parent ); - if( nullptr != first ) { + if( ddl_nullptr != first ) { node->setProperties( first ); } @@ -108,19 +108,19 @@ static void logMessage( LogSeverity severity, const std::string &msg ) { OpenDDLParser::OpenDDLParser() : m_logCallback( logMessage ) , m_ownsBuffer( false ) -,m_buffer( nullptr ) +, m_buffer( ddl_nullptr ) , m_len( 0 ) , m_stack() -, m_context( nullptr ) { +, m_context( ddl_nullptr ) { // empty } OpenDDLParser::OpenDDLParser( char *buffer, size_t len, bool ownsIt ) : m_logCallback( &logMessage ) , m_ownsBuffer( false ) -, m_buffer( nullptr ) +, m_buffer( ddl_nullptr ) , m_len( 0 ) -, m_context( nullptr ) { +, m_context( ddl_nullptr ) { if( 0 != m_len ) { setBuffer( buffer, len, ownsIt ); } @@ -147,7 +147,7 @@ OpenDDLParser::logCallback OpenDDLParser::getLogCallback() const { void OpenDDLParser::setBuffer( char *buffer, size_t len, bool ownsIt ) { if( m_buffer && m_ownsBuffer ) { delete[] m_buffer; - m_buffer = nullptr; + m_buffer = ddl_nullptr; m_len = 0; } @@ -176,11 +176,11 @@ void OpenDDLParser::clear() { if( m_ownsBuffer ) { delete [] m_buffer; } - m_buffer = nullptr; + m_buffer = ddl_nullptr; m_len = 0; if( m_context ) { - m_context->m_root = nullptr; + m_context->m_root = ddl_nullptr; } DDLNode::releaseNodes(); @@ -194,7 +194,7 @@ bool OpenDDLParser::parse() { normalizeBuffer( m_buffer, m_len ); m_context = new Context; - m_context->m_root = DDLNode::create( "root", "", nullptr ); + m_context->m_root = DDLNode::create( "root", "", ddl_nullptr ); pushNode( m_context->m_root ); // do the main parsing @@ -218,7 +218,7 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { return in; } - Identifier *id( nullptr ); + Identifier *id( ddl_nullptr ); in = OpenDDLParser::parseIdentifier( in, end, &id ); #ifdef DEBUG_HEADER_NAME @@ -228,11 +228,11 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { #endif // DEBUG_HEADER_NAME in = getNextToken( in, end ); - Property *first( nullptr ); - if( nullptr != id ) { + Property *first( ddl_nullptr ); + if( ddl_nullptr != id ) { if( *in == '(' ) { in++; - Property *prop( nullptr ), *prev( nullptr ); + Property *prop( ddl_nullptr ), *prev( ddl_nullptr ); while( *in != ')' && in != end ) { in = parseProperty( in, end, &prop ); in = getNextToken( in, end ); @@ -241,11 +241,11 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { logInvalidTokenError( in, ")", m_logCallback ); return in; } - if( nullptr != prop && *in != ',' ) { - if( nullptr == first ) { + if( ddl_nullptr != prop && *in != ',' ) { + if( ddl_nullptr == first ) { first = prop; } - if( nullptr != prev ) { + if( ddl_nullptr != prev ) { prev->m_next = prop; } prev = prop; @@ -262,9 +262,9 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { std::cerr << "nullptr returned by creating DDLNode." << std::endl; } - Name *name( nullptr ); + Name *name( ddl_nullptr ); in = OpenDDLParser::parseName( in, end, &name ); - if( nullptr != name ) { + if( ddl_nullptr != name ) { const std::string nodeName( name->m_id->m_buffer ); node->setName( nodeName ); } @@ -288,21 +288,21 @@ char *OpenDDLParser::parseStructure( char *in, char *end ) { if( Value::ddl_none != type ) { in = getNextToken( in, end ); if( *in == '{' ) { - DataArrayList *dtArrayList( nullptr ); - Value *values( nullptr ); + DataArrayList *dtArrayList( ddl_nullptr ); + Value *values( ddl_nullptr ); if( 1 == arrayLen ) { in = parseDataList( in, end, &values ); - if( nullptr != values ){ + if( ddl_nullptr != values ){ DDLNode *currentNode( top() ); - if( nullptr != currentNode ) { + if( ddl_nullptr != currentNode ) { currentNode->setValue( values ); } } } else if( arrayLen > 1 ) { in = parseDataArrayList( in, end, &dtArrayList ); - if( nullptr != dtArrayList ) { + if( ddl_nullptr != dtArrayList ) { DDLNode *currentNode( top() ); - if( nullptr != currentNode ) { + if( ddl_nullptr != currentNode ) { currentNode->setDataArrayList( dtArrayList ); } } @@ -341,7 +341,7 @@ void OpenDDLParser::pushNode( DDLNode *node ) { DDLNode *OpenDDLParser::popNode() { if( m_stack.empty() ) { - return nullptr; + return ddl_nullptr; } DDLNode *topNode( top() ); @@ -352,7 +352,7 @@ DDLNode *OpenDDLParser::popNode() { DDLNode *OpenDDLParser::top() { if( m_stack.empty() ) { - return nullptr; + return ddl_nullptr; } DDLNode *top( m_stack.back() ); @@ -361,7 +361,7 @@ DDLNode *OpenDDLParser::top() { DDLNode *OpenDDLParser::getRoot() const { if( nullptr == m_context ) { - return nullptr; + return ddl_nullptr; } return m_context->m_root; @@ -401,8 +401,8 @@ void OpenDDLParser::normalizeBuffer( char *buffer, size_t len ) { } char *OpenDDLParser::parseName( char *in, char *end, Name **name ) { - *name = nullptr; - if( nullptr == in || in == end ) { + *name = ddl_nullptr; + if( ddl_nullptr == in || in == end ) { return in; } @@ -417,8 +417,8 @@ char *OpenDDLParser::parseName( char *in, char *end, Name **name ) { ntype = LocalName; } - Name *currentName( nullptr ); - Identifier *id( nullptr ); + Name *currentName( ddl_nullptr ); + Identifier *id( ddl_nullptr ); in = parseIdentifier( in, end, &id ); if( id ) { currentName = new Name( ntype, id ); @@ -431,8 +431,8 @@ char *OpenDDLParser::parseName( char *in, char *end, Name **name ) { } char *OpenDDLParser::parseIdentifier( char *in, char *end, Identifier **id ) { - *id = nullptr; - if( nullptr == in || in == end ) { + *id = ddl_nullptr; + if( ddl_nullptr == in || in == end ) { return in; } @@ -464,7 +464,7 @@ char *OpenDDLParser::parseIdentifier( char *in, char *end, Identifier **id ) { char *OpenDDLParser::parsePrimitiveDataType( char *in, char *end, Value::ValueType &type, size_t &len ) { type = Value::ddl_none; len = 0; - if( nullptr == in || in == end ) { + if( ddl_nullptr == in || in == end ) { return in; } @@ -508,7 +508,7 @@ char *OpenDDLParser::parsePrimitiveDataType( char *in, char *end, Value::ValueTy } char *OpenDDLParser::parseReference( char *in, char *end, std::vector &names ) { - if( nullptr == in || in == end ) { + if( ddl_nullptr == in || in == end ) { return in; } @@ -527,7 +527,7 @@ char *OpenDDLParser::parseReference( char *in, char *end, std::vector &na } in = getNextToken( in, end ); - Name *nextName( nullptr ); + Name *nextName( ddl_nullptr ); in = parseName( in, end, &nextName ); if( nextName ) { names.push_back( nextName ); @@ -548,8 +548,8 @@ char *OpenDDLParser::parseReference( char *in, char *end, std::vector &na } char *OpenDDLParser::parseBooleanLiteral( char *in, char *end, Value **boolean ) { - *boolean = nullptr; - if( nullptr == in || in == end ) { + *boolean = ddl_nullptr; + if( ddl_nullptr == in || in == end ) { return in; } @@ -565,7 +565,7 @@ char *OpenDDLParser::parseBooleanLiteral( char *in, char *end, Value **boolean ) if( 0 != res ) { res = ::strncmp( BoolFalse, start, strlen( BoolFalse ) ); if( 0 != res ) { - *boolean = nullptr; + *boolean = ddl_nullptr; return in; } *boolean = ValueAllocator::allocPrimData( Value::ddl_bool ); @@ -579,7 +579,7 @@ char *OpenDDLParser::parseBooleanLiteral( char *in, char *end, Value **boolean ) } char *OpenDDLParser::parseIntegerLiteral( char *in, char *end, Value **integer, Value::ValueType integerType ) { - *integer = nullptr; + *integer = ddl_nullptr; if( nullptr == in || in == end ) { return in; } @@ -619,8 +619,8 @@ char *OpenDDLParser::parseIntegerLiteral( char *in, char *end, Value **integer, } char *OpenDDLParser::parseFloatingLiteral( char *in, char *end, Value **floating ) { - *floating = nullptr; - if( nullptr == in || in == end ) { + *floating = ddl_nullptr; + if( ddl_nullptr == in || in == end ) { return in; } @@ -652,8 +652,8 @@ char *OpenDDLParser::parseFloatingLiteral( char *in, char *end, Value **floating } char *OpenDDLParser::parseStringLiteral( char *in, char *end, Value **stringData ) { - *stringData = nullptr; - if( nullptr == in || in == end ) { + *stringData = ddl_nullptr; + if( ddl_nullptr == in || in == end ) { return in; } @@ -685,8 +685,8 @@ static void createPropertyWithData( Identifier *id, Value *primData, Property ** } char *OpenDDLParser::parseHexaLiteral( char *in, char *end, Value **data ) { - *data = nullptr; - if( nullptr == in || in == end ) { + *data = ddl_nullptr; + if( ddl_nullptr == in || in == end ) { return in; } @@ -731,20 +731,20 @@ char *OpenDDLParser::parseHexaLiteral( char *in, char *end, Value **data ) { } char *OpenDDLParser::parseProperty( char *in, char *end, Property **prop ) { - *prop = nullptr; - if( nullptr == in || in == end ) { + *prop = ddl_nullptr; + if( ddl_nullptr == in || in == end ) { return in; } in = getNextToken( in, end ); - Identifier *id( nullptr ); + Identifier *id( ddl_nullptr ); in = parseIdentifier( in, end, &id ); if( nullptr != id ) { in = getNextToken( in, end ); if( *in == '=' ) { in++; in = getNextToken( in, end ); - Value *primData( nullptr ); + Value *primData( ddl_nullptr ); if( isInteger( in, end ) ) { in = parseIntegerLiteral( in, end, &primData ); createPropertyWithData( id, primData, prop ); @@ -770,17 +770,17 @@ char *OpenDDLParser::parseProperty( char *in, char *end, Property **prop ) { } char *OpenDDLParser::parseDataList( char *in, char *end, Value **data ) { - *data = nullptr; - if( nullptr == in || in == end ) { + *data = ddl_nullptr; + if( ddl_nullptr == in || in == end ) { return in; } in = getNextToken( in, end ); if( *in == '{' ) { in++; - Value *current( nullptr ), *prev( nullptr ); + Value *current( ddl_nullptr ), *prev( ddl_nullptr ); while( '}' != *in ) { - current = nullptr; + current = ddl_nullptr; in = getNextToken( in, end ); if( isInteger( in, end ) ) { in = parseIntegerLiteral( in, end, ¤t ); @@ -792,8 +792,8 @@ char *OpenDDLParser::parseDataList( char *in, char *end, Value **data ) { in = parseHexaLiteral( in, end, ¤t ); } - if( nullptr != current ) { - if( nullptr == *data ) { + if( ddl_nullptr != current ) { + if( ddl_nullptr == *data ) { *data = current; prev = current; } else { @@ -814,26 +814,26 @@ char *OpenDDLParser::parseDataList( char *in, char *end, Value **data ) { } char *OpenDDLParser::parseDataArrayList( char *in, char *end, DataArrayList **dataList ) { - *dataList = nullptr; - if( nullptr == in || in == end ) { + *dataList = ddl_nullptr; + if( ddl_nullptr == in || in == end ) { return in; } in = getNextToken( in, end ); if( *in == '{' ) { in++; - Value *current( nullptr ); - DataArrayList *prev( nullptr ), *currentDataList( nullptr ); + Value *current( ddl_nullptr ); + DataArrayList *prev( ddl_nullptr ), *currentDataList( ddl_nullptr ); do { in = parseDataList( in, end, ¤t ); - if( nullptr != current ) { - if( nullptr == prev ) { + if( ddl_nullptr != current ) { + if( ddl_nullptr == prev ) { *dataList = new DataArrayList; (*dataList)->m_dataList = current; prev = *dataList; } else { currentDataList = new DataArrayList; - if( nullptr != prev ) { + if( ddl_nullptr != prev ) { prev->m_next = currentDataList; prev = currentDataList; } diff --git a/contrib/openddlparser/code/Value.cpp b/contrib/openddlparser/code/Value.cpp index 71e97a566..802e8dd61 100644 --- a/contrib/openddlparser/code/Value.cpp +++ b/contrib/openddlparser/code/Value.cpp @@ -29,8 +29,8 @@ BEGIN_ODDLPARSER_NS Value::Value() : m_type( ddl_none ) , m_size( 0 ) -, m_data( nullptr ) -, m_next( nullptr ) { +, m_data( ddl_nullptr ) +, m_next( ddl_nullptr ) { // empty } @@ -171,7 +171,7 @@ Value *Value::getNext() const { Value *ValueAllocator::allocPrimData( Value::ValueType type, size_t len ) { if( type == Value::ddl_none || Value::ddl_types_max == type ) { - return nullptr; + return ddl_nullptr; } Value *data = new Value; @@ -236,8 +236,7 @@ void ValueAllocator::releasePrimData( Value **data ) { } delete *data; - *data = nullptr; + *data = ddl_nullptr; } - END_ODDLPARSER_NS diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h index 490ea46ac..4a3c16782 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h @@ -46,6 +46,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. BEGIN_ODDLPARSER_NS +#ifndef OPENDDL_NO_USE_CPP11 +# define ddl_nullptr nullptr +#else +# define ddl_nullptr NULL +#endif + class DDLNode; class Value; @@ -106,9 +112,9 @@ struct Property { Property( Identifier *id ) : m_id( id ) - , m_primData( nullptr ) - , m_ref( nullptr ) - , m_next( nullptr ) { + , m_primData( ddl_nullptr ) + , m_ref( ddl_nullptr ) + , m_next( ddl_nullptr ) { // empty } }; @@ -120,8 +126,8 @@ struct DataArrayList { DataArrayList() : m_numItems( 0 ) - , m_dataList( nullptr ) - , m_next( nullptr ) { + , m_dataList( ddl_nullptr ) + , m_next( ddl_nullptr ) { // empty } }; @@ -131,8 +137,8 @@ struct Context { DDLNode *m_root; Context() - : m_properties( nullptr ) - , m_root( nullptr ) { + : m_properties( ddl_nullptr ) + , m_root( ddl_nullptr ) { // empty } }; diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h index 76c5ca7ef..6457e8b74 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h @@ -106,7 +106,6 @@ private: bool m_ownsBuffer; char *m_buffer; size_t m_len; - //DDLNode *m_root; typedef std::vector DDLNodeStack; DDLNodeStack m_stack; Context *m_context; From 0e579bfb06debfec01b68c6a32ea40618500a121 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 31 Jan 2015 12:01:18 +0100 Subject: [PATCH 14/74] openddl-parser: latest greatest. Signed-off-by: Kim Kulling --- contrib/openddlparser/code/OpenDDLParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index 3ae26fdf2..0acd9dd39 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -513,7 +513,7 @@ char *OpenDDLParser::parseReference( char *in, char *end, std::vector &na } if( 0 != strncmp( in, RefToken, strlen( RefToken ) ) ) { - return false; + return in; } else { const size_t refTokenLen( strlen( RefToken ) ); in += refTokenLen; From e9dcad418183e0786613a97d424e3ad607401d22 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 31 Jan 2015 21:23:50 +0100 Subject: [PATCH 15/74] openddl-parser: latest greatest. Signed-off-by: Kim Kulling --- contrib/openddlparser/code/DDLNode.cpp | 8 -------- contrib/openddlparser/code/OpenDDLParser.cpp | 12 +++++++----- .../openddlparser/include/openddlparser/DDLNode.h | 2 -- .../include/openddlparser/OpenDDLCommon.h | 14 ++++++++++++++ 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/contrib/openddlparser/code/DDLNode.cpp b/contrib/openddlparser/code/DDLNode.cpp index 94e8eb4d7..d7c034ba1 100644 --- a/contrib/openddlparser/code/DDLNode.cpp +++ b/contrib/openddlparser/code/DDLNode.cpp @@ -116,14 +116,6 @@ const std::string &DDLNode::getName() const { return m_name; } -void DDLNode::setProperties( Property *first ) { - m_properties = first; -} - -Property *DDLNode::getProperties() const { - return m_properties; -} - void DDLNode::setValue( Value *val ) { m_value = val; } diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index 0acd9dd39..66f55f057 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -72,7 +72,7 @@ static bool isIntegerType( Value::ValueType integerType ) { return true; } -static DDLNode *createDDLNode( Identifier *id, Property *first, OpenDDLParser *parser ) { +static DDLNode *createDDLNode( Identifier *id, OpenDDLParser *parser ) { if( nullptr == id || ddl_nullptr == parser ) { return ddl_nullptr; } @@ -80,9 +80,6 @@ static DDLNode *createDDLNode( Identifier *id, Property *first, OpenDDLParser *p const std::string type( id->m_buffer ); DDLNode *parent( parser->top() ); DDLNode *node = DDLNode::create( type, "", parent ); - if( ddl_nullptr != first ) { - node->setProperties( first ); - } return node; } @@ -254,8 +251,13 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { in++; } + // set the properties + if( ddl_nullptr != first ) { + m_context->setProperties( first ); + } + // store the node - DDLNode *node( createDDLNode( id, first, this ) ); + DDLNode *node( createDDLNode( id, this ) ); if( nullptr != node ) { pushNode( node ); } else { diff --git a/contrib/openddlparser/include/openddlparser/DDLNode.h b/contrib/openddlparser/include/openddlparser/DDLNode.h index ceaff3d5e..85b542b5e 100644 --- a/contrib/openddlparser/include/openddlparser/DDLNode.h +++ b/contrib/openddlparser/include/openddlparser/DDLNode.h @@ -55,8 +55,6 @@ public: const std::string &getType() const; void setName( const std::string &name ); const std::string &getName() const; - void setProperties( Property *first ); - Property *getProperties() const; void setValue( Value *val ); Value *getValue() const; void setDataArrayList( DataArrayList *dtArrayList ); diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h index 4a3c16782..65530c866 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h @@ -141,8 +141,22 @@ struct Context { , m_root( ddl_nullptr ) { // empty } + + void setProperties( Property *first ); + Property *getProperties() const; }; + +inline +void Context::setProperties( Property *first ) { + m_properties = first; +} + +inline +Property *Context::getProperties() const { + return m_properties; +} + END_ODDLPARSER_NS #define ODDL_NO_COPYING( classname ) \ From 56132ed9bbcc1b4d4ac8ff5d672be45be13ad1ed Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Tue, 3 Feb 2015 21:14:37 +0100 Subject: [PATCH 16/74] Regression test suite: make Python files 2.7 backwards-compatible to ease use on Windows where oftentimes only one is easily accessible. Drop deprecated logic to locate assimp binary, we now require the binary to use to be specified in the command line. --- test/regression/gen_db.py | 29 +++++++++++---- test/regression/run.py | 38 ++++++++++++++----- test/regression/utils.py | 77 ++++----------------------------------- 3 files changed, 59 insertions(+), 85 deletions(-) diff --git a/test/regression/gen_db.py b/test/regression/gen_db.py index ef10a298d..d405af9db 100644 --- a/test/regression/gen_db.py +++ b/test/regression/gen_db.py @@ -40,8 +40,15 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # --------------------------------------------------------------------------- -"""Generate the regression database db.zip from the files in the -/test/models directory. Older databases are overwritten with no prompt. +""" +Generate the regression database db.zip from the files in the /test/models +directory. Older databases are overwritten with no prompt but can be restored +using Git as needed. + +Use --help for usage. + +On Windows, use ``py run.py `` to make sure command line parameters +are forwarded to the script. """ import sys @@ -52,9 +59,14 @@ import zipfile import settings import utils -usage = """gen_db [-i=...] [-e=...] [-p] [-n] +usage = """gen_db [assimp_binary] [-i=...] [-e=...] [-p] [-n] + +The assimp_cmd (or assimp) binary to use is specified by the first +command line argument and defaults to ``assimp``. + +To build, set ``ASSIMP_BUILD_ASSIMP_TOOLS=ON`` in CMake. If generating +configs for an IDE, make sure to build the assimp_cmd project. -(lists of file extensions are comma delimited, i.e. `3ds,lwo,x`) -i,--include: List of file extensions to update dumps for. If omitted, all file extensions are updated except those in `exclude`. @@ -66,6 +78,8 @@ usage = """gen_db [-i=...] [-e=...] [-p] [-n] Dont' change anything. -n,--nozip: Don't pack to ZIP archive. Keep all dumps in individual files. + +(lists of file extensions are comma delimited, i.e. `3ds,lwo,x`) """ # ------------------------------------------------------------------------------- @@ -87,7 +101,7 @@ def process_dir(d, outfile, file_filter): outf = os.path.join(os.getcwd(), settings.database_name, utils.hashing(fullp, pp)) - cmd = [utils.assimp_bin_path,"dump",fullp,outf,"-b","-s","-l"] + pp.split() + cmd = [ assimp_bin_path, "dump", fullp, outf, "-b", "-s", "-l" ] + pp.split() outfile.write("assimp dump "+"-"*80+"\n") outfile.flush() if subprocess.call(cmd, stdout=outfile, stderr=outfile, shell=False): @@ -158,7 +172,8 @@ def gen_db(ext_list,outfile): # ------------------------------------------------------------------------------- if __name__ == "__main__": - utils.find_assimp_or_die() + assimp_bin_path = sys.argv[1] if len(sys.argv) > 1 else 'assimp' + def clean(f): f = f.strip("* \'") return "."+f if f[:1] != '.' else f @@ -184,7 +199,7 @@ if __name__ == "__main__": outfile = open(os.path.join("..", "results", "gen_regression_db_output.txt"), "w") if ext_list is None: - (ext_list, err) = subprocess.Popen([utils.assimp_bin_path, "listext"], + (ext_list, err) = subprocess.Popen([assimp_bin_path, "listext"], stdout=subprocess.PIPE).communicate() ext_list = str(ext_list).lower().split(";") diff --git a/test/regression/run.py b/test/regression/run.py index 6d776b362..427f48d47 100644 --- a/test/regression/run.py +++ b/test/regression/run.py @@ -41,8 +41,16 @@ # --------------------------------------------------------------------------- """ -Run the regression test suite using the settings from settings.py. +Run the regression test suite using settings from settings.py. +The assimp_cmd (or assimp) binary to use is specified by the first +command line argument and defaults to ``assimp``. + +To build, set ``ASSIMP_BUILD_ASSIMP_TOOLS=ON`` in CMake. If generating +configs for an IDE, make sure to build the assimp_cmd project. + +On Windows, use ``py run.py `` to make sure the command +line parameter is forwarded to the script. """ import sys @@ -124,8 +132,11 @@ class results: def report_results(self): """Write results to ../results/run_regression_suite_failures.txt""" + count_success = len(self.success) + count_fail = len(self.failures) + percent_good = float(count_success) / (count_success + count_fail) print("\n" + ('='*60) + "\n" + "SUCCESS: {0}\nFAILURE: {1}\nPercentage good: {2}".format( - len(self.success), len(self.failures), len(self.success)/(len(self.success)+len(self.failures)) ) + + count_success, count_fail, percent_good) + "\n" + ('='*60) + "\n") with open(os.path.join('..', 'results',outfilename_failur), "wt") as f: @@ -138,7 +149,7 @@ class results: + " for more details\n\n") # ------------------------------------------------------------------------------- -def mkoutputdir_andgetpath(fullpath, myhash, app): +def prepare_output_dir(fullpath, myhash, app): outfile = os.path.join(settings.results, "tmp", os.path.split(fullpath)[1] + "_" + myhash) try: os.mkdir(outfile) @@ -167,13 +178,16 @@ def process_dir(d, outfile_results, zipin, result): for pppreset in settings.pp_configs_to_test: filehash = utils.hashing(fullpath, pppreset) failure = False + try: input_expected = zipin.open(filehash, "r").read() # empty dump files indicate 'expected import failure' if not len(input_expected): failure = True except KeyError: - #print("Didn't find "+fullpath+" (Hash is "+filehash+") in database") + # TODO(acgessler): Keep track of this and report as error in the end. + print("Didn't find "+fullpath+" (Hash is "+filehash+") in database. Outdated "+\ + "regression database? Use gen_db.zip to re-generate.") continue # Ignore extensions via settings.py configured list @@ -184,13 +198,18 @@ def process_dir(d, outfile_results, zipin, result): print("-"*60 + "\n " + os.path.realpath(fullpath) + " pp: " + pppreset) - outfile_actual = mkoutputdir_andgetpath(fullpath, filehash, "ACTUAL") - outfile_expect = mkoutputdir_andgetpath(fullpath, filehash, "EXPECT") + outfile_actual = prepare_output_dir(fullpath, filehash, "ACTUAL") + outfile_expect = prepare_output_dir(fullpath, filehash, "EXPECT") outfile_results.write("assimp dump "+"-"*80+"\n") outfile_results.flush() - command = [utils.assimp_bin_path,"dump",fullpath,outfile_actual,"-b","-s","-l"]+pppreset.split() + command = [assimp_bin_path, + "dump", + fullpath, outfile_actual, "-b", "-s", "-l" ] +\ + pppreset.split() + r = subprocess.call(command, **shellparams) + print(r) if r and not failure: result.fail(fullpath, outfile_expect, pppreset, IMPORT_FAILURE, r) @@ -216,7 +235,7 @@ def process_dir(d, outfile_results, zipin, result): outfile_results.write("assimp cmpdump "+"-"*80+"\n") outfile_results.flush() - command = [utils.assimp_bin_path,'cmpdump',outfile_actual,outfile_expect] + command = [ assimp_bin_path, 'cmpdump', outfile_actual, outfile_expect ] if subprocess.call(command, **shellparams) != 0: result.fail(fullpath, outfile_expect, pppreset, DATABASE_VALUE_MISMATCH) continue @@ -235,7 +254,6 @@ def del_folder_with_contents(folder): # ------------------------------------------------------------------------------- def run_test(): - utils.find_assimp_or_die() tmp_target_path = os.path.join(settings.results, "tmp") try: os.mkdir(tmp_target_path) @@ -261,6 +279,8 @@ def run_test(): # ------------------------------------------------------------------------------- if __name__ == "__main__": + assimp_bin_path = sys.argv[1] if len(sys.argv) > 1 else 'assimp' + print('Using assimp binary: ' + assimp_bin_path) run_test() # vim: ai ts=4 sts=4 et sw=4 diff --git a/test/regression/utils.py b/test/regression/utils.py index 73c8336ca..8e358e9b0 100644 --- a/test/regression/utils.py +++ b/test/regression/utils.py @@ -40,7 +40,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # --------------------------------------------------------------------------- -"""Shared stuff for the gen_db and run scripts """ +"""Shared stuff for the gen_db and run scripts""" # ------------------------------------------------------------------------------- def hashing(file,pp): @@ -51,75 +51,14 @@ def hashing(file,pp): and platforms, so we implement the hashing manually. """ - def myhash(instring): - # sdbm hash - res = 0 - for t in instring: - res = (ord(t) + (res<<6) + (res<<16) - res) % 2**32 - return res + file = file.replace('\\','/')+":"+pp + # SDBM hash + res = 0 + for t in file: + res = (ord(t) + (res<<6) + (res<<16) - res) % 2**32 - return hex(myhash(file.replace('\\','/')+":"+pp)) + # Python 2.7 normalization: strip 'L' suffix. + return hex(res).rstrip('L') -assimp_bin_path = None -# ------------------------------------------------------------------------------- -def find_assimp_or_die(): - """Find assimp_cmd's binary for the current platform. - - The path to the binary is stored in assimp_bin_path, the process - is aborted if it can't be found. - - """ - - import os - import platform - import sys - - def locate_file(f_list): - for f in f_list: - try: - fl = open(f,"rb") - except IOError: - continue - fl.close() - return f - return None - - global assimp_bin_path - if os.name == "nt": - search_x86 = [ - os.path.join("assimp.exe"), - os.path.join("..","..","bin","assimpcmd_release-dll_Win32","assimp.exe"), - os.path.join("..","..","bin","x86","assimp"), - os.path.join("..","..","bin","Release","assimp.exe") - ] - if platform.machine() == "x86": - search = search_x86 - else: # amd64, hopefully - search = [ - os.path.join("..","..","bin","assimpcmd_release-dll_x64","assimp.exe"), - os.path.join("..","..","bin","x64","assimp") - ] - # x64 platform does not guarantee a x64 build. Also look for x86 as last paths. - search += search_x86 - - assimp_bin_path = locate_file(search) - if assimp_bin_path is None: - print("Can't locate assimp_cmd binary") - print("Looked in", search) - sys.exit(-5) - - print("Located assimp/assimp_cmd binary from", assimp_bin_path) - elif os.name == "posix": - #search = [os.path.join("..","..","bin","gcc","assimp"), - # os.path.join("/usr","local","bin",'assimp')] - assimp_bin_path = "assimp" - print("Taking system-wide assimp binary") - else: - print("Unsupported operating system") - sys.exit(-5) - -if __name__ == '__main__': - find_assimp_or_die() - # vim: ai ts=4 sts=4 et sw=4 From b2ea487bda3152134baf9d7cc3a94699e34c86fd Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Tue, 3 Feb 2015 21:16:16 +0100 Subject: [PATCH 17/74] Regression test suite: Test files in proper lexicographic order instead of relying on OS directory iteration order. This ensures failures are listed in the same order. --- test/regression/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/regression/run.py b/test/regression/run.py index 427f48d47..73172cf04 100644 --- a/test/regression/run.py +++ b/test/regression/run.py @@ -165,7 +165,7 @@ def process_dir(d, outfile_results, zipin, result): shellparams = {'stdout':outfile_results, 'stderr':outfile_results, 'shell':False} print("Processing directory " + d) - for f in os.listdir(d): + for f in sorted(os.listdir(d)): fullpath = os.path.join(d, f) if os.path.isdir(fullpath) and not f == ".svn": process_dir(fullpath, outfile_results, zipin, result) From 20b3ce6a40fc02c845c48c3919c1c5180bef3b8e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 7 Feb 2015 12:47:53 +0100 Subject: [PATCH 18/74] replace opengexparser by using openddl-parser. Signed-off-by: Kim Kulling --- CMakeLists.txt | 3 + code/CMakeLists.txt | 4 +- code/ImporterRegistry.cpp | 88 ++-- code/OpenGEXImporter.cpp | 37 +- code/OpenGEXImporter.h | 7 +- code/OpenGEXParser.cpp | 409 ------------------ code/OpenGEXParser.h | 121 ------ contrib/openddlparser/code/DDLNode.cpp | 8 + contrib/openddlparser/code/OpenDDLParser.cpp | 101 +++-- .../include/openddlparser/DDLNode.h | 2 + .../include/openddlparser/OpenDDLCommon.h | 27 +- .../include/openddlparser/OpenDDLParser.h | 23 +- .../include/openddlparser/Value.h | 30 +- 13 files changed, 190 insertions(+), 670 deletions(-) delete mode 100644 code/OpenGEXParser.cpp delete mode 100644 code/OpenGEXParser.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 618f46a54..267f471c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,6 +155,9 @@ endif(NOT ZLIB_FOUND) INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR}) add_subdirectory( contrib/openddlparser ) +INCLUDE_DIRECTORIES( contrib/openddlparser/include ) +SET( OPENDDL_PARSER_LIBRARIES openddl_parser ) + # Search for unzip if (PKG_CONFIG_FOUND) PKG_CHECK_MODULES(UNZIP minizip) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 162e9a2ef..40ddb1215 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -353,8 +353,6 @@ SOURCE_GROUP( Ogre FILES ${Ogre_SRCS}) SET( OpenGEX_SRCS OpenGEXImporter.cpp OpenGEXImporter.h - OpenGEXParser.cpp - OpenGEXParser.h OpenGEXStructs.h ) SOURCE_GROUP( OpenGEX FILES ${OpenGEX_SRCS}) @@ -729,7 +727,7 @@ SET( assimp_src ADD_LIBRARY( assimp ${assimp_src} ) -TARGET_LINK_LIBRARIES(assimp ${ZLIB_LIBRARIES}) +TARGET_LINK_LIBRARIES(assimp ${ZLIB_LIBRARIES} ${OPENDDL_PARSER_LIBRARIES} ) if(ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM) set(ASSIMP_ANDROID_JNIIOSYSTEM_PATH port/AndroidJNI) diff --git a/code/ImporterRegistry.cpp b/code/ImporterRegistry.cpp index ec804d141..ea9e7526c 100644 --- a/code/ImporterRegistry.cpp +++ b/code/ImporterRegistry.cpp @@ -178,130 +178,130 @@ namespace Assimp { // ------------------------------------------------------------------------------------------------ void GetImporterInstanceList(std::vector< BaseImporter* >& out) { - // ---------------------------------------------------------------------------- - // Add an instance of each worker class here - // (register_new_importers_here) - // ---------------------------------------------------------------------------- - out.reserve(64); + // ---------------------------------------------------------------------------- + // Add an instance of each worker class here + // (register_new_importers_here) + // ---------------------------------------------------------------------------- + out.reserve(64); #if (!defined ASSIMP_BUILD_NO_X_IMPORTER) - out.push_back( new XFileImporter()); + out.push_back( new XFileImporter()); #endif #if (!defined ASSIMP_BUILD_NO_OBJ_IMPORTER) - out.push_back( new ObjFileImporter()); + out.push_back( new ObjFileImporter()); #endif #if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER) - out.push_back( new Discreet3DSImporter()); + out.push_back( new Discreet3DSImporter()); #endif #if (!defined ASSIMP_BUILD_NO_MD3_IMPORTER) - out.push_back( new MD3Importer()); + out.push_back( new MD3Importer()); #endif #if (!defined ASSIMP_BUILD_NO_MD2_IMPORTER) - out.push_back( new MD2Importer()); + out.push_back( new MD2Importer()); #endif #if (!defined ASSIMP_BUILD_NO_PLY_IMPORTER) - out.push_back( new PLYImporter()); + out.push_back( new PLYImporter()); #endif #if (!defined ASSIMP_BUILD_NO_MDL_IMPORTER) - out.push_back( new MDLImporter()); + out.push_back( new MDLImporter()); #endif #if (!defined ASSIMP_BUILD_NO_ASE_IMPORTER) - out.push_back( new ASEImporter()); + out.push_back( new ASEImporter()); #endif #if (!defined ASSIMP_BUILD_NO_HMP_IMPORTER) - out.push_back( new HMPImporter()); + out.push_back( new HMPImporter()); #endif #if (!defined ASSIMP_BUILD_NO_SMD_IMPORTER) - out.push_back( new SMDImporter()); + out.push_back( new SMDImporter()); #endif #if (!defined ASSIMP_BUILD_NO_MDC_IMPORTER) - out.push_back( new MDCImporter()); + out.push_back( new MDCImporter()); #endif #if (!defined ASSIMP_BUILD_NO_MD5_IMPORTER) - out.push_back( new MD5Importer()); + out.push_back( new MD5Importer()); #endif #if (!defined ASSIMP_BUILD_NO_STL_IMPORTER) - out.push_back( new STLImporter()); + out.push_back( new STLImporter()); #endif #if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER) - out.push_back( new LWOImporter()); + out.push_back( new LWOImporter()); #endif #if (!defined ASSIMP_BUILD_NO_DXF_IMPORTER) - out.push_back( new DXFImporter()); + out.push_back( new DXFImporter()); #endif #if (!defined ASSIMP_BUILD_NO_NFF_IMPORTER) - out.push_back( new NFFImporter()); + out.push_back( new NFFImporter()); #endif #if (!defined ASSIMP_BUILD_NO_RAW_IMPORTER) - out.push_back( new RAWImporter()); + out.push_back( new RAWImporter()); #endif #if (!defined ASSIMP_BUILD_NO_OFF_IMPORTER) - out.push_back( new OFFImporter()); + out.push_back( new OFFImporter()); #endif #if (!defined ASSIMP_BUILD_NO_AC_IMPORTER) - out.push_back( new AC3DImporter()); + out.push_back( new AC3DImporter()); #endif #if (!defined ASSIMP_BUILD_NO_BVH_IMPORTER) - out.push_back( new BVHLoader()); + out.push_back( new BVHLoader()); #endif #if (!defined ASSIMP_BUILD_NO_IRRMESH_IMPORTER) - out.push_back( new IRRMeshImporter()); + out.push_back( new IRRMeshImporter()); #endif #if (!defined ASSIMP_BUILD_NO_IRR_IMPORTER) - out.push_back( new IRRImporter()); + out.push_back( new IRRImporter()); #endif #if (!defined ASSIMP_BUILD_NO_Q3D_IMPORTER) - out.push_back( new Q3DImporter()); + out.push_back( new Q3DImporter()); #endif #if (!defined ASSIMP_BUILD_NO_B3D_IMPORTER) - out.push_back( new B3DImporter()); + out.push_back( new B3DImporter()); #endif #if (!defined ASSIMP_BUILD_NO_COLLADA_IMPORTER) - out.push_back( new ColladaLoader()); + out.push_back( new ColladaLoader()); #endif #if (!defined ASSIMP_BUILD_NO_TERRAGEN_IMPORTER) - out.push_back( new TerragenImporter()); + out.push_back( new TerragenImporter()); #endif #if (!defined ASSIMP_BUILD_NO_CSM_IMPORTER) - out.push_back( new CSMImporter()); + out.push_back( new CSMImporter()); #endif #if (!defined ASSIMP_BUILD_NO_3D_IMPORTER) - out.push_back( new UnrealImporter()); + out.push_back( new UnrealImporter()); #endif #if (!defined ASSIMP_BUILD_NO_LWS_IMPORTER) - out.push_back( new LWSImporter()); + out.push_back( new LWSImporter()); #endif #if (!defined ASSIMP_BUILD_NO_OGRE_IMPORTER) - out.push_back( new Ogre::OgreImporter()); + out.push_back( new Ogre::OgreImporter()); #endif #if (!defined ASSIMP_BUILD_NO_OPEMGEX_IMPORTER ) out.push_back( new OpenGEX::OpenGEXImporter() ); #endif #if (!defined ASSIMP_BUILD_NO_MS3D_IMPORTER) - out.push_back( new MS3DImporter()); + out.push_back( new MS3DImporter()); #endif #if (!defined ASSIMP_BUILD_NO_COB_IMPORTER) - out.push_back( new COBImporter()); + out.push_back( new COBImporter()); #endif #if (!defined ASSIMP_BUILD_NO_BLEND_IMPORTER) - out.push_back( new BlenderImporter()); + out.push_back( new BlenderImporter()); #endif #if (!defined ASSIMP_BUILD_NO_Q3BSP_IMPORTER) - out.push_back( new Q3BSPFileImporter() ); + out.push_back( new Q3BSPFileImporter() ); #endif #if (!defined ASSIMP_BUILD_NO_NDO_IMPORTER) - out.push_back( new NDOImporter() ); + out.push_back( new NDOImporter() ); #endif #if (!defined ASSIMP_BUILD_NO_IFC_IMPORTER) - out.push_back( new IFCImporter() ); + out.push_back( new IFCImporter() ); #endif #if ( !defined ASSIMP_BUILD_NO_XGL_IMPORTER ) - out.push_back( new XGLImporter() ); + out.push_back( new XGLImporter() ); #endif #if ( !defined ASSIMP_BUILD_NO_FBX_IMPORTER ) - out.push_back( new FBXImporter() ); + out.push_back( new FBXImporter() ); #endif #if ( !defined ASSIMP_BUILD_NO_ASSBIN_IMPORTER ) - out.push_back( new AssbinImporter() ); + out.push_back( new AssbinImporter() ); #endif } diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index f385cc11d..7de3eaaea 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -41,9 +41,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AssimpPCH.h" #include "OpenGEXImporter.h" -#include "OpenGEXParser.h" #include "DefaultIOSystem.h" +#include + #include static const aiImporterDesc desc = { @@ -62,6 +63,8 @@ static const aiImporterDesc desc = { namespace Assimp { namespace OpenGEX { + USE_ODDLPARSER_NS + //------------------------------------------------------------------------------------------------ OpenGEXImporter::OpenGEXImporter() { @@ -95,8 +98,14 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce std::vector buffer; TextFileToBuffer( file, buffer ); - OpenGEXParser myParser( buffer ); - myParser.parse(); + + OpenDDLParser myParser; + myParser.setBuffer( &buffer[ 0 ], buffer.size() ); + bool success( myParser.parse() ); + if( success ) { + Context *ctx = myParser.getContext(); + importMetric( ctx ); + } } //------------------------------------------------------------------------------------------------ @@ -109,6 +118,28 @@ void OpenGEXImporter::SetupProperties( const Importer *pImp ) { } +//------------------------------------------------------------------------------------------------ +void OpenGEXImporter::importMetric( Context *ctx ) { + if( NULL == ctx || NULL == ctx->getProperties() ) { + return; + } + + Property *prop = ctx->getProperties(); + while( NULL != prop ) { + prop = prop->m_next; + } +} + +//------------------------------------------------------------------------------------------------ +void OpenGEXImporter::ParseGeoObject() { + +} + +//------------------------------------------------------------------------------------------------ +void OpenGEXImporter::ParseMaterial() { + +} + //------------------------------------------------------------------------------------------------ } // Namespace OpenGEX diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index 821692c25..601721704 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -44,6 +44,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "BaseImporter.h" + +namespace ODDLParser { + struct Context; +} + namespace Assimp { namespace OpenGEX { @@ -72,7 +77,7 @@ public: virtual void SetupProperties( const Importer *pImp ); protected: - void ParseMetric(); + void importMetric( ODDLParser::Context *ctx ); void ParseGeoObject(); void ParseMaterial(); }; diff --git a/code/OpenGEXParser.cpp b/code/OpenGEXParser.cpp deleted file mode 100644 index 3dcc83e44..000000000 --- a/code/OpenGEXParser.cpp +++ /dev/null @@ -1,409 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2014, 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 "AssimpPCH.h" -#ifndef ASSIMP_BUILD_NO_OPEMGEX_IMPORTER - -#include "OpenGEXParser.h" -#include "OpenGEXStructs.h" -#include "ParsingUtils.h" -#include "fast_atof.h" - -#include - -namespace Assimp { -namespace OpenGEX { - -//------------------------------------------------------------------------------------------------ -static const std::string Metric = "Metric"; -static const std::string GeometryNode = "GeometryNode"; -static const std::string GeometryObject = "GeometryObject"; -static const std::string Material = "Material"; -static const size_t NumObjects = 4; -static const std::string RootNodes[ NumObjects ] = { - Metric, - GeometryNode, - GeometryObject, - Material -}; - -static const size_t NumSeparator = 4; -static const std::string Separator[ NumSeparator ] = { - "(", ")", "{", "}" -}; - - -static const size_t NumToken = 8; -static const std::string Token[ NumToken ] = { - RootNodes[ 0 ], - RootNodes[ 1 ], - RootNodes[ 2 ], - RootNodes[ 3 ], - Separator[ 0 ], - Separator[ 1 ], - Separator[ 2 ], - Separator[ 3 ] -}; - -static bool isSeparator( char in ) { - return ( in == '(' || in == ')' || in == '{' || in == '}' ); -} - -static bool containsNode( const char *bufferPtr, size_t size, const std::string *nodes, - size_t numNodes, std::string &tokenFound ) { - tokenFound = ""; - if( 0 == numNodes ) { - return false; - } - - bool found( false ); - for( size_t i = 0; i < numNodes; ++i ) { - if( TokenMatch( bufferPtr, nodes[ i ].c_str(), nodes[ i ].size() ) ) { - tokenFound = nodes[ i ]; - found = true; - break; - } - } - - return found; -} - -static OpenGEXParser::TokenType getTokenTypeByName( const char *in ) { - ai_assert( NULL != in ); - - OpenGEXParser::TokenType type( OpenGEXParser::None ); - for( size_t i = 0; i < NumToken; ++i ) { - if( TokenMatch( in, Token[ i ].c_str(), Token[ i ].size() ) ) { - type = static_cast( i+1 ); - break; - } - } - - return type; -} - -static void removeQuotes( std::string &attribName ) { - std::string tmp; - for( unsigned int i = 0; i < attribName.size(); ++i ) { - if( attribName[ i ] != '\"' ) { - tmp += attribName[ i ]; - } - } - attribName = tmp; -} - -//------------------------------------------------------------------------------------------------ -OpenGEXParser::OpenGEXParser( const std::vector &buffer ) -: m_buffer( buffer ) -, m_index( 0 ) -, m_buffersize( buffer.size() ) -, m_nodeTypeStack() { - // empty -} - -//------------------------------------------------------------------------------------------------ -OpenGEXParser::~OpenGEXParser() { - -} - -//------------------------------------------------------------------------------------------------ -void OpenGEXParser::parse() { - while( parseNextNode() ) { - - } -} - -//------------------------------------------------------------------------------------------------ -std::string OpenGEXParser::getNextToken() { - std::string token; - while( m_index < m_buffersize && IsSpace( m_buffer[ m_index ] ) ) { - m_index++; - } - - while( m_index < m_buffersize && !IsSpace( m_buffer[ m_index ] ) && !isSeparator( m_buffer[ m_index ] ) ) { - token += m_buffer[ m_index ]; - m_index++; - } - - if( token == "//" ) { - skipComments(); - token = getNextToken(); - } - - if( token.empty() ) { - if( isSeparator( m_buffer[ m_index ] ) ) { - token += m_buffer[ m_index ]; - m_index++; - } - } - - return token; -} - -//------------------------------------------------------------------------------------------------ -bool OpenGEXParser::skipComments() { - bool skipped( false ); - if( strncmp( &m_buffer[ m_index ], "//", 2 ) == 0) { - while( !IsLineEnd( m_buffer[ m_index ] ) ) { - ++m_index; - } - skipped = true; - } - - return skipped; -} - -//------------------------------------------------------------------------------------------------ -void OpenGEXParser::readUntilEndOfLine() { - while( !IsLineEnd( m_buffer[ m_index ] ) ) { - ++m_index; - } - ++m_index; - if( IsLineEnd( m_buffer[ m_index ] ) ) { - ++m_index; - } -} - -//------------------------------------------------------------------------------------------------ -bool OpenGEXParser::parseNextNode() { - std::string token( getNextToken() ); - std::string rootNodeName, nodeType; - if( containsNode( token.c_str(), token.size(), RootNodes, NumObjects, rootNodeName ) ) { - m_nodeTypeStack.push_back( getTokenTypeByName( rootNodeName.c_str() ) ); - if( !getNodeHeader( nodeType ) ) { - return false; - } - - if( !getNodeData( nodeType ) ) { - return false; - } - - readUntilEndOfLine(); - - m_nodeTypeStack.pop_back(); - - } - - return true; -} - -//------------------------------------------------------------------------------------------------ -bool OpenGEXParser::getNodeHeader( std::string &name ) { - bool success( false ); - TokenType tokenType( m_nodeTypeStack.back() ); - if( tokenType == MetricNode ) { - if( getMetricAttributeKey( name ) ) { - success = true; - } - } else if( tokenType == GeometryNode ) { - - } else if( tokenType == GeometryObject ) { - - } else if( tokenType == Material ) { - - } else { - DefaultLogger::get()->warn( "Unknown token type in file." ); - } - - return success; -} - -//------------------------------------------------------------------------------------------------ -bool OpenGEXParser::getBracketOpen() { - const std::string token( getNextToken() ); - if( "{" == token ) { - return true; - } - - return false; -} - -//------------------------------------------------------------------------------------------------ -bool OpenGEXParser::getBracketClose() { - const std::string token( getNextToken() ); - if( "}" == token ) { - return true; - } - - return false; -} - -//------------------------------------------------------------------------------------------------ -bool OpenGEXParser::getStringData( std::string &data ) { - if( !getBracketOpen() ) { - return false; - } - - if( !getBracketClose() ) { - return false; - } - return false; -} - -//------------------------------------------------------------------------------------------------ -bool OpenGEXParser::getFloatData( size_t num, float *data ) { - ai_assert( NULL != data ); - - std::string tk; - tk = getNextToken(); - - if( !getBracketOpen() ) { - return false; - } - - bool ok( true ); - size_t dataIdx( 0 ); - for( unsigned int i = 0; i < num; ++i ) { - data[ dataIdx ] = fast_atof( &m_buffer[ m_index ] ); - ++dataIdx; - tk = getNextToken(); - if( tk == "," ) { - if( i >= ( num - 1 ) ) { - ok = false; - break; - } - } - } - - if( !getBracketClose() ) { - return false; - } - - return ok; -} - -//------------------------------------------------------------------------------------------------ -bool OpenGEXParser::getNodeData( const std::string &nodeType ) { - bool success( false ); - - if( !getBracketOpen() ) { - return false; - } - - const TokenType type( m_nodeTypeStack.back() ); - if( type == MetricNode ) { - success = onMetricNode( nodeType ); - } else if( type == GeometryNode ) { - success = onGeometryNode(); - } else if( type == GeometryObject ) { - success = onGeometryObject(); - } else if( type == Material ) { - success = onMaterial(); - } else { - DefaultLogger::get()->warn( "Unknown token type in file." ); - } - - if( !getBracketClose() ) { - return false; - } - - return success; -} - -//------------------------------------------------------------------------------------------------ -bool OpenGEXParser::getMetricAttributeKey( std::string &attribName ) { - bool ok( false ); - attribName = ""; - std::string token( getNextToken() ); - if( token[ 0 ] == '(' ) { - // get attribute - token = getNextToken(); - if( "key" == token ) { - std::string equal = getNextToken(); - attribName = getNextToken(); - token = getNextToken(); - if( token[ 0 ] == ')' ) { - ok = true; - removeQuotes( attribName ); - } - } - } - - return ok; -} - -//------------------------------------------------------------------------------------------------ -bool OpenGEXParser::onMetricNode( const std::string &attribName ) { - float value( 0.0f ); - bool success( true ); - if( "distance" == attribName ) { - if( getFloatData( 1, &value ) ) { - m_model.m_metrics.m_distance = value; - } - } else if( "angle" == attribName ) { - if( getFloatData( 1, &value ) ) { - m_model.m_metrics.m_angle = value; - } - } else if( "time" == attribName ) { - if( getFloatData( 1, &value ) ) { - m_model.m_metrics.m_time = value; - } - } else if( "up" == attribName ) { - std::string up; - if( getStringData( up ) ) { - m_model.m_metrics.m_up = up; - } - } else { - success = false; - } - - return success; -} - -//------------------------------------------------------------------------------------------------ -bool OpenGEXParser::onGeometryNode() { - return true; -} - -//------------------------------------------------------------------------------------------------ -bool OpenGEXParser::onGeometryObject() { - return true; -} - -//------------------------------------------------------------------------------------------------ -bool OpenGEXParser::onMaterial() { - return true; -} - -//------------------------------------------------------------------------------------------------ - -} // Namespace OpenGEX -} // Namespace Assimp - -#endif ASSIMP_BUILD_NO_OPEMGEX_IMPORTER diff --git a/code/OpenGEXParser.h b/code/OpenGEXParser.h deleted file mode 100644 index 64debe43c..000000000 --- a/code/OpenGEXParser.h +++ /dev/null @@ -1,121 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2014, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -#ifndef ASSIMP_OPENGEX_OPENGEXPARSER_H_INC -#define ASSIMP_OPENGEX_OPENGEXPARSER_H_INC - -#ifndef ASSIMP_BUILD_NO_OPEMGEX_IMPORTER - -#include -#include - -namespace Assimp { -namespace OpenGEX { - -struct OpenGEXModel { - struct Metrics { - float m_distance; - float m_angle; - float m_time; - std::string m_up; - - Metrics() - : m_distance( 0.0f ) - , m_angle( 0.0f ) - , m_time( 0.0f ) - , m_up() { - // empty - } - } m_metrics; -}; - -class OpenGEXParser { -public: - enum TokenType { - None = 0, - MetricNode, - GeometryNode, - GeometryObject, - Material, - BracketIn, - BracketOut, - CurlyBracketIn, - CurlyBracketOut, - }; - -public: - OpenGEXParser( const std::vector &buffer ); - ~OpenGEXParser(); - void parse(); - -protected: - std::string getNextToken(); - bool skipComments(); - void readUntilEndOfLine(); - bool parseNextNode(); - bool getNodeHeader( std::string &name ); - bool getBracketOpen(); - bool getBracketClose(); - bool getStringData( std::string &data ); - bool getFloatData( size_t num, float *data ); - bool getNodeData( const std::string &nodeType ); - bool getMetricAttributeKey( std::string &attribName ); - bool onMetricNode( const std::string &attribName ); - bool onGeometryNode(); - bool onGeometryObject(); - bool onMaterial(); - -private: - OpenGEXParser( const OpenGEXParser & ); - OpenGEXParser &operator = ( const OpenGEXParser & ); - -private: - const std::vector &m_buffer; - std::vector m_nodeTypeStack; - OpenGEXModel m_model; - size_t m_index; - size_t m_buffersize; -}; - -} // Namespace openGEX -} // Namespace Assimp - -#endif // ASSIMP_BUILD_NO_OPEMGEX_IMPORTER - -#endif // ASSIMP_OPENGEX_OPENGEXPARSER_H_INC diff --git a/contrib/openddlparser/code/DDLNode.cpp b/contrib/openddlparser/code/DDLNode.cpp index d7c034ba1..aba11d8ae 100644 --- a/contrib/openddlparser/code/DDLNode.cpp +++ b/contrib/openddlparser/code/DDLNode.cpp @@ -116,6 +116,14 @@ const std::string &DDLNode::getName() const { return m_name; } +void DDLNode::setProperties( Property *prop ) { + m_properties = prop; +} + +Property *DDLNode::getProperties() const { + return m_properties; +} + void DDLNode::setValue( Value *val ) { m_value = val; } diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index 66f55f057..684eb20b7 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -104,22 +104,18 @@ static void logMessage( LogSeverity severity, const std::string &msg ) { OpenDDLParser::OpenDDLParser() : m_logCallback( logMessage ) -, m_ownsBuffer( false ) -, m_buffer( ddl_nullptr ) -, m_len( 0 ) +, m_buffer() , m_stack() , m_context( ddl_nullptr ) { // empty } -OpenDDLParser::OpenDDLParser( char *buffer, size_t len, bool ownsIt ) +OpenDDLParser::OpenDDLParser( char *buffer, size_t len ) : m_logCallback( &logMessage ) -, m_ownsBuffer( false ) -, m_buffer( ddl_nullptr ) -, m_len( 0 ) +, m_buffer() , m_context( ddl_nullptr ) { - if( 0 != m_len ) { - setBuffer( buffer, len, ownsIt ); + if( 0 != len ) { + setBuffer( buffer, len ); } } @@ -141,41 +137,36 @@ OpenDDLParser::logCallback OpenDDLParser::getLogCallback() const { return m_logCallback; } -void OpenDDLParser::setBuffer( char *buffer, size_t len, bool ownsIt ) { - if( m_buffer && m_ownsBuffer ) { - delete[] m_buffer; - m_buffer = ddl_nullptr; - m_len = 0; +void OpenDDLParser::setBuffer( char *buffer, size_t len ) { + clear(); + if( 0 == len ) { + return; } - m_ownsBuffer = ownsIt; - if( m_ownsBuffer ) { - // when we are owning the buffer we will do a deep copy - m_buffer = new char[ len ]; - m_len = len; - ::memcpy( m_buffer, buffer, len ); - } else { - // when we are not owning the buffer, we just do a shallow copy - m_buffer = buffer; - m_len = len; - } + m_buffer.resize( len ); + ::memcpy(&m_buffer[ 0 ], buffer, len ); } -char *OpenDDLParser::getBuffer() const { - return m_buffer; +void OpenDDLParser::setBuffer( const std::vector &buffer ) { + clear(); + m_buffer.resize( buffer.size() ); + std::copy( buffer.begin(), buffer.end(), m_buffer.begin() ); +} + +const char *OpenDDLParser::getBuffer() const { + if( m_buffer.empty() ) { + return ddl_nullptr; + } + + return &m_buffer[ 0 ]; } size_t OpenDDLParser::getBufferSize() const { - return m_len; + return m_buffer.size(); } void OpenDDLParser::clear() { - if( m_ownsBuffer ) { - delete [] m_buffer; - } - m_buffer = ddl_nullptr; - m_len = 0; - + m_buffer.resize( 0 ); if( m_context ) { m_context->m_root = ddl_nullptr; } @@ -184,11 +175,11 @@ void OpenDDLParser::clear() { } bool OpenDDLParser::parse() { - if( 0 == m_len ) { + if( m_buffer.empty() ) { return false; } - normalizeBuffer( m_buffer, m_len ); + normalizeBuffer( m_buffer ); m_context = new Context; m_context->m_root = DDLNode::create( "root", "", ddl_nullptr ); @@ -196,9 +187,11 @@ bool OpenDDLParser::parse() { // do the main parsing char *current( &m_buffer[ 0 ] ); - char *end( &m_buffer[ m_len - 1 ] + 1 ); - while( current != end ) { + char *end( &m_buffer[ m_buffer.size() - 1 ] + 1 ); + size_t pos( current - &m_buffer[ 0 ] ); + while( pos < m_buffer.size() ) { current = parseNextNode( current, end ); + pos = current - &m_buffer[ 0 ]; } return true; } @@ -219,7 +212,7 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { in = OpenDDLParser::parseIdentifier( in, end, &id ); #ifdef DEBUG_HEADER_NAME - if( id ) { + if( ddl_nullptr != id ) { std::cout << id->m_buffer << std::endl; } #endif // DEBUG_HEADER_NAME @@ -253,7 +246,11 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { // set the properties if( ddl_nullptr != first ) { - m_context->setProperties( first ); + std::cout << id->m_buffer << std::endl; + DDLNode *current( top() ); + if( current ) { + current->setProperties( first ); + } } // store the node @@ -317,6 +314,9 @@ char *OpenDDLParser::parseStructure( char *in, char *end ) { if( *in != '}' ) { logInvalidTokenError( in, "}", m_logCallback ); } + else { + in++; + } } else { in = parseHeader( in, end ); in = parseStructure( in, end ); @@ -327,6 +327,7 @@ char *OpenDDLParser::parseStructure( char *in, char *end ) { return in; } + in = getNextToken( in, end ); in++; @@ -373,33 +374,29 @@ Context *OpenDDLParser::getContext() const { return m_context; } -void OpenDDLParser::normalizeBuffer( char *buffer, size_t len ) { - if( nullptr == buffer || 0 == len ) { +void OpenDDLParser::normalizeBuffer( std::vector &buffer) { + if( buffer.empty() ) { return; } - size_t writeIdx( 0 ); - char *end( &buffer[ len ] + 1 ); + std::vector newBuffer; + const size_t len( buffer.size() ); + char *end( &buffer[ len-1 ] + 1 ); for( size_t readIdx = 0; readIdx( c, end ) ) { - buffer[ writeIdx ] = buffer[ readIdx ]; - writeIdx++; + newBuffer.push_back( buffer[ readIdx ] ); } else { readIdx++; // skip the comment and the rest of the line while( !isEndofLine( buffer[ readIdx ] ) ) { readIdx++; } - buffer[writeIdx] = '\n'; - writeIdx++; + newBuffer.push_back( '\n' ); } } - - if( writeIdx < len ) { - buffer[ writeIdx ] = '\0'; - } + buffer = newBuffer; } char *OpenDDLParser::parseName( char *in, char *end, Name **name ) { diff --git a/contrib/openddlparser/include/openddlparser/DDLNode.h b/contrib/openddlparser/include/openddlparser/DDLNode.h index 85b542b5e..d5b4a56c9 100644 --- a/contrib/openddlparser/include/openddlparser/DDLNode.h +++ b/contrib/openddlparser/include/openddlparser/DDLNode.h @@ -55,6 +55,8 @@ public: const std::string &getType() const; void setName( const std::string &name ); const std::string &getName() const; + void setProperties( Property *prop ); + Property *getProperties() const; void setValue( Value *val ); Value *getValue() const; void setDataArrayList( DataArrayList *dtArrayList ); diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h index 65530c866..789bc15ad 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h @@ -25,6 +25,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define OPENDDLPARSER_OPENDDLPARSERCOMMON_H_INC #include +#include + #include #ifdef _WIN32 @@ -133,29 +135,24 @@ struct DataArrayList { }; struct Context { - Property *m_properties; DDLNode *m_root; Context() - : m_properties( ddl_nullptr ) - , m_root( ddl_nullptr ) { + : m_root( ddl_nullptr ) { // empty } - - void setProperties( Property *first ); - Property *getProperties() const; }; +struct BufferIt { + std::vector m_buffer; + size_t m_idx; -inline -void Context::setProperties( Property *first ) { - m_properties = first; -} - -inline -Property *Context::getProperties() const { - return m_properties; -} + BufferIt( const std::vector &buffer ) + : m_buffer( buffer ) + , m_idx( 0 ) { + // empty + } +}; END_ODDLPARSER_NS diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h index 6457e8b74..4f003a0e4 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h @@ -50,11 +50,12 @@ T *getNextToken( T *in, T *end ) { return in; } +/// @brief Defines the log severity. enum LogSeverity { - ddl_debug_msg = 0, - ddl_info_msg, - ddl_warn_msg, - ddl_error_msg, + ddl_debug_msg = 0, ///< Debug message, for debugging + ddl_info_msg, ///< Info messages, normal mode + ddl_warn_msg, ///< Parser warnings + ddl_error_msg ///< Parser errors }; class DLL_ODDLPARSER_EXPORT OpenDDLParser { @@ -63,12 +64,13 @@ public: public: OpenDDLParser(); - OpenDDLParser( char *buffer, size_t len, bool ownsIt = false ); + OpenDDLParser( char *buffer, size_t len ); ~OpenDDLParser(); void setLogCallback( logCallback callback ); logCallback getLogCallback() const; - void setBuffer( char *buffer, size_t len, bool ownsIt = false ); - char *getBuffer() const; + void setBuffer( char *buffer, size_t len ); + void setBuffer( const std::vector &buffer ); + const char *getBuffer() const; size_t getBufferSize() const; void clear(); bool parse(); @@ -82,7 +84,7 @@ public: Context *getContext() const; public: // static parser helpers - static void normalizeBuffer( char *buffer, size_t len ); + static void normalizeBuffer( std::vector &buffer ); static char *parseName( char *in, char *end, Name **name ); static char *parseIdentifier( char *in, char *end, Identifier **id ); static char *parsePrimitiveDataType( char *in, char *end, Value::ValueType &type, size_t &len ); @@ -103,9 +105,8 @@ private: private: logCallback m_logCallback; - bool m_ownsBuffer; - char *m_buffer; - size_t m_len; + std::vector m_buffer; + typedef std::vector DDLNodeStack; DDLNodeStack m_stack; Context *m_context; diff --git a/contrib/openddlparser/include/openddlparser/Value.h b/contrib/openddlparser/include/openddlparser/Value.h index 665c407ed..0d719f994 100644 --- a/contrib/openddlparser/include/openddlparser/Value.h +++ b/contrib/openddlparser/include/openddlparser/Value.h @@ -28,20 +28,28 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. BEGIN_ODDLPARSER_NS +///------------------------------------------------------------------------------------------------ +/// @brief This class implements a value. +/// +/// Values are used to store data types like boolean, integer, floats, double and many mode. To get +/// an overview please check the enum VylueType ( @see Value::ValueType ). +/// Values can be single items or lists of items. They are implemented as linked lists. +///------------------------------------------------------------------------------------------------ class DLL_ODDLPARSER_EXPORT Value { public: + /// @brief This enum describes the data type stored in the value. enum ValueType { - ddl_none = -1, - ddl_bool = 0, - ddl_int8, - ddl_int16, - ddl_int32, - ddl_int64, - ddl_unsigned_int8, - ddl_unsigned_int16, - ddl_unsigned_int32, - ddl_unsigned_int64, - ddl_half, + ddl_none = -1, ///< Nothing specified + ddl_bool = 0, ///< A boolean type + ddl_int8, ///< Integer type, 8 bytes + ddl_int16, ///< Integer type, 16 bytes + ddl_int32, ///< Integer type, 32 bytes + ddl_int64, ///< Integer type, 64 bytes + ddl_unsigned_int8, ///< Unsigned integer type, 8 bytes + ddl_unsigned_int16, ///< Unsigned integer type, 16 bytes + ddl_unsigned_int32, ///< Unsigned integer type, 32 bytes + ddl_unsigned_int64, ///< Unsigned integer type, 64 bytes + ddl_half, ddl_float, ddl_double, ddl_string, From 303a6893fc6805e260bea6df816ae7b8fbb65781 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 7 Feb 2015 13:23:32 +0100 Subject: [PATCH 19/74] fix the build. Signed-off-by: Kim Kulling --- code/OpenGEXImporter.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 7de3eaaea..673deb6f8 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -120,14 +120,9 @@ void OpenGEXImporter::SetupProperties( const Importer *pImp ) { //------------------------------------------------------------------------------------------------ void OpenGEXImporter::importMetric( Context *ctx ) { - if( NULL == ctx || NULL == ctx->getProperties() ) { + if( NULL == ctx ) { return; } - - Property *prop = ctx->getProperties(); - while( NULL != prop ) { - prop = prop->m_next; - } } //------------------------------------------------------------------------------------------------ From 065ad7173aa464bc42d4d6459a89dca8d659c84d Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 7 Feb 2015 18:26:57 +0100 Subject: [PATCH 20/74] fix build: no c++11 support Signed-off-by: Kim Kulling --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 267f471c5..023263739 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,8 @@ set (PROJECT_VERSION "${ASSIMP_VERSION}") set(ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used for uploading the sources") +add_definitions( -DOPENDDL_NO_USE_CPP11 ) + # Get the current working branch execute_process( COMMAND git rev-parse --abbrev-ref HEAD From 124f40897627f34334fe1fd286a0ea174cd6de31 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 7 Feb 2015 19:52:06 +0100 Subject: [PATCH 21/74] add property parsing. Signed-off-by: Kim Kulling --- code/OpenGEXImporter.cpp | 55 +++++++++++++++++++++++++++++++++++++--- code/OpenGEXImporter.h | 4 ++- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 673deb6f8..af9b550b5 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -60,6 +60,23 @@ static const aiImporterDesc desc = { "ogex" }; +namespace Grammar { + static const char *MetricType = "Metric"; + static const char *NameType = "Name"; + static const char *ObjectRefType = "ObjectRef"; + static const char *MaterialRefType = "MaterialRef"; + static const char *MetricKeyType = "key"; + static const char *GeometryNodeType = "GeometryNode"; + static const char *GeometryObjectType = "GeometryObject"; + static const char *TransformType = "Transform"; + static const char *MeshType = "Mesh"; + static const char *VertexArrayType = "VertexArray"; + static const char *IndexArrayType = "IndexArray"; + static const char *MaterialType = "Material"; + static const char *ColorType = "Color"; + static const char *TextureType = "Texture"; +} // Namespace Grammar + namespace Assimp { namespace OpenGEX { @@ -104,7 +121,7 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce bool success( myParser.parse() ); if( success ) { Context *ctx = myParser.getContext(); - importMetric( ctx ); + handleNodes( ctx->m_root ); } } @@ -115,14 +132,44 @@ const aiImporterDesc *OpenGEXImporter::GetInfo() const { //------------------------------------------------------------------------------------------------ void OpenGEXImporter::SetupProperties( const Importer *pImp ) { - + if( NULL == pImp ) { + return; + } } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::importMetric( Context *ctx ) { - if( NULL == ctx ) { +void OpenGEXImporter::handleNodes( ODDLParser::DDLNode *node ) { + if( NULL == node ) { return; } + + DDLNode::DllNodeList childs = node->getChildNodeList(); + for( DDLNode::DllNodeList::iterator it = childs.begin(); it != childs.end(); it++ ) { + if( ( *it )->getType() == Grammar::MetricType ) { + importMetric( *it ); + } + } +} + +//------------------------------------------------------------------------------------------------ +void OpenGEXImporter::importMetric( DDLNode *node ) { + if( NULL == node ) { + return; + } + + Property *prop( node->getProperties() ); + while( NULL != prop ) { + if( NULL != prop->m_id ) { + if( Value::ddl_string == prop->m_primData->m_type ) { + std::string valName( (char*) prop->m_primData->m_data ); + Value *val( node->getValue() ); + if( NULL != val ) { + + } + } + } + prop = prop->m_next; + } } //------------------------------------------------------------------------------------------------ diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index 601721704..58641775e 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace ODDLParser { + class DDLNode; struct Context; } @@ -77,7 +78,8 @@ public: virtual void SetupProperties( const Importer *pImp ); protected: - void importMetric( ODDLParser::Context *ctx ); + void handleNodes( ODDLParser::DDLNode *node ); + void importMetric( ODDLParser::DDLNode *node ); void ParseGeoObject(); void ParseMaterial(); }; From c3e2fa07613afe25f269c330fa6347b9fea92d0b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 10 Feb 2015 19:31:23 +0100 Subject: [PATCH 22/74] - untabify obj parser. - bugfix openddl: latest version Signed-off-by: Kim Kulling --- code/ObjFileImporter.cpp | 946 +++++++++---------- code/ObjFileImporter.h | 74 +- code/ObjFileParser.h | 118 +-- contrib/openddlparser/code/OpenDDLParser.cpp | 24 +- 4 files changed, 582 insertions(+), 580 deletions(-) diff --git a/code/ObjFileImporter.cpp b/code/ObjFileImporter.cpp index b44ca4f05..8f201aa5a 100644 --- a/code/ObjFileImporter.cpp +++ b/code/ObjFileImporter.cpp @@ -48,16 +48,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "ObjFileData.h" static const aiImporterDesc desc = { - "Wavefront Object Importer", - "", - "", - "surfaces not supported", - aiImporterFlags_SupportTextFlavour, - 0, - 0, - 0, - 0, - "obj" + "Wavefront Object Importer", + "", + "", + "surfaces not supported", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "obj" }; static const unsigned int ObjMinSize = 16; @@ -69,99 +69,99 @@ using namespace std; // ------------------------------------------------------------------------------------------------ // Default constructor ObjFileImporter::ObjFileImporter() : - m_Buffer(), - m_pRootObject( NULL ), - m_strAbsPath( "" ) + m_Buffer(), + m_pRootObject( NULL ), + m_strAbsPath( "" ) { DefaultIOSystem io; - m_strAbsPath = io.getOsSeparator(); + m_strAbsPath = io.getOsSeparator(); } // ------------------------------------------------------------------------------------------------ // Destructor. ObjFileImporter::~ObjFileImporter() { - delete m_pRootObject; - m_pRootObject = NULL; + delete m_pRootObject; + m_pRootObject = NULL; } // ------------------------------------------------------------------------------------------------ // Returns true, if file is an obj file. bool ObjFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler , bool checkSig ) const { - if(!checkSig) //Check File Extension - { - return SimpleExtensionCheck(pFile,"obj"); - } - else //Check file Header - { - static const char *pTokens[] = { "mtllib", "usemtl", "v ", "vt ", "vn ", "o ", "g ", "s ", "f " }; - return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 9 ); - } + if(!checkSig) //Check File Extension + { + return SimpleExtensionCheck(pFile,"obj"); + } + else //Check file Header + { + static const char *pTokens[] = { "mtllib", "usemtl", "v ", "vt ", "vn ", "o ", "g ", "s ", "f " }; + return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 9 ); + } } // ------------------------------------------------------------------------------------------------ const aiImporterDesc* ObjFileImporter::GetInfo () const { - return &desc; + return &desc; } // ------------------------------------------------------------------------------------------------ // Obj-file import implementation void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { - // Read file into memory - const std::string mode = "rb"; - boost::scoped_ptr file( pIOHandler->Open( pFile, mode)); + // Read file into memory + const std::string mode = "rb"; + boost::scoped_ptr file( pIOHandler->Open( pFile, mode)); if( !file.get() ) { throw DeadlyImportError( "Failed to open file " + pFile + "." ); } - // Get the file-size and validate it, throwing an exception when fails - size_t fileSize = file->FileSize(); + // Get the file-size and validate it, throwing an exception when fails + size_t fileSize = file->FileSize(); if( fileSize < ObjMinSize ) { - throw DeadlyImportError( "OBJ-file is too small."); + throw DeadlyImportError( "OBJ-file is too small."); } - // Allocate buffer and read file into it - TextFileToBuffer(file.get(),m_Buffer); + // Allocate buffer and read file into it + TextFileToBuffer(file.get(),m_Buffer); - // Get the model name - std::string strModelName; - std::string::size_type pos = pFile.find_last_of( "\\/" ); - if ( pos != std::string::npos ) - { - strModelName = pFile.substr(pos+1, pFile.size() - pos - 1); - } - else - { - strModelName = pFile; - } + // Get the model name + std::string strModelName; + std::string::size_type pos = pFile.find_last_of( "\\/" ); + if ( pos != std::string::npos ) + { + strModelName = pFile.substr(pos+1, pFile.size() - pos - 1); + } + else + { + strModelName = pFile; + } - // process all '\' - std::vector ::iterator iter = m_Buffer.begin(); - while (iter != m_Buffer.end()) - { - if (*iter == '\\') - { - // remove '\' - iter = m_Buffer.erase(iter); - // remove next character - while (*iter == '\r' || *iter == '\n') - iter = m_Buffer.erase(iter); - } - else - ++iter; - } + // process all '\' + std::vector ::iterator iter = m_Buffer.begin(); + while (iter != m_Buffer.end()) + { + if (*iter == '\\') + { + // remove '\' + iter = m_Buffer.erase(iter); + // remove next character + while (*iter == '\r' || *iter == '\n') + iter = m_Buffer.erase(iter); + } + else + ++iter; + } - // parse the file into a temporary representation - ObjFileParser parser(m_Buffer, strModelName, pIOHandler); + // parse the file into a temporary representation + ObjFileParser parser(m_Buffer, strModelName, pIOHandler); - // And create the proper return structures out of it - CreateDataFromImport(parser.GetModel(), pScene); + // And create the proper return structures out of it + CreateDataFromImport(parser.GetModel(), pScene); - // Clean up allocated storage for the next import - m_Buffer.clear(); + // Clean up allocated storage for the next import + m_Buffer.clear(); } // ------------------------------------------------------------------------------------------------ @@ -170,98 +170,98 @@ void ObjFileImporter::CreateDataFromImport(const ObjFile::Model* pModel, aiScene if( 0L == pModel ) { return; } - - // Create the root node of the scene - pScene->mRootNode = new aiNode; - if ( !pModel->m_ModelName.empty() ) - { - // Set the name of the scene - pScene->mRootNode->mName.Set(pModel->m_ModelName); - } - else - { - // This is a fatal error, so break down the application - ai_assert(false); - } + + // Create the root node of the scene + pScene->mRootNode = new aiNode; + if ( !pModel->m_ModelName.empty() ) + { + // Set the name of the scene + pScene->mRootNode->mName.Set(pModel->m_ModelName); + } + else + { + // This is a fatal error, so break down the application + ai_assert(false); + } - // Create nodes for the whole scene - std::vector MeshArray; - for (size_t index = 0; index < pModel->m_Objects.size(); index++) - { - createNodes(pModel, pModel->m_Objects[ index ], pScene->mRootNode, pScene, MeshArray); - } + // Create nodes for the whole scene + std::vector MeshArray; + for (size_t index = 0; index < pModel->m_Objects.size(); index++) + { + createNodes(pModel, pModel->m_Objects[ index ], pScene->mRootNode, pScene, MeshArray); + } - // Create mesh pointer buffer for this scene - if (pScene->mNumMeshes > 0) - { - pScene->mMeshes = new aiMesh*[ MeshArray.size() ]; - for (size_t index =0; index < MeshArray.size(); index++) - { - pScene->mMeshes [ index ] = MeshArray[ index ]; - } - } + // Create mesh pointer buffer for this scene + if (pScene->mNumMeshes > 0) + { + pScene->mMeshes = new aiMesh*[ MeshArray.size() ]; + for (size_t index =0; index < MeshArray.size(); index++) + { + pScene->mMeshes [ index ] = MeshArray[ index ]; + } + } - // Create all materials - createMaterials( pModel, pScene ); + // Create all materials + createMaterials( pModel, pScene ); } // ------------------------------------------------------------------------------------------------ // Creates all nodes of the model aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pObject, - aiNode *pParent, aiScene* pScene, - std::vector &MeshArray ) + aiNode *pParent, aiScene* pScene, + std::vector &MeshArray ) { - ai_assert( NULL != pModel ); + ai_assert( NULL != pModel ); if( NULL == pObject ) { return NULL; } - - // Store older mesh size to be able to computes mesh offsets for new mesh instances - const size_t oldMeshSize = MeshArray.size(); - aiNode *pNode = new aiNode; + + // Store older mesh size to be able to computes mesh offsets for new mesh instances + const size_t oldMeshSize = MeshArray.size(); + aiNode *pNode = new aiNode; - pNode->mName = pObject->m_strObjName; - - // If we have a parent node, store it + pNode->mName = pObject->m_strObjName; + + // If we have a parent node, store it if( pParent != NULL ) { appendChildToParentNode( pParent, pNode ); } - for ( unsigned int i=0; i< pObject->m_Meshes.size(); i++ ) - { - unsigned int meshId = pObject->m_Meshes[ i ]; - aiMesh *pMesh = createTopology( pModel, pObject, meshId ); + for ( unsigned int i=0; i< pObject->m_Meshes.size(); i++ ) + { + unsigned int meshId = pObject->m_Meshes[ i ]; + aiMesh *pMesh = createTopology( pModel, pObject, meshId ); if( pMesh && pMesh->mNumFaces > 0 ) { - MeshArray.push_back( pMesh ); - } - } + MeshArray.push_back( pMesh ); + } + } - // Create all nodes from the sub-objects stored in the current object - if ( !pObject->m_SubObjects.empty() ) - { - size_t numChilds = pObject->m_SubObjects.size(); - pNode->mNumChildren = static_cast( numChilds ); - pNode->mChildren = new aiNode*[ numChilds ]; - pNode->mNumMeshes = 1; - pNode->mMeshes = new unsigned int[ 1 ]; - } + // Create all nodes from the sub-objects stored in the current object + if ( !pObject->m_SubObjects.empty() ) + { + size_t numChilds = pObject->m_SubObjects.size(); + pNode->mNumChildren = static_cast( numChilds ); + pNode->mChildren = new aiNode*[ numChilds ]; + pNode->mNumMeshes = 1; + pNode->mMeshes = new unsigned int[ 1 ]; + } - // Set mesh instances into scene- and node-instances - const size_t meshSizeDiff = MeshArray.size()- oldMeshSize; - if ( meshSizeDiff > 0 ) - { - pNode->mMeshes = new unsigned int[ meshSizeDiff ]; - pNode->mNumMeshes = static_cast( meshSizeDiff ); - size_t index = 0; - for (size_t i = oldMeshSize; i < MeshArray.size(); i++) - { - pNode->mMeshes[ index ] = pScene->mNumMeshes; - pScene->mNumMeshes++; - index++; - } - } - - return pNode; + // Set mesh instances into scene- and node-instances + const size_t meshSizeDiff = MeshArray.size()- oldMeshSize; + if ( meshSizeDiff > 0 ) + { + pNode->mMeshes = new unsigned int[ meshSizeDiff ]; + pNode->mNumMeshes = static_cast( meshSizeDiff ); + size_t index = 0; + for (size_t i = oldMeshSize; i < MeshArray.size(); i++) + { + pNode->mMeshes[ index ] = pScene->mNumMeshes; + pScene->mNumMeshes++; + index++; + } + } + + return pNode; } // ------------------------------------------------------------------------------------------------ @@ -269,83 +269,83 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile aiMesh *ObjFileImporter::createTopology( const ObjFile::Model* pModel, const ObjFile::Object* pData, unsigned int uiMeshIndex ) { - // Checking preconditions - ai_assert( NULL != pModel ); + // Checking preconditions + ai_assert( NULL != pModel ); if( NULL == pData ) { return NULL; } - // Create faces - ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ uiMeshIndex ]; + // Create faces + ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ uiMeshIndex ]; if( !pObjMesh ) { return NULL; } ai_assert( NULL != pObjMesh ); aiMesh* pMesh = new aiMesh; - for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++) - { - ObjFile::Face *const inp = pObjMesh->m_Faces[ index ]; + for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++) + { + ObjFile::Face *const inp = pObjMesh->m_Faces[ index ]; ai_assert( NULL != inp ); - if (inp->m_PrimitiveType == aiPrimitiveType_LINE) { - pMesh->mNumFaces += inp->m_pVertices->size() - 1; - pMesh->mPrimitiveTypes |= aiPrimitiveType_LINE; - } else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) { - pMesh->mNumFaces += inp->m_pVertices->size(); - pMesh->mPrimitiveTypes |= aiPrimitiveType_POINT; - } else { - ++pMesh->mNumFaces; - if (inp->m_pVertices->size() > 3) { - pMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; - } else { - pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; - } - } - } + if (inp->m_PrimitiveType == aiPrimitiveType_LINE) { + pMesh->mNumFaces += inp->m_pVertices->size() - 1; + pMesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + } else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) { + pMesh->mNumFaces += inp->m_pVertices->size(); + pMesh->mPrimitiveTypes |= aiPrimitiveType_POINT; + } else { + ++pMesh->mNumFaces; + if (inp->m_pVertices->size() > 3) { + pMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + } else { + pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + } + } + } - unsigned int uiIdxCount( 0u ); - if ( pMesh->mNumFaces > 0 ) - { - pMesh->mFaces = new aiFace[ pMesh->mNumFaces ]; - if ( pObjMesh->m_uiMaterialIndex != ObjFile::Mesh::NoMaterial ) - { - pMesh->mMaterialIndex = pObjMesh->m_uiMaterialIndex; - } + unsigned int uiIdxCount( 0u ); + if ( pMesh->mNumFaces > 0 ) + { + pMesh->mFaces = new aiFace[ pMesh->mNumFaces ]; + if ( pObjMesh->m_uiMaterialIndex != ObjFile::Mesh::NoMaterial ) + { + pMesh->mMaterialIndex = pObjMesh->m_uiMaterialIndex; + } - unsigned int outIndex( 0 ); + unsigned int outIndex( 0 ); - // Copy all data from all stored meshes - for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++) - { - ObjFile::Face* const inp = pObjMesh->m_Faces[ index ]; - if (inp->m_PrimitiveType == aiPrimitiveType_LINE) { - for(size_t i = 0; i < inp->m_pVertices->size() - 1; ++i) { - aiFace& f = pMesh->mFaces[ outIndex++ ]; - uiIdxCount += f.mNumIndices = 2; - f.mIndices = new unsigned int[2]; - } - continue; - } - else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) { - for(size_t i = 0; i < inp->m_pVertices->size(); ++i) { - aiFace& f = pMesh->mFaces[ outIndex++ ]; - uiIdxCount += f.mNumIndices = 1; - f.mIndices = new unsigned int[1]; - } - continue; - } + // Copy all data from all stored meshes + for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++) + { + ObjFile::Face* const inp = pObjMesh->m_Faces[ index ]; + if (inp->m_PrimitiveType == aiPrimitiveType_LINE) { + for(size_t i = 0; i < inp->m_pVertices->size() - 1; ++i) { + aiFace& f = pMesh->mFaces[ outIndex++ ]; + uiIdxCount += f.mNumIndices = 2; + f.mIndices = new unsigned int[2]; + } + continue; + } + else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) { + for(size_t i = 0; i < inp->m_pVertices->size(); ++i) { + aiFace& f = pMesh->mFaces[ outIndex++ ]; + uiIdxCount += f.mNumIndices = 1; + f.mIndices = new unsigned int[1]; + } + continue; + } - aiFace *pFace = &pMesh->mFaces[ outIndex++ ]; - const unsigned int uiNumIndices = (unsigned int) pObjMesh->m_Faces[ index ]->m_pVertices->size(); - uiIdxCount += pFace->mNumIndices = (unsigned int) uiNumIndices; - if (pFace->mNumIndices > 0) { - pFace->mIndices = new unsigned int[ uiNumIndices ]; - } - } - } + aiFace *pFace = &pMesh->mFaces[ outIndex++ ]; + const unsigned int uiNumIndices = (unsigned int) pObjMesh->m_Faces[ index ]->m_pVertices->size(); + uiIdxCount += pFace->mNumIndices = (unsigned int) uiNumIndices; + if (pFace->mNumIndices > 0) { + pFace->mIndices = new unsigned int[ uiNumIndices ]; + } + } + } - // Create mesh vertices - createVertexArray(pModel, pData, uiMeshIndex, pMesh, uiIdxCount); + // Create mesh vertices + createVertexArray(pModel, pData, uiMeshIndex, pMesh, uiIdxCount); return pMesh; } @@ -353,335 +353,335 @@ aiMesh *ObjFileImporter::createTopology( const ObjFile::Model* pModel, const Obj // ------------------------------------------------------------------------------------------------ // Creates a vertex array void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel, - const ObjFile::Object* pCurrentObject, - unsigned int uiMeshIndex, - aiMesh* pMesh, - unsigned int uiIdxCount) + const ObjFile::Object* pCurrentObject, + unsigned int uiMeshIndex, + aiMesh* pMesh, + unsigned int uiIdxCount) { - // Checking preconditions - ai_assert( NULL != pCurrentObject ); - - // Break, if no faces are stored in object - if ( pCurrentObject->m_Meshes.empty() ) - return; + // Checking preconditions + ai_assert( NULL != pCurrentObject ); + + // Break, if no faces are stored in object + if ( pCurrentObject->m_Meshes.empty() ) + return; - // Get current mesh - ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ uiMeshIndex ]; - if ( NULL == pObjMesh || pObjMesh->m_uiNumIndices < 1) - return; + // Get current mesh + ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ uiMeshIndex ]; + if ( NULL == pObjMesh || pObjMesh->m_uiNumIndices < 1) + return; - // Copy vertices of this mesh instance - pMesh->mNumVertices = uiIdxCount; - pMesh->mVertices = new aiVector3D[ pMesh->mNumVertices ]; - - // Allocate buffer for normal vectors - if ( !pModel->m_Normals.empty() && pObjMesh->m_hasNormals ) - pMesh->mNormals = new aiVector3D[ pMesh->mNumVertices ]; - - // Allocate buffer for texture coordinates - if ( !pModel->m_TextureCoord.empty() && pObjMesh->m_uiUVCoordinates[0] ) - { - pMesh->mNumUVComponents[ 0 ] = 2; - pMesh->mTextureCoords[ 0 ] = new aiVector3D[ pMesh->mNumVertices ]; - } - - // Copy vertices, normals and textures into aiMesh instance - unsigned int newIndex = 0, outIndex = 0; - for ( size_t index=0; index < pObjMesh->m_Faces.size(); index++ ) - { - // Get source face - ObjFile::Face *pSourceFace = pObjMesh->m_Faces[ index ]; + // Copy vertices of this mesh instance + pMesh->mNumVertices = uiIdxCount; + pMesh->mVertices = new aiVector3D[ pMesh->mNumVertices ]; + + // Allocate buffer for normal vectors + if ( !pModel->m_Normals.empty() && pObjMesh->m_hasNormals ) + pMesh->mNormals = new aiVector3D[ pMesh->mNumVertices ]; + + // Allocate buffer for texture coordinates + if ( !pModel->m_TextureCoord.empty() && pObjMesh->m_uiUVCoordinates[0] ) + { + pMesh->mNumUVComponents[ 0 ] = 2; + pMesh->mTextureCoords[ 0 ] = new aiVector3D[ pMesh->mNumVertices ]; + } + + // Copy vertices, normals and textures into aiMesh instance + unsigned int newIndex = 0, outIndex = 0; + for ( size_t index=0; index < pObjMesh->m_Faces.size(); index++ ) + { + // Get source face + ObjFile::Face *pSourceFace = pObjMesh->m_Faces[ index ]; - // Copy all index arrays - for ( size_t vertexIndex = 0, outVertexIndex = 0; vertexIndex < pSourceFace->m_pVertices->size(); vertexIndex++ ) - { - const unsigned int vertex = pSourceFace->m_pVertices->at( vertexIndex ); - if ( vertex >= pModel->m_Vertices.size() ) - throw DeadlyImportError( "OBJ: vertex index out of range" ); - - pMesh->mVertices[ newIndex ] = pModel->m_Vertices[ vertex ]; - - // Copy all normals - if ( !pModel->m_Normals.empty() && vertexIndex < pSourceFace->m_pNormals->size()) - { - const unsigned int normal = pSourceFace->m_pNormals->at( vertexIndex ); - if ( normal >= pModel->m_Normals.size() ) - throw DeadlyImportError("OBJ: vertex normal index out of range"); + // Copy all index arrays + for ( size_t vertexIndex = 0, outVertexIndex = 0; vertexIndex < pSourceFace->m_pVertices->size(); vertexIndex++ ) + { + const unsigned int vertex = pSourceFace->m_pVertices->at( vertexIndex ); + if ( vertex >= pModel->m_Vertices.size() ) + throw DeadlyImportError( "OBJ: vertex index out of range" ); + + pMesh->mVertices[ newIndex ] = pModel->m_Vertices[ vertex ]; + + // Copy all normals + if ( !pModel->m_Normals.empty() && vertexIndex < pSourceFace->m_pNormals->size()) + { + const unsigned int normal = pSourceFace->m_pNormals->at( vertexIndex ); + if ( normal >= pModel->m_Normals.size() ) + throw DeadlyImportError("OBJ: vertex normal index out of range"); - pMesh->mNormals[ newIndex ] = pModel->m_Normals[ normal ]; - } - - // Copy all texture coordinates - if ( !pModel->m_TextureCoord.empty() && vertexIndex < pSourceFace->m_pTexturCoords->size()) - { - const unsigned int tex = pSourceFace->m_pTexturCoords->at( vertexIndex ); - ai_assert( tex < pModel->m_TextureCoord.size() ); - - if ( tex >= pModel->m_TextureCoord.size() ) - throw DeadlyImportError("OBJ: texture coordinate index out of range"); + pMesh->mNormals[ newIndex ] = pModel->m_Normals[ normal ]; + } + + // Copy all texture coordinates + if ( !pModel->m_TextureCoord.empty() && vertexIndex < pSourceFace->m_pTexturCoords->size()) + { + const unsigned int tex = pSourceFace->m_pTexturCoords->at( vertexIndex ); + ai_assert( tex < pModel->m_TextureCoord.size() ); + + if ( tex >= pModel->m_TextureCoord.size() ) + throw DeadlyImportError("OBJ: texture coordinate index out of range"); - const aiVector3D &coord3d = pModel->m_TextureCoord[ tex ]; + const aiVector3D &coord3d = pModel->m_TextureCoord[ tex ]; pMesh->mTextureCoords[ 0 ][ newIndex ] = aiVector3D( coord3d.x, coord3d.y, coord3d.z ); - } + } - ai_assert( pMesh->mNumVertices > newIndex ); + ai_assert( pMesh->mNumVertices > newIndex ); - // Get destination face - aiFace *pDestFace = &pMesh->mFaces[ outIndex ]; + // Get destination face + aiFace *pDestFace = &pMesh->mFaces[ outIndex ]; - const bool last = ( vertexIndex == pSourceFace->m_pVertices->size() - 1 ); - if (pSourceFace->m_PrimitiveType != aiPrimitiveType_LINE || !last) - { - pDestFace->mIndices[ outVertexIndex ] = newIndex; - outVertexIndex++; - } + const bool last = ( vertexIndex == pSourceFace->m_pVertices->size() - 1 ); + if (pSourceFace->m_PrimitiveType != aiPrimitiveType_LINE || !last) + { + pDestFace->mIndices[ outVertexIndex ] = newIndex; + outVertexIndex++; + } - if (pSourceFace->m_PrimitiveType == aiPrimitiveType_POINT) - { - outIndex++; - outVertexIndex = 0; - } - else if (pSourceFace->m_PrimitiveType == aiPrimitiveType_LINE) - { - outVertexIndex = 0; + if (pSourceFace->m_PrimitiveType == aiPrimitiveType_POINT) + { + outIndex++; + outVertexIndex = 0; + } + else if (pSourceFace->m_PrimitiveType == aiPrimitiveType_LINE) + { + outVertexIndex = 0; - if(!last) - outIndex++; + if(!last) + outIndex++; - if (vertexIndex) { - if(!last) { - pMesh->mVertices[ newIndex+1 ] = pMesh->mVertices[ newIndex ]; - if ( !pSourceFace->m_pNormals->empty() && !pModel->m_Normals.empty()) { - pMesh->mNormals[ newIndex+1 ] = pMesh->mNormals[newIndex ]; - } - if ( !pModel->m_TextureCoord.empty() ) { - for ( size_t i=0; i < pMesh->GetNumUVChannels(); i++ ) { - pMesh->mTextureCoords[ i ][ newIndex+1 ] = pMesh->mTextureCoords[ i ][ newIndex ]; - } - } - ++newIndex; - } + if (vertexIndex) { + if(!last) { + pMesh->mVertices[ newIndex+1 ] = pMesh->mVertices[ newIndex ]; + if ( !pSourceFace->m_pNormals->empty() && !pModel->m_Normals.empty()) { + pMesh->mNormals[ newIndex+1 ] = pMesh->mNormals[newIndex ]; + } + if ( !pModel->m_TextureCoord.empty() ) { + for ( size_t i=0; i < pMesh->GetNumUVChannels(); i++ ) { + pMesh->mTextureCoords[ i ][ newIndex+1 ] = pMesh->mTextureCoords[ i ][ newIndex ]; + } + } + ++newIndex; + } - pDestFace[-1].mIndices[1] = newIndex; - } - } - else if (last) { - outIndex++; - } - ++newIndex; - } - } + pDestFace[-1].mIndices[1] = newIndex; + } + } + else if (last) { + outIndex++; + } + ++newIndex; + } + } } // ------------------------------------------------------------------------------------------------ // Counts all stored meshes void ObjFileImporter::countObjects(const std::vector &rObjects, int &iNumMeshes) { - iNumMeshes = 0; - if ( rObjects.empty() ) - return; + iNumMeshes = 0; + if ( rObjects.empty() ) + return; - iNumMeshes += static_cast( rObjects.size() ); - for (std::vector::const_iterator it = rObjects.begin(); - it != rObjects.end(); - ++it) - { - if (!(*it)->m_SubObjects.empty()) - { - countObjects((*it)->m_SubObjects, iNumMeshes); - } - } + iNumMeshes += static_cast( rObjects.size() ); + for (std::vector::const_iterator it = rObjects.begin(); + it != rObjects.end(); + ++it) + { + if (!(*it)->m_SubObjects.empty()) + { + countObjects((*it)->m_SubObjects, iNumMeshes); + } + } } // ------------------------------------------------------------------------------------------------ // Add clamp mode property to material if necessary void ObjFileImporter::addTextureMappingModeProperty(aiMaterial* mat, aiTextureType type, int clampMode) { - ai_assert( NULL != mat); - mat->AddProperty(&clampMode, 1, AI_MATKEY_MAPPINGMODE_U(type, 0)); - mat->AddProperty(&clampMode, 1, AI_MATKEY_MAPPINGMODE_V(type, 0)); + ai_assert( NULL != mat); + mat->AddProperty(&clampMode, 1, AI_MATKEY_MAPPINGMODE_U(type, 0)); + mat->AddProperty(&clampMode, 1, AI_MATKEY_MAPPINGMODE_V(type, 0)); } // ------------------------------------------------------------------------------------------------ // Creates the material void ObjFileImporter::createMaterials(const ObjFile::Model* pModel, aiScene* pScene ) { - ai_assert( NULL != pScene ); - if ( NULL == pScene ) - return; + ai_assert( NULL != pScene ); + if ( NULL == pScene ) + return; - const unsigned int numMaterials = (unsigned int) pModel->m_MaterialLib.size(); - pScene->mNumMaterials = 0; - if ( pModel->m_MaterialLib.empty() ) { - DefaultLogger::get()->debug("OBJ: no materials specified"); - return; - } - - pScene->mMaterials = new aiMaterial*[ numMaterials ]; - for ( unsigned int matIndex = 0; matIndex < numMaterials; matIndex++ ) - { - // Store material name - std::map::const_iterator it; - it = pModel->m_MaterialMap.find( pModel->m_MaterialLib[ matIndex ] ); - - // No material found, use the default material - if ( pModel->m_MaterialMap.end() == it ) - continue; + const unsigned int numMaterials = (unsigned int) pModel->m_MaterialLib.size(); + pScene->mNumMaterials = 0; + if ( pModel->m_MaterialLib.empty() ) { + DefaultLogger::get()->debug("OBJ: no materials specified"); + return; + } + + pScene->mMaterials = new aiMaterial*[ numMaterials ]; + for ( unsigned int matIndex = 0; matIndex < numMaterials; matIndex++ ) + { + // Store material name + std::map::const_iterator it; + it = pModel->m_MaterialMap.find( pModel->m_MaterialLib[ matIndex ] ); + + // No material found, use the default material + if ( pModel->m_MaterialMap.end() == it ) + continue; - aiMaterial* mat = new aiMaterial; - ObjFile::Material *pCurrentMaterial = (*it).second; - mat->AddProperty( &pCurrentMaterial->MaterialName, AI_MATKEY_NAME ); + aiMaterial* mat = new aiMaterial; + ObjFile::Material *pCurrentMaterial = (*it).second; + mat->AddProperty( &pCurrentMaterial->MaterialName, AI_MATKEY_NAME ); - // convert illumination model - int sm = 0; - switch (pCurrentMaterial->illumination_model) - { - case 0: - sm = aiShadingMode_NoShading; - break; - case 1: - sm = aiShadingMode_Gouraud; - break; - case 2: - sm = aiShadingMode_Phong; - break; - default: - sm = aiShadingMode_Gouraud; - DefaultLogger::get()->error("OBJ: unexpected illumination model (0-2 recognized)"); - } - - mat->AddProperty( &sm, 1, AI_MATKEY_SHADING_MODEL); + // convert illumination model + int sm = 0; + switch (pCurrentMaterial->illumination_model) + { + case 0: + sm = aiShadingMode_NoShading; + break; + case 1: + sm = aiShadingMode_Gouraud; + break; + case 2: + sm = aiShadingMode_Phong; + break; + default: + sm = aiShadingMode_Gouraud; + DefaultLogger::get()->error("OBJ: unexpected illumination model (0-2 recognized)"); + } + + mat->AddProperty( &sm, 1, AI_MATKEY_SHADING_MODEL); - // multiplying the specular exponent with 2 seems to yield better results - pCurrentMaterial->shineness *= 4.f; + // multiplying the specular exponent with 2 seems to yield better results + pCurrentMaterial->shineness *= 4.f; - // Adding material colors - mat->AddProperty( &pCurrentMaterial->ambient, 1, AI_MATKEY_COLOR_AMBIENT ); - mat->AddProperty( &pCurrentMaterial->diffuse, 1, AI_MATKEY_COLOR_DIFFUSE ); - mat->AddProperty( &pCurrentMaterial->specular, 1, AI_MATKEY_COLOR_SPECULAR ); - mat->AddProperty( &pCurrentMaterial->emissive, 1, AI_MATKEY_COLOR_EMISSIVE ); - mat->AddProperty( &pCurrentMaterial->shineness, 1, AI_MATKEY_SHININESS ); - mat->AddProperty( &pCurrentMaterial->alpha, 1, AI_MATKEY_OPACITY ); + // Adding material colors + mat->AddProperty( &pCurrentMaterial->ambient, 1, AI_MATKEY_COLOR_AMBIENT ); + mat->AddProperty( &pCurrentMaterial->diffuse, 1, AI_MATKEY_COLOR_DIFFUSE ); + mat->AddProperty( &pCurrentMaterial->specular, 1, AI_MATKEY_COLOR_SPECULAR ); + mat->AddProperty( &pCurrentMaterial->emissive, 1, AI_MATKEY_COLOR_EMISSIVE ); + mat->AddProperty( &pCurrentMaterial->shineness, 1, AI_MATKEY_SHININESS ); + mat->AddProperty( &pCurrentMaterial->alpha, 1, AI_MATKEY_OPACITY ); - // Adding refraction index - mat->AddProperty( &pCurrentMaterial->ior, 1, AI_MATKEY_REFRACTI ); + // Adding refraction index + mat->AddProperty( &pCurrentMaterial->ior, 1, AI_MATKEY_REFRACTI ); - // Adding textures - if ( 0 != pCurrentMaterial->texture.length ) - { - mat->AddProperty( &pCurrentMaterial->texture, AI_MATKEY_TEXTURE_DIFFUSE(0)); - if (pCurrentMaterial->clamp[ObjFile::Material::TextureDiffuseType]) - { - addTextureMappingModeProperty(mat, aiTextureType_DIFFUSE); - } - } + // Adding textures + if ( 0 != pCurrentMaterial->texture.length ) + { + mat->AddProperty( &pCurrentMaterial->texture, AI_MATKEY_TEXTURE_DIFFUSE(0)); + if (pCurrentMaterial->clamp[ObjFile::Material::TextureDiffuseType]) + { + addTextureMappingModeProperty(mat, aiTextureType_DIFFUSE); + } + } - if ( 0 != pCurrentMaterial->textureAmbient.length ) - { - mat->AddProperty( &pCurrentMaterial->textureAmbient, AI_MATKEY_TEXTURE_AMBIENT(0)); - if (pCurrentMaterial->clamp[ObjFile::Material::TextureAmbientType]) - { - addTextureMappingModeProperty(mat, aiTextureType_AMBIENT); - } - } + if ( 0 != pCurrentMaterial->textureAmbient.length ) + { + mat->AddProperty( &pCurrentMaterial->textureAmbient, AI_MATKEY_TEXTURE_AMBIENT(0)); + if (pCurrentMaterial->clamp[ObjFile::Material::TextureAmbientType]) + { + addTextureMappingModeProperty(mat, aiTextureType_AMBIENT); + } + } - if ( 0 != pCurrentMaterial->textureEmissive.length ) - mat->AddProperty( &pCurrentMaterial->textureEmissive, AI_MATKEY_TEXTURE_EMISSIVE(0)); + if ( 0 != pCurrentMaterial->textureEmissive.length ) + mat->AddProperty( &pCurrentMaterial->textureEmissive, AI_MATKEY_TEXTURE_EMISSIVE(0)); - if ( 0 != pCurrentMaterial->textureSpecular.length ) - { - mat->AddProperty( &pCurrentMaterial->textureSpecular, AI_MATKEY_TEXTURE_SPECULAR(0)); - if (pCurrentMaterial->clamp[ObjFile::Material::TextureSpecularType]) - { - addTextureMappingModeProperty(mat, aiTextureType_SPECULAR); - } - } + if ( 0 != pCurrentMaterial->textureSpecular.length ) + { + mat->AddProperty( &pCurrentMaterial->textureSpecular, AI_MATKEY_TEXTURE_SPECULAR(0)); + if (pCurrentMaterial->clamp[ObjFile::Material::TextureSpecularType]) + { + addTextureMappingModeProperty(mat, aiTextureType_SPECULAR); + } + } - if ( 0 != pCurrentMaterial->textureBump.length ) - { - mat->AddProperty( &pCurrentMaterial->textureBump, AI_MATKEY_TEXTURE_HEIGHT(0)); - if (pCurrentMaterial->clamp[ObjFile::Material::TextureBumpType]) - { - addTextureMappingModeProperty(mat, aiTextureType_HEIGHT); - } - } + if ( 0 != pCurrentMaterial->textureBump.length ) + { + mat->AddProperty( &pCurrentMaterial->textureBump, AI_MATKEY_TEXTURE_HEIGHT(0)); + if (pCurrentMaterial->clamp[ObjFile::Material::TextureBumpType]) + { + addTextureMappingModeProperty(mat, aiTextureType_HEIGHT); + } + } - if ( 0 != pCurrentMaterial->textureNormal.length ) - { - mat->AddProperty( &pCurrentMaterial->textureNormal, AI_MATKEY_TEXTURE_NORMALS(0)); - if (pCurrentMaterial->clamp[ObjFile::Material::TextureNormalType]) - { - addTextureMappingModeProperty(mat, aiTextureType_NORMALS); - } - } + if ( 0 != pCurrentMaterial->textureNormal.length ) + { + mat->AddProperty( &pCurrentMaterial->textureNormal, AI_MATKEY_TEXTURE_NORMALS(0)); + if (pCurrentMaterial->clamp[ObjFile::Material::TextureNormalType]) + { + addTextureMappingModeProperty(mat, aiTextureType_NORMALS); + } + } - if ( 0 != pCurrentMaterial->textureDisp.length ) - { - mat->AddProperty( &pCurrentMaterial->textureDisp, AI_MATKEY_TEXTURE_DISPLACEMENT(0) ); - if (pCurrentMaterial->clamp[ObjFile::Material::TextureDispType]) - { - addTextureMappingModeProperty(mat, aiTextureType_DISPLACEMENT); - } - } + if ( 0 != pCurrentMaterial->textureDisp.length ) + { + mat->AddProperty( &pCurrentMaterial->textureDisp, AI_MATKEY_TEXTURE_DISPLACEMENT(0) ); + if (pCurrentMaterial->clamp[ObjFile::Material::TextureDispType]) + { + addTextureMappingModeProperty(mat, aiTextureType_DISPLACEMENT); + } + } - if ( 0 != pCurrentMaterial->textureOpacity.length ) - { - mat->AddProperty( &pCurrentMaterial->textureOpacity, AI_MATKEY_TEXTURE_OPACITY(0)); - if (pCurrentMaterial->clamp[ObjFile::Material::TextureOpacityType]) - { - addTextureMappingModeProperty(mat, aiTextureType_OPACITY); - } - } + if ( 0 != pCurrentMaterial->textureOpacity.length ) + { + mat->AddProperty( &pCurrentMaterial->textureOpacity, AI_MATKEY_TEXTURE_OPACITY(0)); + if (pCurrentMaterial->clamp[ObjFile::Material::TextureOpacityType]) + { + addTextureMappingModeProperty(mat, aiTextureType_OPACITY); + } + } - if ( 0 != pCurrentMaterial->textureSpecularity.length ) - { - mat->AddProperty( &pCurrentMaterial->textureSpecularity, AI_MATKEY_TEXTURE_SHININESS(0)); - if (pCurrentMaterial->clamp[ObjFile::Material::TextureSpecularityType]) - { - addTextureMappingModeProperty(mat, aiTextureType_SHININESS); - } - } - - // Store material property info in material array in scene - pScene->mMaterials[ pScene->mNumMaterials ] = mat; - pScene->mNumMaterials++; - } - - // Test number of created materials. - ai_assert( pScene->mNumMaterials == numMaterials ); + if ( 0 != pCurrentMaterial->textureSpecularity.length ) + { + mat->AddProperty( &pCurrentMaterial->textureSpecularity, AI_MATKEY_TEXTURE_SHININESS(0)); + if (pCurrentMaterial->clamp[ObjFile::Material::TextureSpecularityType]) + { + addTextureMappingModeProperty(mat, aiTextureType_SHININESS); + } + } + + // Store material property info in material array in scene + pScene->mMaterials[ pScene->mNumMaterials ] = mat; + pScene->mNumMaterials++; + } + + // Test number of created materials. + ai_assert( pScene->mNumMaterials == numMaterials ); } // ------------------------------------------------------------------------------------------------ // Appends this node to the parent node void ObjFileImporter::appendChildToParentNode(aiNode *pParent, aiNode *pChild) { - // Checking preconditions - ai_assert( NULL != pParent ); - ai_assert( NULL != pChild ); + // Checking preconditions + ai_assert( NULL != pParent ); + ai_assert( NULL != pChild ); - // Assign parent to child - pChild->mParent = pParent; - - // If already children was assigned to the parent node, store them in a - std::vector temp; - if (pParent->mChildren != NULL) - { - ai_assert( 0 != pParent->mNumChildren ); - for (size_t index = 0; index < pParent->mNumChildren; index++) - { - temp.push_back(pParent->mChildren [ index ] ); - } - delete [] pParent->mChildren; - } - - // Copy node instances into parent node - pParent->mNumChildren++; - pParent->mChildren = new aiNode*[ pParent->mNumChildren ]; - for (size_t index = 0; index < pParent->mNumChildren-1; index++) - { - pParent->mChildren[ index ] = temp [ index ]; - } - pParent->mChildren[ pParent->mNumChildren-1 ] = pChild; + // Assign parent to child + pChild->mParent = pParent; + + // If already children was assigned to the parent node, store them in a + std::vector temp; + if (pParent->mChildren != NULL) + { + ai_assert( 0 != pParent->mNumChildren ); + for (size_t index = 0; index < pParent->mNumChildren; index++) + { + temp.push_back(pParent->mChildren [ index ] ); + } + delete [] pParent->mChildren; + } + + // Copy node instances into parent node + pParent->mNumChildren++; + pParent->mChildren = new aiNode*[ pParent->mNumChildren ]; + for (size_t index = 0; index < pParent->mNumChildren-1; index++) + { + pParent->mChildren[ index ] = temp [ index ]; + } + pParent->mChildren[ pParent->mNumChildren-1 ] = pChild; } // ------------------------------------------------------------------------------------------------ diff --git a/code/ObjFileImporter.h b/code/ObjFileImporter.h index 716cf65d1..0168c2673 100644 --- a/code/ObjFileImporter.h +++ b/code/ObjFileImporter.h @@ -64,57 +64,57 @@ struct Model; class ObjFileImporter : public BaseImporter { public: - /// \brief Default constructor - ObjFileImporter(); + /// \brief Default constructor + ObjFileImporter(); - /// \brief Destructor - ~ObjFileImporter(); + /// \brief Destructor + ~ObjFileImporter(); public: - /// \brief Returns whether the class can handle the format of the given file. - /// \remark See BaseImporter::CanRead() for details. - bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; + /// \brief Returns whether the class can handle the format of the given file. + /// \remark See BaseImporter::CanRead() for details. + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; private: - //! \brief Appends the supported extension. - const aiImporterDesc* GetInfo () const; + //! \brief Appends the supported extension. + const aiImporterDesc* GetInfo () const; - //! \brief File import implementation. - void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); - - //! \brief Create the data from imported content. - void CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene); - - //! \brief Creates all nodes stored in imported content. - aiNode *createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pData, - aiNode *pParent, aiScene* pScene, std::vector &MeshArray); + //! \brief File import implementation. + void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + + //! \brief Create the data from imported content. + void CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene); + + //! \brief Creates all nodes stored in imported content. + aiNode *createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pData, + aiNode *pParent, aiScene* pScene, std::vector &MeshArray); - //! \brief Creates topology data like faces and meshes for the geometry. + //! \brief Creates topology data like faces and meshes for the geometry. aiMesh *createTopology( const ObjFile::Model* pModel, const ObjFile::Object* pData, - unsigned int uiMeshIndex ); - - //! \brief Creates vertices from model. - void createVertexArray(const ObjFile::Model* pModel, const ObjFile::Object* pCurrentObject, - unsigned int uiMeshIndex, aiMesh* pMesh,unsigned int uiIdxCount); + unsigned int uiMeshIndex ); + + //! \brief Creates vertices from model. + void createVertexArray(const ObjFile::Model* pModel, const ObjFile::Object* pCurrentObject, + unsigned int uiMeshIndex, aiMesh* pMesh,unsigned int uiIdxCount); - //! \brief Object counter helper method. - void countObjects(const std::vector &rObjects, int &iNumMeshes); + //! \brief Object counter helper method. + void countObjects(const std::vector &rObjects, int &iNumMeshes); - //! \brief Material creation. - void createMaterials(const ObjFile::Model* pModel, aiScene* pScene); - void addTextureMappingModeProperty(aiMaterial* mat, aiTextureType type, int clampMode = 1); + //! \brief Material creation. + void createMaterials(const ObjFile::Model* pModel, aiScene* pScene); + void addTextureMappingModeProperty(aiMaterial* mat, aiTextureType type, int clampMode = 1); - //! \brief Appends a child node to a parent node and updates the data structures. - void appendChildToParentNode(aiNode *pParent, aiNode *pChild); + //! \brief Appends a child node to a parent node and updates the data structures. + void appendChildToParentNode(aiNode *pParent, aiNode *pChild); private: - //! Data buffer - std::vector m_Buffer; - //! Pointer to root object instance - ObjFile::Object *m_pRootObject; - //! Absolute pathname of model in file system - std::string m_strAbsPath; + //! Data buffer + std::vector m_Buffer; + //! Pointer to root object instance + ObjFile::Object *m_pRootObject; + //! Absolute pathname of model in file system + std::string m_strAbsPath; }; // ------------------------------------------------------------------------------------------------ diff --git a/code/ObjFileParser.h b/code/ObjFileParser.h index 500e8d0f9..d22ca3d8e 100644 --- a/code/ObjFileParser.h +++ b/code/ObjFileParser.h @@ -63,76 +63,76 @@ class IOSystem; class ObjFileParser { public: - static const size_t BUFFERSIZE = 4096; - typedef std::vector DataArray; - typedef std::vector::iterator DataArrayIt; - typedef std::vector::const_iterator ConstDataArrayIt; + static const size_t BUFFERSIZE = 4096; + typedef std::vector DataArray; + typedef std::vector::iterator DataArrayIt; + typedef std::vector::const_iterator ConstDataArrayIt; public: - /// \brief Constructor with data array. - ObjFileParser(std::vector &Data,const std::string &strModelName, IOSystem* io); - /// \brief Destructor - ~ObjFileParser(); - /// \brief Model getter. - ObjFile::Model *GetModel() const; + /// \brief Constructor with data array. + ObjFileParser(std::vector &Data,const std::string &strModelName, IOSystem* io); + /// \brief Destructor + ~ObjFileParser(); + /// \brief Model getter. + ObjFile::Model *GetModel() const; private: - /// Parse the loaded file - void parseFile(); - /// Method to copy the new delimited word in the current line. - void copyNextWord(char *pBuffer, size_t length); - /// Method to copy the new line. - void copyNextLine(char *pBuffer, size_t length); + /// Parse the loaded file + void parseFile(); + /// Method to copy the new delimited word in the current line. + void copyNextWord(char *pBuffer, size_t length); + /// Method to copy the new line. + void copyNextLine(char *pBuffer, size_t length); /// Stores the vector void getVector( std::vector &point3d_array ); /// Stores the following 3d vector. - void getVector3( std::vector &point3d_array ); - /// Stores the following 3d vector. - void getVector2(std::vector &point2d_array); + void getVector3( std::vector &point3d_array ); + /// Stores the following 3d vector. + void getVector2(std::vector &point2d_array); /// Stores the following face. - void getFace(aiPrimitiveType type); - /// Reads the material description. + void getFace(aiPrimitiveType type); + /// Reads the material description. void getMaterialDesc(); - /// Gets a comment. - void getComment(); - /// Gets a a material library. - void getMaterialLib(); - /// Creates a new material. - void getNewMaterial(); - /// Gets the group name from file. - void getGroupName(); - /// Gets the group number from file. - void getGroupNumber(); - /// Gets the group number and resolution from file. - void getGroupNumberAndResolution(); - /// Returns the index of the material. Is -1 if not material was found. - int getMaterialIndex( const std::string &strMaterialName ); - /// Parse object name - void getObjectName(); - /// Creates a new object. - void createObject(const std::string &strObjectName); - /// Creates a new mesh. - void createMesh(); - /// Returns true, if a new mesh instance must be created. - bool needsNewMesh( const std::string &rMaterialName ); - /// Error report in token - void reportErrorTokenInFace(); + /// Gets a comment. + void getComment(); + /// Gets a a material library. + void getMaterialLib(); + /// Creates a new material. + void getNewMaterial(); + /// Gets the group name from file. + void getGroupName(); + /// Gets the group number from file. + void getGroupNumber(); + /// Gets the group number and resolution from file. + void getGroupNumberAndResolution(); + /// Returns the index of the material. Is -1 if not material was found. + int getMaterialIndex( const std::string &strMaterialName ); + /// Parse object name + void getObjectName(); + /// Creates a new object. + void createObject(const std::string &strObjectName); + /// Creates a new mesh. + void createMesh(); + /// Returns true, if a new mesh instance must be created. + bool needsNewMesh( const std::string &rMaterialName ); + /// Error report in token + void reportErrorTokenInFace(); private: - /// Default material name - static const std::string DEFAULT_MATERIAL; - //! Iterator to current position in buffer - DataArrayIt m_DataIt; - //! Iterator to end position of buffer - DataArrayIt m_DataItEnd; - //! Pointer to model instance - ObjFile::Model *m_pModel; - //! Current line (for debugging) - unsigned int m_uiLine; - //! Helper buffer - char m_buffer[BUFFERSIZE]; - /// Pointer to IO system instance. - IOSystem *m_pIO; + /// Default material name + static const std::string DEFAULT_MATERIAL; + //! Iterator to current position in buffer + DataArrayIt m_DataIt; + //! Iterator to end position of buffer + DataArrayIt m_DataItEnd; + //! Pointer to model instance + ObjFile::Model *m_pModel; + //! Current line (for debugging) + unsigned int m_uiLine; + //! Helper buffer + char m_buffer[BUFFERSIZE]; + /// Pointer to IO system instance. + IOSystem *m_pIO; }; } // Namespace Assimp diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index 684eb20b7..b869aa2a1 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -244,15 +244,6 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { in++; } - // set the properties - if( ddl_nullptr != first ) { - std::cout << id->m_buffer << std::endl; - DDLNode *current( top() ); - if( current ) { - current->setProperties( first ); - } - } - // store the node DDLNode *node( createDDLNode( id, this ) ); if( nullptr != node ) { @@ -261,6 +252,11 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { std::cerr << "nullptr returned by creating DDLNode." << std::endl; } + // set the properties + if( ddl_nullptr != first ) { + node->setProperties( first ); + } + Name *name( ddl_nullptr ); in = OpenDDLParser::parseName( in, end, &name ); if( ddl_nullptr != name ) { @@ -277,6 +273,7 @@ char *OpenDDLParser::parseStructure( char *in, char *end ) { return in; } + bool error( false ); in = getNextToken( in, end ); if( *in == '{' ) { in++; @@ -307,6 +304,7 @@ char *OpenDDLParser::parseStructure( char *in, char *end ) { } } else { std::cerr << "0 for array is invalid." << std::endl; + error = true; } } @@ -324,12 +322,16 @@ char *OpenDDLParser::parseStructure( char *in, char *end ) { } else { in++; logInvalidTokenError( in, "{", m_logCallback ); + error = true; return in; } in = getNextToken( in, end ); - - in++; + + // pop node from stack after successful parsing + if( !error ) { + popNode(); + } return in; } From 556bc9c1f34c0d344535617a464f4eb87992f49e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 10 Feb 2015 20:09:03 +0100 Subject: [PATCH 23/74] fix build Signed-off-by: Kim Kulling --- contrib/openddlparser/include/openddlparser/DDLNode.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/openddlparser/include/openddlparser/DDLNode.h b/contrib/openddlparser/include/openddlparser/DDLNode.h index d5b4a56c9..bb5f87b79 100644 --- a/contrib/openddlparser/include/openddlparser/DDLNode.h +++ b/contrib/openddlparser/include/openddlparser/DDLNode.h @@ -61,10 +61,10 @@ public: Value *getValue() const; void setDataArrayList( DataArrayList *dtArrayList ); DataArrayList *getDataArrayList() const; - static DDLNode *create( const std::string &type, const std::string &name, DDLNode *parent = nullptr ); + static DDLNode *create( const std::string &type, const std::string &name, DDLNode *parent = ddl_nullptr ); private: - DDLNode( const std::string &type, const std::string &name, size_t idx, DDLNode *parent = nullptr ); + DDLNode( const std::string &type, const std::string &name, size_t idx, DDLNode *parent = ddl_nullptr ); DDLNode(); DDLNode( const DDLNode & ); DDLNode &operator = ( const DDLNode & ); From 9e87fa178af58fda5d2acb6a2d32c86128217e6c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 10 Feb 2015 20:09:26 +0100 Subject: [PATCH 24/74] add property parsing to opengex parser. Signed-off-by: Kim Kulling --- code/OpenGEXImporter.cpp | 167 ++++++++++++++++++++++++++++++++------- code/OpenGEXImporter.h | 17 ++++ 2 files changed, 157 insertions(+), 27 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index af9b550b5..83bde51fe 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -61,35 +61,116 @@ static const aiImporterDesc desc = { }; namespace Grammar { - static const char *MetricType = "Metric"; - static const char *NameType = "Name"; - static const char *ObjectRefType = "ObjectRef"; - static const char *MaterialRefType = "MaterialRef"; - static const char *MetricKeyType = "key"; - static const char *GeometryNodeType = "GeometryNode"; - static const char *GeometryObjectType = "GeometryObject"; - static const char *TransformType = "Transform"; - static const char *MeshType = "Mesh"; - static const char *VertexArrayType = "VertexArray"; - static const char *IndexArrayType = "IndexArray"; - static const char *MaterialType = "Material"; - static const char *ColorType = "Color"; - static const char *TextureType = "Texture"; + static const char *MetricType = "Metric"; + static const char *Metric_DistanceType = "distance"; + static const char *Metric_AngleType = "angle"; + static const char *Metric_TimeType = "time"; + static const char *Metric_UpType = "up"; + static const char *NameType = "Name"; + static const char *ObjectRefType = "ObjectRef"; + static const char *MaterialRefType = "MaterialRef"; + static const char *MetricKeyType = "key"; + static const char *GeometryNodeType = "GeometryNode"; + static const char *GeometryObjectType = "GeometryObject"; + static const char *TransformType = "Transform"; + static const char *MeshType = "Mesh"; + static const char *VertexArrayType = "VertexArray"; + static const char *IndexArrayType = "IndexArray"; + static const char *MaterialType = "Material"; + static const char *ColorType = "Color"; + static const char *TextureType = "Texture"; + + enum TokenType { + NoneType = -1, + MetricToken, + NameToken, + ObjectRefToken, + MaterialRefToken, + MetricKeyToken, + GeometryNodeToken, + GeometryObjectToken, + TransformToken, + MeshToken, + VertexArrayToken, + IndexArrayToken, + MaterialToken, + ColorToken, + TextureToken + }; + + static const char *ValidMetricToken[ 4 ] = { + Metric_DistanceType, + Metric_AngleType, + Metric_TimeType, + Metric_UpType + }; + + static int isValidMetricType( const char *token ) { + if( NULL == token ) { + return false; + } + + int idx( -1 ); + for( size_t i = 0; i < 4; i++ ) { + if( 0 == strncmp( ValidMetricToken[ i ], token, strlen( token ) ) ) { + idx = (int) i; + break; + } + } + + return idx; + } + + static TokenType matchTokenType( const char *tokenType ) { + if( 0 == strncmp( MetricType, tokenType, strlen( tokenType ) ) ) { + return MetricToken; + } else if( 0 == strncmp( NameType, tokenType, strlen( tokenType ) ) ) { + return NameToken; + } else if( 0 == strncmp( ObjectRefType, tokenType, strlen( tokenType ) ) ) { + return ObjectRefToken; + } else if( 0 == strncmp( MaterialRefType, tokenType, strlen( tokenType ) ) ) { + return MaterialRefToken; + } else if( 0 == strncmp( MetricKeyType, tokenType, strlen( tokenType ) ) ) { + return MetricKeyToken; + } else if( 0 == strncmp( GeometryNodeType, tokenType, strlen( tokenType ) ) ) { + return GeometryNodeToken; + } else if( 0 == strncmp( GeometryObjectType, tokenType, strlen( tokenType ) ) ) { + return GeometryObjectToken; + } else if( 0 == strncmp( TransformType, tokenType, strlen( tokenType ) ) ) { + return TransformToken; + } else if( 0 == strncmp( MeshType, tokenType, strlen( tokenType ) ) ) { + return MeshToken; + } else if( 0 == strncmp( VertexArrayType, tokenType, strlen( tokenType ) ) ) { + return VertexArrayToken; + } else if( 0 == strncmp( IndexArrayType, tokenType, strlen( tokenType ) ) ) { + return IndexArrayToken; + } else if( 0 == strncmp( MaterialType, tokenType, strlen( tokenType ) ) ) { + return MaterialToken; + } else if( 0 == strncmp( ColorType, tokenType, strlen( tokenType ) ) ) { + return ColorToken; + } else if( 0 == strncmp( TextureType, tokenType, strlen( tokenType ) ) ) { + return TextureToken; + } + + return NoneType; + } + } // Namespace Grammar namespace Assimp { namespace OpenGEX { - USE_ODDLPARSER_NS +USE_ODDLPARSER_NS //------------------------------------------------------------------------------------------------ -OpenGEXImporter::OpenGEXImporter() { - +OpenGEXImporter::OpenGEXImporter() +: m_ctx( NULL ) { + // empty } //------------------------------------------------------------------------------------------------ OpenGEXImporter::~OpenGEXImporter() { - + m_ctx = NULL; } //------------------------------------------------------------------------------------------------ @@ -120,8 +201,8 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce myParser.setBuffer( &buffer[ 0 ], buffer.size() ); bool success( myParser.parse() ); if( success ) { - Context *ctx = myParser.getContext(); - handleNodes( ctx->m_root ); + m_ctx = myParser.getContext(); + handleNodes( m_ctx->m_root ); } } @@ -138,22 +219,45 @@ void OpenGEXImporter::SetupProperties( const Importer *pImp ) { } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleNodes( ODDLParser::DDLNode *node ) { +void OpenGEXImporter::handleNodes( DDLNode *node ) { if( NULL == node ) { return; } DDLNode::DllNodeList childs = node->getChildNodeList(); for( DDLNode::DllNodeList::iterator it = childs.begin(); it != childs.end(); it++ ) { - if( ( *it )->getType() == Grammar::MetricType ) { - importMetric( *it ); + Grammar::TokenType tokenType( Grammar::matchTokenType( ( *it )->getType().c_str() ) ); + switch( tokenType ) { + case Grammar::MetricToken: + importMetric( *it ); + break; + + case Grammar::NameToken: + case Grammar::ObjectRefToken: + case Grammar::MaterialRefToken: + case Grammar::MetricKeyToken: + case Grammar::GeometryNodeToken: + case Grammar::GeometryObjectToken: + case Grammar::TransformToken: + case Grammar::MeshToken: + case Grammar::VertexArrayToken: + case Grammar::IndexArrayToken: + case Grammar::MaterialToken: + case Grammar::ColorToken: + case Grammar::TextureToken: + default: + break; } } } //------------------------------------------------------------------------------------------------ void OpenGEXImporter::importMetric( DDLNode *node ) { - if( NULL == node ) { + if( NULL == node || NULL == m_ctx ) { + return; + } + + if( m_ctx->m_root != node->getParent() ) { return; } @@ -162,9 +266,18 @@ void OpenGEXImporter::importMetric( DDLNode *node ) { if( NULL != prop->m_id ) { if( Value::ddl_string == prop->m_primData->m_type ) { std::string valName( (char*) prop->m_primData->m_data ); - Value *val( node->getValue() ); - if( NULL != val ) { - + int type( Grammar::isValidMetricType( valName.c_str() ) ); + if( -1 != type ) { + Value *val( node->getValue() ); + if( NULL != val ) { + if( Value::ddl_float == val->m_type ) { + m_metrics[ type ].m_floatValue = val->getFloat(); + } else if( Value::ddl_int32 == val->m_type ) { + m_metrics[ type ].m_floatValue = val->getInt32(); + } else { + throw DeadlyImportError( "OpenGEX: invalid data type for Metric node." ); + } + } } } } diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index 58641775e..9e8b06ff3 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -53,6 +53,19 @@ namespace ODDLParser { namespace Assimp { namespace OpenGEX { +struct MetricInfo { + enum Type { + Distance = 0, + Angle, + Time, + Up, + Max + }; + + std::string m_stringValue; + float m_floatValue; +}; + /** @brief This class is used to implement the OpenGEX importer * * See http://opengex.org/OpenGEX.pdf for spec. @@ -82,6 +95,10 @@ protected: void importMetric( ODDLParser::DDLNode *node ); void ParseGeoObject(); void ParseMaterial(); + +private: + ODDLParser::Context *m_ctx; + MetricInfo m_metrics[ MetricInfo::Max ]; }; } // Namespace OpenGEX From bd1168af30b89f3c02a3c04369cbebc64867b977 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 10 Feb 2015 20:46:02 +0100 Subject: [PATCH 25/74] fix invalid line endling handling. Signed-off-by: Kim Kulling --- code/ObjFileParser.cpp | 914 +++++++++--------- code/ParsingUtils.h | 70 +- .../openddlparser/OpenDDLParserUtils.h | 2 +- 3 files changed, 493 insertions(+), 493 deletions(-) diff --git a/code/ObjFileParser.cpp b/code/ObjFileParser.cpp index 34bb01e0a..c4dff0ba0 100644 --- a/code/ObjFileParser.cpp +++ b/code/ObjFileParser.cpp @@ -57,177 +57,177 @@ const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME; // ------------------------------------------------------------------- // Constructor with loaded data and directories. ObjFileParser::ObjFileParser(std::vector &Data,const std::string &strModelName, IOSystem *io ) : - m_DataIt(Data.begin()), - m_DataItEnd(Data.end()), - m_pModel(NULL), - m_uiLine(0), - m_pIO( io ) + m_DataIt(Data.begin()), + m_DataItEnd(Data.end()), + m_pModel(NULL), + m_uiLine(0), + m_pIO( io ) { - std::fill_n(m_buffer,BUFFERSIZE,0); + std::fill_n(m_buffer,BUFFERSIZE,0); - // Create the model instance to store all the data - m_pModel = new ObjFile::Model(); - m_pModel->m_ModelName = strModelName; - + // Create the model instance to store all the data + m_pModel = new ObjFile::Model(); + m_pModel->m_ModelName = strModelName; + // create default material and store it - m_pModel->m_pDefaultMaterial = new ObjFile::Material(); - m_pModel->m_pDefaultMaterial->MaterialName.Set( DEFAULT_MATERIAL ); + m_pModel->m_pDefaultMaterial = new ObjFile::Material(); + m_pModel->m_pDefaultMaterial->MaterialName.Set( DEFAULT_MATERIAL ); m_pModel->m_MaterialLib.push_back( DEFAULT_MATERIAL ); - m_pModel->m_MaterialMap[ DEFAULT_MATERIAL ] = m_pModel->m_pDefaultMaterial; - - // Start parsing the file - parseFile(); + m_pModel->m_MaterialMap[ DEFAULT_MATERIAL ] = m_pModel->m_pDefaultMaterial; + + // Start parsing the file + parseFile(); } // ------------------------------------------------------------------- // Destructor ObjFileParser::~ObjFileParser() { - delete m_pModel; - m_pModel = NULL; + delete m_pModel; + m_pModel = NULL; } // ------------------------------------------------------------------- // Returns a pointer to the model instance. ObjFile::Model *ObjFileParser::GetModel() const { - return m_pModel; + return m_pModel; } // ------------------------------------------------------------------- // File parsing method. void ObjFileParser::parseFile() { - if (m_DataIt == m_DataItEnd) - return; + if (m_DataIt == m_DataItEnd) + return; - while (m_DataIt != m_DataItEnd) - { - switch (*m_DataIt) - { - case 'v': // Parse a vertex texture coordinate - { - ++m_DataIt; - if (*m_DataIt == ' ' || *m_DataIt == '\t') { - // read in vertex definition - getVector3(m_pModel->m_Vertices); - } else if (*m_DataIt == 't') { - // read in texture coordinate ( 2D or 3D ) + while (m_DataIt != m_DataItEnd) + { + switch (*m_DataIt) + { + case 'v': // Parse a vertex texture coordinate + { + ++m_DataIt; + if (*m_DataIt == ' ' || *m_DataIt == '\t') { + // read in vertex definition + getVector3(m_pModel->m_Vertices); + } else if (*m_DataIt == 't') { + // read in texture coordinate ( 2D or 3D ) ++m_DataIt; getVector( m_pModel->m_TextureCoord ); - } else if (*m_DataIt == 'n') { - // Read in normal vector definition - ++m_DataIt; - getVector3( m_pModel->m_Normals ); - } - } - break; + } else if (*m_DataIt == 'n') { + // Read in normal vector definition + ++m_DataIt; + getVector3( m_pModel->m_Normals ); + } + } + break; - case 'p': // Parse a face, line or point statement - case 'l': - case 'f': - { - getFace(*m_DataIt == 'f' ? aiPrimitiveType_POLYGON : (*m_DataIt == 'l' - ? aiPrimitiveType_LINE : aiPrimitiveType_POINT)); - } - break; + case 'p': // Parse a face, line or point statement + case 'l': + case 'f': + { + getFace(*m_DataIt == 'f' ? aiPrimitiveType_POLYGON : (*m_DataIt == 'l' + ? aiPrimitiveType_LINE : aiPrimitiveType_POINT)); + } + break; - case '#': // Parse a comment - { - getComment(); - } - break; + case '#': // Parse a comment + { + getComment(); + } + break; - case 'u': // Parse a material desc. setter - { - getMaterialDesc(); - } - break; + case 'u': // Parse a material desc. setter + { + getMaterialDesc(); + } + break; - case 'm': // Parse a material library or merging group ('mg') - { - if (*(m_DataIt + 1) == 'g') - getGroupNumberAndResolution(); - else - getMaterialLib(); - } - break; + case 'm': // Parse a material library or merging group ('mg') + { + if (*(m_DataIt + 1) == 'g') + getGroupNumberAndResolution(); + else + getMaterialLib(); + } + break; - case 'g': // Parse group name - { - getGroupName(); - } - break; + case 'g': // Parse group name + { + getGroupName(); + } + break; - case 's': // Parse group number - { - getGroupNumber(); - } - break; + case 's': // Parse group number + { + getGroupNumber(); + } + break; - case 'o': // Parse object name - { - getObjectName(); - } - break; - - default: - { - m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); - } - break; - } - } + case 'o': // Parse object name + { + getObjectName(); + } + break; + + default: + { + m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); + } + break; + } + } } // ------------------------------------------------------------------- // Copy the next word in a temporary buffer void ObjFileParser::copyNextWord(char *pBuffer, size_t length) { - size_t index = 0; - m_DataIt = getNextWord(m_DataIt, m_DataItEnd); + size_t index = 0; + m_DataIt = getNextWord(m_DataIt, m_DataItEnd); while( m_DataIt != m_DataItEnd && !IsSpaceOrNewLine( *m_DataIt ) ) { - pBuffer[index] = *m_DataIt; - index++; + pBuffer[index] = *m_DataIt; + index++; if( index == length - 1 ) { break; } - ++m_DataIt; - } + ++m_DataIt; + } - ai_assert(index < length); - pBuffer[index] = '\0'; + ai_assert(index < length); + pBuffer[index] = '\0'; } // ------------------------------------------------------------------- // Copy the next line into a temporary buffer void ObjFileParser::copyNextLine(char *pBuffer, size_t length) { - size_t index = 0u; + size_t index = 0u; - // some OBJ files have line continuations using \ (such as in C++ et al) - bool continuation = false; - for (;m_DataIt != m_DataItEnd && index < length-1; ++m_DataIt) - { - const char c = *m_DataIt; - if (c == '\\') { - continuation = true; - continue; - } - - if (c == '\n' || c == '\r') { - if(continuation) { - pBuffer[ index++ ] = ' '; - continue; - } - break; - } + // some OBJ files have line continuations using \ (such as in C++ et al) + bool continuation = false; + for (;m_DataIt != m_DataItEnd && index < length-1; ++m_DataIt) + { + const char c = *m_DataIt; + if (c == '\\') { + continuation = true; + continue; + } + + if (c == '\n' || c == '\r') { + if(continuation) { + pBuffer[ index++ ] = ' '; + continue; + } + break; + } - continuation = false; - pBuffer[ index++ ] = c; - } - ai_assert(index < length); - pBuffer[ index ] = '\0'; + continuation = false; + pBuffer[ index++ ] = c; + } + ai_assert(index < length); + pBuffer[ index ] = '\0'; } // ------------------------------------------------------------------- @@ -268,391 +268,391 @@ void ObjFileParser::getVector( std::vector &point3d_array ) { // ------------------------------------------------------------------- // Get values for a new 3D vector instance void ObjFileParser::getVector3(std::vector &point3d_array) { - float x, y, z; - copyNextWord(m_buffer, BUFFERSIZE); - x = (float) fast_atof(m_buffer); - - copyNextWord(m_buffer, BUFFERSIZE); - y = (float) fast_atof(m_buffer); + float x, y, z; + copyNextWord(m_buffer, BUFFERSIZE); + x = (float) fast_atof(m_buffer); + + copyNextWord(m_buffer, BUFFERSIZE); + y = (float) fast_atof(m_buffer); copyNextWord( m_buffer, BUFFERSIZE ); z = ( float ) fast_atof( m_buffer ); - point3d_array.push_back( aiVector3D( x, y, z ) ); - m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); + point3d_array.push_back( aiVector3D( x, y, z ) ); + m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- // Get values for a new 2D vector instance void ObjFileParser::getVector2( std::vector &point2d_array ) { - float x, y; - copyNextWord(m_buffer, BUFFERSIZE); - x = (float) fast_atof(m_buffer); - - copyNextWord(m_buffer, BUFFERSIZE); - y = (float) fast_atof(m_buffer); + float x, y; + copyNextWord(m_buffer, BUFFERSIZE); + x = (float) fast_atof(m_buffer); + + copyNextWord(m_buffer, BUFFERSIZE); + y = (float) fast_atof(m_buffer); - point2d_array.push_back(aiVector2D(x, y)); + point2d_array.push_back(aiVector2D(x, y)); - m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); + m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- // Get values for a new face instance void ObjFileParser::getFace(aiPrimitiveType type) { - copyNextLine(m_buffer, BUFFERSIZE); - if (m_DataIt == m_DataItEnd) - return; + copyNextLine(m_buffer, BUFFERSIZE); + if (m_DataIt == m_DataItEnd) + return; - char *pPtr = m_buffer; - char *pEnd = &pPtr[BUFFERSIZE]; - pPtr = getNextToken(pPtr, pEnd); - if (pPtr == pEnd || *pPtr == '\0') - return; + char *pPtr = m_buffer; + char *pEnd = &pPtr[BUFFERSIZE]; + pPtr = getNextToken(pPtr, pEnd); + if (pPtr == pEnd || *pPtr == '\0') + return; - std::vector *pIndices = new std::vector; - std::vector *pTexID = new std::vector; - std::vector *pNormalID = new std::vector; - bool hasNormal = false; + std::vector *pIndices = new std::vector; + std::vector *pTexID = new std::vector; + std::vector *pNormalID = new std::vector; + bool hasNormal = false; - const int vSize = m_pModel->m_Vertices.size(); - const int vtSize = m_pModel->m_TextureCoord.size(); - const int vnSize = m_pModel->m_Normals.size(); + const int vSize = m_pModel->m_Vertices.size(); + const int vtSize = m_pModel->m_TextureCoord.size(); + const int vnSize = m_pModel->m_Normals.size(); - const bool vt = (!m_pModel->m_TextureCoord.empty()); - const bool vn = (!m_pModel->m_Normals.empty()); - int iStep = 0, iPos = 0; - while (pPtr != pEnd) - { - iStep = 1; + const bool vt = (!m_pModel->m_TextureCoord.empty()); + const bool vn = (!m_pModel->m_Normals.empty()); + int iStep = 0, iPos = 0; + while (pPtr != pEnd) + { + iStep = 1; - if (IsLineEnd(*pPtr)) - break; + if (IsLineEnd(*pPtr)) + break; - if (*pPtr=='/' ) - { - if (type == aiPrimitiveType_POINT) { - DefaultLogger::get()->error("Obj: Separator unexpected in point statement"); - } - if (iPos == 0) - { - //if there are no texture coordinates in the file, but normals - if (!vt && vn) { - iPos = 1; - iStep++; - } - } - iPos++; - } + if (*pPtr=='/' ) + { + if (type == aiPrimitiveType_POINT) { + DefaultLogger::get()->error("Obj: Separator unexpected in point statement"); + } + if (iPos == 0) + { + //if there are no texture coordinates in the file, but normals + if (!vt && vn) { + iPos = 1; + iStep++; + } + } + iPos++; + } else if( IsSpaceOrNewLine( *pPtr ) ) - { - iPos = 0; - } - else - { - //OBJ USES 1 Base ARRAYS!!!! - const int iVal = atoi( pPtr ); + { + iPos = 0; + } + else + { + //OBJ USES 1 Base ARRAYS!!!! + const int iVal = atoi( pPtr ); - // increment iStep position based off of the sign and # of digits - int tmp = iVal; - if (iVal < 0) - ++iStep; - while ( ( tmp = tmp / 10 )!=0 ) - ++iStep; + // increment iStep position based off of the sign and # of digits + int tmp = iVal; + if (iVal < 0) + ++iStep; + while ( ( tmp = tmp / 10 )!=0 ) + ++iStep; - if ( iVal > 0 ) - { - // Store parsed index - if ( 0 == iPos ) - { - pIndices->push_back( iVal-1 ); - } - else if ( 1 == iPos ) - { - pTexID->push_back( iVal-1 ); - } - else if ( 2 == iPos ) - { - pNormalID->push_back( iVal-1 ); - hasNormal = true; - } - else - { - reportErrorTokenInFace(); - } - } - else if ( iVal < 0 ) - { - // Store relatively index - if ( 0 == iPos ) - { - pIndices->push_back( vSize + iVal ); - } - else if ( 1 == iPos ) - { - pTexID->push_back( vtSize + iVal ); - } - else if ( 2 == iPos ) - { - pNormalID->push_back( vnSize + iVal ); - hasNormal = true; - } - else - { - reportErrorTokenInFace(); - } - } - } - pPtr += iStep; - } + if ( iVal > 0 ) + { + // Store parsed index + if ( 0 == iPos ) + { + pIndices->push_back( iVal-1 ); + } + else if ( 1 == iPos ) + { + pTexID->push_back( iVal-1 ); + } + else if ( 2 == iPos ) + { + pNormalID->push_back( iVal-1 ); + hasNormal = true; + } + else + { + reportErrorTokenInFace(); + } + } + else if ( iVal < 0 ) + { + // Store relatively index + if ( 0 == iPos ) + { + pIndices->push_back( vSize + iVal ); + } + else if ( 1 == iPos ) + { + pTexID->push_back( vtSize + iVal ); + } + else if ( 2 == iPos ) + { + pNormalID->push_back( vnSize + iVal ); + hasNormal = true; + } + else + { + reportErrorTokenInFace(); + } + } + } + pPtr += iStep; + } - if ( pIndices->empty() ) - { - DefaultLogger::get()->error("Obj: Ignoring empty face"); - m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); - return; - } + if ( pIndices->empty() ) + { + DefaultLogger::get()->error("Obj: Ignoring empty face"); + m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); + return; + } - ObjFile::Face *face = new ObjFile::Face( pIndices, pNormalID, pTexID, type ); - - // Set active material, if one set - if (NULL != m_pModel->m_pCurrentMaterial) - face->m_pMaterial = m_pModel->m_pCurrentMaterial; - else - face->m_pMaterial = m_pModel->m_pDefaultMaterial; + ObjFile::Face *face = new ObjFile::Face( pIndices, pNormalID, pTexID, type ); + + // Set active material, if one set + if (NULL != m_pModel->m_pCurrentMaterial) + face->m_pMaterial = m_pModel->m_pCurrentMaterial; + else + face->m_pMaterial = m_pModel->m_pDefaultMaterial; - // Create a default object, if nothing is there - if ( NULL == m_pModel->m_pCurrent ) - createObject( "defaultobject" ); - - // Assign face to mesh - if ( NULL == m_pModel->m_pCurrentMesh ) - { - createMesh(); - } - - // Store the face - m_pModel->m_pCurrentMesh->m_Faces.push_back( face ); - m_pModel->m_pCurrentMesh->m_uiNumIndices += (unsigned int)face->m_pVertices->size(); - m_pModel->m_pCurrentMesh->m_uiUVCoordinates[ 0 ] += (unsigned int)face->m_pTexturCoords[0].size(); - if( !m_pModel->m_pCurrentMesh->m_hasNormals && hasNormal ) - { - m_pModel->m_pCurrentMesh->m_hasNormals = true; - } - // Skip the rest of the line - m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); + // Create a default object, if nothing is there + if ( NULL == m_pModel->m_pCurrent ) + createObject( "defaultobject" ); + + // Assign face to mesh + if ( NULL == m_pModel->m_pCurrentMesh ) + { + createMesh(); + } + + // Store the face + m_pModel->m_pCurrentMesh->m_Faces.push_back( face ); + m_pModel->m_pCurrentMesh->m_uiNumIndices += (unsigned int)face->m_pVertices->size(); + m_pModel->m_pCurrentMesh->m_uiUVCoordinates[ 0 ] += (unsigned int)face->m_pTexturCoords[0].size(); + if( !m_pModel->m_pCurrentMesh->m_hasNormals && hasNormal ) + { + m_pModel->m_pCurrentMesh->m_hasNormals = true; + } + // Skip the rest of the line + m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- // Get values for a new material description void ObjFileParser::getMaterialDesc() { - // Each material request a new object. - // Sometimes the object is already created (see 'o' tag by example), but it is not initialized ! - // So, we create a new object only if the current on is already initialized ! - if (m_pModel->m_pCurrent != NULL && - ( m_pModel->m_pCurrent->m_Meshes.size() > 1 || - (m_pModel->m_pCurrent->m_Meshes.size() == 1 && m_pModel->m_Meshes[m_pModel->m_pCurrent->m_Meshes[0]]->m_Faces.size() != 0) ) - ) - m_pModel->m_pCurrent = NULL; + // Each material request a new object. + // Sometimes the object is already created (see 'o' tag by example), but it is not initialized ! + // So, we create a new object only if the current on is already initialized ! + if (m_pModel->m_pCurrent != NULL && + ( m_pModel->m_pCurrent->m_Meshes.size() > 1 || + (m_pModel->m_pCurrent->m_Meshes.size() == 1 && m_pModel->m_Meshes[m_pModel->m_pCurrent->m_Meshes[0]]->m_Faces.size() != 0) ) + ) + m_pModel->m_pCurrent = NULL; - // Get next data for material data - m_DataIt = getNextToken(m_DataIt, m_DataItEnd); - if (m_DataIt == m_DataItEnd) - return; + // Get next data for material data + m_DataIt = getNextToken(m_DataIt, m_DataItEnd); + if (m_DataIt == m_DataItEnd) + return; - char *pStart = &(*m_DataIt); + char *pStart = &(*m_DataIt); while( m_DataIt != m_DataItEnd && !IsSpaceOrNewLine( *m_DataIt ) ) { ++m_DataIt; } - // Get name - std::string strName(pStart, &(*m_DataIt)); - if ( strName.empty()) - return; + // Get name + std::string strName(pStart, &(*m_DataIt)); + if ( strName.empty()) + return; - // Search for material - std::map::iterator it = m_pModel->m_MaterialMap.find( strName ); - if ( it == m_pModel->m_MaterialMap.end() ) - { - // Not found, use default material - m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial; - DefaultLogger::get()->error("OBJ: failed to locate material " + strName + ", skipping"); - } - else - { - // Found, using detected material - m_pModel->m_pCurrentMaterial = (*it).second; - if ( needsNewMesh( strName )) - { - createMesh(); - } - m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strName ); - } + // Search for material + std::map::iterator it = m_pModel->m_MaterialMap.find( strName ); + if ( it == m_pModel->m_MaterialMap.end() ) + { + // Not found, use default material + m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial; + DefaultLogger::get()->error("OBJ: failed to locate material " + strName + ", skipping"); + } + else + { + // Found, using detected material + m_pModel->m_pCurrentMaterial = (*it).second; + if ( needsNewMesh( strName )) + { + createMesh(); + } + m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strName ); + } - // Skip rest of line - m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); + // Skip rest of line + m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- // Get a comment, values will be skipped void ObjFileParser::getComment() { - while (m_DataIt != m_DataItEnd) - { - if ( '\n' == (*m_DataIt)) - { - ++m_DataIt; - break; - } - else - { - ++m_DataIt; - } - } + while (m_DataIt != m_DataItEnd) + { + if ( '\n' == (*m_DataIt)) + { + ++m_DataIt; + break; + } + else + { + ++m_DataIt; + } + } } // ------------------------------------------------------------------- // Get material library from file. void ObjFileParser::getMaterialLib() { - // Translate tuple - m_DataIt = getNextToken(m_DataIt, m_DataItEnd); + // Translate tuple + m_DataIt = getNextToken(m_DataIt, m_DataItEnd); if( m_DataIt == m_DataItEnd ) { return; } - - char *pStart = &(*m_DataIt); + + char *pStart = &(*m_DataIt); while( m_DataIt != m_DataItEnd && !IsLineEnd( *m_DataIt ) ) { ++m_DataIt; } - // Check for existence - const std::string strMatName(pStart, &(*m_DataIt)); - IOStream *pFile = m_pIO->Open(strMatName); + // Check for existence + const std::string strMatName(pStart, &(*m_DataIt)); + IOStream *pFile = m_pIO->Open(strMatName); - if (!pFile ) - { - DefaultLogger::get()->error("OBJ: Unable to locate material file " + strMatName); - m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); - return; - } + if (!pFile ) + { + DefaultLogger::get()->error("OBJ: Unable to locate material file " + strMatName); + m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); + return; + } - // Import material library data from file - std::vector buffer; - BaseImporter::TextFileToBuffer(pFile,buffer); - m_pIO->Close( pFile ); + // Import material library data from file + std::vector buffer; + BaseImporter::TextFileToBuffer(pFile,buffer); + m_pIO->Close( pFile ); - // Importing the material library - ObjFileMtlImporter mtlImporter( buffer, strMatName, m_pModel ); + // Importing the material library + ObjFileMtlImporter mtlImporter( buffer, strMatName, m_pModel ); } // ------------------------------------------------------------------- // Set a new material definition as the current material. void ObjFileParser::getNewMaterial() { - m_DataIt = getNextToken(m_DataIt, m_DataItEnd); - m_DataIt = getNextWord(m_DataIt, m_DataItEnd); + m_DataIt = getNextToken(m_DataIt, m_DataItEnd); + m_DataIt = getNextWord(m_DataIt, m_DataItEnd); if( m_DataIt == m_DataItEnd ) { return; } - char *pStart = &(*m_DataIt); - std::string strMat( pStart, *m_DataIt ); + char *pStart = &(*m_DataIt); + std::string strMat( pStart, *m_DataIt ); while( m_DataIt != m_DataItEnd && IsSpaceOrNewLine( *m_DataIt ) ) { ++m_DataIt; } - std::map::iterator it = m_pModel->m_MaterialMap.find( strMat ); - if ( it == m_pModel->m_MaterialMap.end() ) - { - // Show a warning, if material was not found - DefaultLogger::get()->warn("OBJ: Unsupported material requested: " + strMat); - m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial; - } - else - { - // Set new material - if ( needsNewMesh( strMat ) ) - { - createMesh(); - } - m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strMat ); - } + std::map::iterator it = m_pModel->m_MaterialMap.find( strMat ); + if ( it == m_pModel->m_MaterialMap.end() ) + { + // Show a warning, if material was not found + DefaultLogger::get()->warn("OBJ: Unsupported material requested: " + strMat); + m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial; + } + else + { + // Set new material + if ( needsNewMesh( strMat ) ) + { + createMesh(); + } + m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strMat ); + } - m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); + m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- int ObjFileParser::getMaterialIndex( const std::string &strMaterialName ) { - int mat_index = -1; + int mat_index = -1; if( strMaterialName.empty() ) { return mat_index; } - for (size_t index = 0; index < m_pModel->m_MaterialLib.size(); ++index) - { - if ( strMaterialName == m_pModel->m_MaterialLib[ index ]) - { - mat_index = (int)index; - break; - } - } - return mat_index; + for (size_t index = 0; index < m_pModel->m_MaterialLib.size(); ++index) + { + if ( strMaterialName == m_pModel->m_MaterialLib[ index ]) + { + mat_index = (int)index; + break; + } + } + return mat_index; } // ------------------------------------------------------------------- // Getter for a group name. void ObjFileParser::getGroupName() { - std::string strGroupName; + std::string strGroupName; - m_DataIt = getName(m_DataIt, m_DataItEnd, strGroupName); + m_DataIt = getName(m_DataIt, m_DataItEnd, strGroupName); if( isEndOfBuffer( m_DataIt, m_DataItEnd ) ) { return; } - // Change active group, if necessary - if ( m_pModel->m_strActiveGroup != strGroupName ) - { - // Search for already existing entry - ObjFile::Model::ConstGroupMapIt it = m_pModel->m_Groups.find(strGroupName); - - // We are mapping groups into the object structure - createObject( strGroupName ); - - // New group name, creating a new entry - if (it == m_pModel->m_Groups.end()) - { - std::vector *pFaceIDArray = new std::vector; - m_pModel->m_Groups[ strGroupName ] = pFaceIDArray; - m_pModel->m_pGroupFaceIDs = (pFaceIDArray); - } - else - { - m_pModel->m_pGroupFaceIDs = (*it).second; - } - m_pModel->m_strActiveGroup = strGroupName; - } - m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); + // Change active group, if necessary + if ( m_pModel->m_strActiveGroup != strGroupName ) + { + // Search for already existing entry + ObjFile::Model::ConstGroupMapIt it = m_pModel->m_Groups.find(strGroupName); + + // We are mapping groups into the object structure + createObject( strGroupName ); + + // New group name, creating a new entry + if (it == m_pModel->m_Groups.end()) + { + std::vector *pFaceIDArray = new std::vector; + m_pModel->m_Groups[ strGroupName ] = pFaceIDArray; + m_pModel->m_pGroupFaceIDs = (pFaceIDArray); + } + else + { + m_pModel->m_pGroupFaceIDs = (*it).second; + } + m_pModel->m_strActiveGroup = strGroupName; + } + m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- // Not supported void ObjFileParser::getGroupNumber() { - // Not used + // Not used - m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); + m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- // Not supported void ObjFileParser::getGroupNumberAndResolution() { - // Not used + // Not used - m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); + m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- @@ -660,105 +660,105 @@ void ObjFileParser::getGroupNumberAndResolution() // identify it. void ObjFileParser::getObjectName() { - m_DataIt = getNextToken(m_DataIt, m_DataItEnd); + m_DataIt = getNextToken(m_DataIt, m_DataItEnd); if( m_DataIt == m_DataItEnd ) { return; } - char *pStart = &(*m_DataIt); + char *pStart = &(*m_DataIt); while( m_DataIt != m_DataItEnd && !IsSpaceOrNewLine( *m_DataIt ) ) { ++m_DataIt; } - std::string strObjectName(pStart, &(*m_DataIt)); - if (!strObjectName.empty()) - { - // Reset current object - m_pModel->m_pCurrent = NULL; - - // Search for actual object - for (std::vector::const_iterator it = m_pModel->m_Objects.begin(); - it != m_pModel->m_Objects.end(); - ++it) - { - if ((*it)->m_strObjName == strObjectName) - { - m_pModel->m_pCurrent = *it; - break; - } - } + std::string strObjectName(pStart, &(*m_DataIt)); + if (!strObjectName.empty()) + { + // Reset current object + m_pModel->m_pCurrent = NULL; + + // Search for actual object + for (std::vector::const_iterator it = m_pModel->m_Objects.begin(); + it != m_pModel->m_Objects.end(); + ++it) + { + if ((*it)->m_strObjName == strObjectName) + { + m_pModel->m_pCurrent = *it; + break; + } + } - // Allocate a new object, if current one was not found before + // Allocate a new object, if current one was not found before if( NULL == m_pModel->m_pCurrent ) { createObject( strObjectName ); } - } - m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); + } + m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- // Creates a new object instance void ObjFileParser::createObject(const std::string &strObjectName) { - ai_assert( NULL != m_pModel ); - //ai_assert( !strObjectName.empty() ); + ai_assert( NULL != m_pModel ); + //ai_assert( !strObjectName.empty() ); - m_pModel->m_pCurrent = new ObjFile::Object; - m_pModel->m_pCurrent->m_strObjName = strObjectName; - m_pModel->m_Objects.push_back( m_pModel->m_pCurrent ); - - createMesh(); + m_pModel->m_pCurrent = new ObjFile::Object; + m_pModel->m_pCurrent->m_strObjName = strObjectName; + m_pModel->m_Objects.push_back( m_pModel->m_pCurrent ); + + createMesh(); - if( m_pModel->m_pCurrentMaterial ) - { - m_pModel->m_pCurrentMesh->m_uiMaterialIndex = - getMaterialIndex( m_pModel->m_pCurrentMaterial->MaterialName.data ); - m_pModel->m_pCurrentMesh->m_pMaterial = m_pModel->m_pCurrentMaterial; - } + if( m_pModel->m_pCurrentMaterial ) + { + m_pModel->m_pCurrentMesh->m_uiMaterialIndex = + getMaterialIndex( m_pModel->m_pCurrentMaterial->MaterialName.data ); + m_pModel->m_pCurrentMesh->m_pMaterial = m_pModel->m_pCurrentMaterial; + } } // ------------------------------------------------------------------- // Creates a new mesh void ObjFileParser::createMesh() { - ai_assert( NULL != m_pModel ); - m_pModel->m_pCurrentMesh = new ObjFile::Mesh; - m_pModel->m_Meshes.push_back( m_pModel->m_pCurrentMesh ); - unsigned int meshId = m_pModel->m_Meshes.size()-1; - if ( NULL != m_pModel->m_pCurrent ) - { - m_pModel->m_pCurrent->m_Meshes.push_back( meshId ); - } - else - { - DefaultLogger::get()->error("OBJ: No object detected to attach a new mesh instance."); - } + ai_assert( NULL != m_pModel ); + m_pModel->m_pCurrentMesh = new ObjFile::Mesh; + m_pModel->m_Meshes.push_back( m_pModel->m_pCurrentMesh ); + unsigned int meshId = m_pModel->m_Meshes.size()-1; + if ( NULL != m_pModel->m_pCurrent ) + { + m_pModel->m_pCurrent->m_Meshes.push_back( meshId ); + } + else + { + DefaultLogger::get()->error("OBJ: No object detected to attach a new mesh instance."); + } } // ------------------------------------------------------------------- // Returns true, if a new mesh must be created. bool ObjFileParser::needsNewMesh( const std::string &rMaterialName ) { - if(m_pModel->m_pCurrentMesh == 0) - { - // No mesh data yet - return true; - } - bool newMat = false; - int matIdx = getMaterialIndex( rMaterialName ); - int curMatIdx = m_pModel->m_pCurrentMesh->m_uiMaterialIndex; - if ( curMatIdx != int(ObjFile::Mesh::NoMaterial) || curMatIdx != matIdx ) - { - // New material -> only one material per mesh, so we need to create a new - // material - newMat = true; - } - return newMat; + if(m_pModel->m_pCurrentMesh == 0) + { + // No mesh data yet + return true; + } + bool newMat = false; + int matIdx = getMaterialIndex( rMaterialName ); + int curMatIdx = m_pModel->m_pCurrentMesh->m_uiMaterialIndex; + if ( curMatIdx != int(ObjFile::Mesh::NoMaterial) || curMatIdx != matIdx ) + { + // New material -> only one material per mesh, so we need to create a new + // material + newMat = true; + } + return newMat; } // ------------------------------------------------------------------- // Shows an error in parsing process. void ObjFileParser::reportErrorTokenInFace() { - m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); - DefaultLogger::get()->error("OBJ: Not supported token in face description detected"); + m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); + DefaultLogger::get()->error("OBJ: Not supported token in face description detected"); } // ------------------------------------------------------------------- diff --git a/code/ParsingUtils.h b/code/ParsingUtils.h index 25495fd51..ce15f790f 100644 --- a/code/ParsingUtils.h +++ b/code/ParsingUtils.h @@ -64,7 +64,7 @@ static const unsigned int BufferSize = 4096; template AI_FORCE_INLINE char_t ToLower( char_t in) { - return (in >= (char_t)'A' && in <= (char_t)'Z') ? (char_t)(in+0x20) : in; + return (in >= (char_t)'A' && in <= (char_t)'Z') ? (char_t)(in+0x20) : in; } // --------------------------------------------------------------------------------- @@ -77,35 +77,35 @@ AI_FORCE_INLINE char_t ToUpper( char_t in) { template AI_FORCE_INLINE bool IsUpper( char_t in) { - return (in >= (char_t)'A' && in <= (char_t)'Z'); + return (in >= (char_t)'A' && in <= (char_t)'Z'); } // --------------------------------------------------------------------------------- template AI_FORCE_INLINE bool IsLower( char_t in) { - return (in >= (char_t)'a' && in <= (char_t)'z'); + return (in >= (char_t)'a' && in <= (char_t)'z'); } // --------------------------------------------------------------------------------- template AI_FORCE_INLINE bool IsSpace( char_t in) { - return (in == (char_t)' ' || in == (char_t)'\t'); + return (in == (char_t)' ' || in == (char_t)'\t'); } // --------------------------------------------------------------------------------- template AI_FORCE_INLINE bool IsLineEnd( char_t in) { - return (in==(char_t)'\r'||in==(char_t)'\n'||in==(char_t)'\0'||in==(char_t)'\f'); + return (in==(char_t)'\r'||in==(char_t)'\n'||in==(char_t)'\0'||in==(char_t)'\f'); } // --------------------------------------------------------------------------------- template AI_FORCE_INLINE bool IsSpaceOrNewLine( char_t in) { - return IsSpace(in) || IsLineEnd(in); + return IsSpace(in) || IsLineEnd(in); } // --------------------------------------------------------------------------------- @@ -115,15 +115,15 @@ AI_FORCE_INLINE bool SkipSpaces( const char_t* in, const char_t** out) while( *in == ( char_t )' ' || *in == ( char_t )'\t' ) { ++in; } - *out = in; - return !IsLineEnd(*in); + *out = in; + return !IsLineEnd(*in); } // --------------------------------------------------------------------------------- template AI_FORCE_INLINE bool SkipSpaces( const char_t** inout) { - return SkipSpaces(*inout,inout); + return SkipSpaces(*inout,inout); } // --------------------------------------------------------------------------------- @@ -134,19 +134,19 @@ AI_FORCE_INLINE bool SkipLine( const char_t* in, const char_t** out) ++in; } - // files are opened in binary mode. Ergo there are both NL and CR + // files are opened in binary mode. Ergo there are both NL and CR while( *in == ( char_t )'\r' || *in == ( char_t )'\n' ) { ++in; } - *out = in; - return *in != (char_t)'\0'; + *out = in; + return *in != (char_t)'\0'; } // --------------------------------------------------------------------------------- template AI_FORCE_INLINE bool SkipLine( const char_t** inout) { - return SkipLine(*inout,inout); + return SkipLine(*inout,inout); } // --------------------------------------------------------------------------------- @@ -156,15 +156,15 @@ AI_FORCE_INLINE bool SkipSpacesAndLineEnd( const char_t* in, const char_t** out) while( *in == ( char_t )' ' || *in == ( char_t )'\t' || *in == ( char_t )'\r' || *in == ( char_t )'\n' ) { ++in; } - *out = in; - return *in != '\0'; + *out = in; + return *in != '\0'; } // --------------------------------------------------------------------------------- template AI_FORCE_INLINE bool SkipSpacesAndLineEnd( const char_t** inout) { - return SkipSpacesAndLineEnd(*inout,inout); + return SkipSpacesAndLineEnd(*inout,inout); } // --------------------------------------------------------------------------------- @@ -175,12 +175,12 @@ AI_FORCE_INLINE bool GetNextLine( const char_t*& buffer, char_t out[ BufferSize return false; } - char* _out = out; + char* _out = out; char* const end = _out + BufferSize; while( !IsLineEnd( *buffer ) && _out < end ) { *_out++ = *buffer++; } - *_out = (char_t)'\0'; + *_out = (char_t)'\0'; while( IsLineEnd( *buffer ) && '\0' != *buffer ) { ++buffer; @@ -193,19 +193,19 @@ AI_FORCE_INLINE bool GetNextLine( const char_t*& buffer, char_t out[ BufferSize template AI_FORCE_INLINE bool IsNumeric( char_t in) { - return ( in >= '0' && in <= '9' ) || '-' == in || '+' == in; + return ( in >= '0' && in <= '9' ) || '-' == in || '+' == in; } // --------------------------------------------------------------------------------- template AI_FORCE_INLINE bool TokenMatch(char_t*& in, const char* token, unsigned int len) { - if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len])) { - in += len+1; - return true; - } + if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len])) { + in += len+1; + return true; + } - return false; + return false; } // --------------------------------------------------------------------------------- /** @brief Case-ignoring version of TokenMatch @@ -215,25 +215,25 @@ AI_FORCE_INLINE bool TokenMatch(char_t*& in, const char* token, unsigned int len */ AI_FORCE_INLINE bool TokenMatchI(const char*& in, const char* token, unsigned int len) { - if (!ASSIMP_strincmp(token,in,len) && IsSpaceOrNewLine(in[len])) { - in += len+1; - return true; - } - return false; + if (!ASSIMP_strincmp(token,in,len) && IsSpaceOrNewLine(in[len])) { + in += len+1; + return true; + } + return false; } // --------------------------------------------------------------------------------- AI_FORCE_INLINE void SkipToken(const char*& in) { - SkipSpaces(&in); - while (!IsSpaceOrNewLine(*in))++in; + SkipSpaces(&in); + while (!IsSpaceOrNewLine(*in))++in; } // --------------------------------------------------------------------------------- AI_FORCE_INLINE std::string GetNextToken(const char*& in) { - SkipSpacesAndLineEnd(&in); - const char* cur = in; - while (!IsSpaceOrNewLine(*in))++in; - return std::string(cur,(size_t)(in-cur)); + SkipSpacesAndLineEnd(&in); + const char* cur = in; + while (!IsSpaceOrNewLine(*in))++in; + return std::string(cur,(size_t)(in-cur)); } // --------------------------------------------------------------------------------- diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h b/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h index 9e978ade2..179a51326 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h @@ -62,7 +62,7 @@ bool isSpace( const T in ) { template inline bool isNewLine( const T in ) { - return ( '\n' == in ); + return ( '\n' == in || ( '\r' == in ) ); } template From f5f0c9f7cf15ca6ecabc8e6ec82346191f204b00 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 14 Feb 2015 16:11:46 +0100 Subject: [PATCH 26/74] update openddl-parser. Signed-off-by: Kim Kulling --- contrib/openddlparser/code/Value.cpp | 16 +++++++++++++++- .../openddlparser/include/openddlparser/Value.h | 4 ++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/contrib/openddlparser/code/Value.cpp b/contrib/openddlparser/code/Value.cpp index 802e8dd61..5e88d6572 100644 --- a/contrib/openddlparser/code/Value.cpp +++ b/contrib/openddlparser/code/Value.cpp @@ -21,6 +21,7 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -----------------------------------------------------------------------------------------------*/ #include + #include #include @@ -109,6 +110,15 @@ double Value::getDouble() const { return v; } +void Value::setString( const std::string &str ) { + assert( ddl_string == m_type ); + ::memcpy( m_data, str.c_str(), str.size() ); + m_data[ str.size() ] = '\0'; +} +const char *Value::getString() const { + return (const char*) m_data; +} + void Value::dump() { switch( m_type ) { case ddl_none: @@ -223,7 +233,11 @@ Value *ValueAllocator::allocPrimData( Value::ValueType type, size_t len ) { } if( data->m_size ) { - data->m_size *= len; + size_t len1( len ); + if( Value::ddl_string == type ) { + len1++; + } + data->m_size *= len1; data->m_data = new unsigned char[ data->m_size ]; } diff --git a/contrib/openddlparser/include/openddlparser/Value.h b/contrib/openddlparser/include/openddlparser/Value.h index 0d719f994..48eb6e771 100644 --- a/contrib/openddlparser/include/openddlparser/Value.h +++ b/contrib/openddlparser/include/openddlparser/Value.h @@ -26,6 +26,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +#include + BEGIN_ODDLPARSER_NS ///------------------------------------------------------------------------------------------------ @@ -73,6 +75,8 @@ public: float getFloat() const; void setDouble( double value ); double getDouble() const; + void setString( const std::string &str ); + const char *getString() const; void dump(); void setNext( Value *next ); Value *getNext() const; From 029286891799b1d306e59408903247b69b6dfc52 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 14 Feb 2015 16:12:09 +0100 Subject: [PATCH 27/74] add geomentry node handling. Signed-off-by: Kim Kulling --- code/OpenGEXImporter.cpp | 41 +++++++++++++++----- code/OpenGEXImporter.h | 15 +++++-- contrib/openddlparser/code/OpenDDLParser.cpp | 2 +- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 83bde51fe..0b940d39e 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -164,7 +164,8 @@ USE_ODDLPARSER_NS //------------------------------------------------------------------------------------------------ OpenGEXImporter::OpenGEXImporter() -: m_ctx( NULL ) { +: m_ctx( NULL ) +, m_currentNode( NULL ) { // empty } @@ -202,7 +203,7 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce bool success( myParser.parse() ); if( success ) { m_ctx = myParser.getContext(); - handleNodes( m_ctx->m_root ); + handleNodes( m_ctx->m_root, pScene ); } } @@ -219,7 +220,7 @@ void OpenGEXImporter::SetupProperties( const Importer *pImp ) { } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleNodes( DDLNode *node ) { +void OpenGEXImporter::handleNodes( DDLNode *node, aiScene *pScene ) { if( NULL == node ) { return; } @@ -229,14 +230,20 @@ void OpenGEXImporter::handleNodes( DDLNode *node ) { Grammar::TokenType tokenType( Grammar::matchTokenType( ( *it )->getType().c_str() ) ); switch( tokenType ) { case Grammar::MetricToken: - importMetric( *it ); + handleMetricNode( *it, pScene ); break; case Grammar::NameToken: + handleNameNode( *it, pScene ); + break; + case Grammar::ObjectRefToken: case Grammar::MaterialRefToken: case Grammar::MetricKeyToken: case Grammar::GeometryNodeToken: + handleGeometryNode( *it, pScene ); + break; + case Grammar::GeometryObjectToken: case Grammar::TransformToken: case Grammar::MeshToken: @@ -252,7 +259,7 @@ void OpenGEXImporter::handleNodes( DDLNode *node ) { } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::importMetric( DDLNode *node ) { +void OpenGEXImporter::handleMetricNode( DDLNode *node, aiScene *pScene ) { if( NULL == node || NULL == m_ctx ) { return; } @@ -267,13 +274,15 @@ void OpenGEXImporter::importMetric( DDLNode *node ) { if( Value::ddl_string == prop->m_primData->m_type ) { std::string valName( (char*) prop->m_primData->m_data ); int type( Grammar::isValidMetricType( valName.c_str() ) ); - if( -1 != type ) { + if( Grammar::NoneType != type ) { Value *val( node->getValue() ); if( NULL != val ) { if( Value::ddl_float == val->m_type ) { m_metrics[ type ].m_floatValue = val->getFloat(); } else if( Value::ddl_int32 == val->m_type ) { m_metrics[ type ].m_floatValue = val->getInt32(); + } else if( Value::ddl_string == val->m_type ) { + m_metrics[type].m_stringValue = std::string( val->getString() ); } else { throw DeadlyImportError( "OpenGEX: invalid data type for Metric node." ); } @@ -286,13 +295,27 @@ void OpenGEXImporter::importMetric( DDLNode *node ) { } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::ParseGeoObject() { +void OpenGEXImporter::handleNameNode( ODDLParser::DDLNode *node, aiScene *pScene ) { + if( NULL == m_currentNode ) { + throw DeadlyImportError( "No parent node for name." ); + return; + } + Value *val( node->getValue() ); + if( NULL != val ) { + if( Value::ddl_string != val->m_type ) { + throw DeadlyImportError( "OpenGEX: invalid data type for value in node name." ); + } + + std::string name( val->getString() ); + m_currentNode->mName.Set( name.c_str() ); + } } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::ParseMaterial() { - +void OpenGEXImporter::handleGeometryNode( ODDLParser::DDLNode *node, aiScene *pScene ) { + m_currentNode = new aiNode; + handleNodes( node, pScene ); } //------------------------------------------------------------------------------------------------ diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index 9e8b06ff3..2bea23b45 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -64,6 +64,12 @@ struct MetricInfo { std::string m_stringValue; float m_floatValue; + + MetricInfo() + : m_stringValue( "" ) + , m_floatValue( 0.0f ) { + // empty + } }; /** @brief This class is used to implement the OpenGEX importer @@ -91,14 +97,15 @@ public: virtual void SetupProperties( const Importer *pImp ); protected: - void handleNodes( ODDLParser::DDLNode *node ); - void importMetric( ODDLParser::DDLNode *node ); - void ParseGeoObject(); - void ParseMaterial(); + void handleNodes( ODDLParser::DDLNode *node, aiScene *pScene ); + void handleMetricNode( ODDLParser::DDLNode *node, aiScene *pScene ); + void handleNameNode( ODDLParser::DDLNode *node, aiScene *pScene ); + void handleGeometryNode( ODDLParser::DDLNode *node, aiScene *pScene ); private: ODDLParser::Context *m_ctx; MetricInfo m_metrics[ MetricInfo::Max ]; + aiNode *m_currentNode; }; } // Namespace OpenGEX diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index b869aa2a1..c85f498de 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -669,7 +669,7 @@ char *OpenDDLParser::parseStringLiteral( char *in, char *end, Value **stringData len++; } - *stringData = ValueAllocator::allocPrimData( Value::ddl_string, len + 1 ); + *stringData = ValueAllocator::allocPrimData( Value::ddl_string, len ); ::strncpy( ( char* ) ( *stringData )->m_data, start, len ); ( *stringData )->m_data[len] = '\0'; in++; From aae0f7e7b1bb5eef5db518c1a1f250fde79708c1 Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Sun, 15 Feb 2015 22:12:19 +0100 Subject: [PATCH 28/74] 3DSExporter: export materials first, then meshes. Our own importer requires this. --- code/3DSExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/3DSExporter.cpp b/code/3DSExporter.cpp index 8ad367058..de47bc064 100644 --- a/code/3DSExporter.cpp +++ b/code/3DSExporter.cpp @@ -188,8 +188,8 @@ Discreet3DSExporter:: Discreet3DSExporter(boost::shared_ptr outfile, c { ChunkWriter chunk(writer, Discreet3DS::CHUNK_OBJMESH); - WriteMeshes(); WriteMaterials(); + WriteMeshes(); { ChunkWriter chunk(writer, Discreet3DS::CHUNK_MASTER_SCALE); From ce1302cd716194255d51c01237eaa429d9acbb51 Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Sun, 15 Feb 2015 22:20:34 +0100 Subject: [PATCH 29/74] Fix VC12 warning "empty but controlled statement found". --- code/ColladaParser.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp index 9e68c9332..353c50812 100644 --- a/code/ColladaParser.cpp +++ b/code/ColladaParser.cpp @@ -1950,9 +1950,11 @@ void ColladaParser::ReadIndexData( Mesh* pMesh) } } - // small sanity check - if (primType != Prim_TriFans && primType != Prim_TriStrips) - ai_assert(actualPrimitives == numPrimitives); +#ifdef ASSIMP_BUILD_DEBUG + if (primType != Prim_TriFans && primType != Prim_TriStrips) { + ai_assert(actualPrimitives == numPrimitives) + } +#endif // only when we're done reading all

tags (and thus know the final vertex count) can we commit the submesh subgroup.mNumFaces = actualPrimitives; From 51b790c482075c370a1f59ad031767703e976b2c Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Sun, 15 Feb 2015 22:25:04 +0100 Subject: [PATCH 30/74] ColladaLoader: format. --- code/ColladaLoader.cpp | 390 ++++++++++++++++++++--------------------- 1 file changed, 195 insertions(+), 195 deletions(-) diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index 5771bc7af..2f3942689 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -73,7 +73,7 @@ static const aiImporterDesc desc = { // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer ColladaLoader::ColladaLoader() -: noSkeletonMesh(), ignoreUpDirection(false), mNodeNameCounter() + : noSkeletonMesh(), ignoreUpDirection(false), mNodeNameCounter() {} // ------------------------------------------------------------------------------------------------ @@ -87,16 +87,16 @@ bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, boo { // check file extension std::string extension = GetExtension(pFile); - + if( extension == "dae") return true; // 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 NULL and it's our duty to return true here. - */ + * support a specific file extension in general pIOHandler + * might be NULL and it's our duty to return true here. + */ if (!pIOHandler)return true; const char* tokens[] = {"collada"}; return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); @@ -157,26 +157,26 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I // ... then fill the materials with the now adjusted settings FillMaterials(parser, pScene); - // Apply unitsize scale calculation - pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0, 0, 0, - 0, parser.mUnitSize, 0, 0, - 0, 0, parser.mUnitSize, 0, - 0, 0, 0, 1); - if( !ignoreUpDirection ) { - // Convert to Y_UP, if different orientation + // Apply unitsize scale calculation + pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0, 0, 0, + 0, parser.mUnitSize, 0, 0, + 0, 0, parser.mUnitSize, 0, + 0, 0, 0, 1); + if( !ignoreUpDirection ) { + // Convert to Y_UP, if different orientation if( parser.mUpDirection == ColladaParser::UP_X) pScene->mRootNode->mTransformation *= aiMatrix4x4( - 0, -1, 0, 0, - 1, 0, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); + 0, -1, 0, 0, + 1, 0, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); else if( parser.mUpDirection == ColladaParser::UP_Z) pScene->mRootNode->mTransformation *= aiMatrix4x4( - 1, 0, 0, 0, - 0, 0, 1, 0, - 0, -1, 0, 0, - 0, 0, 0, 1); - } + 1, 0, 0, 0, + 0, 0, 1, 0, + 0, -1, 0, 0, + 0, 0, 0, 1); + } // store all meshes StoreSceneMeshes( pScene); @@ -195,7 +195,7 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I // If no meshes have been loaded, it's probably just an animated skeleton. if (!pScene->mNumMeshes) { - + if (!noSkeletonMesh) { SkeletonMeshBuilder hero(pScene); } @@ -251,14 +251,14 @@ aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const Colla // ------------------------------------------------------------------------------------------------ // Resolve node instances void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode, - std::vector& resolved) + std::vector& resolved) { // reserve enough storage resolved.reserve(pNode->mNodeInstances.size()); // ... and iterate through all nodes to be instanced as children of pNode for (std::vector::const_iterator it = pNode->mNodeInstances.begin(), - end = pNode->mNodeInstances.end(); it != end; ++it) + end = pNode->mNodeInstances.end(); it != end; ++it) { // find the corresponding node in the library const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find((*it).mNode); @@ -272,7 +272,7 @@ void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Co } if (!nd) DefaultLogger::get()->error("Collada: Unable to resolve reference to instanced node " + (*it).mNode); - + else { // attach this node to the list of children resolved.push_back(nd); @@ -283,7 +283,7 @@ void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Co // ------------------------------------------------------------------------------------------------ // Resolve UV channels void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler, - const Collada::SemanticMappingTable& table) + const Collada::SemanticMappingTable& table) { std::map::const_iterator it = table.mMap.find(sampler.mUVChannel); if (it != table.mMap.end()) { @@ -308,7 +308,7 @@ void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Coll continue; } const Collada::Light* srcLight = &srcLightIt->second; - + // now fill our ai data structure aiLight* out = new aiLight(); out->mName = pTarget->mName; @@ -326,7 +326,7 @@ void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Coll // convert falloff angle and falloff exponent in our representation, if given if (out->mType == aiLightSource_SPOT) { - + out->mAngleInnerCone = AI_DEG_TO_RAD( srcLight->mFalloffAngle ); // ... some extension magic. @@ -387,7 +387,7 @@ void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Col // ... but for the rest some values are optional // and we need to compute the others in any combination. - if (srcCamera->mAspect != 10e10f) + if (srcCamera->mAspect != 10e10f) out->mAspect = srcCamera->mAspect; if (srcCamera->mHorFov != 10e10f) { @@ -395,12 +395,12 @@ void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Col if (srcCamera->mVerFov != 10e10f && srcCamera->mAspect == 10e10f) { out->mAspect = tan(AI_DEG_TO_RAD(srcCamera->mHorFov)) / - tan(AI_DEG_TO_RAD(srcCamera->mVerFov)); + tan(AI_DEG_TO_RAD(srcCamera->mVerFov)); } } else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f) { out->mHorizontalFOV = 2.0f * AI_RAD_TO_DEG(atan(srcCamera->mAspect * - tan(AI_DEG_TO_RAD(srcCamera->mVerFov) * 0.5f))); + tan(AI_DEG_TO_RAD(srcCamera->mVerFov) * 0.5f))); } // Collada uses degrees, we use radians @@ -518,11 +518,11 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll // assign the material index dstMesh->mMaterialIndex = matIdx; - if(dstMesh->mName.length == 0) - { - dstMesh->mName = mid.mMeshOrController; - } - } + if(dstMesh->mName.length == 0) + { + dstMesh->mName = mid.mMeshOrController; + } + } } } @@ -538,11 +538,11 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll // ------------------------------------------------------------------------------------------------ // Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh, - const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace) + const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace) { aiMesh* dstMesh = new aiMesh; - - dstMesh->mName = pSrcMesh->mName; + + dstMesh->mName = pSrcMesh->mName; // count the vertices addressed by its faces const size_t numVertices = std::accumulate( pSrcMesh->mFaceSize.begin() + pStartFace, @@ -589,7 +589,7 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: dstMesh->mTextureCoords[real] = new aiVector3D[numVertices]; for( size_t b = 0; b < numVertices; ++b) dstMesh->mTextureCoords[real][b] = pSrcMesh->mTexCoords[a][pStartVertex+b]; - + dstMesh->mNumUVComponents[real] = pSrcMesh->mNumUVComponents[a]; ++real; } @@ -624,8 +624,8 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: if( pSrcController) { // refuse if the vertex count does not match -// if( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices) -// throw DeadlyImportError( "Joint Controller vertex count does not match mesh vertex count"); + // if( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices) + // throw DeadlyImportError( "Joint Controller vertex count does not match mesh vertex count"); // resolve references - joint names const Collada::Accessor& jointNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointNameSource); @@ -937,7 +937,7 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars // find the collada node corresponding to the aiNode const Collada::Node* srcNode = FindNode( pParser.mRootNode, nodeName); -// ai_assert( srcNode != NULL); + // ai_assert( srcNode != NULL); if( !srcNode) continue; @@ -990,7 +990,7 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars { entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1, bracketPos - slashPos - 1); std::string subElement = srcChannel.mTarget.substr(bracketPos); - + if (subElement == "(0)(0)") entry.mSubElement = 0; else if (subElement == "(1)(0)") @@ -1058,134 +1058,134 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars if( e.mTimeAccessor->mCount != e.mValueAccessor->mCount) throw DeadlyImportError( boost::str( boost::format( "Time count / value count mismatch in animation channel \"%s\".") % e.mChannel->mTarget)); - if( e.mTimeAccessor->mCount > 0 ) - { - // find bounding times - startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0)); - endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0)); - } + if( e.mTimeAccessor->mCount > 0 ) + { + // find bounding times + startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0)); + endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0)); + } } - std::vector resultTrafos; - if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 ) - { - // create a local transformation chain of the node's transforms - std::vector transforms = srcNode->mTransforms; + std::vector resultTrafos; + if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 ) + { + // create a local transformation chain of the node's transforms + std::vector transforms = srcNode->mTransforms; - // now for every unique point in time, find or interpolate the key values for that time - // and apply them to the transform chain. Then the node's present transformation can be calculated. - float time = startTime; - while( 1) - { - for( std::vector::iterator it = entries.begin(); it != entries.end(); ++it) - { - Collada::ChannelEntry& e = *it; + // now for every unique point in time, find or interpolate the key values for that time + // and apply them to the transform chain. Then the node's present transformation can be calculated. + float time = startTime; + while( 1) + { + for( std::vector::iterator it = entries.begin(); it != entries.end(); ++it) + { + Collada::ChannelEntry& e = *it; - // find the keyframe behind the current point in time - size_t pos = 0; - float postTime = 0.f; - while( 1) - { - if( pos >= e.mTimeAccessor->mCount) - break; - postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); - if( postTime >= time) - break; - ++pos; - } + // find the keyframe behind the current point in time + size_t pos = 0; + float postTime = 0.f; + while( 1) + { + if( pos >= e.mTimeAccessor->mCount) + break; + postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); + if( postTime >= time) + break; + ++pos; + } - pos = std::min( pos, e.mTimeAccessor->mCount-1); + pos = std::min( pos, e.mTimeAccessor->mCount-1); - // read values from there - float temp[16]; - for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) - temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c); + // read values from there + float temp[16]; + for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) + temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c); - // if not exactly at the key time, interpolate with previous value set - if( postTime > time && pos > 0) - { - float preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0); - float factor = (time - postTime) / (preTime - postTime); + // if not exactly at the key time, interpolate with previous value set + if( postTime > time && pos > 0) + { + float preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0); + float factor = (time - postTime) / (preTime - postTime); - for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) - { - float v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c); - temp[c] += (v - temp[c]) * factor; - } - } + for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) + { + float v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c); + temp[c] += (v - temp[c]) * factor; + } + } - // Apply values to current transformation - std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement); - } + // Apply values to current transformation + std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement); + } - // Calculate resulting transformation - aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms); + // Calculate resulting transformation + aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms); - // out of lazyness: we store the time in matrix.d4 - mat.d4 = time; - resultTrafos.push_back( mat); + // out of lazyness: we store the time in matrix.d4 + mat.d4 = time; + resultTrafos.push_back( mat); - // find next point in time to evaluate. That's the closest frame larger than the current in any channel - float nextTime = 1e20f; - for( std::vector::iterator it = entries.begin(); it != entries.end(); ++it) - { - Collada::ChannelEntry& e = *it; + // find next point in time to evaluate. That's the closest frame larger than the current in any channel + float nextTime = 1e20f; + for( std::vector::iterator it = entries.begin(); it != entries.end(); ++it) + { + Collada::ChannelEntry& e = *it; - // find the next time value larger than the current - size_t pos = 0; - while( pos < e.mTimeAccessor->mCount) - { - float t = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); - if( t > time) - { - nextTime = std::min( nextTime, t); - break; - } - ++pos; - } - } + // find the next time value larger than the current + size_t pos = 0; + while( pos < e.mTimeAccessor->mCount) + { + float t = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); + if( t > time) + { + nextTime = std::min( nextTime, t); + break; + } + ++pos; + } + } - // no more keys on any channel after the current time -> we're done - if( nextTime > 1e19) - break; + // no more keys on any channel after the current time -> we're done + if( nextTime > 1e19) + break; - // else construct next keyframe at this following time point - time = nextTime; - } - } + // else construct next keyframe at this following time point + time = nextTime; + } + } // there should be some keyframes, but we aren't that fixated on valid input data -// ai_assert( resultTrafos.size() > 0); + // ai_assert( resultTrafos.size() > 0); // build an animation channel for the given node out of these trafo keys - if( !resultTrafos.empty() ) - { - aiNodeAnim* dstAnim = new aiNodeAnim; - dstAnim->mNodeName = nodeName; - dstAnim->mNumPositionKeys = resultTrafos.size(); - dstAnim->mNumRotationKeys= resultTrafos.size(); - dstAnim->mNumScalingKeys = resultTrafos.size(); - dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()]; - dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()]; - dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()]; + if( !resultTrafos.empty() ) + { + aiNodeAnim* dstAnim = new aiNodeAnim; + dstAnim->mNodeName = nodeName; + dstAnim->mNumPositionKeys = resultTrafos.size(); + dstAnim->mNumRotationKeys= resultTrafos.size(); + dstAnim->mNumScalingKeys = resultTrafos.size(); + dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()]; + dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()]; + dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()]; - for( size_t a = 0; a < resultTrafos.size(); ++a) - { - aiMatrix4x4 mat = resultTrafos[a]; - double time = double( mat.d4); // remember? time is stored in mat.d4 - mat.d4 = 1.0f; + for( size_t a = 0; a < resultTrafos.size(); ++a) + { + aiMatrix4x4 mat = resultTrafos[a]; + double time = double( mat.d4); // remember? time is stored in mat.d4 + mat.d4 = 1.0f; - dstAnim->mPositionKeys[a].mTime = time; - dstAnim->mRotationKeys[a].mTime = time; - dstAnim->mScalingKeys[a].mTime = time; - mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue); - } + dstAnim->mPositionKeys[a].mTime = time; + dstAnim->mRotationKeys[a].mTime = time; + dstAnim->mScalingKeys[a].mTime = time; + mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue); + } - anims.push_back( dstAnim); - } else - { - DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter."); - } + anims.push_back( dstAnim); + } else + { + DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter."); + } } if( !anims.empty()) @@ -1210,9 +1210,9 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars // ------------------------------------------------------------------------------------------------ // Add a texture to a material structure void ColladaLoader::AddTexture ( aiMaterial& mat, const ColladaParser& pParser, - const Collada::Effect& effect, - const Collada::Sampler& sampler, - aiTextureType type, unsigned int idx) + const Collada::Effect& effect, + const Collada::Sampler& sampler, + aiTextureType type, unsigned int idx) { // first of all, basic file name const aiString name = FindFilenameForEffectTexture( pParser, effect, sampler.mName ); @@ -1339,8 +1339,8 @@ void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* /*pSce // add textures, if given if( !effect.mTexAmbient.mName.empty()) - /* It is merely a lightmap */ - AddTexture( mat, pParser, effect, effect.mTexAmbient, aiTextureType_LIGHTMAP); + /* It is merely a lightmap */ + AddTexture( mat, pParser, effect, effect.mTexAmbient, aiTextureType_LIGHTMAP); if( !effect.mTexEmissive.mName.empty()) AddTexture( mat, pParser, effect, effect.mTexEmissive, aiTextureType_EMISSIVE); @@ -1411,7 +1411,7 @@ void ColladaLoader::BuildMaterials( ColladaParser& pParser, aiScene* /*pScene*/) // ------------------------------------------------------------------------------------------------ // Resolves the texture name for the given effect texture entry aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pParser, - const Collada::Effect& pEffect, const std::string& pName) + const Collada::Effect& pEffect, const std::string& pName) { // recurse through the param references until we end up at an image std::string name = pName; @@ -1488,35 +1488,35 @@ void ColladaLoader::ConvertPath (aiString& ss) ss.data[ss.length] = '\0'; } - // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes... - // I need to filter it without destroying linux paths starting with "/somewhere" - if( ss.data[0] == '/' && isalpha( ss.data[1]) && ss.data[2] == ':' ) - { - ss.length--; - memmove( ss.data, ss.data+1, ss.length); - ss.data[ss.length] = 0; - } + // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes... + // I need to filter it without destroying linux paths starting with "/somewhere" + if( ss.data[0] == '/' && isalpha( ss.data[1]) && ss.data[2] == ':' ) + { + ss.length--; + memmove( ss.data, ss.data+1, ss.length); + ss.data[ss.length] = 0; + } - // find and convert all %xy special chars - char* out = ss.data; - for( const char* it = ss.data; it != ss.data + ss.length; /**/ ) - { - if( *it == '%' && (it + 3) < ss.data + ss.length ) - { - // separate the number to avoid dragging in chars from behind into the parsing - char mychar[3] = { it[1], it[2], 0 }; - size_t nbr = strtoul16( mychar); - it += 3; - *out++ = (char)(nbr & 0xFF); - } else - { - *out++ = *it++; - } - } + // find and convert all %xy special chars + char* out = ss.data; + for( const char* it = ss.data; it != ss.data + ss.length; /**/ ) + { + if( *it == '%' && (it + 3) < ss.data + ss.length ) + { + // separate the number to avoid dragging in chars from behind into the parsing + char mychar[3] = { it[1], it[2], 0 }; + size_t nbr = strtoul16( mychar); + it += 3; + *out++ = (char)(nbr & 0xFF); + } else + { + *out++ = *it++; + } + } - // adjust length and terminator of the shortened string - *out = 0; - ss.length = (ptrdiff_t) (out - ss.data); + // adjust length and terminator of the shortened string + *out = 0; + ss.length = (ptrdiff_t) (out - ss.data); } // ------------------------------------------------------------------------------------------------ @@ -1569,17 +1569,17 @@ const Collada::Node* ColladaLoader::FindNode( const Collada::Node* pNode, const // Finds a node in the collada scene by the given SID const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const { - if( pNode->mSID == pSID) - return pNode; + if( pNode->mSID == pSID) + return pNode; - for( size_t a = 0; a < pNode->mChildren.size(); ++a) - { - const Collada::Node* node = FindNodeBySID( pNode->mChildren[a], pSID); - if( node) - return node; - } + for( size_t a = 0; a < pNode->mChildren.size(); ++a) + { + const Collada::Node* node = FindNodeBySID( pNode->mChildren[a], pSID); + if( node) + return node; + } - return NULL; + return NULL; } // ------------------------------------------------------------------------------------------------ @@ -1593,12 +1593,12 @@ std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode) else if (!pNode->mID.empty()) return pNode->mID; else if (!pNode->mSID.empty()) - return pNode->mSID; - else + return pNode->mSID; + else { // No need to worry. Unnamed nodes are no problem at all, except // if cameras or lights need to be assigned to them. - return boost::str( boost::format( "$ColladaAutoName$_%d") % mNodeNameCounter++); + return boost::str( boost::format( "$ColladaAutoName$_%d") % mNodeNameCounter++); } } From 76c69205b1d4ba9cf62394c27da48941afba110a Mon Sep 17 00:00:00 2001 From: ulf Date: Fri, 6 Mar 2015 14:14:45 +0100 Subject: [PATCH 31/74] - reintroduced IFC openings to floors and ceilings. Were disabled for some unknown reasons, I hope I didn't break anything. Everything I tested works fine. --- code/IFCOpenings.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/code/IFCOpenings.cpp b/code/IFCOpenings.cpp index 2bb7646b2..c47f8254a 100644 --- a/code/IFCOpenings.cpp +++ b/code/IFCOpenings.cpp @@ -1184,16 +1184,13 @@ bool GenerateOpenings(std::vector& openings, profile_data = opening.profileMesh2D.get(); is_2d_source = true; } - else { - //continue; - } } else { // vertical extrusion if (std::fabs(norm_extrusion_dir * nor) > 0.9) { - continue; - } - continue; + profile_data = opening.profileMesh2D.get(); + is_2d_source = true; + } } } std::vector profile_verts = profile_data->verts; From c89274d3660394e5389116d3d61bd864b5c09698 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sun, 8 Mar 2015 00:16:48 +0200 Subject: [PATCH 32/74] Free edges map before recursive calls to reduce memory consumption --- code/Subdivision.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/Subdivision.cpp b/code/Subdivision.cpp index 03200818c..4bc39c268 100644 --- a/code/Subdivision.cpp +++ b/code/Subdivision.cpp @@ -290,6 +290,8 @@ void CatmullClarkSubdivider::InternSubdivide ( } } + { + // we want edges to go away before the recursive calls so begin a new scope EdgeMap edges; // --------------------------------------------------------------------- @@ -572,6 +574,7 @@ void CatmullClarkSubdivider::InternSubdivide ( } } } + } // end of scope for edges, freeing its memory // --------------------------------------------------------------------- // 7. Apply the next subdivision step. From 873ae5db3f16998ce9dc8df3840f858e596729c8 Mon Sep 17 00:00:00 2001 From: abma Date: Sun, 8 Mar 2015 03:25:53 +0100 Subject: [PATCH 33/74] addition to c4997f16dba72ab82dd4cb0374459b98d7b33a63 (thanks turol) --- code/SmoothingGroups.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/SmoothingGroups.inl b/code/SmoothingGroups.inl index 4c0b60466..f82548896 100644 --- a/code/SmoothingGroups.inl +++ b/code/SmoothingGroups.inl @@ -106,7 +106,7 @@ void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& sMesh) std::vector poResult; for (unsigned int c = 0; c < 3;++c) { - register unsigned int idx = (*i).mIndices[c]; + unsigned int idx = (*i).mIndices[c]; if (vertexDone[idx])continue; sSort.FindPositions(sMesh.mPositions[idx],(*i).iSmoothGroup, From c410512173d9d3f7801b963451977e00f44b6d34 Mon Sep 17 00:00:00 2001 From: DenisMikhalev Date: Sun, 8 Mar 2015 22:37:48 +0300 Subject: [PATCH 34/74] Use material names, set default direction to UP_Y, process extra tag --- code/ColladaHelper.h | 1 + code/ColladaLoader.cpp | 4 ++-- code/ColladaParser.cpp | 42 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/code/ColladaHelper.h b/code/ColladaHelper.h index f249a28d5..99bbee269 100644 --- a/code/ColladaHelper.h +++ b/code/ColladaHelper.h @@ -395,6 +395,7 @@ struct Controller /** A collada material. Pretty much the only member is a reference to an effect. */ struct Material { + std::string mName; std::string mEffect; }; diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index 5771bc7af..33340cf9f 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -176,7 +176,7 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1); - } + } // store all meshes StoreSceneMeshes( pScene); @@ -1379,7 +1379,7 @@ void ColladaLoader::BuildMaterials( ColladaParser& pParser, aiScene* /*pScene*/) // create material aiMaterial* mat = new aiMaterial; - aiString name( matIt->first); + aiString name( material.mName.empty() ? matIt->first : material.mName ); mat->AddProperty(&name,AI_MATKEY_NAME); // store the material diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp index b7b5a7908..dc177744f 100644 --- a/code/ColladaParser.cpp +++ b/code/ColladaParser.cpp @@ -60,7 +60,7 @@ ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile) { mRootNode = NULL; mUnitSize = 1.0f; - mUpDirection = UP_Z; + mUpDirection = UP_Y; // We assume the newest file format by default mFormat = FV_1_5_n; @@ -225,10 +225,10 @@ void ColladaParser::ReadAssetInfo() const char* content = GetTextContent(); if( strncmp( content, "X_UP", 4) == 0) mUpDirection = UP_X; - else if( strncmp( content, "Y_UP", 4) == 0) - mUpDirection = UP_Y; - else + else if( strncmp( content, "Z_UP", 4) == 0) mUpDirection = UP_Z; + else + mUpDirection = UP_Y; // check element end TestClosing( "up_axis"); @@ -817,6 +817,7 @@ void ColladaParser::ReadMaterialLibrary() if( mReader->isEmptyElement()) return; + std::map names; while( mReader->read()) { if( mReader->getNodeType() == irr::io::EXN_ELEMENT) @@ -827,8 +828,32 @@ void ColladaParser::ReadMaterialLibrary() int attrID = GetAttribute( "id"); std::string id = mReader->getAttributeValue( attrID); + std::string name; + int attrName = TestAttribute("name"); + if (attrName >= 0) + name = mReader->getAttributeValue( attrName); + // create an entry and store it in the library under its ID - ReadMaterial(mMaterialLibrary[id] = Material()); + mMaterialLibrary[id] = Material(); + + if( !name.empty()) + { + std::map::iterator it = names.find( name); + if( it != names.end()) + { + std::ostringstream strStream; + strStream << ++it->second; + name.append( " " + strStream.str()); + } + else + { + names[name] = 0; + } + + mMaterialLibrary[id].mName = name; + } + + ReadMaterial( mMaterialLibrary[id]); } else { // ignore the rest @@ -1385,6 +1410,9 @@ void ColladaParser::ReadEffectColor( aiColor4D& pColor, Sampler& pSampler) if( attrTex >= 0 ) pSampler.mUVChannel = mReader->getAttributeValue( attrTex); //SkipElement(); + + // as we've read texture, the color needs to be 1,1,1,1 + pColor = aiColor4D(1.f, 1.f, 1.f, 1.f); } else if( IsElement( "technique")) { @@ -1936,6 +1964,10 @@ void ColladaParser::ReadIndexData( Mesh* pMesh) // now here the actual fun starts - these are the indices to construct the mesh data from actualPrimitives += ReadPrimitives(pMesh, perIndexData, numPrimitives, vcount, primType); } + } + else if (IsElement("extra")) + { + SkipElement("extra"); } else { ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag <%s>") % mReader->getNodeName() % elementName)); From 79db48a0d14163da70a1d9f61969cd8762463285 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 9 Mar 2015 10:17:23 +0100 Subject: [PATCH 35/74] openddl-parser: latest greatest. Signed-off-by: Kim Kulling --- contrib/openddlparser/code/DDLNode.cpp | 11 +- contrib/openddlparser/code/OpenDDLParser.cpp | 216 ++++++++++-------- .../include/openddlparser/DDLNode.h | 3 + .../include/openddlparser/OpenDDLCommon.h | 10 +- .../include/openddlparser/OpenDDLParser.h | 3 +- .../openddlparser/OpenDDLParserUtils.h | 14 ++ 6 files changed, 162 insertions(+), 95 deletions(-) diff --git a/contrib/openddlparser/code/DDLNode.cpp b/contrib/openddlparser/code/DDLNode.cpp index aba11d8ae..e65f1c45e 100644 --- a/contrib/openddlparser/code/DDLNode.cpp +++ b/contrib/openddlparser/code/DDLNode.cpp @@ -52,7 +52,8 @@ DDLNode::DDLNode( const std::string &type, const std::string &name, size_t idx, , m_properties( ddl_nullptr ) , m_value( ddl_nullptr ) , m_idx( idx ) -, m_dtArrayList( ddl_nullptr ) { +, m_dtArrayList( ddl_nullptr ) +, m_references( ddl_nullptr ) { if( m_parent ) { m_parent->m_children.push_back( this ); } @@ -140,6 +141,14 @@ DataArrayList *DDLNode::getDataArrayList() const { return m_dtArrayList; } +void DDLNode::setReferences( Reference *refs ) { + m_references = refs; +} + +Reference *DDLNode::getReferences() const { + return m_references; +} + DDLNode *DDLNode::create( const std::string &type, const std::string &name, DDLNode *parent ) { const size_t idx( s_allocatedNodes.size() ); DDLNode *node = new DDLNode( type, name, idx, parent ); diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index c85f498de..46eb50fd8 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -37,26 +37,33 @@ BEGIN_ODDLPARSER_NS static const char *Version = "0.1.0"; -static const char* PrimitiveTypeToken[ Value::ddl_types_max ] = { - "bool", - "int8", - "int16", - "int32", - "int64", - "unsigned_int8", - "unsigned_int16", - "unsigned_int32", - "unsigned_int64", - "half", - "float", - "double", - "string", - "ref" -}; +namespace Grammar { + static const char *OpenBracketToken = "{"; + static const char *CloseBracketToken = "}"; + static const char *OpenPropertyToken = "("; + static const char *ClosePropertyToken = ")"; + static const char *BoolTrue = "true"; + static const char *BoolFalse = "false"; + static const char *RefToken = "ref"; + + static const char* PrimitiveTypeToken[ Value::ddl_types_max ] = { + "bool", + "int8", + "int16", + "int32", + "int64", + "unsigned_int8", + "unsigned_int16", + "unsigned_int32", + "unsigned_int64", + "half", + "float", + "double", + "string", + "ref" + }; +} // Namespace Grammar -static const char *BoolTrue = "true"; -static const char *BoolFalse = "false"; -static const char *RefToken = "ref"; static void logInvalidTokenError( char *in, char *exp, OpenDDLParser::logCallback callback ) { std::stringstream stream; @@ -65,7 +72,8 @@ static void logInvalidTokenError( char *in, char *exp, OpenDDLParser::logCallbac } static bool isIntegerType( Value::ValueType integerType ) { - if( integerType != Value::ddl_int8 && integerType != Value::ddl_int16 && integerType != Value::ddl_int32 && integerType != Value::ddl_int64 ) { + if( integerType != Value::ddl_int8 && integerType != Value::ddl_int16 && + integerType != Value::ddl_int32 && integerType != Value::ddl_int64 ) { return false; } @@ -203,6 +211,12 @@ char *OpenDDLParser::parseNextNode( char *in, char *end ) { return in; } +static void dumpId( Identifier *id ) { + if( ddl_nullptr != id ) { + std::cout << id->m_buffer << std::endl; + } +} + char *OpenDDLParser::parseHeader( char *in, char *end ) { if( nullptr == in || in == end ) { return in; @@ -212,9 +226,7 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { in = OpenDDLParser::parseIdentifier( in, end, &id ); #ifdef DEBUG_HEADER_NAME - if( ddl_nullptr != id ) { - std::cout << id->m_buffer << std::endl; - } + dumpId( id ); #endif // DEBUG_HEADER_NAME in = getNextToken( in, end ); @@ -224,13 +236,14 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { in++; Property *prop( ddl_nullptr ), *prev( ddl_nullptr ); while( *in != ')' && in != end ) { - in = parseProperty( in, end, &prop ); + in = OpenDDLParser::parseProperty( in, end, &prop ); in = getNextToken( in, end ); if( *in != ',' && *in != ')' ) { logInvalidTokenError( in, ")", m_logCallback ); return in; } + if( ddl_nullptr != prop && *in != ',' ) { if( ddl_nullptr == first ) { first = prop; @@ -276,55 +289,17 @@ char *OpenDDLParser::parseStructure( char *in, char *end ) { bool error( false ); in = getNextToken( in, end ); if( *in == '{' ) { + do { + // loop over all childs ( data and nodes ) + in = parseStructureBody( in, end, error ); + } while ( *in != '}' ); in++; - in = getNextToken( in, end ); - Value::ValueType type( Value::ddl_none ); - size_t arrayLen( 0 ); - in = OpenDDLParser::parsePrimitiveDataType( in, end, type, arrayLen ); - if( Value::ddl_none != type ) { - in = getNextToken( in, end ); - if( *in == '{' ) { - DataArrayList *dtArrayList( ddl_nullptr ); - Value *values( ddl_nullptr ); - if( 1 == arrayLen ) { - in = parseDataList( in, end, &values ); - if( ddl_nullptr != values ){ - DDLNode *currentNode( top() ); - if( ddl_nullptr != currentNode ) { - currentNode->setValue( values ); - } - } - } else if( arrayLen > 1 ) { - in = parseDataArrayList( in, end, &dtArrayList ); - if( ddl_nullptr != dtArrayList ) { - DDLNode *currentNode( top() ); - if( ddl_nullptr != currentNode ) { - currentNode->setDataArrayList( dtArrayList ); - } - } - } else { - std::cerr << "0 for array is invalid." << std::endl; - error = true; - } - } - - in = getNextToken( in, end ); - if( *in != '}' ) { - logInvalidTokenError( in, "}", m_logCallback ); - } - else { - in++; - } - } else { - in = parseHeader( in, end ); - in = parseStructure( in, end ); - } - } else { + } + else { in++; logInvalidTokenError( in, "{", m_logCallback ); error = true; return in; - } in = getNextToken( in, end ); @@ -336,6 +311,71 @@ char *OpenDDLParser::parseStructure( char *in, char *end ) { return in; } +static void setNodeValues( DDLNode *currentNode, Value *values ) { + if( ddl_nullptr != values ){ + if( ddl_nullptr != currentNode ) { + currentNode->setValue( values ); + } + } +} + +static void setNodeReferences( DDLNode *currentNode, Reference *refs ) { + if( ddl_nullptr != refs ) { + if( ddl_nullptr != currentNode ) { + currentNode->setReferences( refs ); + } + } +} + +static void setNodeDataArrayList( DDLNode *currentNode, DataArrayList *dtArrayList ) { + if( ddl_nullptr != dtArrayList ) { + if( ddl_nullptr != currentNode ) { + currentNode->setDataArrayList( dtArrayList ); + } + } +} + +char *OpenDDLParser::parseStructureBody( char *in, char *end, bool &error ) { + if( !isNumeric( *in ) && !isCharacter( *in ) ) { + in++; + } + + in = getNextToken( in, end ); + Value::ValueType type( Value::ddl_none ); + size_t arrayLen( 0 ); + in = OpenDDLParser::parsePrimitiveDataType( in, end, type, arrayLen ); + if( Value::ddl_none != type ) { + in = getNextToken( in, end ); + if( *in == '{' ) { + Reference *refs( ddl_nullptr ); + DataArrayList *dtArrayList( ddl_nullptr ); + Value *values( ddl_nullptr ); + if( 1 == arrayLen ) { + in = parseDataList( in, end, &values, &refs ); + setNodeValues( top(), values ); + setNodeReferences( top(), refs ); + } else if( arrayLen > 1 ) { + in = parseDataArrayList( in, end, &dtArrayList ); + setNodeDataArrayList( top(), dtArrayList ); + } else { + std::cerr << "0 for array is invalid." << std::endl; + error = true; + } + } + + in = getNextToken( in, end ); + if( *in != '}' ) { + logInvalidTokenError( in, "}", m_logCallback ); + } else { + //in++; + } + } else { + in = parseNextNode( in, end ); + } + + return in; +} + void OpenDDLParser::pushNode( DDLNode *node ) { if( nullptr == node ) { return; @@ -469,9 +509,10 @@ char *OpenDDLParser::parsePrimitiveDataType( char *in, char *end, Value::ValueTy return in; } + size_t prim_len( 0 ); for( unsigned int i = 0; i < Value::ddl_types_max; i++ ) { - const size_t prim_len( strlen( PrimitiveTypeToken[ i ] ) ); - if( 0 == strncmp( in, PrimitiveTypeToken[ i ], prim_len ) ) { + prim_len = strlen( Grammar::PrimitiveTypeToken[ i ] ); + if( 0 == strncmp( in, Grammar::PrimitiveTypeToken[ i ], prim_len ) ) { type = ( Value::ValueType ) i; break; } @@ -481,7 +522,7 @@ char *OpenDDLParser::parsePrimitiveDataType( char *in, char *end, Value::ValueTy in = getNextToken( in, end ); return in; } else { - in += strlen( PrimitiveTypeToken[ type ] ); + in += prim_len; } bool ok( true ); @@ -513,27 +554,12 @@ char *OpenDDLParser::parseReference( char *in, char *end, std::vector &na return in; } - if( 0 != strncmp( in, RefToken, strlen( RefToken ) ) ) { - return in; - } else { - const size_t refTokenLen( strlen( RefToken ) ); - in += refTokenLen; - } - - in = getNextToken( in, end ); - if( '{' != *in ) { - return in; - } else { - in++; - } - - in = getNextToken( in, end ); Name *nextName( ddl_nullptr ); in = parseName( in, end, &nextName ); if( nextName ) { names.push_back( nextName ); } - while( '}' != *in ) { + while( ',' == *in ) { in = getNextSeparator( in, end ); if( ',' == *in ) { in = parseName( in, end, &nextName ); @@ -562,9 +588,9 @@ char *OpenDDLParser::parseBooleanLiteral( char *in, char *end, Value **boolean ) len++; } len++; - int res = ::strncmp( BoolTrue, start, strlen( BoolTrue ) ); + int res = ::strncmp( Grammar::BoolTrue, start, strlen( Grammar::BoolTrue ) ); if( 0 != res ) { - res = ::strncmp( BoolFalse, start, strlen( BoolFalse ) ); + res = ::strncmp( Grammar::BoolFalse, start, strlen( Grammar::BoolFalse ) ); if( 0 != res ) { *boolean = ddl_nullptr; return in; @@ -770,7 +796,7 @@ char *OpenDDLParser::parseProperty( char *in, char *end, Property **prop ) { return in; } -char *OpenDDLParser::parseDataList( char *in, char *end, Value **data ) { +char *OpenDDLParser::parseDataList( char *in, char *end, Value **data, Reference **refs ) { *data = ddl_nullptr; if( ddl_nullptr == in || in == end ) { return in; @@ -791,6 +817,13 @@ char *OpenDDLParser::parseDataList( char *in, char *end, Value **data ) { in = parseStringLiteral( in, end, ¤t ); } else if( isHexLiteral( in, end ) ) { in = parseHexaLiteral( in, end, ¤t ); + } else { // reference data + std::vector names; + in = parseReference( in, end, names ); + if( !names.empty() ) { + Reference *ref = new Reference( names.size(), &names[ 0 ] ); + *refs = ref; + } } if( ddl_nullptr != current ) { @@ -824,9 +857,10 @@ char *OpenDDLParser::parseDataArrayList( char *in, char *end, DataArrayList **da if( *in == '{' ) { in++; Value *current( ddl_nullptr ); + Reference *refs( nullptr ); DataArrayList *prev( ddl_nullptr ), *currentDataList( ddl_nullptr ); do { - in = parseDataList( in, end, ¤t ); + in = parseDataList( in, end, ¤t, &refs ); if( ddl_nullptr != current ) { if( ddl_nullptr == prev ) { *dataList = new DataArrayList; diff --git a/contrib/openddlparser/include/openddlparser/DDLNode.h b/contrib/openddlparser/include/openddlparser/DDLNode.h index bb5f87b79..93b447bb2 100644 --- a/contrib/openddlparser/include/openddlparser/DDLNode.h +++ b/contrib/openddlparser/include/openddlparser/DDLNode.h @@ -61,6 +61,8 @@ public: Value *getValue() const; void setDataArrayList( DataArrayList *dtArrayList ); DataArrayList *getDataArrayList() const; + void setReferences( Reference *refs ); + Reference *getReferences() const; static DDLNode *create( const std::string &type, const std::string &name, DDLNode *parent = ddl_nullptr ); private: @@ -78,6 +80,7 @@ private: Property *m_properties; Value *m_value; DataArrayList *m_dtArrayList; + Reference *m_references; size_t m_idx; static DllNodeList s_allocatedNodes; }; diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h index 789bc15ad..c7fd844a2 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h @@ -88,9 +88,15 @@ struct Reference { size_t m_numRefs; Name **m_referencedName; + Reference() + : m_numRefs( 0 ) + , m_referencedName( ddl_nullptr ) { + // empty + } + Reference( size_t numrefs, Name **names ) - : m_numRefs( numrefs ) - , m_referencedName( names ) { + : m_numRefs( numrefs ) + , m_referencedName( names ) { // empty } }; diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h index 4f003a0e4..00f2c2e58 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h @@ -77,6 +77,7 @@ public: char *parseNextNode( char *current, char *end ); char *parseHeader( char *in, char *end ); char *parseStructure( char *in, char *end ); + char *parseStructureBody( char *in, char *end, bool &error ); void pushNode( DDLNode *node ); DDLNode *popNode(); DDLNode *top(); @@ -95,7 +96,7 @@ public: // static parser helpers static char *parseStringLiteral( char *in, char *end, Value **stringData ); static char *parseHexaLiteral( char *in, char *end, Value **data ); static char *parseProperty( char *in, char *end, Property **prop ); - static char *parseDataList( char *in, char *end, Value **data ); + static char *parseDataList( char *in, char *end, Value **data, Reference **refs ); static char *parseDataArrayList( char *in, char *end, DataArrayList **dataList ); static const char *getVersion(); diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h b/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h index 179a51326..b1b4f6cde 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h @@ -196,6 +196,20 @@ bool isHexLiteral( T *in, T *end ) { return false; } +template +inline +bool isReference( T *in, T *end ) { + if( *in == 'r' ) { + if( *(in+1) == 'e' ) { + if( *(in+2) == 'f' ) { + return true; + } + } + } + + return false; +} + template inline bool isEndofLine( const T in ) { From c342778f4252a69d32c50feba3a085a983f0ff70 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Mon, 2 Mar 2015 13:52:19 +0200 Subject: [PATCH 36/74] Fix read past end of buffer after call to TokenMatch IsSpaceOrNewLine returns true on end of input (NUL character). But if TokenMatch considers a token at end of input to match it sets "in" to one past end of buffer. This will lead to reading past the end of buffer on any subsequent operation. --- code/ParsingUtils.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/ParsingUtils.h b/code/ParsingUtils.h index 25495fd51..0d27e7fbf 100644 --- a/code/ParsingUtils.h +++ b/code/ParsingUtils.h @@ -201,7 +201,12 @@ template AI_FORCE_INLINE bool TokenMatch(char_t*& in, const char* token, unsigned int len) { if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len])) { + if (in[len] != '\0') { in += len+1; + } else { + // If EOF after the token make sure we don't go past end of buffer + in += len; + } return true; } From ba4689fd0509fc33667671a20a56dcd51c0f16cd Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Mon, 9 Mar 2015 12:18:10 +0200 Subject: [PATCH 37/74] Whitespace --- code/ParsingUtils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ParsingUtils.h b/code/ParsingUtils.h index 0d27e7fbf..93a44d0a6 100644 --- a/code/ParsingUtils.h +++ b/code/ParsingUtils.h @@ -202,7 +202,7 @@ AI_FORCE_INLINE bool TokenMatch(char_t*& in, const char* token, unsigned int len { if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len])) { if (in[len] != '\0') { - in += len+1; + in += len+1; } else { // If EOF after the token make sure we don't go past end of buffer in += len; From 62676b56aa8fdb6b1622029f1ae048c550ba137c Mon Sep 17 00:00:00 2001 From: DenisMikhalev Date: Mon, 9 Mar 2015 19:40:13 +0300 Subject: [PATCH 38/74] Adds safety and prevents crashes for damaged files --- code/PlyLoader.cpp | 72 +++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/code/PlyLoader.cpp b/code/PlyLoader.cpp index d61c4e5a7..deb7da094 100644 --- a/code/PlyLoader.cpp +++ b/code/PlyLoader.cpp @@ -64,6 +64,24 @@ static const aiImporterDesc desc = { "ply" }; + +// ------------------------------------------------------------------------------------------------ +// Internal stuff +namespace +{ + // ------------------------------------------------------------------------------------------------ + // Checks that property index is within range + template + const T &GetProperty(const std::vector &props, int idx) + { + if (idx >= props.size()) + throw DeadlyImportError("Invalid .ply file: Property index is out of range."); + + return props[idx]; + } +} + + // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer PLYImporter::PLYImporter() @@ -431,13 +449,13 @@ void PLYImporter::LoadTextureCoordinates(std::vector* pvOut) if (0xFFFFFFFF != aiPositions[0]) { vOut.x = PLY::PropertyInstance::ConvertTo( - (*i).alProperties[aiPositions[0]].avList.front(),aiTypes[0]); + GetProperty((*i).alProperties, aiPositions[0]).avList.front(),aiTypes[0]); } if (0xFFFFFFFF != aiPositions[1]) { vOut.y = PLY::PropertyInstance::ConvertTo( - (*i).alProperties[aiPositions[1]].avList.front(),aiTypes[1]); + GetProperty((*i).alProperties, aiPositions[1]).avList.front(),aiTypes[1]); } // and add them to our nice list pvOut->push_back(vOut); @@ -541,19 +559,19 @@ void PLYImporter::LoadVertices(std::vector* pvOut, bool p_bNormals) if (0xFFFFFFFF != aiPositions[0]) { vOut.x = PLY::PropertyInstance::ConvertTo( - (*i).alProperties[aiPositions[0]].avList.front(),aiTypes[0]); + GetProperty((*i).alProperties, aiPositions[0]).avList.front(),aiTypes[0]); } if (0xFFFFFFFF != aiPositions[1]) { vOut.y = PLY::PropertyInstance::ConvertTo( - (*i).alProperties[aiPositions[1]].avList.front(),aiTypes[1]); + GetProperty((*i).alProperties, aiPositions[1]).avList.front(),aiTypes[1]); } if (0xFFFFFFFF != aiPositions[2]) { vOut.z = PLY::PropertyInstance::ConvertTo( - (*i).alProperties[aiPositions[2]].avList.front(),aiTypes[2]); + GetProperty((*i).alProperties, aiPositions[2]).avList.front(),aiTypes[2]); } // and add them to our nice list @@ -659,28 +677,28 @@ void PLYImporter::LoadVertexColor(std::vector* pvOut) if (0xFFFFFFFF != aiPositions[0]) { - vOut.r = NormalizeColorValue((*i).alProperties[ - aiPositions[0]].avList.front(),aiTypes[0]); + vOut.r = NormalizeColorValue(GetProperty((*i).alProperties, + aiPositions[0]).avList.front(),aiTypes[0]); } if (0xFFFFFFFF != aiPositions[1]) { - vOut.g = NormalizeColorValue((*i).alProperties[ - aiPositions[1]].avList.front(),aiTypes[1]); + vOut.g = NormalizeColorValue(GetProperty((*i).alProperties, + aiPositions[1]).avList.front(),aiTypes[1]); } if (0xFFFFFFFF != aiPositions[2]) { - vOut.b = NormalizeColorValue((*i).alProperties[ - aiPositions[2]].avList.front(),aiTypes[2]); + vOut.b = NormalizeColorValue(GetProperty((*i).alProperties, + aiPositions[2]).avList.front(),aiTypes[2]); } // assume 1.0 for the alpha channel ifit is not set if (0xFFFFFFFF == aiPositions[3])vOut.a = 1.0f; else { - vOut.a = NormalizeColorValue((*i).alProperties[ - aiPositions[3]].avList.front(),aiTypes[3]); + vOut.a = NormalizeColorValue(GetProperty((*i).alProperties, + aiPositions[3]).avList.front(),aiTypes[3]); } // and add them to our nice list @@ -773,11 +791,11 @@ void PLYImporter::LoadFaces(std::vector* pvOut) // parse the list of vertex indices if (0xFFFFFFFF != iProperty) { - const unsigned int iNum = (unsigned int)(*i).alProperties[iProperty].avList.size(); + const unsigned int iNum = (unsigned int)GetProperty((*i).alProperties, iProperty).avList.size(); sFace.mIndices.resize(iNum); std::vector::const_iterator p = - (*i).alProperties[iProperty].avList.begin(); + GetProperty((*i).alProperties, iProperty).avList.begin(); for (unsigned int a = 0; a < iNum;++a,++p) { @@ -789,7 +807,7 @@ void PLYImporter::LoadFaces(std::vector* pvOut) if (0xFFFFFFFF != iMaterialIndex) { sFace.iMaterialIndex = PLY::PropertyInstance::ConvertTo( - (*i).alProperties[iMaterialIndex].avList.front(),eType2); + GetProperty((*i).alProperties, iMaterialIndex).avList.front(),eType2); } pvOut->push_back(sFace); } @@ -800,7 +818,7 @@ void PLYImporter::LoadFaces(std::vector* pvOut) // a value of -1 indicates a restart of the strip bool flip = false; for (std::vector::const_iterator i = pcList->alInstances.begin();i != pcList->alInstances.end();++i) { - const std::vector& quak = (*i).alProperties[iProperty].avList; + const std::vector& quak = GetProperty((*i).alProperties, iProperty).avList; pvOut->reserve(pvOut->size() + quak.size() + (quak.size()>>2u)); int aiTable[2] = {-1,-1}; @@ -851,30 +869,30 @@ void PLYImporter::GetMaterialColor(const std::vector& avL if (0xFFFFFFFF == aiPositions[0])clrOut->r = 0.0f; else { - clrOut->r = NormalizeColorValue(avList[ - aiPositions[0]].avList.front(),aiTypes[0]); + clrOut->r = NormalizeColorValue(GetProperty(avList, + aiPositions[0]).avList.front(),aiTypes[0]); } if (0xFFFFFFFF == aiPositions[1])clrOut->g = 0.0f; else { - clrOut->g = NormalizeColorValue(avList[ - aiPositions[1]].avList.front(),aiTypes[1]); + clrOut->g = NormalizeColorValue(GetProperty(avList, + aiPositions[1]).avList.front(),aiTypes[1]); } if (0xFFFFFFFF == aiPositions[2])clrOut->b = 0.0f; else { - clrOut->b = NormalizeColorValue(avList[ - aiPositions[2]].avList.front(),aiTypes[2]); + clrOut->b = NormalizeColorValue(GetProperty(avList, + aiPositions[2]).avList.front(),aiTypes[2]); } // assume 1.0 for the alpha channel ifit is not set if (0xFFFFFFFF == aiPositions[3])clrOut->a = 1.0f; else { - clrOut->a = NormalizeColorValue(avList[ - aiPositions[3]].avList.front(),aiTypes[3]); + clrOut->a = NormalizeColorValue(GetProperty(avList, + aiPositions[3]).avList.front(),aiTypes[3]); } } @@ -1025,7 +1043,7 @@ void PLYImporter::LoadMaterial(std::vector* pvOut) // handle phong power and shading mode int iMode; if (0xFFFFFFFF != iPhong) { - float fSpec = PLY::PropertyInstance::ConvertTo((*i).alProperties[iPhong].avList.front(),ePhong); + float fSpec = PLY::PropertyInstance::ConvertTo(GetProperty((*i).alProperties, iPhong).avList.front(),ePhong); // if shininess is 0 (and the pow() calculation would therefore always // become 1, not depending on the angle), use gouraud lighting @@ -1043,7 +1061,7 @@ void PLYImporter::LoadMaterial(std::vector* pvOut) // handle opacity if (0xFFFFFFFF != iOpacity) { - float fOpacity = PLY::PropertyInstance::ConvertTo((*i).alProperties[iPhong].avList.front(),eOpacity); + float fOpacity = PLY::PropertyInstance::ConvertTo(GetProperty((*i).alProperties, iPhong).avList.front(),eOpacity); pcHelper->AddProperty(&fOpacity, 1, AI_MATKEY_OPACITY); } From 80bfe7929e2dabddb9c164e40164cd6fb805b7d6 Mon Sep 17 00:00:00 2001 From: DenisMikhalev Date: Mon, 9 Mar 2015 19:51:04 +0300 Subject: [PATCH 39/74] Adds half of file for testing crash PlyLoader --- test/models/PLY/ant-half.ply | 754 +++++++++++++++++++++++++++++++++++ 1 file changed, 754 insertions(+) create mode 100644 test/models/PLY/ant-half.ply diff --git a/test/models/PLY/ant-half.ply b/test/models/PLY/ant-half.ply new file mode 100644 index 000000000..47cf9a9a4 --- /dev/null +++ b/test/models/PLY/ant-half.ply @@ -0,0 +1,754 @@ +ply +format ascii 1.0 +element vertex 486 +property float32 x +property float32 y +property float32 z +element face 912 +property list uint8 int32 vertex_indices +end_header +-1.106 3.844 9.073 +-0.3523 0.4402 11.07 +1.028 3.76 9.209 +0.137 -0.5632 10.73 +2.01 4.503 5.887 +0.07813 5.232 6.794 +-0.7266 3.741 3.839 +-2.789 3.179 5.07 +-0.9185 2.402 3.279 +1.445 2.139 3.394 +-3.114 1.498 5.285 +-1.317 -1.269 8.385 +-0.854 -1.319 5.036 +-0.9351 5.347 0.6162 +-0.8184 2.622 3.235 +0.4875 5.529 -0.2241 +3.193 2.756 1.633 +3.213 1.145 -2.568 +-2.25 1.208 2.767 +0.3225 0.8339 3.323 +1.354 -1.725 1.105 +0.1887 1.133 -5.785 +-3.637 2.696 0.3325 +-3.706 0.8936 -0.5452 +-0.4326 -0.8737 -4.095 +-2.994 -0.4858 0.9023 +0.1252 -2.079 -1.51 +2.856 -0.7459 -1.187 +2.32 3.733 8.13 +2.031 3.721 9.095 +2.819 3.567 8.537 +1.994 2.581 9.711 +2.64 2.491 9.542 +3.187 2.528 8.375 +1.628 2.491 7.518 +1.62 3.721 8.274 +0.9868 2.471 8.914 +1.552 2.106 9.414 +2.657 1.774 8.918 +-1.805 3.733 8.13 +-2.094 3.721 9.095 +-1.306 3.567 8.537 +-2.131 2.581 9.711 +-1.485 2.491 9.542 +-0.938 2.528 8.375 +-2.497 2.491 7.518 +-2.505 3.721 8.274 +-3.138 2.471 8.914 +-2.573 2.106 9.414 +-1.469 1.774 8.918 +1.549 3.638 6.803 +1.355 3.638 6.7 +1.424 5.126 6.883 +1.615 5.083 6.981 +1.169 3.638 6.815 +1.237 5.115 6.997 +1.176 3.638 7.035 +1.241 5.061 7.21 +1.369 3.638 7.138 +1.433 5.018 7.308 +1.556 3.638 7.023 +1.62 5.029 7.194 +1.625 6.512 7.421 +1.811 6.429 7.504 +1.437 6.49 7.531 +1.434 6.386 7.724 +1.619 6.304 7.807 +1.808 6.326 7.697 +1.945 7.702 8.276 +2.121 7.585 8.335 +1.754 7.672 8.38 +1.74 7.525 8.542 +1.916 7.408 8.601 +2.107 7.439 8.497 +2.362 8.615 9.391 +2.526 8.473 9.417 +2.168 8.578 9.487 +2.138 8.398 9.608 +2.303 8.255 9.635 +2.497 8.293 9.539 +2.847 9.189 10.69 +2.998 9.03 10.68 +2.649 9.148 10.78 +2.603 8.947 10.85 +2.753 8.788 10.84 +2.95 8.83 10.75 +3.368 9.385 12.08 +3.503 9.22 12.03 +3.167 9.342 12.16 +3.101 9.134 12.18 +3.236 8.97 12.13 +3.438 9.013 12.06 +3.889 9.189 13.48 +4.01 9.03 13.39 +3.684 9.148 13.54 +3.599 8.947 13.51 +3.719 8.788 13.42 +3.925 8.83 13.36 +4.374 8.615 14.77 +4.481 8.473 14.65 +4.165 8.578 14.83 +4.063 8.398 14.76 +4.17 8.255 14.63 +4.379 8.293 14.57 +4.791 7.702 15.89 +4.886 7.585 15.73 +4.579 7.672 15.94 +4.462 7.525 15.82 +4.556 7.408 15.66 +4.769 7.439 15.62 +5.111 6.512 16.74 +5.196 6.429 16.56 +4.896 6.49 16.78 +4.768 6.386 16.64 +4.853 6.304 16.46 +5.068 6.326 16.42 +-1.141 5.126 6.883 +-1.072 3.638 6.7 +-1.266 3.638 6.803 +-1.332 5.083 6.981 +-0.9541 5.115 6.997 +-0.886 3.638 6.815 +-0.9585 5.061 7.21 +-0.8928 3.638 7.035 +-1.15 5.018 7.308 +-1.086 3.638 7.138 +-1.337 5.029 7.194 +-1.272 3.638 7.023 +-1.342 6.512 7.421 +-1.528 6.429 7.504 +-1.154 6.49 7.531 +-1.151 6.386 7.724 +-1.336 6.304 7.807 +-1.525 6.326 7.697 +-1.662 7.702 8.276 +-1.838 7.585 8.335 +-1.471 7.672 8.38 +-1.457 7.525 8.542 +-1.633 7.408 8.601 +-1.824 7.439 8.497 +-2.079 8.615 9.391 +-2.243 8.473 9.417 +-1.885 8.578 9.487 +-1.855 8.398 9.608 +-2.02 8.255 9.635 +-2.214 8.293 9.539 +-2.564 9.189 10.69 +-2.715 9.03 10.68 +-2.366 9.148 10.78 +-2.32 8.947 10.85 +-2.47 8.788 10.84 +-2.667 8.83 10.75 +-3.085 9.385 12.08 +-3.22 9.22 12.03 +-2.884 9.342 12.16 +-2.818 9.134 12.18 +-2.953 8.97 12.13 +-3.155 9.013 12.06 +-3.606 9.189 13.48 +-3.726 9.03 13.39 +-3.4 9.148 13.54 +-3.316 8.947 13.51 +-3.437 8.788 13.42 +-3.642 8.83 13.36 +-4.091 8.615 14.77 +-4.198 8.473 14.65 +-3.882 8.578 14.83 +-3.78 8.398 14.76 +-3.887 8.255 14.63 +-4.096 8.293 14.57 +-4.508 7.702 15.89 +-4.603 7.585 15.73 +-4.296 7.672 15.94 +-4.179 7.525 15.82 +-4.273 7.408 15.66 +-4.485 7.439 15.62 +-4.828 6.512 16.74 +-4.913 6.429 16.56 +-4.613 6.49 16.78 +-4.484 6.386 16.64 +-4.57 6.304 16.46 +-4.784 6.326 16.42 +-1.519 -2.27 2.989 +-1.987 -1.082 3.408 +-2.709 -1.989 3.536 +-1.513 -2.259 2.974 +-2.524 -1.536 2.471 +-4.318 -0.3933 4.187 +-4.692 -0.6204 3.123 +-6.274 0.9227 4.173 +-5.533 1.852 4.043 +-6.084 1.387 3.082 +-6.844 2.279 4.015 +-6.83 1.957 4.012 +-6.886 2.118 3.737 +-7.536 1.346 4.658 +-7.229 0.9683 4.298 +-8.053 1.724 3.821 +-7.747 1.346 3.46 +-9.085 -0.1982 5.078 +-9.347 -0.1982 3.597 +-8.702 -0.7393 4.247 +-10.91 -1.528 5.154 +-10.39 -2.11 4.847 +-11.08 -1.528 4.148 +-10.49 -2.11 4.26 +-11.58 -2.509 4.768 +-11.57 -2.523 4.76 +-11.59 -2.509 4.742 +-11.57 -2.523 4.744 +-12.39 -3.546 5.269 +-11.93 -3.852 5.034 +-12.52 -3.546 4.548 +-12.87 -4.803 5.622 +-12.58 -5.09 4.606 +-13.49 -4.516 4.767 +-13.11 -6.264 5.38 +-14.78 -6.803 5.643 +-14.49 -7.092 4.622 +-15.35 -8.042 5.637 +-14.73 -8.43 5.528 +-15.15 -8.236 4.95 +-15.78 -9.378 5.503 +-15.77 -9.385 5.497 +-15.78 -9.375 5.495 +-15.78 -9.378 5.486 +-15.77 -9.383 5.484 +-1.522 -2.262 -1.24 +-1.519 -2.258 -1.231 +-1.522 -2.262 -1.222 +-1.529 -2.27 -1.222 +-1.529 -2.27 -1.24 +-2.061 -1.082 -0.8906 +-2.428 -1.536 -1.907 +-3.082 -0.4963 -0.5522 +-3.082 -0.4963 -1.91 +-3.759 -1.333 -1.628 +-5.567 0.1257 -0.8323 +-5.665 1.852 -0.8818 +-6.417 0.9227 -0.8818 +-6.041 1.387 -1.924 +-6.95 2.279 -1.137 +-6.937 1.957 -1.326 +-6.943 2.118 -1.418 +-7.378 0.9683 -0.9246 +-8.108 1.724 -0.9246 +-7.743 1.346 -1.84 +-8.797 0.8371 -0.6052 +-9.793 0.269 -1.611 +-8.891 -0.6654 -1.611 +-10.59 -2.11 -0.9326 +-10.57 -0.523 -0.8679 +-11.3 -1.375 -0.9326 +-10.94 -1.742 -1.823 +-11.75 -2.506 -1.223 +-11.73 -2.523 -1.223 +-11.74 -2.514 -1.247 +-12.76 -3.466 -1.017 +-12.14 -3.852 -1.017 +-12.45 -3.659 -1.656 +-13.16 -4.803 -0.6001 +-12.7 -5.09 -1.55 +-13.62 -4.516 -1.55 +-13.36 -6.264 -0.8809 +-15.05 -6.803 -0.9114 +-14.58 -7.092 -1.866 +-15.6 -8.042 -1.016 +-14.98 -8.43 -1.016 +-15.29 -8.236 -1.658 +-16.01 -9.378 -1.222 +-15.99 -9.385 -1.226 +-16.01 -9.375 -1.231 +-16.01 -9.378 -1.24 +-16 -9.383 -1.24 +-1.522 -2.27 -4.918 +-2.105 -1.082 -4.684 +-2.827 -1.989 -4.812 +-1.513 -2.262 -4.935 +-2.29 -1.536 -5.749 +-4.562 -0.3933 -4.75 +-4.55 -0.6204 -5.878 +-6.395 0.9227 -5.432 +-5.655 1.852 -5.302 +-5.844 1.387 -6.393 +-6.877 2.279 -5.776 +-6.863 1.957 -5.773 +-6.821 2.118 -6.051 +-7.747 1.346 -5.408 +-7.335 0.9683 -5.641 +-7.947 1.724 -6.372 +-7.536 1.346 -6.605 +-9.347 -0.1982 -5.543 +-9.085 -0.1982 -7.025 +-8.702 -0.7393 -6.193 +-11.08 -1.528 -6.095 +-10.49 -2.11 -6.206 +-10.91 -1.528 -7.101 +-10.39 -2.11 -6.794 +-11.59 -2.509 -6.689 +-11.57 -2.523 -6.691 +-11.58 -2.509 -6.715 +-11.57 -2.523 -6.707 +-12.52 -3.546 -6.495 +-12.01 -3.852 -6.559 +-12.39 -3.546 -7.217 +-13.09 -4.803 -6.326 +-12.47 -5.09 -7.18 +-13.38 -4.516 -7.341 +-13.24 -6.264 -6.637 +-14.89 -6.803 -6.96 +-14.27 -7.092 -7.819 +-15.42 -8.042 -7.159 +-14.8 -8.43 -7.051 +-15 -8.236 -7.738 +-15.78 -9.378 -7.432 +-15.77 -9.385 -7.434 +-15.78 -9.375 -7.442 +-15.78 -9.378 -7.45 +-15.77 -9.383 -7.448 +1.522 -2.27 -4.918 +1.511 -2.258 -4.926 +1.513 -2.262 -4.935 +2.828 -1.989 -4.812 +2.105 -1.082 -4.684 +2.29 -1.536 -5.749 +4.55 -0.6204 -5.878 +4.562 -0.3933 -4.75 +6.395 0.9227 -5.432 +5.655 1.852 -5.302 +5.844 1.387 -6.393 +6.877 2.279 -5.776 +6.863 1.957 -5.773 +6.821 2.118 -6.051 +7.335 0.9683 -5.641 +7.747 1.346 -5.408 +7.947 1.724 -6.372 +7.536 1.346 -6.605 +9.347 -0.1982 -5.543 +9.086 -0.1982 -7.025 +8.702 -0.7393 -6.193 +11.08 -1.528 -6.095 +10.49 -2.11 -6.206 +10.91 -1.528 -7.101 +10.39 -2.11 -6.794 +11.59 -2.509 -6.689 +11.57 -2.523 -6.691 +11.58 -2.509 -6.715 +11.57 -2.523 -6.707 +12.52 -3.546 -6.495 +12.01 -3.852 -6.559 +12.39 -3.546 -7.217 +12.47 -5.09 -7.18 +13.09 -4.803 -6.326 +13.38 -4.516 -7.341 +13.24 -6.264 -6.637 +14.89 -6.803 -6.96 +14.27 -7.092 -7.819 +15.42 -8.042 -7.159 +14.8 -8.43 -7.051 +15 -8.236 -7.738 +15.78 -9.378 -7.432 +15.77 -9.385 -7.434 +15.78 -9.375 -7.442 +15.78 -9.378 -7.45 +15.77 -9.383 -7.448 +-0.9592 -0.9547 10.35 +0.007813 0.3928 11.6 +-0.7324 1.062 10.63 +0.007813 -1.855 11.37 +0.007813 -2.459 10.63 +0.007813 0.3928 9.662 +-0.3928 1.509 10.63 +0.7327 1.062 10.63 +-0.007813 0.3928 11.6 +0.9595 -0.9547 10.35 +-0.007813 -1.855 11.37 +-0.007813 -2.459 10.63 +-0.007813 0.3928 9.662 +0.3931 1.509 10.63 +1.513 -2.262 2.988 +1.511 -2.258 2.979 +1.516 -2.262 2.97 +1.522 -2.27 2.971 +1.52 -2.27 2.989 +1.987 -1.082 3.408 +2.524 -1.536 2.471 +2.933 -0.4963 3.919 +3.168 -0.4963 2.581 +3.786 -1.333 2.977 +5.429 0.1257 4.074 +5.534 1.852 4.042 +6.274 0.9227 4.173 +6.085 1.387 3.082 +6.863 1.957 3.827 +6.844 2.279 4.015 +6.886 2.118 3.737 +7.229 0.9683 4.298 +7.947 1.724 4.425 +7.747 1.346 3.46 +8.57 0.8371 4.859 +9.726 0.269 4.042 +8.838 -0.6654 3.885 +10.39 -2.11 4.847 +10.37 -0.523 4.909 +11.09 -1.375 4.97 +10.89 -1.742 4.032 +11.59 -2.506 4.763 +11.57 -2.523 4.76 +11.58 -2.514 4.739 +12.55 -3.466 5.142 +11.93 -3.852 5.034 +12.35 -3.659 4.459 +12.58 -5.09 4.606 +12.87 -4.803 5.622 +13.49 -4.516 4.767 +13.11 -6.264 5.38 +14.78 -6.803 5.643 +14.49 -7.092 4.622 +15.35 -8.042 5.637 +14.73 -8.43 5.528 +15.15 -8.236 4.95 +15.78 -9.378 5.503 +15.77 -9.385 5.497 +15.78 -9.375 5.495 +15.78 -9.378 5.486 +15.77 -9.383 5.484 +1.529 -2.27 -1.222 +1.519 -2.258 -1.231 +1.522 -2.262 -1.24 +2.795 -1.989 -0.8906 +2.062 -1.082 -0.8906 +2.428 -1.536 -1.907 +4.677 -0.6204 -1.642 +4.493 -0.3933 -0.5283 +6.417 0.9227 -0.8818 +5.665 1.852 -0.8818 +6.041 1.387 -1.924 +6.951 2.279 -1.137 +6.937 1.957 -1.137 +6.944 2.118 -1.418 +7.379 0.9683 -0.9246 +7.743 1.346 -0.623 +8.108 1.724 -1.538 +7.743 1.346 -1.84 +9.343 -0.1982 -0.4792 +9.343 -0.1982 -1.983 +8.82 -0.7393 -1.231 +11.15 -1.528 -0.7207 +10.59 -2.11 -0.9326 +11.15 -1.528 -1.742 +10.59 -2.11 -1.53 +11.75 -2.509 -1.218 +11.73 -2.523 -1.223 +11.75 -2.509 -1.245 +11.73 -2.523 -1.239 +12.63 -3.546 -0.8647 +12.14 -3.852 -1.017 +12.63 -3.546 -1.598 +12.7 -5.09 -1.55 +13.16 -4.803 -0.6001 +13.62 -4.516 -1.55 +13.36 -6.264 -0.8804 +15.05 -6.803 -0.9111 +14.58 -7.092 -1.866 +15.6 -8.042 -1.016 +14.98 -8.43 -1.016 +15.29 -8.236 -1.658 +16.01 -9.378 -1.222 +15.99 -9.385 -1.226 +16.01 -9.375 -1.231 +16.01 -9.378 -1.24 +16 -9.383 -1.24 +-0.7932 4.462 -7.734 +-0.3843 1.974 -5.495 +1.95 3.241 -7.348 +1.167 0.6428 -6.109 +3.808 0.1871 -9.572 +3.644 1.688 -11.88 +0.07617 4.961 -10.12 +3.28 -0.5704 -14.15 +0.2488 -1.848 -16.78 +-0.9744 -2.686 -16.57 +0.7307 -3.159 -16.47 +-2.678 2.963 -8.185 +-2.616 -2.779 -12.24 +-1.159 -1.533 -8.922 +3 0 1 2 +3 1 3 2 +3 2 4 5 +3 4 6 5 +3 5 6 7 +3 7 6 8 +3 2 3 9 +3 2 9 4 +3 4 9 6 +3 6 9 8 +3 5 0 2 +3 5 7 0 +3 8 10 7 +3 7 10 0 +3 0 10 1 +3 1 10 11 +3 1 11 3 +3 10 12 11 +3 11 12 3 +3 3 12 9 +3 10 8 12 +3 12 8 9 +3 13 14 15 +3 15 14 16 +3 17 15 16 +3 18 19 14 +3 14 19 16 +3 19 20 16 +3 15 17 21 +3 15 21 13 +3 14 13 18 +3 21 22 13 +3 13 22 18 +3 21 23 22 +3 21 24 23 +3 23 25 22 +3 22 25 18 +3 18 25 20 +3 18 20 19 +3 23 26 25 +3 25 26 20 +3 23 24 26 +3 26 24 27 +3 26 27 20 +3 20 27 16 +3 24 21 27 +3 27 21 17 +3 27 17 16 +3 28 29 30 +3 31 32 29 +3 29 32 30 +3 32 33 30 +3 30 33 28 +3 28 33 34 +3 28 34 35 +3 35 34 36 +3 28 35 29 +3 35 36 29 +3 29 36 31 +3 36 37 31 +3 31 37 32 +3 37 38 32 +3 32 38 33 +3 36 34 37 +3 37 34 38 +3 38 34 33 +3 39 40 41 +3 42 43 40 +3 40 43 41 +3 43 44 41 +3 41 44 39 +3 39 44 45 +3 39 45 46 +3 46 45 47 +3 39 46 40 +3 46 47 40 +3 40 47 42 +3 47 48 42 +3 42 48 43 +3 48 49 43 +3 43 49 44 +3 47 45 48 +3 48 45 49 +3 49 45 44 +3 50 51 52 +3 50 52 53 +3 51 54 55 +3 51 55 52 +3 54 56 57 +3 54 57 55 +3 56 58 59 +3 56 59 57 +3 58 60 61 +3 58 61 59 +3 60 50 53 +3 60 53 61 +3 53 52 62 +3 53 62 63 +3 52 55 64 +3 52 64 62 +3 55 57 65 +3 55 65 64 +3 57 59 66 +3 57 66 65 +3 59 61 67 +3 59 67 66 +3 61 53 63 +3 61 63 67 +3 63 62 68 +3 63 68 69 +3 62 64 70 +3 62 70 68 +3 64 65 71 +3 64 71 70 +3 65 66 72 +3 65 72 71 +3 66 67 73 +3 66 73 72 +3 67 63 69 +3 67 69 73 +3 69 68 74 +3 69 74 75 +3 68 70 76 +3 68 76 74 +3 70 71 77 +3 70 77 76 +3 71 72 78 +3 71 78 77 +3 72 73 79 +3 72 79 78 +3 73 69 75 +3 73 75 79 +3 75 74 80 +3 75 80 81 +3 74 76 82 +3 74 82 80 +3 76 77 83 +3 76 83 82 +3 77 78 84 +3 77 84 83 +3 78 79 85 +3 78 85 84 +3 79 75 81 +3 79 81 85 +3 81 80 86 +3 81 86 87 +3 80 82 88 +3 80 88 86 +3 82 83 89 +3 82 89 88 +3 83 84 90 +3 83 90 89 +3 84 85 91 +3 84 91 90 +3 85 81 87 +3 85 87 91 +3 87 86 92 +3 87 92 93 +3 86 88 94 +3 86 94 92 +3 88 89 95 +3 88 95 94 +3 89 90 96 +3 89 96 95 +3 90 91 97 +3 90 97 96 +3 91 87 93 +3 91 93 97 +3 93 92 98 +3 93 98 99 +3 92 94 100 +3 92 100 98 +3 94 95 101 +3 94 101 100 +3 95 96 102 +3 95 102 101 +3 96 97 103 +3 96 103 102 +3 97 93 99 +3 97 99 103 +3 99 98 104 +3 99 104 105 +3 98 100 106 +3 98 106 104 +3 100 101 107 +3 100 107 106 +3 101 102 108 +3 101 108 107 +3 102 103 109 +3 102 109 108 +3 103 99 105 +3 103 105 109 +3 105 104 110 +3 105 110 111 +3 104 106 112 +3 104 112 110 +3 106 107 113 +3 106 113 112 +3 107 108 114 +3 107 114 113 +3 108 109 115 +3 108 115 114 +3 109 105 111 +3 109 111 115 +3 54 51 50 +3 56 54 50 +3 58 56 50 +3 60 58 50 +3 112 113 110 +3 113 114 110 +3 114 115 110 +3 115 111 110 +3 116 117 118 +3 119 116 118 +3 120 121 117 +3 116 120 117 +3 122 123 121 +3 120 122 121 +3 124 125 123 +3 122 124 123 +3 126 127 125 +3 124 126 125 +3 119 118 127 +3 126 119 127 +3 128 116 119 +3 129 128 119 +3 130 120 116 +3 128 130 116 +3 131 122 120 +3 130 131 120 +3 132 124 122 +3 131 132 122 +3 133 126 124 +3 132 133 124 +3 129 119 126 +3 133 129 126 +3 134 128 129 +3 135 134 129 +3 136 130 128 +3 134 136 128 +3 137 131 130 +3 136 137 130 +3 138 132 131 +3 137 138 131 +3 139 133 132 +3 138 139 132 +3 135 129 133 +3 139 135 133 +3 140 134 135 +3 141 140 135 +3 142 136 134 +3 140 142 134 +3 143 137 136 +3 142 143 136 +3 144 138 137 +3 143 144 137 +3 145 139 138 +3 144 145 138 +3 141 135 139 From fa8cdb241d1c131c9b46a5d6bee70f6d3fb588f2 Mon Sep 17 00:00:00 2001 From: DenisMikhalev Date: Tue, 10 Mar 2015 09:18:23 +0300 Subject: [PATCH 40/74] Move ply file to models-nonbsd dir --- test/{models => models-nonbsd}/PLY/ant-half.ply | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{models => models-nonbsd}/PLY/ant-half.ply (100%) diff --git a/test/models/PLY/ant-half.ply b/test/models-nonbsd/PLY/ant-half.ply similarity index 100% rename from test/models/PLY/ant-half.ply rename to test/models-nonbsd/PLY/ant-half.ply From 615041ba159c866f95d44e477666f86a8b1ccab6 Mon Sep 17 00:00:00 2001 From: DenisMikhalev Date: Tue, 10 Mar 2015 15:00:49 +0300 Subject: [PATCH 41/74] Add license file for ant-half.ply --- test/models-nonbsd/PLY/ant-half.ply.license | 165 ++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 test/models-nonbsd/PLY/ant-half.ply.license diff --git a/test/models-nonbsd/PLY/ant-half.ply.license b/test/models-nonbsd/PLY/ant-half.ply.license new file mode 100644 index 000000000..cca7fc278 --- /dev/null +++ b/test/models-nonbsd/PLY/ant-half.ply.license @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. From d9d7dd31ea5eb23f9ed9aac2625e8ab7fca0aba1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 11 Mar 2015 19:05:37 +0100 Subject: [PATCH 42/74] fix linker error: undefined references to openddl_parser. Signed-off-by: Kim Kulling --- tools/assimp_cmd/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/assimp_cmd/CMakeLists.txt b/tools/assimp_cmd/CMakeLists.txt index b549f9b10..78b3139ee 100644 --- a/tools/assimp_cmd/CMakeLists.txt +++ b/tools/assimp_cmd/CMakeLists.txt @@ -28,7 +28,7 @@ IF( WIN32 ) MAIN_DEPENDENCY assimp) ENDIF( WIN32 ) -TARGET_LINK_LIBRARIES( assimp_cmd assimp ${ZLIB_LIBRARIES}) +TARGET_LINK_LIBRARIES( assimp_cmd assimp ${ZLIB_LIBRARIES} openddl_parser) SET_TARGET_PROPERTIES( assimp_cmd PROPERTIES OUTPUT_NAME assimp ) From 90a07713c01840cdbd28d3a9770866fe2ce23136 Mon Sep 17 00:00:00 2001 From: Wil Shipley Date: Wed, 11 Mar 2015 16:55:21 -0700 Subject: [PATCH 43/74] Added 'const' to () operator on CompareVector So it can build on clang. --- code/IFCUtil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/IFCUtil.cpp b/code/IFCUtil.cpp index 6fcda33c7..800ffd3ed 100644 --- a/code/IFCUtil.cpp +++ b/code/IFCUtil.cpp @@ -236,7 +236,7 @@ IfcVector3 TempMesh::ComputeLastPolygonNormal(bool normalize) const struct CompareVector { - bool operator () (const IfcVector3& a, const IfcVector3& b) + bool operator () (const IfcVector3& a, const IfcVector3& b) const { IfcVector3 d = a - b; IfcFloat eps = 1e-6; From ead3b26ea9df33c9ef57c5ffbd24a5a535effe3f Mon Sep 17 00:00:00 2001 From: arris69 Date: Thu, 12 Mar 2015 20:37:33 +0100 Subject: [PATCH 44/74] no one ever tried this java stuff??? (crashes if the model has a lot of meshes...) --- port/jassimp/jassimp-native/src/jassimp.cpp | 76 +++++++++++++++++---- 1 file changed, 62 insertions(+), 14 deletions(-) diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index db803f106..1eeafe304 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -5,12 +5,30 @@ #ifdef JNI_LOG +#ifdef ANDROID +#include +#define lprintf(...) __android_log_print(ANDROID_LOG_VERBOSE, __func__, __VA_ARGS__) +#else #define lprintf(...) printf (__VA_ARGS__) +#endif /* ANDROID */ #else #define lprintf #endif +static void dumpRefTable(JNIEnv *env){ + /* DEBUG STUFF */ + jclass clazz = env->FindClass("dalvik/system/VMDebug"); + jmethodID dump_mid = env->GetStaticMethodID( clazz, "dumpReferenceTables", "()V" ); + env->CallStaticVoidMethod( clazz, dump_mid ); + env->DeleteLocalRef(clazz); + + /*jint res = env->EnsureLocalCapacity(20); + if(res < 0){ + lprintf("too many meshes on the model!\n"); + }*/ +} + static bool createInstance(JNIEnv *env, const char* className, jobject& newInstance) { jclass clazz = env->FindClass(className); @@ -30,6 +48,7 @@ static bool createInstance(JNIEnv *env, const char* className, jobject& newInsta } newInstance = env->NewObject(clazz, ctr_id); + env->DeleteLocalRef(clazz); if (NULL == newInstance) { @@ -41,7 +60,7 @@ static bool createInstance(JNIEnv *env, const char* className, jobject& newInsta } -static bool createInstance(JNIEnv *env, const char* className, const char* signature, const jvalue* params, jobject& newInstance) +static bool createInstance(JNIEnv *env, const char* className, const char* signature,/* const*/ jvalue* params, jobject& newInstance) { jclass clazz = env->FindClass(className); @@ -60,6 +79,7 @@ static bool createInstance(JNIEnv *env, const char* className, const char* signa } newInstance = env->NewObjectA(clazz, ctr_id, params); + env->DeleteLocalRef(clazz); if (NULL == newInstance) { @@ -82,6 +102,7 @@ static bool getField(JNIEnv *env, jobject object, const char* fieldName, const c } jfieldID fieldId = env->GetFieldID(clazz, fieldName, signature); + env->DeleteLocalRef(clazz); if (NULL == fieldId) { @@ -106,6 +127,7 @@ static bool setIntField(JNIEnv *env, jobject object, const char* fieldName, jint } jfieldID fieldId = env->GetFieldID(clazz, fieldName, "I"); + env->DeleteLocalRef(clazz); if (NULL == fieldId) { @@ -130,6 +152,7 @@ static bool setFloatField(JNIEnv *env, jobject object, const char* fieldName, jf } jfieldID fieldId = env->GetFieldID(clazz, fieldName, "F"); + env->DeleteLocalRef(clazz); if (NULL == fieldId) { @@ -154,6 +177,7 @@ static bool setObjectField(JNIEnv *env, jobject object, const char* fieldName, c } jfieldID fieldId = env->GetFieldID(clazz, fieldName, signature); + env->DeleteLocalRef(clazz); if (NULL == fieldId) { @@ -192,7 +216,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, - const char* signature, const jvalue* params) + const char* signature,/* const*/ jvalue* params) { jclass clazz = env->FindClass(typeName); @@ -203,6 +227,7 @@ static bool call(JNIEnv *env, jobject object, const char* typeName, const char* } jmethodID mid = env->GetMethodID(clazz, methodName, signature); + env->DeleteLocalRef(clazz); if (NULL == mid) { @@ -210,6 +235,27 @@ static bool call(JNIEnv *env, jobject object, const char* typeName, const char* return false; } + jboolean jReturnValue = env->CallBooleanMethod(object, mid, params[0].l); + + return (bool)jReturnValue; +} +static bool callv(JNIEnv *env, jobject object, const char* typeName, + const char* methodName, const char* signature,/* const*/ jvalue* params) { + jclass clazz = env->FindClass(typeName); + + if (NULL == clazz) { + lprintf("could not find class %s\n", typeName); + return false; + } + + jmethodID mid = env->GetMethodID(clazz, methodName, signature); + env->DeleteLocalRef(clazz); + + if (NULL == mid) { + lprintf("could not find method %s with signature %s in type %s\n", methodName, signature, typeName); + return false; + } + env->CallVoidMethodA(object, mid, params); return true; @@ -217,7 +263,7 @@ static bool call(JNIEnv *env, jobject object, const char* typeName, const char* static bool callStaticObject(JNIEnv *env, const char* typeName, const char* methodName, - const char* signature, const jvalue* params, jobject& returnValue) + const char* signature,/* const*/ jvalue* params, jobject& returnValue) { jclass clazz = env->FindClass(typeName); @@ -338,7 +384,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) /* set general mesh data in java */ jvalue setTypesParams[1]; setTypesParams[0].i = cMesh->mPrimitiveTypes; - if (!call(env, jMesh, "jassimp/AiMesh", "setPrimitiveTypes", "(I)V", setTypesParams)) + if (!callv(env, jMesh, "jassimp/AiMesh", "setPrimitiveTypes", "(I)V", setTypesParams)) { return false; } @@ -380,7 +426,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) allocateBuffersParams[1].i = cMesh->mNumFaces; allocateBuffersParams[2].z = isPureTriangle; allocateBuffersParams[3].i = (jint) faceBufferSize; - if (!call(env, jMesh, "jassimp/AiMesh", "allocateBuffers", "(IIZI)V", allocateBuffersParams)) + if (!callv(env, jMesh, "jassimp/AiMesh", "allocateBuffers", "(IIZI)V", allocateBuffersParams)) { return false; } @@ -470,7 +516,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) jvalue allocateDataChannelParams[2]; allocateDataChannelParams[0].i = 0; allocateDataChannelParams[1].i = 0; - if (!call(env, jMesh, "jassimp/AiMesh", "allocateDataChannel", "(II)V", allocateDataChannelParams)) + if (!callv(env, jMesh, "jassimp/AiMesh", "allocateDataChannel", "(II)V", allocateDataChannelParams)) { lprintf("could not allocate normal data channel\n"); return false; @@ -491,7 +537,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) jvalue allocateDataChannelParams[2]; allocateDataChannelParams[0].i = 1; allocateDataChannelParams[1].i = 0; - if (!call(env, jMesh, "jassimp/AiMesh", "allocateDataChannel", "(II)V", allocateDataChannelParams)) + if (!callv(env, jMesh, "jassimp/AiMesh", "allocateDataChannel", "(II)V", allocateDataChannelParams)) { lprintf("could not allocate tangents data channel\n"); return false; @@ -512,7 +558,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) jvalue allocateDataChannelParams[2]; allocateDataChannelParams[0].i = 2; allocateDataChannelParams[1].i = 0; - if (!call(env, jMesh, "jassimp/AiMesh", "allocateDataChannel", "(II)V", allocateDataChannelParams)) + if (!callv(env, jMesh, "jassimp/AiMesh", "allocateDataChannel", "(II)V", allocateDataChannelParams)) { lprintf("could not allocate bitangents data channel\n"); return false; @@ -535,7 +581,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) jvalue allocateDataChannelParams[2]; allocateDataChannelParams[0].i = 3; allocateDataChannelParams[1].i = c; - if (!call(env, jMesh, "jassimp/AiMesh", "allocateDataChannel", "(II)V", allocateDataChannelParams)) + if (!callv(env, jMesh, "jassimp/AiMesh", "allocateDataChannel", "(II)V", allocateDataChannelParams)) { lprintf("could not allocate colorset data channel\n"); return false; @@ -574,7 +620,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) } allocateDataChannelParams[1].i = c; - if (!call(env, jMesh, "jassimp/AiMesh", "allocateDataChannel", "(II)V", allocateDataChannelParams)) + if (!callv(env, jMesh, "jassimp/AiMesh", "allocateDataChannel", "(II)V", allocateDataChannelParams)) { lprintf("could not allocate texture coordinates data channel\n"); return false; @@ -701,6 +747,8 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) } } } + env->DeleteLocalRef(jMeshes); + env->DeleteLocalRef(jMesh); } return true; @@ -719,7 +767,7 @@ static bool loadSceneNode(JNIEnv *env, const aiNode *cNode, jobject parent, jobj wrapMatParams[0].l = jMatrixArr; jobject jMatrix; - if (!callStaticObject(env, "Ljassimp/Jassimp;", "wrapMatrix", "([F)Ljava/lang/Object;", wrapMatParams, jMatrix)) + if (!callStaticObject(env, "jassimp/Jassimp", "wrapMatrix", "([F)Ljava/lang/Object;", wrapMatParams, jMatrix)) { return false; } @@ -749,7 +797,7 @@ static bool loadSceneNode(JNIEnv *env, const aiNode *cNode, jobject parent, jobj wrapNodeParams[2].l = jMeshrefArr; wrapNodeParams[3].l = jNodeName; jobject jNode; - if (!callStaticObject(env, "Ljassimp/Jassimp;", "wrapSceneNode", + if (!callStaticObject(env, "jassimp/Jassimp", "wrapSceneNode", "(Ljava/lang/Object;Ljava/lang/Object;[ILjava/lang/String;)Ljava/lang/Object;", wrapNodeParams, jNode)) { return false; @@ -842,7 +890,7 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) setNumberParams[0].i = ttInd; setNumberParams[1].i = num; - if (!call(env, jMaterial, "jassimp/AiMaterial", "setTextureNumber", "(II)V", setNumberParams)) + if (!callv(env, jMaterial, "jassimp/AiMaterial", "setTextureNumber", "(II)V", setNumberParams)) { return false; } @@ -880,7 +928,7 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) wrapColorParams[0].f = ((float*) cProperty->mData)[0]; wrapColorParams[1].f = ((float*) cProperty->mData)[1]; wrapColorParams[2].f = ((float*) cProperty->mData)[2]; - if (!callStaticObject(env, "Ljassimp/Jassimp;", "wrapColor3", "(FFF)Ljava/lang/Object;", wrapColorParams, jData)) + if (!callStaticObject(env, "jassimp/Jassimp", "wrapColor3", "(FFF)Ljava/lang/Object;", wrapColorParams, jData)) { return false; } From 8b22ba25baa63e4ce30e5f3cd5ecb68f50993207 Mon Sep 17 00:00:00 2001 From: arris69 Date: Thu, 12 Mar 2015 21:24:49 +0100 Subject: [PATCH 45/74] removed debug function and add buildfile for android ndk-build command --- port/jassimp/jassimp-native/Android.mk | 13 +++++++++++++ port/jassimp/jassimp-native/src/jassimp.cpp | 14 -------------- 2 files changed, 13 insertions(+), 14 deletions(-) create mode 100644 port/jassimp/jassimp-native/Android.mk diff --git a/port/jassimp/jassimp-native/Android.mk b/port/jassimp/jassimp-native/Android.mk new file mode 100644 index 000000000..94b233b32 --- /dev/null +++ b/port/jassimp/jassimp-native/Android.mk @@ -0,0 +1,13 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := jassimp +LOCAL_SRC_FILES := src/jassimp.cpp + +LOCAL_CFLAGS += -DJNI_LOG + +#LOCAL_STATIC_LIBRARIES := assimp_static +LOCAL_SHARED_LIBRARIES := assimp +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index 1eeafe304..cc064bd3e 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -15,20 +15,6 @@ #define lprintf #endif - -static void dumpRefTable(JNIEnv *env){ - /* DEBUG STUFF */ - jclass clazz = env->FindClass("dalvik/system/VMDebug"); - jmethodID dump_mid = env->GetStaticMethodID( clazz, "dumpReferenceTables", "()V" ); - env->CallStaticVoidMethod( clazz, dump_mid ); - env->DeleteLocalRef(clazz); - - /*jint res = env->EnsureLocalCapacity(20); - if(res < 0){ - lprintf("too many meshes on the model!\n"); - }*/ -} - static bool createInstance(JNIEnv *env, const char* className, jobject& newInstance) { jclass clazz = env->FindClass(className); From 9094770d38fd278c09bcbced06d03e771f11bf11 Mon Sep 17 00:00:00 2001 From: arris69 Date: Fri, 13 Mar 2015 12:52:03 +0100 Subject: [PATCH 46/74] fixed light and camera access. --- port/jassimp/jassimp-native/src/jassimp.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index cc064bd3e..14d9cb2d4 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -703,7 +703,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) wrapParams[0].l = jMatrixArr; jobject jMatrix; - if (!callStaticObject(env, "Ljassimp/Jassimp;", "wrapMatrix", "([F)Ljava/lang/Object;", wrapParams, jMatrix)) + if (!callStaticObject(env, "jassimp/Jassimp", "wrapMatrix", "([F)Ljava/lang/Object;", wrapParams, jMatrix)) { return false; } @@ -939,7 +939,7 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) wrapColorParams[1].f = ((float*) cProperty->mData)[1]; wrapColorParams[2].f = ((float*) cProperty->mData)[2]; wrapColorParams[3].f = ((float*) cProperty->mData)[3]; - if (!callStaticObject(env, "Ljassimp/Jassimp;", "wrapColor4", "(FFFF)Ljava/lang/Object;", wrapColorParams, jData)) + if (!callStaticObject(env, "jassimp/Jassimp", "wrapColor4", "(FFFF)Ljava/lang/Object;", wrapColorParams, jData)) { return false; } @@ -1171,7 +1171,7 @@ static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) wrapColorParams[1].f = cLight->mColorDiffuse.g; wrapColorParams[2].f = cLight->mColorDiffuse.b; jobject jDiffuse; - if (!callStaticObject(env, "Ljassimp/Jassimp;", "wrapColor3", "(FFF)Ljava/lang/Object;", wrapColorParams, jDiffuse)) + if (!callStaticObject(env, "jassimp/Jassimp", "wrapColor3", "(FFF)Ljava/lang/Object;", wrapColorParams, jDiffuse)) { return false; } @@ -1180,7 +1180,7 @@ static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) wrapColorParams[1].f = cLight->mColorSpecular.g; wrapColorParams[2].f = cLight->mColorSpecular.b; jobject jSpecular; - if (!callStaticObject(env, "Ljassimp/Jassimp;", "wrapColor3", "(FFF)Ljava/lang/Object;", wrapColorParams, jSpecular)) + if (!callStaticObject(env, "jassimp/Jassimp", "wrapColor3", "(FFF)Ljava/lang/Object;", wrapColorParams, jSpecular)) { return false; } @@ -1189,7 +1189,7 @@ static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) wrapColorParams[1].f = cLight->mColorAmbient.g; wrapColorParams[2].f = cLight->mColorAmbient.b; jobject jAmbient; - if (!callStaticObject(env, "Ljassimp/Jassimp;", "wrapColor3", "(FFF)Ljava/lang/Object;", wrapColorParams, jAmbient)) + if (!callStaticObject(env, "jassimp/Jassimp", "wrapColor3", "(FFF)Ljava/lang/Object;", wrapColorParams, jAmbient)) { return false; } @@ -1201,7 +1201,7 @@ static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) wrapVec3Params[1].f = cLight->mPosition.y; wrapVec3Params[2].f = cLight->mPosition.z; jobject jPosition; - if (!callStaticObject(env, "Ljassimp/Jassimp;", "wrapVec3", "(FFF)Ljava/lang/Object;", wrapVec3Params, jPosition)) + if (!callStaticObject(env, "jassimp/Jassimp", "wrapVec3", "(FFF)Ljava/lang/Object;", wrapVec3Params, jPosition)) { return false; } @@ -1210,7 +1210,7 @@ static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) wrapVec3Params[1].f = cLight->mPosition.y; wrapVec3Params[2].f = cLight->mPosition.z; jobject jDirection; - if (!callStaticObject(env, "Ljassimp/Jassimp;", "wrapVec3", "(FFF)Ljava/lang/Object;", wrapVec3Params, jDirection)) + if (!callStaticObject(env, "jassimp/Jassimp", "wrapVec3", "(FFF)Ljava/lang/Object;", wrapVec3Params, jDirection)) { return false; } @@ -1276,7 +1276,7 @@ static bool loadCameras(JNIEnv *env, const aiScene* cScene, jobject& jScene) wrapPositionParams[1].f = cCamera->mPosition.y; wrapPositionParams[2].f = cCamera->mPosition.z; jobject jPosition; - if (!callStaticObject(env, "Ljassimp/Jassimp;", "wrapVec3", "(FFF)Ljava/lang/Object;", wrapPositionParams, jPosition)) + if (!callStaticObject(env, "jassimp/Jassimp", "wrapVec3", "(FFF)Ljava/lang/Object;", wrapPositionParams, jPosition)) { return false; } @@ -1285,7 +1285,7 @@ static bool loadCameras(JNIEnv *env, const aiScene* cScene, jobject& jScene) wrapPositionParams[1].f = cCamera->mUp.y; wrapPositionParams[2].f = cCamera->mUp.z; jobject jUp; - if (!callStaticObject(env, "Ljassimp/Jassimp;", "wrapVec3", "(FFF)Ljava/lang/Object;", wrapPositionParams, jUp)) + if (!callStaticObject(env, "jassimp/Jassimp", "wrapVec3", "(FFF)Ljava/lang/Object;", wrapPositionParams, jUp)) { return false; } @@ -1294,7 +1294,7 @@ static bool loadCameras(JNIEnv *env, const aiScene* cScene, jobject& jScene) wrapPositionParams[1].f = cCamera->mLookAt.y; wrapPositionParams[2].f = cCamera->mLookAt.z; jobject jLookAt; - if (!callStaticObject(env, "Ljassimp/Jassimp;", "wrapVec3", "(FFF)Ljava/lang/Object;", wrapPositionParams, jLookAt)) + if (!callStaticObject(env, "jassimp/Jassimp", "wrapVec3", "(FFF)Ljava/lang/Object;", wrapPositionParams, jLookAt)) { return false; } From ad9d178f0ad71f39fbe5d1e289c611b079d3a593 Mon Sep 17 00:00:00 2001 From: ulf Date: Fri, 13 Mar 2015 15:13:53 +0100 Subject: [PATCH 47/74] - Bugfix: IfcLoader lost nodes and geometry when they were stored in subnodes of IfcSpace and the setting "Filter IfcSpace" was enabled --- code/IFCLoader.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/code/IFCLoader.cpp b/code/IFCLoader.cpp index b6e6e0289..24ed772e0 100644 --- a/code/IFCLoader.cpp +++ b/code/IFCLoader.cpp @@ -677,10 +677,11 @@ aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, Conversion const STEP::DB::RefMap& refs = conv.db.GetRefs(); // skip over space and annotation nodes - usually, these have no meaning in Assimp's context + bool skipGeometry = false; if(conv.settings.skipSpaceRepresentations) { if(const IfcSpace* const space = el.ToPtr()) { IFCImporter::LogDebug("skipping IfcSpace entity due to importer settings"); - return NULL; + skipGeometry = true; } } @@ -850,8 +851,10 @@ aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, Conversion conv.apply_openings = &openings; } - ProcessProductRepresentation(el,nd.get(),subnodes,conv); - conv.apply_openings = conv.collect_openings = NULL; + if (!skipGeometry) { + ProcessProductRepresentation(el,nd.get(),subnodes,conv); + conv.apply_openings = conv.collect_openings = NULL; + } if (subnodes.size()) { nd->mChildren = new aiNode*[subnodes.size()](); From ff4e1d9446716a676874a68b00769ddeec8a1a8f Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Sat, 14 Mar 2015 19:57:13 +0100 Subject: [PATCH 48/74] Sub-sample axis-angle channels if the delta between two consecutive key-frame angles is >= 180 degrees. Fixes #458. --- code/ColladaLoader.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index 2f3942689..a11d94ec3 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -1143,6 +1143,26 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars } ++pos; } + + // https://github.com/assimp/assimp/issues/458 + // Sub-sample axis-angle channels if the delta between two consecutive + // key-frame angles is >= 180 degrees. + if (transforms[e.mTransformIndex].mType == Collada::TF_ROTATE && e.mSubElement == 3 && pos > 0 && pos < e.mTimeAccessor->mCount) { + float cur_key_angle = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, 0); + float last_key_angle = ReadFloat( *e.mValueAccessor, *e.mValueData, pos - 1, 0); + float cur_key_time = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); + float last_key_time = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos - 1, 0); + + float last_eval_angle = last_key_angle + (cur_key_angle - last_key_angle) * (time - last_key_time) / (cur_key_time - last_key_time); + float delta = std::fabs(cur_key_angle-last_eval_angle); + if (delta >= 180.0f) { + int subSampleCount = static_cast(std::floorf(delta / 90.0f)); + if (cur_key_time != time) { + float nextSampleTime = time + (cur_key_time - time) / subSampleCount; + nextTime = std::min( nextTime, nextSampleTime); + } + } + } } // no more keys on any channel after the current time -> we're done From 3080c26a6063945f825ed503d83501ef42b3f255 Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Sat, 14 Mar 2015 19:57:43 +0100 Subject: [PATCH 49/74] Add test files for previously fixed #458. --- ...anims_with_full_rotations_between_keys.fbx | Bin 0 -> 368592 bytes ...anims_with_full_rotations_between_keys.DAE | 7569 +++++++++++++++++ 2 files changed, 7569 insertions(+) create mode 100644 test/models-nonbsd/FBX/2013_BINARY/anims_with_full_rotations_between_keys.fbx create mode 100644 test/models/Collada/anims_with_full_rotations_between_keys.DAE diff --git a/test/models-nonbsd/FBX/2013_BINARY/anims_with_full_rotations_between_keys.fbx b/test/models-nonbsd/FBX/2013_BINARY/anims_with_full_rotations_between_keys.fbx new file mode 100644 index 0000000000000000000000000000000000000000..e65b9e8f0d8bd6f5f2b2bf6443883efcef085e08 GIT binary patch literal 368592 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 + + FBX COLLADA exporter2015-02-03T10:29:10Z2015-02-03T10:29:10ZZ_UP + + + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+ + + + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +-0.500000 0.500000 0.000000 +0.500000 0.500000 0.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 -0.500000 0.000000 +0.500000 -0.500000 0.000000 +0.500000 -0.500000 1.000000 +-0.500000 -0.500000 1.000000 +0.500000 -0.500000 0.000000 +0.500000 0.500000 0.000000 +0.500000 0.500000 1.000000 +0.500000 -0.500000 1.000000 +0.500000 0.500000 0.000000 +-0.500000 0.500000 0.000000 +-0.500000 0.500000 1.000000 +0.500000 0.500000 1.000000 +-0.500000 0.500000 0.000000 +-0.500000 -0.500000 0.000000 +-0.500000 -0.500000 1.000000 +-0.500000 0.500000 1.000000 + + + + + + + + + + + +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 -1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 0.000000 1.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +0.000000 -1.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 +-1.000000 0.000000 0.000000 + + + + + + + + + + + +1.000000 0.000000 +0.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +0.000000 1.000000 +1.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 +0.000000 0.000000 +1.000000 0.000000 +1.000000 1.000000 +0.000000 1.000000 + + + + + + + + + + + + + + 4 4 4 4 4 4

0 2 3 1 4 5 7 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

+
+
+ + + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + 0.033333 11.9666670.000000 0.000000LINEAR LINEAR + 0.033333 11.966667-0.000000 -0.000000LINEAR LINEAR + 0.033333 11.9666671080.000000 1440.000000LINEAR LINEAR + + + + 0 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185947 0.000000 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185947 1.891823 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185946 3.783642 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185944 5.675461 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185942 7.567280 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185939 9.459099 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185935 11.350918 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185931 13.242737 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185927 15.134556 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185922 17.026375 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185916 18.918194 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185910 20.810013 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185903 22.701832 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185895 24.593651 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185887 26.485470 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185879 28.377289 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185870 30.269108 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185860 32.160927 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185850 34.052746 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185839 35.944565 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185827 37.836384 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185815 39.728203 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185803 41.620022 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185790 43.511841 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185776 45.403660 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185762 47.295479 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185747 49.187302 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185731 51.079117 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185715 52.970940 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185699 54.862755 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185682 56.754578 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185664 58.646393 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185646 60.538216 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185627 62.430031 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185607 64.321854 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185587 66.213669 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185567 68.105492 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185546 69.997307 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185524 71.889122 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185502 73.780952 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185479 75.672768 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185456 77.564583 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185432 79.456398 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185407 81.348228 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185382 83.240044 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185356 85.131859 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185330 87.023674 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185303 88.915504 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185276 90.807320 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185248 92.699135 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185219 94.590950 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185190 96.482780 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185160 98.374596 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185130 100.266403 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185099 102.158218 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185068 104.050034 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185036 105.941849 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.185003 107.833664 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.184970 109.725479 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.184936 111.617294 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.184902 113.509109 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.184867 115.400925 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.184832 117.292740 0.0000000.000000 0.000000 0.000000 0.0000001.0000000 0 1 0.0000030 1 0 -0.0000001 0 0 0.0000000.184796 119.184555 0.0000000.000000 0.000000 0.000000 0.0000001.000000 + 30.0000000.00000011.966667 + + + + + + From 741b070c7e412ab10888d3de4f5679396d732673 Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Sun, 15 Mar 2015 00:48:16 +0100 Subject: [PATCH 50/74] Add blender 2.71 default scene as test file. --- test/models/BLEND/BlenderDefault_271.blend | Bin 0 -> 484168 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/models/BLEND/BlenderDefault_271.blend diff --git a/test/models/BLEND/BlenderDefault_271.blend b/test/models/BLEND/BlenderDefault_271.blend new file mode 100644 index 0000000000000000000000000000000000000000..bbf04c2a0a35c78e162aad2f91a2c22cc7b9cfef GIT binary patch literal 484168 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˜Xnk*y{mg*xE>?bDE`{i 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 R9%PRwS1s00BlymL{{uz187BY$ literal 0 HcmV?d00001 From 94fd8c9c167659b60580b55d3f7a09365163ffc3 Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Sun, 15 Mar 2015 00:48:51 +0100 Subject: [PATCH 51/74] Remove old batch script to run unit tests. --- test/RunSingleUnitTestSuite.bat | 33 ------------ test/RunUnitTestSuite.bat | 94 --------------------------------- 2 files changed, 127 deletions(-) delete mode 100644 test/RunSingleUnitTestSuite.bat delete mode 100644 test/RunUnitTestSuite.bat diff --git a/test/RunSingleUnitTestSuite.bat b/test/RunSingleUnitTestSuite.bat deleted file mode 100644 index 4a6a8998a..000000000 --- a/test/RunSingleUnitTestSuite.bat +++ /dev/null @@ -1,33 +0,0 @@ - -rem ------------------------------------------------------------------------------ -rem Tiny script to execute a single unit test suite. -rem -rem Usage: -rem SET OUTDIR= -rem SET BINDIR= -rem -rem CALL RunSingleUnitTestSuite -rem -rem Post: -rem FIRSTUTNA - if the test wasn't found, receives the test name -rem FIRSTUTFAILUR - if the test failed, receives the test name -rem -rem ------------------------------------------------------------------------------ -IF NOT EXIST %BINDIR%\%1\unit.exe ( - - echo NOT AVAILABLE. Please rebuild this configuration - echo Unable to find %BINDIR%\%1\unit.exe > %OUTDIR%%2 - SET FIRSTUTNA=%2 -) ELSE ( - - %BINDIR%\%1\unit.exe > %OUTDIR%%2 - IF errorlevel == 0 ( - echo SUCCESS - ) ELSE ( - echo FAILURE, check output file: %2 - SET FIRSTUTFAILURE=%2 - ) -) - -echo. -echo. \ No newline at end of file diff --git a/test/RunUnitTestSuite.bat b/test/RunUnitTestSuite.bat deleted file mode 100644 index 269e8e93d..000000000 --- a/test/RunUnitTestSuite.bat +++ /dev/null @@ -1,94 +0,0 @@ -rem ------------------------------------------------------------------------------ -rem Tiny script to execute Assimp's fully unit test suite for all configurations -rem -rem Usage: call RunUnitTestSuite -rem ------------------------------------------------------------------------------ - -rem Setup the console environment -set errorlevel=0 -color 4e -cls - -@echo off - -rem Setup target architecture -SET ARCHEXT=x64 -IF %PROCESSOR_ARCHITECTURE% == x86 ( - SET ARCHEXT=win32 -) - -rem Setup standard paths from here -SET OUTDIR=results\ -SET BINDIR=..\bin\ -SET FIRSTUTFAILURE=nil -SET FIRSTUTNA=nil - -echo #===================================================================== -echo # Open Asset Import Library - Unittests -echo #===================================================================== -echo # -echo # Executes the Assimp library unit test suite for the following -echo # build configurations (if available): -echo # -echo # Release -echo # Release -st -echo # Release -noboost -echo # Release -dll -echo # -echo # Debug -echo # Debug -st -echo # Debug -noboost -echo # Debug -dll -echo ====================================================================== -echo. -echo. - - -echo assimp-core release -echo ********************************************************************** -call RunSingleUnitTestSuite unit_release_%ARCHEXT% release.txt - -echo assimp-core release -st -echo ********************************************************************** -call RunSingleUnitTestSuite unit_release-st_%ARCHEXT% release-st.txt - -echo assimp-core release -noboost -echo ********************************************************************** -call RunSingleUnitTestSuite unit_release-noboost-st_%ARCHEXT% release-st-noboost.txt - -echo assimp-core release -dll -echo ********************************************************************** -call RunSingleUnitTestSuite unit_release-dll_%ARCHEXT% release-dll.txt - -echo assimp-core debug -echo ********************************************************************** -call RunSingleUnitTestSuite unit_debug_%ARCHEXT% debug.txt - -echo assimp-core debug -st -echo ********************************************************************** -call RunSingleUnitTestSuite unit_debug-st_%ARCHEXT% debug-st.txt - -echo assimp-core debug -noboost -echo ********************************************************************** -call RunSingleUnitTestSuite unit_debug-noboost-st_%ARCHEXT% debug-st-noboost.txt - -echo assimp-core debug -dll -echo ********************************************************************** -call RunSingleUnitTestSuite unit_debug-dll_%ARCHEXT% debug-dll.txt - - -echo ====================================================================== -IF %FIRSTUTNA% == nil ( - echo All test configs have been found. -) ELSE ( - echo One or more test configs are not available. -) - -IF %FIRSTUTFAILURE% == nil ( - echo All tests have been successful. -) ELSE ( - echo One or more tests failed. -) -echo. - -pause \ No newline at end of file From e976cc211702446011a21c5fbade0490c4923163 Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Sun, 15 Mar 2015 01:02:18 +0100 Subject: [PATCH 52/74] Reduce garbage errors during regression checks. --- test/regression/run.py | 2 +- test/regression/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/regression/run.py b/test/regression/run.py index 73172cf04..1c73c227c 100644 --- a/test/regression/run.py +++ b/test/regression/run.py @@ -171,7 +171,7 @@ def process_dir(d, outfile_results, zipin, result): process_dir(fullpath, outfile_results, zipin, result) continue - if f in settings.files_to_ignore: + if f in settings.files_to_ignore or os.path.splitext(f)[1] in settings.exclude_extensions: print("Ignoring " + f) continue diff --git a/test/regression/settings.py b/test/regression/settings.py index b8cd28683..eca2e79fd 100644 --- a/test/regression/settings.py +++ b/test/regression/settings.py @@ -62,7 +62,7 @@ files_to_ignore = ["pond.0.ply"] exclude_extensions = [ ".lws", ".assbin", ".assxml", ".txt", ".md", ".jpeg", ".jpg", ".png", ".gif", ".tga", ".bmp", - ".skeleton", ".skeleton.xml" + ".skeleton", ".skeleton.xml", ".license" ] # ------------------------------------------------------------------------------- From 1c64c590f213b61c0e5f9cbd99bd87c14c6b33cf Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Sun, 15 Mar 2015 01:26:15 +0100 Subject: [PATCH 53/74] Blender importer: always sort sets of objects by their name. Previously we accidentally ordered pointers to objects by their memory address, which was non-deterministic and caused regression tests to be flaky. --- code/BlenderIntermediate.h | 18 +++++++++++++++++- code/BlenderLoader.cpp | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/code/BlenderIntermediate.h b/code/BlenderIntermediate.h index 4804d9251..05f80f33b 100644 --- a/code/BlenderIntermediate.h +++ b/code/BlenderIntermediate.h @@ -118,6 +118,16 @@ namespace Blender { #ifdef _MSC_VER # pragma warning(disable:4351) #endif + + struct ObjectCompare { + bool operator() (const Object* left, const Object* right) const { + return strcmp(left->id.name, right->id.name) == -1; + } + }; + + // When keeping objects in sets, sort them by their name. + typedef std::set ObjectSet; + // -------------------------------------------------------------------- /** ConversionData acts as intermediate storage location for * the various ConvertXXX routines in BlenderImporter.*/ @@ -130,7 +140,13 @@ namespace Blender { , db(db) {} - std::set objects; + struct ObjectCompare { + bool operator() (const Object* left, const Object* right) const { + return strcmp(left->id.name, right->id.name) == -1; + } + }; + + ObjectSet objects; TempArray meshes; TempArray cameras; diff --git a/code/BlenderLoader.cpp b/code/BlenderLoader.cpp index dc88ab840..33175b05c 100644 --- a/code/BlenderLoader.cpp +++ b/code/BlenderLoader.cpp @@ -1044,7 +1044,7 @@ aiLight* BlenderImporter::ConvertLight(const Scene& /*in*/, const Object* obj, c aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, ConversionData& conv_data, const aiMatrix4x4& parentTransform) { std::deque children; - for(std::set::iterator it = conv_data.objects.begin(); it != conv_data.objects.end() ;) { + for(ObjectSet::iterator it = conv_data.objects.begin(); it != conv_data.objects.end() ;) { const Object* object = *it; if (object->parent == obj) { children.push_back(object); From dc441d09e532b1ff957633800692d69b9b1f94d1 Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Sun, 15 Mar 2015 14:42:28 +0100 Subject: [PATCH 54/74] Blender importer: fix accidental use of uninitialized fields in the default material. IMHO C++03+ guarantees zero initialization here, so it may be a MSVC-specific issue. --- code/BlenderLoader.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/code/BlenderLoader.cpp b/code/BlenderLoader.cpp index 33175b05c..80a5a0187 100644 --- a/code/BlenderLoader.cpp +++ b/code/BlenderLoader.cpp @@ -559,24 +559,26 @@ void BlenderImporter::BuildMaterials(ConversionData& conv_data) if (mesh->mMaterialIndex == static_cast( -1 )) { if (index == static_cast( -1 )) { - - // ok, we need to add a dedicated default material for some poor material-less meshes + // Setup a default material. boost::shared_ptr p(new Material()); + ai_assert(::strlen(AI_DEFAULT_MATERIAL_NAME) < sizeof(p->id.name)-2); strcpy( p->id.name+2, AI_DEFAULT_MATERIAL_NAME ); + // Note: MSVC11 does not zero-initialize Material here, although it should. + // Thus all relevant fields should be explicitly initialized. We cannot add + // a default constructor to Material since the DNA codegen does not support + // parsing it. p->r = p->g = p->b = 0.6f; p->specr = p->specg = p->specb = 0.6f; p->ambr = p->ambg = p->ambb = 0.0f; p->mirr = p->mirg = p->mirb = 0.0f; p->emit = 0.f; p->alpha = 0.f; - - // XXX add more / or add default c'tor to Material + p->har = 0; index = static_cast( conv_data.materials_raw.size() ); conv_data.materials_raw.push_back(p); - - LogInfo("Adding default material ..."); + LogInfo("Adding default material"); } mesh->mMaterialIndex = index; } @@ -591,6 +593,7 @@ void BlenderImporter::BuildMaterials(ConversionData& conv_data) aiMaterial* mout = new aiMaterial(); conv_data.materials->push_back(mout); + // For any new material field handled here, the default material above must be updated with an appropriate default value. // set material name aiString name = aiString(mat->id.name+2); // skip over the name prefix 'MA' @@ -1049,7 +1052,7 @@ aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, Convers if (object->parent == obj) { children.push_back(object); - conv_data.objects.erase(it++); + it = conv_data.objects.erase(it); continue; } ++it; From 3554301afec3b6d3583676fe43f91b5832bfecfb Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Sun, 15 Mar 2015 14:42:56 +0100 Subject: [PATCH 55/74] Better regression test logging. --- test/regression/run.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/test/regression/run.py b/test/regression/run.py index 1c73c227c..1898fb962 100644 --- a/test/regression/run.py +++ b/test/regression/run.py @@ -167,7 +167,7 @@ def process_dir(d, outfile_results, zipin, result): print("Processing directory " + d) for f in sorted(os.listdir(d)): fullpath = os.path.join(d, f) - if os.path.isdir(fullpath) and not f == ".svn": + if os.path.isdir(fullpath) and not f[:1] == '.': process_dir(fullpath, outfile_results, zipin, result) continue @@ -190,32 +190,26 @@ def process_dir(d, outfile_results, zipin, result): "regression database? Use gen_db.zip to re-generate.") continue - # Ignore extensions via settings.py configured list - # todo: Fix for multi dot extensions like .skeleton.xml - ext = os.path.splitext(fullpath)[1].lower() - if ext != "" and ext in settings.exclude_extensions: - continue - print("-"*60 + "\n " + os.path.realpath(fullpath) + " pp: " + pppreset) outfile_actual = prepare_output_dir(fullpath, filehash, "ACTUAL") outfile_expect = prepare_output_dir(fullpath, filehash, "EXPECT") outfile_results.write("assimp dump "+"-"*80+"\n") outfile_results.flush() - command = [assimp_bin_path, "dump", fullpath, outfile_actual, "-b", "-s", "-l" ] +\ pppreset.split() - r = subprocess.call(command, **shellparams) - print(r) + outfile_results.flush() if r and not failure: result.fail(fullpath, outfile_expect, pppreset, IMPORT_FAILURE, r) + outfile_results.write("Failed to import\n") continue elif failure and not r: result.fail(fullpath, outfile_expect, pppreset, EXPECTED_FAILURE_NOT_MET) + outfile_results.write("Expected import to fail\n") continue with open(outfile_expect, "wb") as s: @@ -227,21 +221,24 @@ def process_dir(d, outfile_results, zipin, result): except IOError: continue + outfile_results.write("Expected data length: {0}\n".format(len(input_expected))) + outfile_results.write("Actual data length: {0}\n".format(len(input_actual))) + failed = False if len(input_expected) != len(input_actual): result.fail(fullpath, outfile_expect, pppreset, DATABASE_LENGTH_MISMATCH, len(input_expected), len(input_actual)) - continue + # Still compare the dumps to see what the difference is + failed = True outfile_results.write("assimp cmpdump "+"-"*80+"\n") outfile_results.flush() - command = [ assimp_bin_path, 'cmpdump', outfile_actual, outfile_expect ] if subprocess.call(command, **shellparams) != 0: - result.fail(fullpath, outfile_expect, pppreset, DATABASE_VALUE_MISMATCH) + if not failed: + result.fail(fullpath, outfile_expect, pppreset, DATABASE_VALUE_MISMATCH) continue - result.ok(fullpath, pppreset, COMPARE_SUCCESS, - len(input_expected)) + result.ok(fullpath, pppreset, COMPARE_SUCCESS, len(input_expected)) # ------------------------------------------------------------------------------- def del_folder_with_contents(folder): From 09c15331c437318c7e64ee060231fb77270443a8 Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Sun, 15 Mar 2015 21:01:34 +0100 Subject: [PATCH 56/74] Improve error reporting during dump diffing. --- tools/assimp_cmd/CompareDump.cpp | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/tools/assimp_cmd/CompareDump.cpp b/tools/assimp_cmd/CompareDump.cpp index 33db0eb3d..5027a1fc4 100644 --- a/tools/assimp_cmd/CompareDump.cpp +++ b/tools/assimp_cmd/CompareDump.cpp @@ -257,16 +257,32 @@ private: /* read from both streams simult.*/ template void read(T& filla,T& fille) { if(1 != fread(&filla,sizeof(T),1,actual)) { - throw compare_fails_exception("Unexpected EOF reading ACTUAL"); + EOFActual(); } if(1 != fread(&fille,sizeof(T),1,expect)) { - throw compare_fails_exception("Unexpected EOF reading EXPECT"); + EOFExpect(); } } - private: + void EOFActual() { + std::stringstream ss; + throw compare_fails_exception((ss + << "Unexpected EOF reading ACTUAL.\nCurrent position in scene hierarchy is " + << print_hierarchy(),ss.str().c_str() + )); + } + + void EOFExpect() { + std::stringstream ss; + throw compare_fails_exception((ss + << "Unexpected EOF reading EXPECT.\nCurrent position in scene hierarchy is " + << print_hierarchy(),ss.str().c_str() + )); + } + + FILE *const actual, *const expect; typedef std::map PerChunkCounter; @@ -290,10 +306,10 @@ template <> void comparer_context :: read(aiString& filla,aiString& fi read(lena,lene); if(lena && 1 != fread(&filla.data,lena,1,actual)) { - throw compare_fails_exception("Unexpected EOF reading ACTUAL"); + EOFActual(); } if(lene && 1 != fread(&fille.data,lene,1,expect)) { - throw compare_fails_exception("Unexpected EOF reading ACTUAL"); + EOFExpect(); } fille.data[fille.length=static_cast(lene)] = '\0'; @@ -487,7 +503,7 @@ private: res|=fread(&actual.second,4,1,ctx.get_actual()) <<3u; if(res!=0xf) { - ctx.failure("I/OError reading chunk head, dumps are not well-defined",""); + ctx.failure("IO Error reading chunk head, dumps are malformed",""); } if (current.first != actual.first) { @@ -504,7 +520,7 @@ private: if (current.first != actual.first) { std::stringstream ss; ctx.failure((ss - <<"Chunk lenghts do not match. EXPECT: " + <<"Chunk lengths do not match. EXPECT: " < Date: Sun, 15 Mar 2015 23:31:58 +0100 Subject: [PATCH 57/74] Fix build after mis-merge. --- code/ColladaParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp index 19d269682..22d620f55 100644 --- a/code/ColladaParser.cpp +++ b/code/ColladaParser.cpp @@ -1984,7 +1984,7 @@ void ColladaParser::ReadIndexData( Mesh* pMesh) #ifdef ASSIMP_BUILD_DEBUG if (primType != Prim_TriFans && primType != Prim_TriStrips) { - ai_assert(actualPrimitives == numPrimitives) + ai_assert(actualPrimitives == numPrimitives); } #endif From 7ee5eaafc60b78584ee4c7c8f283671b2d6b0f17 Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Sun, 15 Mar 2015 23:40:29 +0100 Subject: [PATCH 58/74] Fix compile: set::erase() returns void prior to C++11. --- code/BlenderLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/BlenderLoader.cpp b/code/BlenderLoader.cpp index 80a5a0187..c9419b400 100644 --- a/code/BlenderLoader.cpp +++ b/code/BlenderLoader.cpp @@ -1052,7 +1052,7 @@ aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, Convers if (object->parent == obj) { children.push_back(object); - it = conv_data.objects.erase(it); + conv_data.objects.erase(it++); continue; } ++it; From dbc7dc005d83dc6ed144b819382198b6828a6c5f Mon Sep 17 00:00:00 2001 From: ulf Date: Mon, 16 Mar 2015 11:35:07 +0100 Subject: [PATCH 59/74] - IfcLoader now evaluates all curve segments at both start and end. Leads to a lot of duplicates which get filtered out afterwards, but fixes the "cutting corners" phenomen that sometimes appeared in conjunction with trimmed curves --- code/IFCCurve.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/IFCCurve.cpp b/code/IFCCurve.cpp index ffe52dfd2..e696bed9a 100644 --- a/code/IFCCurve.cpp +++ b/code/IFCCurve.cpp @@ -648,10 +648,10 @@ void Curve :: SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const ai_assert(InRange(a) && InRange(b)); const size_t cnt = std::max(static_cast(0),EstimateSampleCount(a,b)); - out.verts.reserve( out.verts.size() + cnt ); + out.verts.reserve( out.verts.size() + cnt + 1); IfcFloat p = a, delta = (b-a)/cnt; - for(size_t i = 0; i < cnt; ++i, p += delta) { + for(size_t i = 0; i <= cnt; ++i, p += delta) { out.verts.push_back(Eval(p)); } } From 207906f0393e829fffc4260129e0f6fb681f585f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 16 Mar 2015 20:34:42 +0100 Subject: [PATCH 60/74] first parsing of references and geometry objects. Signed-off-by: Kim Kulling --- code/OpenGEXImporter.cpp | 47 +++++++++++++++++-- code/OpenGEXImporter.h | 6 +++ contrib/openddlparser/code/OpenDDLParser.cpp | 6 +-- .../include/openddlparser/OpenDDLCommon.h | 4 +- .../openddlparser/OpenDDLParserUtils.h | 4 +- 5 files changed, 58 insertions(+), 9 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 0b940d39e..9c953b035 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -164,7 +164,9 @@ USE_ODDLPARSER_NS //------------------------------------------------------------------------------------------------ OpenGEXImporter::OpenGEXImporter() -: m_ctx( NULL ) +: m_meshCache() +, m_mesh2refMap() +, m_ctx( NULL ) , m_currentNode( NULL ) { // empty } @@ -205,6 +207,8 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce m_ctx = myParser.getContext(); handleNodes( m_ctx->m_root, pScene ); } + + resolveReferences(); } //------------------------------------------------------------------------------------------------ @@ -238,6 +242,9 @@ void OpenGEXImporter::handleNodes( DDLNode *node, aiScene *pScene ) { break; case Grammar::ObjectRefToken: + handleObjectRefNode( *it, pScene ); + break; + case Grammar::MaterialRefToken: case Grammar::MetricKeyToken: case Grammar::GeometryNodeToken: @@ -245,6 +252,9 @@ void OpenGEXImporter::handleNodes( DDLNode *node, aiScene *pScene ) { break; case Grammar::GeometryObjectToken: + handleGeometryObject( *it, pScene ); + break; + case Grammar::TransformToken: case Grammar::MeshToken: case Grammar::VertexArrayToken: @@ -295,7 +305,7 @@ void OpenGEXImporter::handleMetricNode( DDLNode *node, aiScene *pScene ) { } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleNameNode( ODDLParser::DDLNode *node, aiScene *pScene ) { +void OpenGEXImporter::handleNameNode( DDLNode *node, aiScene *pScene ) { if( NULL == m_currentNode ) { throw DeadlyImportError( "No parent node for name." ); return; @@ -313,11 +323,42 @@ void OpenGEXImporter::handleNameNode( ODDLParser::DDLNode *node, aiScene *pScene } //------------------------------------------------------------------------------------------------ -void OpenGEXImporter::handleGeometryNode( ODDLParser::DDLNode *node, aiScene *pScene ) { +void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene *pScene ) { + if( NULL == m_currentNode ) { + throw DeadlyImportError( "No parent node for name." ); + return; + } + + Reference *ref = node->getReferences(); + if( NULL != ref ) { + for( size_t i = 0; i < ref->m_numRefs; i++ ) { + Name *currentName( ref->m_referencedName[ i ] ); + } + } +} + +//------------------------------------------------------------------------------------------------ +void OpenGEXImporter::handleGeometryNode( DDLNode *node, aiScene *pScene ) { m_currentNode = new aiNode; handleNodes( node, pScene ); } +//------------------------------------------------------------------------------------------------ +void OpenGEXImporter::handleGeometryObject( DDLNode *node, aiScene *pScene ) { + aiMesh *currentMesh( new aiMesh ); + const size_t idx( m_meshCache.size() ); + m_meshCache.push_back( currentMesh ); + + // store name to reference relation + m_mesh2refMap[ node->getName() ] = idx; + +} + +//------------------------------------------------------------------------------------------------ +void OpenGEXImporter::resolveReferences() { + +} + //------------------------------------------------------------------------------------------------ } // Namespace OpenGEX diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index 2bea23b45..105716f3c 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "BaseImporter.h" +#include namespace ODDLParser { class DDLNode; @@ -100,9 +101,14 @@ protected: void handleNodes( ODDLParser::DDLNode *node, aiScene *pScene ); void handleMetricNode( ODDLParser::DDLNode *node, aiScene *pScene ); void handleNameNode( ODDLParser::DDLNode *node, aiScene *pScene ); + void handleObjectRefNode( ODDLParser::DDLNode *node, aiScene *pScene ); void handleGeometryNode( ODDLParser::DDLNode *node, aiScene *pScene ); + void handleGeometryObject( ODDLParser::DDLNode *node, aiScene *pScene ); + void resolveReferences(); private: + std::vector m_meshCache; + std::map m_mesh2refMap; ODDLParser::Context *m_ctx; MetricInfo m_metrics[ MetricInfo::Max ]; aiNode *m_currentNode; diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index 46eb50fd8..99274f412 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -65,7 +65,7 @@ namespace Grammar { } // Namespace Grammar -static void logInvalidTokenError( char *in, char *exp, OpenDDLParser::logCallback callback ) { +static void logInvalidTokenError( char *in, const std::string &exp, OpenDDLParser::logCallback callback ) { std::stringstream stream; stream << "Invalid token " << *in << ", " << exp << " expected." << std::endl; callback( ddl_error_msg, stream.str() ); @@ -297,7 +297,7 @@ char *OpenDDLParser::parseStructure( char *in, char *end ) { } else { in++; - logInvalidTokenError( in, "{", m_logCallback ); + logInvalidTokenError( in, std::string( Grammar::OpenBracketToken ), m_logCallback ); error = true; return in; } @@ -365,7 +365,7 @@ char *OpenDDLParser::parseStructureBody( char *in, char *end, bool &error ) { in = getNextToken( in, end ); if( *in != '}' ) { - logInvalidTokenError( in, "}", m_logCallback ); + logInvalidTokenError( in, std::string( Grammar::CloseBracketToken ), m_logCallback ); } else { //in++; } diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h index c7fd844a2..4a1ff2749 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h @@ -35,7 +35,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # ifdef OPENDDLPARSER_BUILD # define DLL_ODDLPARSER_EXPORT TAG_DLL_EXPORT # else -# define DLL_ODDLPARSER_EXPORT TAG_DLL_IMPORT +# define DLL_ODDLPARSER_EXPORT TAG_DLL_IMPORT # endif // OPENDDLPARSER_BUILD # pragma warning( disable : 4251 ) #else @@ -52,7 +52,7 @@ BEGIN_ODDLPARSER_NS # define ddl_nullptr nullptr #else # define ddl_nullptr NULL -#endif +#endif // OPENDDL_NO_USE_CPP11 class DDLNode; class Value; diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h b/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h index b1b4f6cde..eb900309c 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h @@ -202,7 +202,9 @@ bool isReference( T *in, T *end ) { if( *in == 'r' ) { if( *(in+1) == 'e' ) { if( *(in+2) == 'f' ) { - return true; + if( ( in + 2 ) != end ) { + return true; + } } } } From adc44d1e83e153a942bcdf9c3527d44fab1ddde8 Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Mon, 16 Mar 2015 23:09:41 +0100 Subject: [PATCH 61/74] If caller provides no ExportProperties, pass a pointer to empty properties to the Exporter implementation (instead of a NULL). --- code/Exporter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 203070f8f..c6fea8856 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -397,7 +397,8 @@ aiReturn Exporter :: Export( const aiScene* pScene, const char* pFormatId, const proc.Execute(scenecopy.get()); } - exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProperties); + ExportProperties emptyProperties; // Never pass NULL ExportProperties so Exporters don't have to worry. + exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProperties ? pProperties : &emptyProperties); } catch (DeadlyExportError& err) { pimpl->mError = err.what(); From 599a61cad23eabe306813906521d0e92f4e9febe Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Mon, 16 Mar 2015 23:20:00 +0100 Subject: [PATCH 62/74] Restrict travis build to master branch. We want the build badge on the front page to be always green. Branches can fail, that's fine, but currently this gives the impression that all of assimp is failing. Branches should do pull requests against master to get a CI build. Such PRs can be marked "do not merge" and updated indefinitely. --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4ec4c7697..b595600e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,10 @@ before_install: - sudo apt-get install cmake python3 +branches: + only: + - master + env: matrix: - LINUX=1 TRAVIS_NO_EXPORT=YES From b304e7b45c7868fa62a3eda67d36db045d1b6f94 Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Mon, 16 Mar 2015 23:26:41 +0100 Subject: [PATCH 63/74] Add missing break in switch statement in BlobIOSystem.h. Fixes #438. This probably didn't affect anyone for real since no on uses relative seeks. --- code/BlobIOSystem.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/BlobIOSystem.h b/code/BlobIOSystem.h index cd48b8826..3e4889cfd 100644 --- a/code/BlobIOSystem.h +++ b/code/BlobIOSystem.h @@ -120,9 +120,11 @@ public: { case aiOrigin_CUR: cursor += pOffset; + break; case aiOrigin_END: cursor = file_size - pOffset; + break; case aiOrigin_SET: cursor = pOffset; From 7d7ccfd6301449c47c884b2092032d42b81d8ec5 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 17 Mar 2015 14:24:28 +0100 Subject: [PATCH 64/74] fix compiler warning. Signed-off-by: Kim Kulling --- code/OpenGEXImporter.h | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index 105716f3c..0bc2378b7 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -65,10 +65,12 @@ struct MetricInfo { std::string m_stringValue; float m_floatValue; + int m_intValue; MetricInfo() : m_stringValue( "" ) - , m_floatValue( 0.0f ) { + , m_floatValue( 0.0f ) + , m_intValue( -1 ) { // empty } }; @@ -102,16 +104,33 @@ protected: void handleMetricNode( ODDLParser::DDLNode *node, aiScene *pScene ); void handleNameNode( ODDLParser::DDLNode *node, aiScene *pScene ); void handleObjectRefNode( ODDLParser::DDLNode *node, aiScene *pScene ); + void handleMaterialRefNode( ODDLParser::DDLNode *node, aiScene *pScene ); void handleGeometryNode( ODDLParser::DDLNode *node, aiScene *pScene ); void handleGeometryObject( ODDLParser::DDLNode *node, aiScene *pScene ); + void handleMaterial( ODDLParser::DDLNode *node, aiScene *pScene ); void resolveReferences(); private: + struct RefInfo { + enum Type { + MeshRef, + MaterialRef + }; + + aiNode *m_node; + Type m_type; + std::vector m_Names; + + RefInfo( aiNode *node, Type type, std::vector &names ); + }; + std::vector m_meshCache; std::map m_mesh2refMap; + ODDLParser::Context *m_ctx; MetricInfo m_metrics[ MetricInfo::Max ]; aiNode *m_currentNode; + std::vector m_unresolvedRefStack; }; } // Namespace OpenGEX From 0d5a2ce4c1106f74cd4c13da30234c91f2af05c3 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 17 Mar 2015 14:25:15 +0100 Subject: [PATCH 65/74] openddl-parser: latest greatest with fixes for invalid reference information. Signed-off-by: Kim Kulling --- contrib/openddlparser/code/DDLNode.cpp | 17 +++++++++++++++-- .../include/openddlparser/OpenDDLCommon.h | 10 +++++++--- .../include/openddlparser/OpenDDLParserUtils.h | 2 +- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/contrib/openddlparser/code/DDLNode.cpp b/contrib/openddlparser/code/DDLNode.cpp index e65f1c45e..24f0da9d1 100644 --- a/contrib/openddlparser/code/DDLNode.cpp +++ b/contrib/openddlparser/code/DDLNode.cpp @@ -44,6 +44,18 @@ static void releaseDataType( T *ptr ) { } } +static void releaseReferencedNames( Reference *ref ) { + if( ddl_nullptr == ref ) { + return; + } + + if( ref->m_referencedName ) { + for( size_t i = 0; i < ref->m_numRefs; i++ ) { + delete ref->m_referencedName; + } + } +} + DDLNode::DDLNode( const std::string &type, const std::string &name, size_t idx, DDLNode *parent ) : m_type( type ) , m_name( name ) @@ -51,9 +63,9 @@ DDLNode::DDLNode( const std::string &type, const std::string &name, size_t idx, , m_children() , m_properties( ddl_nullptr ) , m_value( ddl_nullptr ) -, m_idx( idx ) , m_dtArrayList( ddl_nullptr ) -, m_references( ddl_nullptr ) { +, m_references( ddl_nullptr ) +, m_idx( idx ) { if( m_parent ) { m_parent->m_children.push_back( this ); } @@ -62,6 +74,7 @@ DDLNode::DDLNode( const std::string &type, const std::string &name, size_t idx, DDLNode::~DDLNode() { releaseDataType( m_properties ); releaseDataType( m_value ); + releaseReferencedNames( m_references ); delete m_dtArrayList; m_dtArrayList = ddl_nullptr; diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h index 4a1ff2749..ff182432d 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h @@ -96,8 +96,12 @@ struct Reference { Reference( size_t numrefs, Name **names ) : m_numRefs( numrefs ) - , m_referencedName( names ) { - // empty + , m_referencedName( ddl_nullptr ) { + m_referencedName = new Name *[ numrefs ]; + for( size_t i = 0; i < numrefs; i++ ) { + Name *name = new Name( names[ i ]->m_type, names[ i ]->m_id ); + m_referencedName[ i ] = name; + } } }; @@ -105,7 +109,7 @@ struct Identifier { size_t m_len; char *m_buffer; - Identifier( size_t len, char *buffer ) + Identifier( size_t len, char buffer[] ) : m_len( len ) , m_buffer( buffer ) { // empty diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h b/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h index eb900309c..a8d21b710 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h @@ -173,7 +173,7 @@ bool isFloat( T *in, T *end ) { template inline bool isCharacter( const T in ) { - return ( in >= 'a' && in <= 'z' || in >= 'A' && in <= 'Z' ); + return ( ( in >= 'a' && in <= 'z' ) || ( in >= 'A' && in <= 'Z' ) ); } template From 59633d172f042ca28de0761820245bb58de3edb6 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 17 Mar 2015 14:25:40 +0100 Subject: [PATCH 66/74] implementation for mesh resolving. Signed-off-by: Kim Kulling --- code/OpenGEXImporter.cpp | 99 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 7 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 9c953b035..8f9a359ae 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -162,12 +162,21 @@ namespace OpenGEX { USE_ODDLPARSER_NS +//------------------------------------------------------------------------------------------------ +OpenGEXImporter::RefInfo::RefInfo( aiNode *node, Type type, std::vector &names ) +: m_node( node ) +, m_type( type ) +, m_Names( names ) { + // empty +} + //------------------------------------------------------------------------------------------------ OpenGEXImporter::OpenGEXImporter() : m_meshCache() , m_mesh2refMap() , m_ctx( NULL ) -, m_currentNode( NULL ) { +, m_currentNode( NULL ) +, m_unresolvedRefStack() { // empty } @@ -246,7 +255,12 @@ void OpenGEXImporter::handleNodes( DDLNode *node, aiScene *pScene ) { break; case Grammar::MaterialRefToken: + handleMaterialRefNode( *it, pScene ); + break; + case Grammar::MetricKeyToken: + break; + case Grammar::GeometryNodeToken: handleGeometryNode( *it, pScene ); break; @@ -256,12 +270,27 @@ void OpenGEXImporter::handleNodes( DDLNode *node, aiScene *pScene ) { break; case Grammar::TransformToken: + break; + case Grammar::MeshToken: + break; + case Grammar::VertexArrayToken: + break; + case Grammar::IndexArrayToken: + break; + case Grammar::MaterialToken: + handleMaterial( *it, pScene ); + break; + case Grammar::ColorToken: + break; + case Grammar::TextureToken: + break; + default: break; } @@ -290,7 +319,7 @@ void OpenGEXImporter::handleMetricNode( DDLNode *node, aiScene *pScene ) { if( Value::ddl_float == val->m_type ) { m_metrics[ type ].m_floatValue = val->getFloat(); } else if( Value::ddl_int32 == val->m_type ) { - m_metrics[ type ].m_floatValue = val->getInt32(); + m_metrics[ type ].m_intValue = val->getInt32(); } else if( Value::ddl_string == val->m_type ) { m_metrics[type].m_stringValue = std::string( val->getString() ); } else { @@ -322,6 +351,24 @@ void OpenGEXImporter::handleNameNode( DDLNode *node, aiScene *pScene ) { } } +//------------------------------------------------------------------------------------------------ +static void getRefNames( DDLNode *node, std::vector &names ) { + ai_assert( NULL != node ); + + Reference *ref = node->getReferences(); + if( NULL != ref ) { + for( size_t i = 0; i < ref->m_numRefs; i++ ) { + Name *currentName( ref->m_referencedName[ i ] ); + if( NULL != currentName && NULL != currentName->m_id ) { + const std::string name( currentName->m_id->m_buffer ); + if( !name.empty() ) { + names.push_back( name ); + } + } + } + } +} + //------------------------------------------------------------------------------------------------ void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene *pScene ) { if( NULL == m_currentNode ) { @@ -329,11 +376,26 @@ void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene *pScene ) { return; } - Reference *ref = node->getReferences(); - if( NULL != ref ) { - for( size_t i = 0; i < ref->m_numRefs; i++ ) { - Name *currentName( ref->m_referencedName[ i ] ); - } + std::vector objRefNames; + getRefNames( node, objRefNames ); + m_currentNode->mNumMeshes = objRefNames.size(); + m_currentNode->mMeshes = new unsigned int[ objRefNames.size() ]; + if( !objRefNames.empty() ) { + m_unresolvedRefStack.push_back( new RefInfo( m_currentNode, RefInfo::MeshRef, objRefNames ) ); + } +} + +//------------------------------------------------------------------------------------------------ +void OpenGEXImporter::handleMaterialRefNode( ODDLParser::DDLNode *node, aiScene *pScene ) { + if( NULL == m_currentNode ) { + throw DeadlyImportError( "No parent node for name." ); + return; + } + + std::vector matRefNames; + getRefNames( node, matRefNames ); + if( !matRefNames.empty() ) { + m_unresolvedRefStack.push_back( new RefInfo( m_currentNode, RefInfo::MaterialRef, matRefNames ) ); } } @@ -351,12 +413,35 @@ void OpenGEXImporter::handleGeometryObject( DDLNode *node, aiScene *pScene ) { // store name to reference relation m_mesh2refMap[ node->getName() ] = idx; +} + +//------------------------------------------------------------------------------------------------ +void OpenGEXImporter::handleMaterial( ODDLParser::DDLNode *node, aiScene *pScene ) { } //------------------------------------------------------------------------------------------------ void OpenGEXImporter::resolveReferences() { + if( m_unresolvedRefStack.empty() ) { + return; + } + RefInfo *currentRefInfo( NULL ); + for( std::vector::iterator it = m_unresolvedRefStack.begin(); it != m_unresolvedRefStack.end(); ++it ) { + currentRefInfo = *it; + if( NULL != currentRefInfo ) { + aiNode *node( currentRefInfo->m_node ); + if( RefInfo::MeshRef == currentRefInfo->m_type ) { + for( size_t i = 0; i < currentRefInfo->m_Names.size(); i++ ) { + const std::string &name(currentRefInfo->m_Names[ i ] ); + unsigned int meshIdx = m_mesh2refMap[ name ]; + node->mMeshes[ i ] = meshIdx; + } + } else if( RefInfo::MaterialRef == currentRefInfo->m_type ) { + // ToDo + } + } + } } //------------------------------------------------------------------------------------------------ From 05208a5014bfc029cab3c5b0834c94aa6c22c35a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 17 Mar 2015 19:43:41 +0100 Subject: [PATCH 67/74] try to fix build of win32 crosscompile. Signed-off-by: Kim Kulling --- contrib/openddlparser/include/openddlparser/OpenDDLCommon.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h index ff182432d..87209af86 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h @@ -29,7 +29,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -#ifdef _WIN32 +#ifdef _MSC_VER # define TAG_DLL_EXPORT __declspec(dllexport) # define TAG_DLL_IMPORT __declspec(dllimport ) # ifdef OPENDDLPARSER_BUILD From f15d62194b599cf5c925e6f34aeffb036f6f1052 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 18 Mar 2015 21:37:57 +0100 Subject: [PATCH 68/74] add nodestack to deal with node hierarchy. Signed-off-by: Kim Kulling --- code/OpenGEXImporter.cpp | 55 +++++++++++++++++++++++++++++++++++++++- code/OpenGEXImporter.h | 5 ++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 8f9a359ae..4172ae97b 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -176,6 +176,7 @@ OpenGEXImporter::OpenGEXImporter() , m_mesh2refMap() , m_ctx( NULL ) , m_currentNode( NULL ) +, m_nodeStack() , m_unresolvedRefStack() { // empty } @@ -214,6 +215,8 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce bool success( myParser.parse() ); if( success ) { m_ctx = myParser.getContext(); + pScene->mRootNode = new aiNode; + pScene->mRootNode->mName.Set( filename ); handleNodes( m_ctx->m_root, pScene ); } @@ -401,8 +404,12 @@ void OpenGEXImporter::handleMaterialRefNode( ODDLParser::DDLNode *node, aiScene //------------------------------------------------------------------------------------------------ void OpenGEXImporter::handleGeometryNode( DDLNode *node, aiScene *pScene ) { - m_currentNode = new aiNode; + aiNode *newNode = new aiNode; + pushNode( newNode, pScene ); + m_currentNode = newNode; handleNodes( node, pScene ); + + popNode(); } //------------------------------------------------------------------------------------------------ @@ -413,6 +420,10 @@ void OpenGEXImporter::handleGeometryObject( DDLNode *node, aiScene *pScene ) { // store name to reference relation m_mesh2refMap[ node->getName() ] = idx; + + // todo: child nodes? + + handleNodes( node, pScene ); } //------------------------------------------------------------------------------------------------ @@ -444,6 +455,48 @@ void OpenGEXImporter::resolveReferences() { } } +//------------------------------------------------------------------------------------------------ +void OpenGEXImporter::pushNode( aiNode *node, aiScene *pScene ) { + ai_assert( NULL != pScene ); + + if( NULL != node ) { + if( m_nodeStack.empty() ) { + node->mParent = pScene->mRootNode; + } else { + aiNode *parent( m_nodeStack.back() ); + ai_assert( NULL != parent ); + node->mParent = parent; + } + m_nodeStack.push_back( node ); + } +} + +//------------------------------------------------------------------------------------------------ +aiNode *OpenGEXImporter::popNode() { + if( m_nodeStack.empty() ) { + return NULL; + } + + aiNode *node( top() ); + m_nodeStack.pop_back(); + + return node; +} + +//------------------------------------------------------------------------------------------------ +aiNode *OpenGEXImporter::top() const { + if( m_nodeStack.empty() ) { + return NULL; + } + + return m_nodeStack.back(); +} + +//------------------------------------------------------------------------------------------------ +void OpenGEXImporter::clearNodeStack() { + m_nodeStack.clear(); +} + //------------------------------------------------------------------------------------------------ } // Namespace OpenGEX diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index 0bc2378b7..13154e06d 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -109,6 +109,10 @@ protected: void handleGeometryObject( ODDLParser::DDLNode *node, aiScene *pScene ); void handleMaterial( ODDLParser::DDLNode *node, aiScene *pScene ); void resolveReferences(); + void pushNode( aiNode *node, aiScene *pScene ); + aiNode *popNode(); + aiNode *top() const; + void clearNodeStack(); private: struct RefInfo { @@ -130,6 +134,7 @@ private: ODDLParser::Context *m_ctx; MetricInfo m_metrics[ MetricInfo::Max ]; aiNode *m_currentNode; + std::vector m_nodeStack; std::vector m_unresolvedRefStack; }; From b0753c38de6a51d8da87172faf38377bf73d9312 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 19 Mar 2015 11:37:11 +0100 Subject: [PATCH 69/74] fix code reading finding: rename definition. Signed-off-by: Kim Kulling --- code/OpenGEXImporter.cpp | 4 ++-- code/OpenGEXImporter.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 4172ae97b..c495773a9 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -37,7 +37,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -#ifndef ASSIMP_BUILD_NO_OPEMGEX_IMPORTER +#ifndef ASSIMP_BUILD_NO_OPENGEX_IMPORTER #include "AssimpPCH.h" #include "OpenGEXImporter.h" @@ -502,4 +502,4 @@ void OpenGEXImporter::clearNodeStack() { } // Namespace OpenGEX } // Namespace Assimp -#endif // ASSIMP_BUILD_NO_OPEMGEX_IMPORTER +#endif // ASSIMP_BUILD_NO_OPENGEX_IMPORTER diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index 13154e06d..5355f0dea 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -40,7 +40,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef AI_OPENGEX_IMPORTER_H #define AI_OPENGEX_IMPORTER_H -#ifndef ASSIMP_BUILD_NO_OPEMGEX_IMPORTER +#ifndef ASSIMP_BUILD_NO_OPENGEX_IMPORTER #include "BaseImporter.h" @@ -141,6 +141,6 @@ private: } // Namespace OpenGEX } // Namespace Assimp -#endif // ASSIMP_BUILD_NO_OPEMGEX_IMPORTER +#endif // ASSIMP_BUILD_NO_OPENGEX_IMPORTER #endif // AI_OPENGEX_IMPORTER_H From 12efa1477720011b64e6ff7021d12e6b3958d468 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 19 Mar 2015 11:37:58 +0100 Subject: [PATCH 70/74] add openddl_parser directly to assimp. Signed-off-by: Kim Kulling --- CMakeLists.txt | 4 ---- code/CMakeLists.txt | 18 ++++++++++++++++++ tools/assimp_cmd/CMakeLists.txt | 2 +- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 231166824..8a3a325be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,10 +156,6 @@ else(NOT ZLIB_FOUND) endif(NOT ZLIB_FOUND) INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR}) -add_subdirectory( contrib/openddlparser ) -INCLUDE_DIRECTORIES( contrib/openddlparser/include ) -SET( OPENDDL_PARSER_LIBRARIES openddl_parser ) - # Search for unzip if (PKG_CONFIG_FOUND) PKG_CHECK_MODULES(UNZIP minizip) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 3286d4a1d..e14215838 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -640,6 +640,18 @@ SET( unzip_SRCS ) SOURCE_GROUP( unzip FILES ${unzip_SRCS}) +SET ( openddl_parser_SRCS + ../contrib/openddlparser/code/OpenDDLParser.cpp + ../contrib/openddlparser/code/DDLNode.cpp + ../contrib/openddlparser/code/Value.cpp + ../contrib/openddlparser/include/openddlparser/OpenDDLParser.h + ../contrib/openddlparser/include/openddlparser/OpenDDLParserUtils.h + ../contrib/openddlparser/include/openddlparser/OpenDDLCommon.h + ../contrib/openddlparser/include/openddlparser/DDLNode.h + ../contrib/openddlparser/include/openddlparser/Value.h +) +SOURCE_GROUP( openddl_parser FILES ${openddl_parser_SRCS}) + # VC2010 fixes if(MSVC10) option( VC10_STDINT_FIX "Fix for VC10 Compiler regarding pstdint.h redefinition errors" OFF ) @@ -719,6 +731,7 @@ SET( assimp_src ${unzip_compile_SRCS} ${Poly2Tri_SRCS} ${Clipper_SRCS} + ${openddl_parser_SRCS} # Necessary to show the headers in the project when using the VC++ generator: ${Boost_SRCS} @@ -730,6 +743,11 @@ SET( assimp_src # Moreover it's a drag to recompile assimp entirely each time a modification is made to one of the included header, which is definitely counter-productive.) AssimpPCH.cpp ) +add_definitions( -DOPENDDLPARSER_BUILD ) + +INCLUDE_DIRECTORIES( + ../contrib/openddlparser/include +) IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER) SET( assimp_src ${assimp_src} ${C4D_SRCS}) diff --git a/tools/assimp_cmd/CMakeLists.txt b/tools/assimp_cmd/CMakeLists.txt index 78b3139ee..6857a0ba2 100644 --- a/tools/assimp_cmd/CMakeLists.txt +++ b/tools/assimp_cmd/CMakeLists.txt @@ -28,7 +28,7 @@ IF( WIN32 ) MAIN_DEPENDENCY assimp) ENDIF( WIN32 ) -TARGET_LINK_LIBRARIES( assimp_cmd assimp ${ZLIB_LIBRARIES} openddl_parser) +TARGET_LINK_LIBRARIES( assimp_cmd assimp ${ZLIB_LIBRARIES} ) SET_TARGET_PROPERTIES( assimp_cmd PROPERTIES OUTPUT_NAME assimp ) From 70aafa478d330543ccbe44dd9462d1ddc2fa2f84 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 19 Mar 2015 11:38:15 +0100 Subject: [PATCH 71/74] avoid copying of private struct. Signed-off-by: Kim Kulling --- code/OpenGEXImporter.cpp | 5 +++++ code/OpenGEXImporter.h | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index c495773a9..fcb15e587 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -170,6 +170,11 @@ OpenGEXImporter::RefInfo::RefInfo( aiNode *node, Type type, std::vector m_Names; RefInfo( aiNode *node, Type type, std::vector &names ); + ~RefInfo(); + + private: + RefInfo( const RefInfo & ); + RefInfo &operator = ( const RefInfo & ); }; std::vector m_meshCache; From f7e5f3ec6b345b0f13bc0170bc7ce7c4c9c34027 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 19 Mar 2015 12:07:16 +0100 Subject: [PATCH 72/74] assimp: fix build with latest version of openddl-parser lib. Signed-off-by: Kim Kulling --- contrib/openddlparser/code/OpenDDLParser.cpp | 10 +++++----- .../include/openddlparser/OpenDDLCommon.h | 11 +++-------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index 99274f412..1050baeb4 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -81,7 +81,7 @@ static bool isIntegerType( Value::ValueType integerType ) { } static DDLNode *createDDLNode( Identifier *id, OpenDDLParser *parser ) { - if( nullptr == id || ddl_nullptr == parser ) { + if( ddl_nullptr == id || ddl_nullptr == parser ) { return ddl_nullptr; } @@ -132,7 +132,7 @@ OpenDDLParser::~OpenDDLParser() { } void OpenDDLParser::setLogCallback( logCallback callback ) { - if( nullptr != callback ) { + if( ddl_nullptr != callback ) { // install user-specific log callback m_logCallback = callback; } else { @@ -607,7 +607,7 @@ char *OpenDDLParser::parseBooleanLiteral( char *in, char *end, Value **boolean ) char *OpenDDLParser::parseIntegerLiteral( char *in, char *end, Value **integer, Value::ValueType integerType ) { *integer = ddl_nullptr; - if( nullptr == in || in == end ) { + if( ddl_nullptr == in || in == end ) { return in; } @@ -705,7 +705,7 @@ char *OpenDDLParser::parseStringLiteral( char *in, char *end, Value **stringData } static void createPropertyWithData( Identifier *id, Value *primData, Property **prop ) { - if( nullptr != primData ) { + if( ddl_nullptr != primData ) { ( *prop ) = new Property( id ); ( *prop )->m_primData = primData; } @@ -857,7 +857,7 @@ char *OpenDDLParser::parseDataArrayList( char *in, char *end, DataArrayList **da if( *in == '{' ) { in++; Value *current( ddl_nullptr ); - Reference *refs( nullptr ); + Reference *refs( ddl_nullptr ); DataArrayList *prev( ddl_nullptr ), *currentDataList( ddl_nullptr ); do { in = parseDataList( in, end, ¤t, &refs ); diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h index 87209af86..40e54ea4b 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLCommon.h @@ -74,7 +74,7 @@ enum NameType { }; struct Name { - NameType m_type; + NameType m_type; Identifier *m_id; Name( NameType type, Identifier *id ) @@ -85,8 +85,8 @@ struct Name { }; struct Reference { - size_t m_numRefs; - Name **m_referencedName; + size_t m_numRefs; + Name **m_referencedName; Reference() : m_numRefs( 0 ) @@ -166,10 +166,5 @@ struct BufferIt { END_ODDLPARSER_NS -#define ODDL_NO_COPYING( classname ) \ -private: \ - classname( const classname & ); \ - classname &operator = ( const classname & ); - #endif // OPENDDLPARSER_OPENDDLPARSERCOMMON_H_INC From 40369010c36ce8c2fb8c7f34df8c7e13c28e433c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 19 Mar 2015 12:14:17 +0100 Subject: [PATCH 73/74] fix usage of c++11 type nullptr. Signed-off-by: Kim Kulling --- contrib/openddlparser/code/OpenDDLParser.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index 1050baeb4..4b9313a42 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -218,7 +218,7 @@ static void dumpId( Identifier *id ) { } char *OpenDDLParser::parseHeader( char *in, char *end ) { - if( nullptr == in || in == end ) { + if( ddl_nullptr == in || in == end ) { return in; } @@ -259,7 +259,7 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { // store the node DDLNode *node( createDDLNode( id, this ) ); - if( nullptr != node ) { + if( ddl_nullptr != node ) { pushNode( node ); } else { std::cerr << "nullptr returned by creating DDLNode." << std::endl; @@ -282,7 +282,7 @@ char *OpenDDLParser::parseHeader( char *in, char *end ) { } char *OpenDDLParser::parseStructure( char *in, char *end ) { - if( nullptr == in || in == end ) { + if( ddl_nullptr == in || in == end ) { return in; } @@ -377,7 +377,7 @@ char *OpenDDLParser::parseStructureBody( char *in, char *end, bool &error ) { } void OpenDDLParser::pushNode( DDLNode *node ) { - if( nullptr == node ) { + if( ddl_nullptr == node ) { return; } @@ -405,7 +405,7 @@ DDLNode *OpenDDLParser::top() { } DDLNode *OpenDDLParser::getRoot() const { - if( nullptr == m_context ) { + if( ddl_nullptr == m_context ) { return ddl_nullptr; } @@ -766,7 +766,7 @@ char *OpenDDLParser::parseProperty( char *in, char *end, Property **prop ) { in = getNextToken( in, end ); Identifier *id( ddl_nullptr ); in = parseIdentifier( in, end, &id ); - if( nullptr != id ) { + if( ddl_nullptr != id ) { in = getNextToken( in, end ); if( *in == '=' ) { in++; From abe82a5d73e404d7ead988c87dc9460dc68a4620 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 19 Mar 2015 12:34:11 +0100 Subject: [PATCH 74/74] add missing include Signed-off-by: Kim Kulling --- contrib/openddlparser/code/OpenDDLParser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index 4b9313a42..186323730 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -26,6 +26,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include #ifdef _WIN32 # include