├── .gitignore ├── test ├── js │ ├── .gitignore │ ├── package.json │ ├── README.md │ ├── package-lock.json │ └── test │ │ └── validate.js ├── src │ ├── main.cpp │ └── COLLADA2GLTFWriterTest.cpp └── include │ └── COLLADA2GLTFWriterTest.h ├── GLTF ├── test │ ├── include │ │ ├── GLTFAssetTest.h │ │ ├── GLTFAccessorTest.h │ │ └── GLTFObjectTest.h │ └── src │ │ ├── main.cpp │ │ ├── GLTFAssetTest.cpp │ │ ├── GLTFObjectTest.cpp │ │ └── GLTFAccessorTest.cpp ├── include │ ├── GLTFExtension.h │ ├── Base64.h │ ├── GLTFScene.h │ ├── GLTFShader.h │ ├── GLTFBuffer.h │ ├── GLTFTexture.h │ ├── GLTFSkin.h │ ├── GLTFMesh.h │ ├── GLTFProgram.h │ ├── GLTFSampler.h │ ├── GLTFObject.h │ ├── GLTFDracoExtension.h │ ├── GLTFBufferView.h │ ├── GLTFCamera.h │ ├── GLTFOptions.h │ ├── GLTFImage.h │ ├── GLTFPrimitive.h │ ├── GLTFConstants.h │ ├── GLTFAnimation.h │ ├── GLTFTechnique.h │ ├── GLTFAccessor.h │ ├── GLTFNode.h │ ├── GLTFAsset.h │ └── GLTFMaterial.h ├── src │ ├── GLTFDracoExtension.cpp │ ├── GLTFScene.cpp │ ├── GLTFSampler.cpp │ ├── GLTFShader.cpp │ ├── GLTFBuffer.cpp │ ├── GLTFProgram.cpp │ ├── GLTFTexture.cpp │ ├── GLTFSkin.cpp │ ├── GLTFMesh.cpp │ ├── GLTFBufferView.cpp │ ├── GLTFCamera.cpp │ ├── GLTFObject.cpp │ ├── Base64.cpp │ ├── GLTFPrimitive.cpp │ ├── GLTFTechnique.cpp │ ├── GLTFAnimation.cpp │ ├── GLTFImage.cpp │ ├── GLTFAccessor.cpp │ └── GLTFNode.cpp └── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── include ├── COLLADA2GLTFOptions.h ├── COLLADA2GLTFExtrasHandler.h └── COLLADA2GLTFWriter.h ├── dependencies └── OpenCOLLADA │ ├── README.md │ └── modules │ └── COLLADASaxFrameworkLoader │ ├── COLLADAFramework │ ├── COLLADABaseUtils │ │ ├── pcre │ │ │ └── CMakeLists.txt │ │ └── CMakeLists.txt │ ├── MathMLSolver │ │ └── CMakeLists.txt │ └── CMakeLists.txt │ ├── GeneratedSaxParser │ ├── expat │ │ └── CMakeLists.txt │ ├── CMakeLists.txt │ └── LibXML │ │ ├── CMakeLists.txt │ │ └── include │ │ └── config-mac.h │ └── CMakeLists.txt ├── .gitmodules ├── src ├── COLLADA2GLTFExtrasHandler.cpp └── main.cpp ├── README.md ├── CHANGES.md ├── CMakeLists.txt ├── LICENSE.md └── .github └── workflows └── continuous-integration-workflow.yml /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /test/js/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /GLTF/test/include/GLTFAssetTest.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include "gtest/gtest.h" 5 | 6 | class GLTFAssetTest : public ::testing::Test {}; 7 | -------------------------------------------------------------------------------- /GLTF/test/include/GLTFAccessorTest.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include "gtest/gtest.h" 5 | 6 | class GLTFAccessorTest : public ::testing::Test {}; 7 | -------------------------------------------------------------------------------- /GLTF/include/GLTFExtension.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include "GLTFObject.h" 5 | 6 | namespace GLTF { 7 | class Extension : public GLTF::Object {}; 8 | } // namespace GLTF 9 | -------------------------------------------------------------------------------- /test/src/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "COLLADA2GLTFWriterTest.h" 3 | 4 | int main(int argc, char **argv) { 5 | ::testing::InitGoogleTest(&argc, argv); 6 | return RUN_ALL_TESTS(); 7 | } 8 | -------------------------------------------------------------------------------- /GLTF/test/src/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFAccessorTest.h" 3 | #include "GLTFObjectTest.h" 4 | 5 | int main(int argc, char **argv) { 6 | ::testing::InitGoogleTest(&argc, argv); 7 | return RUN_ALL_TESTS(); 8 | } 9 | -------------------------------------------------------------------------------- /GLTF/include/Base64.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | 6 | namespace Base64 { 7 | char* encode(unsigned char* data, size_t length); 8 | std::string decode(std::string uri); 9 | } // namespace Base64 10 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | A reminder that this issue tracker is managed by the Khronos Group. Interactions here should follow the Khronos Code of Conduct (https://www.khronos.org/developers/code-of-conduct), which prohibits aggressive or derogatory language. Please keep the discussion friendly and civil. 2 | -------------------------------------------------------------------------------- /GLTF/test/include/GLTFObjectTest.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include "GLTFOptions.h" 5 | #include "gtest/gtest.h" 6 | 7 | class GLTFObjectTest : public ::testing::Test { 8 | public: 9 | GLTF::Options* options; 10 | 11 | GLTFObjectTest(); 12 | ~GLTFObjectTest(); 13 | }; 14 | -------------------------------------------------------------------------------- /include/COLLADA2GLTFOptions.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | 6 | #include "GLTFOptions.h" 7 | 8 | namespace COLLADA2GLTF { 9 | class Options : public GLTF::Options { 10 | public: 11 | std::string inputPath; 12 | std::string basePath; 13 | std::string outputPath; 14 | bool invertTransparency = false; 15 | }; 16 | } // namespace COLLADA2GLTF 17 | -------------------------------------------------------------------------------- /GLTF/include/GLTFScene.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include "GLTFNode.h" 8 | #include "GLTFObject.h" 9 | 10 | namespace GLTF { 11 | class Scene : public GLTF::Object { 12 | public: 13 | std::vector nodes; 14 | 15 | virtual std::string typeName(); 16 | virtual void writeJSON(void* writer, GLTF::Options* options); 17 | }; 18 | } // namespace GLTF 19 | -------------------------------------------------------------------------------- /dependencies/OpenCOLLADA/README.md: -------------------------------------------------------------------------------- 1 | # OpenCOLLADA Modules 2 | The included `modules` directory contains trimmed cmake build targets for OpenCOLLADA modules, specifically `COLLADAFramework` and `COLLADABaseUtils`. 3 | 4 | Currently the OpenCOLLADA cmake build process requires global macros declared in the top-level cmake file, which is why it has been circumvented here. If that is corrected, these shims can be removed, and OpenCOLLADA cmake targets should be depended on directly. 5 | -------------------------------------------------------------------------------- /test/include/COLLADA2GLTFWriterTest.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include "COLLADA2GLTFWriter.h" 5 | #include "gtest/gtest.h" 6 | 7 | class COLLADA2GLTFWriterTest : public ::testing::Test { 8 | public: 9 | COLLADA2GLTF::Writer* writer; 10 | COLLADA2GLTF::Options* options; 11 | COLLADA2GLTF::ExtrasHandler* extrasHandler; 12 | GLTF::Asset* asset; 13 | 14 | COLLADA2GLTFWriterTest(); 15 | ~COLLADA2GLTFWriterTest(); 16 | }; 17 | -------------------------------------------------------------------------------- /GLTF/include/GLTFShader.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | 6 | #include "GLTFConstants.h" 7 | #include "GLTFObject.h" 8 | 9 | namespace GLTF { 10 | class Shader : public GLTF::Object { 11 | public: 12 | std::string source; 13 | GLTF::Constants::WebGL type; 14 | std::string uri; 15 | 16 | virtual std::string typeName(); 17 | virtual void writeJSON(void* writer, GLTF::Options* options); 18 | }; 19 | } // namespace GLTF 20 | -------------------------------------------------------------------------------- /GLTF/include/GLTFBuffer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | 6 | #include "GLTFObject.h" 7 | 8 | namespace GLTF { 9 | class Buffer : public GLTF::Object { 10 | public: 11 | unsigned char* data = NULL; 12 | int byteLength; 13 | std::string uri; 14 | 15 | Buffer(unsigned char* data, int dataLength); 16 | virtual ~Buffer(); 17 | 18 | virtual std::string typeName(); 19 | virtual void writeJSON(void* writer, GLTF::Options* options); 20 | }; 21 | } // namespace GLTF 22 | -------------------------------------------------------------------------------- /GLTF/include/GLTFTexture.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | 6 | #include "GLTFConstants.h" 7 | #include "GLTFImage.h" 8 | #include "GLTFObject.h" 9 | #include "GLTFSampler.h" 10 | 11 | namespace GLTF { 12 | class Texture : public GLTF::Object { 13 | public: 14 | GLTF::Sampler* sampler = NULL; 15 | GLTF::Image* source = NULL; 16 | 17 | virtual std::string typeName(); 18 | virtual void writeJSON(void* writer, GLTF::Options* options); 19 | }; 20 | } // namespace GLTF 21 | -------------------------------------------------------------------------------- /dependencies/OpenCOLLADA/modules/COLLADASaxFrameworkLoader/COLLADAFramework/COLLADABaseUtils/pcre/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | 3 | set(PROJECT_NAME pcre) 4 | set(BASE_PATH ../../../../../OpenCOLLADA) 5 | set(PROJECT_PATH ${BASE_PATH}/Externals/pcre) 6 | 7 | project(${PROJECT_NAME}) 8 | 9 | # pcre 10 | include_directories(${PROJECT_PATH}/include) 11 | file(GLOB HEADERS "${PROJECT_PATH}/include/*.h") 12 | file(GLOB SOURCES "${PROJECT_PATH}/src/*.c") 13 | 14 | add_library(${PROJECT_NAME} ${HEADERS} ${SOURCES}) 15 | -------------------------------------------------------------------------------- /test/js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "collada2gltf-test", 3 | "version": "1.0.0", 4 | "description": "Integration testing COLLADA2GLTF converter", 5 | "scripts": { 6 | "test": "mocha -r esm" 7 | }, 8 | "author": "Rob Taglang", 9 | "license": "BSD", 10 | "devDependencies": {}, 11 | "dependencies": { 12 | "bluebird": "^3.5.3", 13 | "chai": "^4.2.0", 14 | "chai-subset": "^1.6.0", 15 | "esm": "^3.1.0", 16 | "gltf-validator": "^2.0.0-dev.2.7", 17 | "mocha": "^5.2.0", 18 | "tmp": "0.0.33" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /GLTF/include/GLTFSkin.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include "GLTFAccessor.h" 8 | #include "GLTFObject.h" 9 | 10 | namespace GLTF { 11 | class Node; 12 | 13 | class Skin : public GLTF::Object { 14 | public: 15 | Accessor* inverseBindMatrices = NULL; 16 | Node* skeleton = NULL; 17 | std::vector joints; 18 | 19 | virtual std::string typeName(); 20 | virtual void writeJSON(void* writer, GLTF::Options* options); 21 | }; 22 | } // namespace GLTF 23 | -------------------------------------------------------------------------------- /GLTF/include/GLTFMesh.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include "GLTFObject.h" 8 | #include "GLTFPrimitive.h" 9 | 10 | namespace GLTF { 11 | class Mesh : public GLTF::Object { 12 | public: 13 | std::vector primitives; 14 | std::vector weights; 15 | 16 | virtual std::string typeName(); 17 | virtual GLTF::Object* clone(GLTF::Object* clone); 18 | virtual void writeJSON(void* writer, GLTF::Options* options); 19 | }; 20 | } // namespace GLTF 21 | -------------------------------------------------------------------------------- /GLTF/include/GLTFProgram.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "GLTFObject.h" 9 | #include "GLTFShader.h" 10 | 11 | namespace GLTF { 12 | class Program : public GLTF::Object { 13 | public: 14 | std::set attributes; 15 | GLTF::Shader* fragmentShader = NULL; 16 | GLTF::Shader* vertexShader = NULL; 17 | 18 | virtual std::string typeName(); 19 | virtual void writeJSON(void* writer, GLTF::Options* options); 20 | }; 21 | } // namespace GLTF 22 | -------------------------------------------------------------------------------- /dependencies/OpenCOLLADA/modules/COLLADASaxFrameworkLoader/GeneratedSaxParser/expat/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | 3 | set(PROJECT_NAME expat) 4 | set(BASE_PATH ../../../../OpenCOLLADA) 5 | set(PROJECT_PATH ${BASE_PATH}/Externals/expat) 6 | 7 | project(${PROJECT_NAME}) 8 | add_definitions(-DHAVE_MEMMOVE) 9 | 10 | # GeneratedSaxParser 11 | include_directories(${PROJECT_PATH}/lib) 12 | file(GLOB HEADERS "${PROJECT_PATH}/lib/*.h") 13 | file(GLOB SOURCES "${PROJECT_PATH}/lib/*.c") 14 | 15 | add_library(${PROJECT_NAME} ${HEADERS} ${SOURCES}) 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dependencies/OpenCOLLADA/OpenCOLLADA"] 2 | path = dependencies/OpenCOLLADA/OpenCOLLADA 3 | url = https://github.com/COLLADA2GLTF/OpenCOLLADA.git 4 | [submodule "dependencies/rapidjson"] 5 | path = GLTF/dependencies/rapidjson 6 | url = https://github.com/miloyip/rapidjson.git 7 | [submodule "GLTF/dependencies/googletest"] 8 | path = GLTF/dependencies/googletest 9 | url = https://github.com/google/googletest.git 10 | [submodule "dependencies/ahoy"] 11 | path = dependencies/ahoy 12 | url = https://github.com/lasalvavida/ahoy.git 13 | [submodule "dependencies/draco"] 14 | path = GLTF/dependencies/draco 15 | url = https://github.com/google/draco.git 16 | -------------------------------------------------------------------------------- /GLTF/include/GLTFSampler.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | 6 | #include "GLTFConstants.h" 7 | #include "GLTFObject.h" 8 | 9 | namespace GLTF { 10 | class Sampler : public GLTF::Object { 11 | public: 12 | GLTF::Constants::WebGL magFilter = GLTF::Constants::WebGL::LINEAR; 13 | GLTF::Constants::WebGL minFilter = 14 | GLTF::Constants::WebGL::NEAREST_MIPMAP_LINEAR; 15 | GLTF::Constants::WebGL wrapS = GLTF::Constants::WebGL::REPEAT; 16 | GLTF::Constants::WebGL wrapT = GLTF::Constants::WebGL::REPEAT; 17 | 18 | virtual std::string typeName(); 19 | virtual void writeJSON(void* writer, GLTF::Options* options); 20 | }; 21 | } // namespace GLTF 22 | -------------------------------------------------------------------------------- /GLTF/include/GLTFObject.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "GLTFOptions.h" 9 | 10 | namespace GLTF { 11 | class Extension; 12 | class Object { 13 | public: 14 | virtual ~Object(); 15 | 16 | int id = -1; 17 | std::string stringId; 18 | std::string name; 19 | std::map extensions; 20 | std::map extras; 21 | 22 | std::string getStringId(); 23 | virtual std::string typeName(); 24 | virtual GLTF::Object* clone(GLTF::Object* clone); 25 | virtual void writeJSON(void* writer, GLTF::Options* options); 26 | }; 27 | } // namespace GLTF 28 | -------------------------------------------------------------------------------- /GLTF/include/GLTFDracoExtension.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "GLTFAccessor.h" 10 | #include "GLTFBufferView.h" 11 | #include "GLTFConstants.h" 12 | #include "GLTFObject.h" 13 | #include "draco/compression/encode.h" 14 | 15 | namespace GLTF { 16 | class DracoExtension : public GLTF::Object { 17 | public: 18 | DracoExtension() : dracoMesh(new draco::Mesh()) {} 19 | GLTF::BufferView* bufferView = NULL; 20 | std::unordered_map attributeToId; 21 | 22 | std::unique_ptr dracoMesh; 23 | virtual void writeJSON(void* writer, GLTF::Options* options); 24 | }; 25 | } // namespace GLTF 26 | -------------------------------------------------------------------------------- /GLTF/src/GLTFDracoExtension.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFDracoExtension.h" 3 | 4 | #include 5 | 6 | #include "rapidjson/stringbuffer.h" 7 | #include "rapidjson/writer.h" 8 | 9 | void GLTF::DracoExtension::writeJSON(void* writer, GLTF::Options* options) { 10 | rapidjson::Writer* jsonWriter = 11 | (rapidjson::Writer*)writer; 12 | jsonWriter->Key("bufferView"); 13 | jsonWriter->Int(this->bufferView->id); 14 | jsonWriter->Key("attributes"); 15 | jsonWriter->StartObject(); 16 | for (const auto& attribute : this->attributeToId) { 17 | jsonWriter->Key(attribute.first.c_str()); 18 | jsonWriter->Int(attribute.second); 19 | } 20 | jsonWriter->EndObject(); 21 | } 22 | -------------------------------------------------------------------------------- /dependencies/OpenCOLLADA/modules/COLLADASaxFrameworkLoader/COLLADAFramework/MathMLSolver/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | 3 | set(PROJECT_NAME MathMLSolver) 4 | set(BASE_PATH ../../../../OpenCOLLADA) 5 | set(PROJECT_PATH ${BASE_PATH}/Externals/MathMLSolver) 6 | 7 | project(${PROJECT_NAME}) 8 | 9 | # MathMLSolver 10 | include_directories(${PROJECT_PATH}/include) 11 | file(GLOB HEADERS "${PROJECT_PATH}/include/*.h") 12 | file(GLOB SOURCES "${PROJECT_PATH}/src/*.cpp") 13 | 14 | # MathMLSolver AST 15 | include_directories(${PROJECT_PATH}/include/AST) 16 | file(GLOB AST_HEADERS "${PROJECT_PATH}/include/AST/*.h") 17 | file(GLOB AST_SOURCES "${PROJECT_PATH}/src/AST/*.cpp") 18 | 19 | add_library(${PROJECT_NAME} ${HEADERS} ${SOURCES} ${AST_HEADERS} ${AST_SOURCES}) 20 | -------------------------------------------------------------------------------- /GLTF/src/GLTFScene.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFScene.h" 3 | 4 | #include "rapidjson/stringbuffer.h" 5 | #include "rapidjson/writer.h" 6 | 7 | std::string GLTF::Scene::typeName() { return "scene"; } 8 | 9 | void GLTF::Scene::writeJSON(void* writer, GLTF::Options* options) { 10 | rapidjson::Writer* jsonWriter = 11 | (rapidjson::Writer*)writer; 12 | jsonWriter->Key("nodes"); 13 | jsonWriter->StartArray(); 14 | for (GLTF::Node* node : this->nodes) { 15 | if (options->version == "1.0") { 16 | jsonWriter->String(node->getStringId().c_str()); 17 | } else { 18 | jsonWriter->Int(node->id); 19 | } 20 | } 21 | jsonWriter->EndArray(); 22 | GLTF::Object::writeJSON(writer, options); 23 | } 24 | -------------------------------------------------------------------------------- /GLTF/src/GLTFSampler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFSampler.h" 3 | 4 | #include "rapidjson/stringbuffer.h" 5 | #include "rapidjson/writer.h" 6 | 7 | std::string GLTF::Sampler::typeName() { return "sampler"; } 8 | 9 | void GLTF::Sampler::writeJSON(void* writer, GLTF::Options* options) { 10 | rapidjson::Writer* jsonWriter = 11 | (rapidjson::Writer*)writer; 12 | jsonWriter->Key("magFilter"); 13 | jsonWriter->Int(static_cast(magFilter)); 14 | jsonWriter->Key("minFilter"); 15 | jsonWriter->Int(static_cast(minFilter)); 16 | jsonWriter->Key("wrapS"); 17 | jsonWriter->Int(static_cast(wrapS)); 18 | jsonWriter->Key("wrapT"); 19 | jsonWriter->Int(static_cast(wrapT)); 20 | GLTF::Object::writeJSON(writer, options); 21 | } 22 | -------------------------------------------------------------------------------- /GLTF/include/GLTFBufferView.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | 6 | #include "GLTFBuffer.h" 7 | #include "GLTFConstants.h" 8 | #include "GLTFObject.h" 9 | 10 | namespace GLTF { 11 | class BufferView : public GLTF::Object { 12 | public: 13 | GLTF::Buffer* buffer = NULL; 14 | int byteOffset = 0; 15 | int byteStride = 0; 16 | int byteLength = 0; 17 | GLTF::Constants::WebGL target = (GLTF::Constants::WebGL)-1; 18 | 19 | BufferView(int byteOffset, int byteLength, GLTF::Buffer* buffer); 20 | BufferView(unsigned char* data, int dataLength); 21 | BufferView(unsigned char* data, int dataLength, 22 | GLTF::Constants::WebGL target); 23 | 24 | virtual std::string typeName(); 25 | virtual void writeJSON(void* writer, GLTF::Options* options); 26 | }; 27 | } // namespace GLTF 28 | -------------------------------------------------------------------------------- /test/js/README.md: -------------------------------------------------------------------------------- 1 | # COLLADA2GLTF E2E Validation 2 | This subproject implements COLLADA2GLTF end-to-end validation. 3 | 4 | COLLADA source models are pulled from the 5 | [glTF-Sample-Models](https://github.com/KhronosGroup/glTF-Sample-Models) 6 | repository and the built converter binary is used to convert them to glTF. 7 | 8 | The 9 | [glTF-Validator](https://github.com/KhronosGroup/glTF-Validator) 10 | is then used to validate the converted models, to make sure that they: 11 | * Have the expected number of primitives 12 | * Use the expected glTF features: 13 | * Animations 14 | * Morph Targets 15 | * Skins 16 | * Textures 17 | * Have no: 18 | * Errors 19 | * Infos 20 | * Warnings 21 | 22 | These tests are implemented as a set of ES6 Mocha tests. You can run them 23 | with: 24 | 25 | ```bash 26 | npm install 27 | 28 | npm run test 29 | ``` 30 | -------------------------------------------------------------------------------- /dependencies/OpenCOLLADA/modules/COLLADASaxFrameworkLoader/GeneratedSaxParser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | 3 | set(PROJECT_NAME GeneratedSaxParser) 4 | set(BASE_PATH ../../../OpenCOLLADA) 5 | set(PROJECT_PATH ${BASE_PATH}/GeneratedSaxParser) 6 | 7 | project(${PROJECT_NAME}) 8 | 9 | # GeneratedSaxParser 10 | include_directories(${PROJECT_PATH}/include) 11 | file(GLOB HEADERS "${PROJECT_PATH}/include/*.h") 12 | file(GLOB SOURCES "${PROJECT_PATH}/src/*.cpp") 13 | 14 | # expat 15 | include_directories(${BASE_PATH}/Externals/expat/lib) 16 | add_subdirectory(expat) 17 | 18 | # LibXML 19 | if(NOT LIBXML2_FOUND) 20 | include_directories(${BASE_PATH}/Externals/LibXML/include) 21 | add_subdirectory(LibXML) 22 | else() 23 | include_directories(${LIBXML2_INCLUDE_DIR}) 24 | endif() 25 | 26 | add_library(${PROJECT_NAME} ${HEADERS} ${SOURCES}) 27 | target_link_libraries(${PROJECT_NAME} expat LibXML) 28 | -------------------------------------------------------------------------------- /GLTF/include/GLTFCamera.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | 6 | #include "GLTFObject.h" 7 | 8 | namespace GLTF { 9 | class Camera : public GLTF::Object { 10 | public: 11 | enum Type { PERSPECTIVE, ORTHOGRAPHIC, UNKNOWN }; 12 | Type type = Type::UNKNOWN; 13 | float zfar; 14 | float znear; 15 | 16 | virtual std::string typeName(); 17 | virtual void writeJSON(void* writer, GLTF::Options* options); 18 | }; 19 | 20 | class CameraOrthographic : public GLTF::Camera { 21 | public: 22 | float xmag; 23 | float ymag; 24 | 25 | CameraOrthographic() { type = Type::ORTHOGRAPHIC; } 26 | virtual void writeJSON(void* writer, GLTF::Options* options); 27 | }; 28 | 29 | class CameraPerspective : public GLTF::Camera { 30 | public: 31 | float aspectRatio = 0; 32 | float yfov; 33 | 34 | CameraPerspective() { type = Type::PERSPECTIVE; } 35 | virtual void writeJSON(void* writer, GLTF::Options* options); 36 | }; 37 | } // namespace GLTF 38 | -------------------------------------------------------------------------------- /dependencies/OpenCOLLADA/modules/COLLADASaxFrameworkLoader/COLLADAFramework/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | 3 | set(PROJECT_NAME COLLADAFramework) 4 | set(BASE_PATH ../../../OpenCOLLADA) 5 | set(PROJECT_PATH ${BASE_PATH}/COLLADAFramework) 6 | 7 | project(${PROJECT_NAME}) 8 | 9 | # COLLADAFramework 10 | include_directories(${PROJECT_PATH}/include) 11 | file(GLOB HEADERS "${PROJECT_PATH}/include/*.h") 12 | file(GLOB SOURCES "${PROJECT_PATH}/src/*.cpp") 13 | 14 | # COLLADABaseUtils 15 | include_directories(${BASE_PATH}/COLLADABaseUtils/include) 16 | add_subdirectory(COLLADABaseUtils) 17 | 18 | # MathMLSolver 19 | include_directories(${BASE_PATH}/Externals/MathMLSolver/include) 20 | include_directories(${BASE_PATH}/Externals/MathMLSolver/include/AST) 21 | add_subdirectory(MathMLSolver) 22 | 23 | # pcre 24 | include_directories(${BASE_PATH}/Externals/pcre/include) 25 | 26 | add_library(${PROJECT_NAME} ${HEADERS} ${SOURCES}) 27 | target_link_libraries(${PROJECT_NAME} COLLADABaseUtils MathMLSolver) 28 | -------------------------------------------------------------------------------- /GLTF/include/GLTFOptions.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace GLTF { 8 | class Options { 9 | public: 10 | std::string name; 11 | bool embeddedBuffers = true; 12 | bool embeddedTextures = true; 13 | bool embeddedShaders = true; 14 | bool binary = false; 15 | bool lockOcclusionMetallicRoughness = false; 16 | bool materialsCommon = false; 17 | bool doubleSided = false; 18 | bool glsl = false; 19 | bool specularGlossiness = false; 20 | bool preserveUnusedSemantics = false; 21 | std::string version = "2.0"; 22 | std::vector metallicRoughnessTexturePaths; 23 | // For Draco compression extension. 24 | bool dracoCompression = false; 25 | int positionQuantizationBits = 14; 26 | int normalQuantizationBits = 10; 27 | int texcoordQuantizationBits = 10; 28 | int colorQuantizationBits = 8; 29 | int jointQuantizationBits = 8; 30 | bool writeAbsoluteUris = false; 31 | }; 32 | } // namespace GLTF 33 | -------------------------------------------------------------------------------- /GLTF/src/GLTFShader.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFShader.h" 3 | 4 | #include 5 | 6 | #include "Base64.h" 7 | #include "GLTFOptions.h" 8 | #include "rapidjson/stringbuffer.h" 9 | #include "rapidjson/writer.h" 10 | 11 | std::string GLTF::Shader::typeName() { return "shader"; } 12 | 13 | void GLTF::Shader::writeJSON(void* writer, GLTF::Options* options) { 14 | rapidjson::Writer* jsonWriter = 15 | (rapidjson::Writer*)writer; 16 | 17 | jsonWriter->Key("type"); 18 | jsonWriter->Int(static_cast(type)); 19 | jsonWriter->Key("uri"); 20 | if (options->embeddedShaders) { 21 | uri = "data:text/plain;base64," + 22 | std::string( 23 | Base64::encode((unsigned char*)source.c_str(), source.length())); 24 | } else { 25 | uri = options->name + std::to_string(id) + 26 | (type == GLTF::Constants::WebGL::VERTEX_SHADER ? ".vert" : ".frag"); 27 | } 28 | jsonWriter->String(uri.c_str()); 29 | GLTF::Object::writeJSON(writer, options); 30 | } 31 | -------------------------------------------------------------------------------- /GLTF/include/GLTFImage.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include "GLTFBufferView.h" 8 | #include "GLTFObject.h" 9 | 10 | namespace GLTF { 11 | class Image : public GLTF::Object { 12 | public: 13 | std::string uri; 14 | unsigned char* data = NULL; 15 | size_t byteLength = 0; 16 | std::string mimeType; 17 | GLTF::BufferView* bufferView = NULL; 18 | 19 | explicit Image(std::string uri); 20 | Image(std::string uri, unsigned char* data, size_t byteLength, 21 | std::string fileExtension); 22 | virtual ~Image(); 23 | 24 | static GLTF::Image* load(std::string path, bool writeAbsoluteUris); 25 | std::pair getDimensions(); 26 | virtual std::string typeName(); 27 | virtual void writeJSON(void* writer, GLTF::Options* options); 28 | 29 | private: 30 | const std::string cacheKey; 31 | 32 | Image(std::string uri, std::string cacheKey); 33 | Image(std::string uri, std::string cacheKey, unsigned char* data, 34 | size_t byteLength, std::string fileExtension); 35 | }; 36 | } // namespace GLTF 37 | -------------------------------------------------------------------------------- /GLTF/src/GLTFBuffer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFBuffer.h" 3 | 4 | #include "Base64.h" 5 | #include "rapidjson/stringbuffer.h" 6 | #include "rapidjson/writer.h" 7 | 8 | GLTF::Buffer::Buffer(unsigned char* data, int dataLength) { 9 | this->data = data; 10 | this->byteLength = dataLength; 11 | } 12 | 13 | GLTF::Buffer::~Buffer() { free(this->data); } 14 | 15 | std::string GLTF::Buffer::typeName() { return "buffer"; } 16 | 17 | void GLTF::Buffer::writeJSON(void* writer, GLTF::Options* options) { 18 | rapidjson::Writer* jsonWriter = 19 | (rapidjson::Writer*)writer; 20 | jsonWriter->Key("byteLength"); 21 | jsonWriter->Int(this->byteLength); 22 | if (!options->binary || !options->embeddedBuffers) { 23 | jsonWriter->Key("uri"); 24 | if (options->embeddedBuffers) { 25 | uri = "data:application/octet-stream;base64," + 26 | std::string(Base64::encode(this->data, this->byteLength)); 27 | } else { 28 | uri = options->name + std::to_string(id) + ".bin"; 29 | } 30 | jsonWriter->String(uri.c_str()); 31 | } 32 | GLTF::Object::writeJSON(writer, options); 33 | } 34 | -------------------------------------------------------------------------------- /GLTF/include/GLTFPrimitive.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "GLTFAccessor.h" 9 | #include "GLTFDracoExtension.h" 10 | #include "GLTFMaterial.h" 11 | #include "GLTFObject.h" 12 | 13 | namespace GLTF { 14 | class Primitive : public GLTF::Object { 15 | public: 16 | enum Mode { 17 | UNKNOWN = -1, 18 | POINTS = 0, 19 | LINES = 1, 20 | LINE_LOOP = 2, 21 | LINE_STRIP = 3, 22 | TRIANGLES = 4, 23 | TRIANGLE_STRIP = 5, 24 | TRIANGLE_FAN = 6, 25 | }; 26 | 27 | class Target { 28 | public: 29 | std::map attributes; 30 | 31 | Target* clone(GLTF::Object* clone); 32 | void writeJSON(void* writer, GLTF::Options* options); 33 | }; 34 | 35 | std::map attributes; 36 | GLTF::Accessor* indices = NULL; 37 | GLTF::Material* material = NULL; 38 | Mode mode = Mode::UNKNOWN; 39 | std::vector targets; 40 | 41 | ~Primitive(); 42 | 43 | virtual GLTF::Object* clone(GLTF::Object* clone); 44 | virtual void writeJSON(void* writer, GLTF::Options* options); 45 | }; 46 | } // namespace GLTF 47 | -------------------------------------------------------------------------------- /GLTF/src/GLTFProgram.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFProgram.h" 3 | 4 | #include "rapidjson/stringbuffer.h" 5 | #include "rapidjson/writer.h" 6 | 7 | std::string GLTF::Program::typeName() { return "program"; } 8 | 9 | void GLTF::Program::writeJSON(void* writer, GLTF::Options* options) { 10 | rapidjson::Writer* jsonWriter = 11 | (rapidjson::Writer*)writer; 12 | 13 | jsonWriter->Key("attributes"); 14 | jsonWriter->StartArray(); 15 | for (std::string attribute : attributes) { 16 | jsonWriter->String(attribute.c_str()); 17 | } 18 | jsonWriter->EndArray(); 19 | 20 | if (fragmentShader != NULL) { 21 | jsonWriter->Key("fragmentShader"); 22 | if (options->version == "1.0") { 23 | jsonWriter->String(fragmentShader->getStringId().c_str()); 24 | } else { 25 | jsonWriter->Int(fragmentShader->id); 26 | } 27 | } 28 | if (vertexShader != NULL) { 29 | jsonWriter->Key("vertexShader"); 30 | if (options->version == "1.0") { 31 | jsonWriter->String(vertexShader->getStringId().c_str()); 32 | } else { 33 | jsonWriter->Int(vertexShader->id); 34 | } 35 | } 36 | GLTF::Object::writeJSON(writer, options); 37 | } 38 | -------------------------------------------------------------------------------- /dependencies/OpenCOLLADA/modules/COLLADASaxFrameworkLoader/COLLADAFramework/COLLADABaseUtils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | 3 | set(PROJECT_NAME COLLADABaseUtils) 4 | set(BASE_PATH ../../../../OpenCOLLADA) 5 | set(PROJECT_PATH ${BASE_PATH}/COLLADABaseUtils) 6 | 7 | project(${PROJECT_NAME}) 8 | 9 | # COLLADABaseUtils 10 | include_directories(${PROJECT_PATH}/include) 11 | file(GLOB HEADERS "${PROJECT_PATH}/include/*.h") 12 | file(GLOB SOURCES "${PROJECT_PATH}/src/*.cpp") 13 | 14 | # COLLADABaseUtils Math 15 | include_directories(${PROJECT_PATH}/include/Math) 16 | file(GLOB MATH_HEADERS "${PROJECT_PATH}/include/Math/*.h") 17 | file(GLOB MATH_SOURCES "${PROJECT_PATH}/src/Math/*.cpp") 18 | 19 | # UTF 20 | include_directories(${BASE_PATH}/Externals/UTF/include) 21 | file(GLOB UTF_HEADERS "${BASE_PATH}/Externals/UTF/include/*.h") 22 | file(GLOB UTF_SOURCES "${BASE_PATH}/Externals/UTF/src/*.c") 23 | 24 | # pcre 25 | if(NOT PCRE_FOUND) 26 | add_definitions(-DPCRE_STATIC) 27 | include_directories(${BASE_PATH}/Externals/pcre/include) 28 | add_subdirectory(pcre) 29 | else() 30 | include_directories(${PCRE_INCLUDE_DIR}) 31 | endif() 32 | 33 | add_library(${PROJECT_NAME} ${HEADERS} ${SOURCES} ${MATH_HEADERS} ${MATH_SOURCES} ${UTF_HEADERS} ${UTF_SOURCES}) 34 | target_link_libraries(${PROJECT_NAME} pcre) 35 | -------------------------------------------------------------------------------- /GLTF/src/GLTFTexture.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFTexture.h" 3 | 4 | #include "rapidjson/stringbuffer.h" 5 | #include "rapidjson/writer.h" 6 | 7 | std::string GLTF::Texture::typeName() { return "texture"; } 8 | 9 | void GLTF::Texture::writeJSON(void* writer, GLTF::Options* options) { 10 | rapidjson::Writer* jsonWriter = 11 | (rapidjson::Writer*)writer; 12 | if (options->version == "1.0") { 13 | jsonWriter->Key("format"); 14 | jsonWriter->Int(static_cast(GLTF::Constants::WebGL::RGBA)); 15 | jsonWriter->Key("internalFormat"); 16 | jsonWriter->Int(static_cast(GLTF::Constants::WebGL::RGBA)); 17 | jsonWriter->Key("target"); 18 | jsonWriter->Int(static_cast(GLTF::Constants::WebGL::TEXTURE_2D)); 19 | jsonWriter->Key("type"); 20 | jsonWriter->Int(static_cast(GLTF::Constants::WebGL::UNSIGNED_BYTE)); 21 | } 22 | jsonWriter->Key("sampler"); 23 | if (options->version == "1.0") { 24 | jsonWriter->String(sampler->getStringId().c_str()); 25 | } else { 26 | jsonWriter->Int(sampler->id); 27 | } 28 | jsonWriter->Key("source"); 29 | if (options->version == "1.0") { 30 | jsonWriter->String(source->getStringId().c_str()); 31 | } else { 32 | jsonWriter->Int(source->id); 33 | } 34 | GLTF::Object::writeJSON(writer, options); 35 | } 36 | -------------------------------------------------------------------------------- /GLTF/src/GLTFSkin.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFSkin.h" 3 | 4 | #include "GLTFNode.h" 5 | #include "rapidjson/stringbuffer.h" 6 | #include "rapidjson/writer.h" 7 | 8 | std::string GLTF::Skin::typeName() { return "skin"; } 9 | 10 | void GLTF::Skin::writeJSON(void* writer, GLTF::Options* options) { 11 | rapidjson::Writer* jsonWriter = 12 | (rapidjson::Writer*)writer; 13 | 14 | if (inverseBindMatrices != NULL) { 15 | jsonWriter->Key("inverseBindMatrices"); 16 | if (options->version == "1.0") { 17 | jsonWriter->String(inverseBindMatrices->getStringId().c_str()); 18 | } else { 19 | jsonWriter->Int(inverseBindMatrices->id); 20 | } 21 | } 22 | if (options->version != "1.0" && skeleton != NULL) { 23 | jsonWriter->Key("skeleton"); 24 | jsonWriter->Int(skeleton->id); 25 | } 26 | if (options->version == "1.0") { 27 | jsonWriter->Key("jointNames"); 28 | } else { 29 | jsonWriter->Key("joints"); 30 | } 31 | jsonWriter->StartArray(); 32 | for (GLTF::Node* node : joints) { 33 | if (node != NULL) { 34 | if (options->version == "1.0") { 35 | jsonWriter->String(node->jointName.c_str()); 36 | } else { 37 | jsonWriter->Int(node->id); 38 | } 39 | } 40 | } 41 | jsonWriter->EndArray(); 42 | GLTF::Object::writeJSON(writer, options); 43 | } 44 | -------------------------------------------------------------------------------- /GLTF/include/GLTFConstants.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | namespace GLTF { 5 | namespace Constants { 6 | enum class WebGL { 7 | BYTE = 5120, 8 | UNSIGNED_BYTE = 5121, 9 | SHORT = 5122, 10 | UNSIGNED_SHORT = 5123, 11 | FLOAT = 5126, 12 | UNSIGNED_INT = 5125, 13 | 14 | FLOAT_VEC2 = 35664, 15 | FLOAT_VEC3 = 35665, 16 | FLOAT_VEC4 = 35666, 17 | INT_VEC2 = 35667, 18 | INT_VEC3 = 35668, 19 | INT_VEC4 = 35669, 20 | BOOL = 35670, 21 | BOOL_VEC2 = 35671, 22 | BOOL_VEC3 = 35672, 23 | BOOL_VEC4 = 35673, 24 | FLOAT_MAT2 = 35674, 25 | FLOAT_MAT3 = 35675, 26 | FLOAT_MAT4 = 35676, 27 | SAMPLER_2D = 35678, 28 | SAMPLER_CUBE = 35680, 29 | 30 | CULL_FACE = 2884, 31 | DEPTH_TEST = 2929, 32 | 33 | ARRAY_BUFFER = 34962, 34 | ELEMENT_ARRAY_BUFFER = 34963, 35 | 36 | FRAGMENT_SHADER = 35632, 37 | VERTEX_SHADER = 35633, 38 | 39 | ALPHA = 6406, 40 | RGB = 6407, 41 | RGBA = 6408, 42 | LUMINANCE = 6409, 43 | LUMINANCE_ALPHA = 6410, 44 | 45 | TEXTURE_2D = 3553, 46 | 47 | NEAREST = 9728, 48 | LINEAR = 9729, 49 | NEAREST_MIPMAP_NEAREST = 9984, 50 | LINEAR_MIPMAP_NEAREST = 9985, 51 | NEAREST_MIPMAP_LINEAR = 9986, 52 | LINEAR_MIPMAP_LINEAR = 9987, 53 | 54 | CLAMP_TO_EDGE = 33071, 55 | MIRRORED_REPEAT = 33648, 56 | REPEAT = 10497, 57 | 58 | FUNC_ADD = 32774, 59 | ONE = 1, 60 | ONE_MINUS_SRC_ALPHA = 771 61 | }; 62 | } // namespace Constants 63 | } // namespace GLTF 64 | -------------------------------------------------------------------------------- /GLTF/include/GLTFAnimation.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "GLTFAccessor.h" 9 | #include "GLTFNode.h" 10 | #include "GLTFObject.h" 11 | 12 | namespace GLTF { 13 | class Animation : public GLTF::Object { 14 | public: 15 | enum class Path { TRANSLATION, ROTATION, SCALE, WEIGHTS }; 16 | 17 | class Sampler : public GLTF::Object { 18 | public: 19 | GLTF::Accessor* input; 20 | std::string interpolation = "LINEAR"; 21 | GLTF::Accessor* output; 22 | std::string inputString; 23 | std::string outputString; 24 | Path path; 25 | 26 | virtual std::string typeName(); 27 | virtual void writeJSON(void* writer, GLTF::Options* options); 28 | }; 29 | 30 | class Channel : public GLTF::Object { 31 | public: 32 | ~Channel(); 33 | 34 | class Target : public GLTF::Object { 35 | public: 36 | GLTF::Node* node; 37 | Path path; 38 | 39 | virtual void writeJSON(void* writer, GLTF::Options* options); 40 | }; 41 | 42 | GLTF::Animation::Sampler* sampler = nullptr; 43 | Target* target = nullptr; 44 | 45 | virtual void writeJSON(void* writer, GLTF::Options* options); 46 | }; 47 | 48 | ~Animation(); 49 | 50 | std::vector channels; 51 | 52 | virtual std::string typeName(); 53 | virtual void writeJSON(void* writer, GLTF::Options* options); 54 | }; 55 | } // namespace GLTF 56 | -------------------------------------------------------------------------------- /include/COLLADA2GLTFExtrasHandler.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "COLLADASaxFWLIExtraDataCallbackHandler.h" 9 | #include "COLLADASaxFWLLoader.h" 10 | #include "GeneratedSaxParser.h" 11 | 12 | namespace COLLADA2GLTF { 13 | class ExtrasHandler : COLLADASaxFWL::IExtraDataCallbackHandler { 14 | private: 15 | virtual bool elementBegin(const COLLADASaxFWL::ParserChar* elementName, 16 | const GeneratedSaxParser::xmlChar** attributes); 17 | virtual bool elementEnd(const COLLADASaxFWL::ParserChar* elementName); 18 | virtual bool textData(const COLLADASaxFWL::ParserChar* text, 19 | size_t textLength); 20 | 21 | virtual bool parseElement(const COLLADASaxFWL::ParserChar* profileName, 22 | const COLLADASaxFWL::StringHash& elementHash, 23 | const COLLADAFW::UniqueId& uniqueId, 24 | COLLADAFW::Object* object); 25 | 26 | COLLADASaxFWL::Loader* _loader; 27 | COLLADAFW::UniqueId _currentId; 28 | bool _inBump = false; 29 | bool _inDoubleSided = false; 30 | 31 | public: 32 | std::set lockAmbientDiffuse; 33 | std::map bumpTextures; 34 | std::set doubleSided; 35 | explicit ExtrasHandler(COLLADASaxFWL::Loader* loader) : _loader(loader) {} 36 | }; 37 | } // namespace COLLADA2GLTF 38 | -------------------------------------------------------------------------------- /GLTF/src/GLTFMesh.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFMesh.h" 3 | 4 | #include "rapidjson/stringbuffer.h" 5 | #include "rapidjson/writer.h" 6 | 7 | std::string GLTF::Mesh::typeName() { return "mesh"; } 8 | 9 | GLTF::Object* GLTF::Mesh::clone(GLTF::Object* clone) { 10 | GLTF::Mesh* mesh = dynamic_cast(clone); 11 | if (mesh != NULL) { 12 | for (GLTF::Primitive* primitive : this->primitives) { 13 | GLTF::Primitive* clonePrimitive = new GLTF::Primitive(); 14 | primitive->clone(clonePrimitive); 15 | if (clonePrimitive != NULL) { 16 | mesh->primitives.push_back(clonePrimitive); 17 | } 18 | } 19 | mesh->weights = this->weights; 20 | } 21 | GLTF::Object::clone(clone); 22 | return mesh; 23 | } 24 | 25 | void GLTF::Mesh::writeJSON(void* writer, GLTF::Options* options) { 26 | auto* jsonWriter = 27 | static_cast*>(writer); 28 | jsonWriter->Key("primitives"); 29 | jsonWriter->StartArray(); 30 | for (GLTF::Primitive* primitive : this->primitives) { 31 | jsonWriter->StartObject(); 32 | primitive->writeJSON(jsonWriter, options); 33 | jsonWriter->EndObject(); 34 | } 35 | jsonWriter->EndArray(); 36 | if (!weights.empty()) { 37 | jsonWriter->Key("weights"); 38 | jsonWriter->StartArray(); 39 | for (float weight : this->weights) { 40 | jsonWriter->Double(weight); 41 | } 42 | jsonWriter->EndArray(); 43 | } 44 | GLTF::Object::writeJSON(writer, options); 45 | } 46 | -------------------------------------------------------------------------------- /GLTF/test/src/GLTFAssetTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFAssetTest.h" 3 | 4 | #include "GLTFAsset.h" 5 | 6 | TEST(GLTFAssetTest, RemoveUnusedSemantics) { 7 | GLTF::Asset* asset = new GLTF::Asset(); 8 | 9 | GLTF::Scene* scene = new GLTF::Scene(); 10 | asset->scenes.push_back(scene); 11 | asset->scene = 0; 12 | 13 | GLTF::Node* node = new GLTF::Node(); 14 | scene->nodes.push_back(node); 15 | 16 | GLTF::Mesh* mesh = new GLTF::Mesh(); 17 | node->mesh = mesh; 18 | 19 | GLTF::Primitive* primitive = new GLTF::Primitive(); 20 | mesh->primitives.push_back(primitive); 21 | 22 | GLTF::Material* material = new GLTF::Material(); 23 | primitive->material = material; 24 | 25 | // Add an unused texture coordinate attribute 26 | primitive->attributes["TEXCOORD_0"] = NULL; 27 | 28 | EXPECT_EQ(primitive->attributes.size(), 1); 29 | asset->removeUnusedSemantics(); 30 | EXPECT_EQ(primitive->attributes.size(), 0); 31 | 32 | // Add an unused and a used texture coordinaate 33 | primitive->attributes["TEXCOORD_0"] = NULL; 34 | primitive->attributes["TEXCOORD_1"] = (GLTF::Accessor*)1; 35 | 36 | material->values->ambientTexture = new GLTF::Texture(); 37 | material->values->ambientTexCoord = 1; 38 | 39 | EXPECT_EQ(primitive->attributes.size(), 2); 40 | asset->removeUnusedSemantics(); 41 | EXPECT_EQ(primitive->attributes.size(), 1); 42 | EXPECT_EQ(primitive->attributes["TEXCOORD_0"], (GLTF::Accessor*)1); 43 | EXPECT_EQ(material->values->ambientTexCoord, 0); 44 | } 45 | -------------------------------------------------------------------------------- /GLTF/include/GLTFTechnique.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "GLTFObject.h" 10 | #include "GLTFProgram.h" 11 | 12 | namespace GLTF { 13 | class Technique : public GLTF::Object { 14 | public: 15 | class Parameter { 16 | public: 17 | std::string semantic; 18 | GLTF::Constants::WebGL type; 19 | int valueLength; 20 | float* value = NULL; 21 | int node = -1; 22 | std::string nodeString; 23 | int count = -1; 24 | 25 | explicit Parameter(GLTF::Constants::WebGL type) : type(type) {} 26 | Parameter(GLTF::Constants::WebGL type, float* value, int valueLength) 27 | : type(type), value(value), valueLength(valueLength) {} 28 | Parameter(std::string semantic, GLTF::Constants::WebGL type) 29 | : semantic(semantic), type(type) {} 30 | Parameter(std::string semantic, GLTF::Constants::WebGL type, int count) 31 | : semantic(semantic), type(type), count(count) {} 32 | }; 33 | 34 | ~Technique(); 35 | 36 | std::map parameters; 37 | std::map attributes; 38 | std::map uniforms; 39 | std::set enableStates; 40 | std::vector blendEquationSeparate; 41 | std::vector blendFuncSeparate; 42 | bool* depthMask = NULL; 43 | GLTF::Program* program = NULL; 44 | 45 | virtual std::string typeName(); 46 | virtual void writeJSON(void* writer, GLTF::Options* options); 47 | }; 48 | } // namespace GLTF 49 | -------------------------------------------------------------------------------- /GLTF/src/GLTFBufferView.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFBufferView.h" 3 | 4 | #include "rapidjson/stringbuffer.h" 5 | #include "rapidjson/writer.h" 6 | 7 | GLTF::BufferView::BufferView(int byteOffset, int byteLength, 8 | GLTF::Buffer* buffer) { 9 | this->byteOffset = byteOffset; 10 | this->byteLength = byteLength; 11 | this->buffer = buffer; 12 | } 13 | 14 | GLTF::BufferView::BufferView(unsigned char* data, int dataLength) { 15 | this->byteOffset = 0; 16 | this->byteLength = dataLength; 17 | this->buffer = new Buffer(data, dataLength); 18 | } 19 | 20 | GLTF::BufferView::BufferView(unsigned char* data, int dataLength, 21 | GLTF::Constants::WebGL target) 22 | : GLTF::BufferView::BufferView(data, dataLength) { 23 | this->target = target; 24 | } 25 | 26 | std::string GLTF::BufferView::typeName() { return "bufferView"; } 27 | 28 | void GLTF::BufferView::writeJSON(void* writer, GLTF::Options* options) { 29 | rapidjson::Writer* jsonWriter = 30 | (rapidjson::Writer*)writer; 31 | if (this->buffer) { 32 | jsonWriter->Key("buffer"); 33 | if (options->version == "1.0") { 34 | jsonWriter->String(buffer->getStringId().c_str()); 35 | } else { 36 | jsonWriter->Int(this->buffer->id); 37 | } 38 | } 39 | jsonWriter->Key("byteOffset"); 40 | jsonWriter->Int(this->byteOffset); 41 | jsonWriter->Key("byteLength"); 42 | jsonWriter->Int(this->byteLength); 43 | if (byteStride != 0 && options->version != "1.0") { 44 | jsonWriter->Key("byteStride"); 45 | jsonWriter->Int(this->byteStride); 46 | } 47 | if (static_cast(target) > 0) { 48 | jsonWriter->Key("target"); 49 | jsonWriter->Int(static_cast(this->target)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /GLTF/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 3 | 4 | set(PROJECT_NAME GLTF) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | set(CMAKE_CXX_STANDARD 11) 7 | set(CMAKE_CXX_EXTENSIONS OFF) 8 | 9 | project(${PROJECT_NAME}) 10 | 11 | # cmake -Dtest=ON to build with tests 12 | option(test "Build all tests." OFF) 13 | 14 | # RapidJSON 15 | if(DEFINED RAPIDJSON_INCLUDE_DIR) 16 | include_directories(${RAPIDJSON_INCLUDE_DIR}) 17 | else() 18 | include_directories(dependencies/rapidjson/include) 19 | endif() 20 | 21 | # Draco 22 | if(DEFINED DRACO_INCLUDE_DIR) 23 | include_directories(${DRACO_INCLUDE_DIR}) 24 | else() 25 | include_directories(dependencies/draco/src) 26 | add_subdirectory(dependencies/draco) 27 | get_property(draco_targets DIRECTORY dependencies/draco PROPERTY BUILDSYSTEM_TARGETS) 28 | foreach(draco_target IN LISTS draco_targets) 29 | set_property(TARGET ${draco_target} PROPERTY FOLDER "draco") 30 | endforeach() 31 | endif() 32 | 33 | # gltf 34 | include_directories(include) 35 | file(GLOB HEADERS "include/*.h") 36 | file(GLOB SOURCES "src/*.cpp") 37 | 38 | add_library(GLTF ${HEADERS} ${SOURCES}) 39 | target_link_libraries(${PROJECT_NAME} draco) 40 | 41 | if (test) 42 | enable_testing() 43 | 44 | # gtest 45 | if(!GTEST_LINKED) 46 | include_directories(dependencies/googletest/googletest/include) 47 | add_subdirectory(dependencies/googletest/googletest) 48 | endif() 49 | 50 | # Unit Tests 51 | include_directories(test/include) 52 | file(GLOB TEST_HEADERS "test/include/*.h") 53 | file(GLOB TEST_SOURCES "test/src/*.cpp") 54 | 55 | add_executable(${PROJECT_NAME}-test ${TEST_HEADERS} ${TEST_SOURCES}) 56 | target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME} gtest) 57 | 58 | add_test(GLTFTest ${PROJECT_NAME}-test) 59 | endif() 60 | -------------------------------------------------------------------------------- /GLTF/include/GLTFAccessor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | 6 | #include "GLTFBufferView.h" 7 | #include "GLTFConstants.h" 8 | #include "GLTFObject.h" 9 | 10 | namespace GLTF { 11 | class Accessor : public GLTF::Object { 12 | public: 13 | enum class Type { SCALAR, VEC2, VEC3, VEC4, MAT2, MAT3, MAT4, UNKNOWN }; 14 | 15 | GLTF::BufferView* bufferView = NULL; 16 | int byteOffset = 0; 17 | GLTF::Constants::WebGL componentType; 18 | int count = 0; 19 | float* max = NULL; 20 | float* min = NULL; 21 | Type type = Type::UNKNOWN; 22 | 23 | Accessor(GLTF::Accessor::Type type, GLTF::Constants::WebGL componentType); 24 | 25 | Accessor(GLTF::Accessor::Type type, GLTF::Constants::WebGL componentType, 26 | unsigned char* data, int count, GLTF::Constants::WebGL target); 27 | 28 | Accessor(GLTF::Accessor::Type type, GLTF::Constants::WebGL componentType, 29 | unsigned char* data, int count, GLTF::BufferView* bufferView); 30 | 31 | Accessor(GLTF::Accessor::Type type, GLTF::Constants::WebGL componentType, 32 | int byteOffset, int count, GLTF::BufferView* bufferView); 33 | 34 | explicit Accessor(GLTF::Accessor* accessor); 35 | 36 | static int getComponentByteLength(GLTF::Constants::WebGL componentType); 37 | static int getNumberOfComponents(GLTF::Accessor::Type type); 38 | 39 | bool computeMinMax(); 40 | int getByteStride(); 41 | bool getComponentAtIndex(int index, float* component); 42 | bool writeComponentAtIndex(int index, float* component); 43 | int getComponentByteLength(); 44 | int getNumberOfComponents(); 45 | bool equals(GLTF::Accessor* accessor); 46 | const char* getTypeName(); 47 | 48 | virtual std::string typeName(); 49 | virtual void writeJSON(void* writer, GLTF::Options* options); 50 | }; 51 | } // namespace GLTF 52 | -------------------------------------------------------------------------------- /GLTF/src/GLTFCamera.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFCamera.h" 3 | 4 | #include "rapidjson/stringbuffer.h" 5 | #include "rapidjson/writer.h" 6 | 7 | std::string GLTF::Camera::typeName() { return "camera"; } 8 | 9 | void GLTF::Camera::writeJSON(void* writer, GLTF::Options* options) { 10 | rapidjson::Writer* jsonWriter = 11 | (rapidjson::Writer*)writer; 12 | 13 | if (type != Type::UNKNOWN) { 14 | jsonWriter->Key("type"); 15 | if (type == Type::PERSPECTIVE) { 16 | jsonWriter->String("perspective"); 17 | } else if (type == Type::ORTHOGRAPHIC) { 18 | jsonWriter->String("orthographic"); 19 | } 20 | } 21 | GLTF::Object::writeJSON(writer, options); 22 | } 23 | 24 | void GLTF::CameraOrthographic::writeJSON(void* writer, GLTF::Options* options) { 25 | rapidjson::Writer* jsonWriter = 26 | (rapidjson::Writer*)writer; 27 | 28 | jsonWriter->Key("orthographic"); 29 | jsonWriter->StartObject(); 30 | jsonWriter->Key("xmag"); 31 | jsonWriter->Double(xmag); 32 | jsonWriter->Key("ymag"); 33 | jsonWriter->Double(ymag); 34 | jsonWriter->Key("zfar"); 35 | jsonWriter->Double(zfar); 36 | jsonWriter->Key("znear"); 37 | jsonWriter->Double(znear); 38 | jsonWriter->EndObject(); 39 | 40 | GLTF::Camera::writeJSON(writer, options); 41 | } 42 | 43 | void GLTF::CameraPerspective::writeJSON(void* writer, GLTF::Options* options) { 44 | rapidjson::Writer* jsonWriter = 45 | (rapidjson::Writer*)writer; 46 | 47 | jsonWriter->Key("perspective"); 48 | jsonWriter->StartObject(); 49 | if (aspectRatio > 0) { 50 | jsonWriter->Key("aspectRatio"); 51 | jsonWriter->Double(aspectRatio); 52 | } 53 | jsonWriter->Key("yfov"); 54 | jsonWriter->Double(yfov); 55 | jsonWriter->Key("zfar"); 56 | jsonWriter->Double(zfar); 57 | jsonWriter->Key("znear"); 58 | jsonWriter->Double(znear); 59 | jsonWriter->EndObject(); 60 | 61 | GLTF::Camera::writeJSON(writer, options); 62 | } 63 | -------------------------------------------------------------------------------- /GLTF/src/GLTFObject.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFObject.h" 3 | 4 | #include "GLTFExtension.h" 5 | #include "rapidjson/stringbuffer.h" 6 | #include "rapidjson/writer.h" 7 | 8 | GLTF::Object::~Object() { 9 | for (auto& kv : extensions) { 10 | delete kv.second; 11 | } 12 | for (auto& kv : extras) { 13 | delete kv.second; 14 | } 15 | } 16 | 17 | std::string GLTF::Object::getStringId() { 18 | if (stringId == "") { 19 | return typeName() + "_" + std::to_string(id); 20 | } 21 | return stringId; 22 | } 23 | 24 | std::string GLTF::Object::typeName() { return "object"; } 25 | 26 | GLTF::Object* GLTF::Object::clone(GLTF::Object* clone) { 27 | clone->id = this->id; 28 | clone->name = this->name; 29 | for (const auto extra : this->extras) { 30 | clone->extras[extra.first] = extra.second; 31 | } 32 | for (const auto extension : this->extensions) { 33 | clone->extensions[extension.first] = extension.second; 34 | } 35 | return clone; 36 | } 37 | 38 | void GLTF::Object::writeJSON(void* writer, GLTF::Options* options) { 39 | rapidjson::Writer* jsonWriter = 40 | (rapidjson::Writer*)writer; 41 | if (this->name.length() > 0) { 42 | jsonWriter->Key("name"); 43 | jsonWriter->String(this->name.c_str()); 44 | } 45 | if (this->extensions.size() > 0) { 46 | jsonWriter->Key("extensions"); 47 | jsonWriter->StartObject(); 48 | for (const auto extension : this->extensions) { 49 | jsonWriter->Key(extension.first.c_str()); 50 | jsonWriter->StartObject(); 51 | extension.second->writeJSON(writer, options); 52 | jsonWriter->EndObject(); 53 | } 54 | jsonWriter->EndObject(); 55 | } 56 | if (this->extras.size() > 0) { 57 | jsonWriter->Key("extras"); 58 | jsonWriter->StartObject(); 59 | for (const auto extra : this->extras) { 60 | jsonWriter->Key(extra.first.c_str()); 61 | jsonWriter->StartObject(); 62 | extra.second->writeJSON(writer, options); 63 | jsonWriter->EndObject(); 64 | } 65 | jsonWriter->EndObject(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /GLTF/test/src/GLTFObjectTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFObjectTest.h" 3 | 4 | #include "GLTFExtension.h" 5 | #include "GLTFObject.h" 6 | #include "rapidjson/stringbuffer.h" 7 | #include "rapidjson/writer.h" 8 | 9 | GLTFObjectTest::GLTFObjectTest() { options = new GLTF::Options(); } 10 | 11 | GLTFObjectTest::~GLTFObjectTest() { delete options; } 12 | 13 | rapidjson::StringBuffer writeObject(GLTF::Object* object, 14 | GLTF::Options* options) { 15 | rapidjson::StringBuffer s; 16 | rapidjson::Writer writer(s); 17 | writer.StartObject(); 18 | object->writeJSON(&writer, options); 19 | writer.EndObject(); 20 | return s; 21 | } 22 | 23 | TEST_F(GLTFObjectTest, WriteJSON_NoParameters) { 24 | GLTF::Object* object = new GLTF::Object(); 25 | rapidjson::StringBuffer s = writeObject(object, this->options); 26 | 27 | EXPECT_STREQ(s.GetString(), "{}"); 28 | 29 | free(object); 30 | } 31 | 32 | TEST_F(GLTFObjectTest, WriteJSON_WithName) { 33 | GLTF::Object* object = new GLTF::Object(); 34 | object->name = "test"; 35 | rapidjson::StringBuffer s = writeObject(object, this->options); 36 | 37 | EXPECT_STREQ(s.GetString(), "{\"name\":\"test\"}"); 38 | 39 | free(object); 40 | } 41 | 42 | TEST_F(GLTFObjectTest, WriteJSON_WithExtra) { 43 | GLTF::Object* object = new GLTF::Object(); 44 | GLTF::Object* extra = new GLTF::Object(); 45 | extra->name = "extra,extra"; 46 | object->extras["extra"] = extra; 47 | rapidjson::StringBuffer s = writeObject(object, this->options); 48 | 49 | EXPECT_STREQ(s.GetString(), 50 | "{\"extras\":{\"extra\":{\"name\":\"extra,extra\"}}}"); 51 | 52 | free(object); 53 | } 54 | 55 | TEST_F(GLTFObjectTest, WriteJSON_WithExtension) { 56 | GLTF::Object* object = new GLTF::Object(); 57 | GLTF::Extension* extension = new GLTF::Extension(); 58 | object->extensions["KHR_materials_common"] = extension; 59 | rapidjson::StringBuffer s = writeObject(object, this->options); 60 | 61 | EXPECT_STREQ(s.GetString(), "{\"extensions\":{\"KHR_materials_common\":{}}}"); 62 | 63 | free(object); 64 | } 65 | -------------------------------------------------------------------------------- /dependencies/OpenCOLLADA/modules/COLLADASaxFrameworkLoader/GeneratedSaxParser/LibXML/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | 3 | set(PROJECT_NAME LibXML) 4 | set(BASE_PATH ../../../../OpenCOLLADA) 5 | set(PROJECT_PATH ${BASE_PATH}/Externals/LibXML) 6 | 7 | project(${PROJECT_NAME}) 8 | add_definitions( 9 | -DLIBXML_AUTOMATA_ENABLED 10 | -DLIBXML_PATTERN_ENABLED 11 | -DLIBXML_READER_ENABLED 12 | -DLIBXML_REGEXP_ENABLED 13 | -DLIBXML_SCHEMAS_ENABLED 14 | -DLIBXML_XPATH_ENABLED 15 | ) 16 | 17 | if(APPLE) 18 | add_definitions(-Dmacintosh) 19 | include_directories(include) 20 | endif() 21 | 22 | set(SYS_SOURCES) 23 | set(SYS_HEADERS) 24 | # if(MSVC) 25 | # include_directories(${PROJECT_PATH}/include) 26 | # set(SYS_SOURCES ${PROJECT_PATH}/win32/wince/wincecompat.c) 27 | # set(SYS_HEADERS ${PROJECT_PATH}/win32/wince/wincecompat.h) 28 | # endif() 29 | 30 | # LibXML 31 | include_directories(${PROJECT_PATH}/include) 32 | file(GLOB HEADERS "${PROJECT_PATH}/include/*.h") 33 | set(SOURCES 34 | ${PROJECT_PATH}/c14n.c 35 | ${PROJECT_PATH}/catalog.c 36 | ${PROJECT_PATH}/chvalid.c 37 | ${PROJECT_PATH}/debugXML.c 38 | ${PROJECT_PATH}/dict.c 39 | ${PROJECT_PATH}/DOCBparser.c 40 | ${PROJECT_PATH}/encoding.c 41 | ${PROJECT_PATH}/entities.c 42 | ${PROJECT_PATH}/error.c 43 | ${PROJECT_PATH}/legacy.c 44 | ${PROJECT_PATH}/list.c 45 | ${PROJECT_PATH}/parser.c 46 | ${PROJECT_PATH}/parserInternals.c 47 | ${PROJECT_PATH}/pattern.c 48 | ${PROJECT_PATH}/globals.c 49 | ${PROJECT_PATH}/hash.c 50 | ${PROJECT_PATH}/HTMLparser.c 51 | ${PROJECT_PATH}/HTMLtree.c 52 | ${PROJECT_PATH}/SAX2.c 53 | ${PROJECT_PATH}/SAX.c 54 | ${PROJECT_PATH}/threads.c 55 | ${PROJECT_PATH}/relaxng.c 56 | ${PROJECT_PATH}/uri.c 57 | ${PROJECT_PATH}/valid.c 58 | ${PROJECT_PATH}/xinclude.c 59 | ${PROJECT_PATH}/xlink.c 60 | ${PROJECT_PATH}/xmlIO.c 61 | ${PROJECT_PATH}/xmlmemory.c 62 | ${PROJECT_PATH}/xmlmodule.c 63 | ${PROJECT_PATH}/xmlreader.c 64 | ${PROJECT_PATH}/xmlregexp.c 65 | ${PROJECT_PATH}/xmlsave.c 66 | ${PROJECT_PATH}/xmlschemas.c 67 | ${PROJECT_PATH}/xmlschemastypes.c 68 | ${PROJECT_PATH}/xmlstring.c 69 | ${PROJECT_PATH}/xmlunicode.c 70 | ${PROJECT_PATH}/xmlwriter.c 71 | ${PROJECT_PATH}/xpath.c 72 | ${PROJECT_PATH}/tree.c 73 | ) 74 | 75 | add_library(${PROJECT_NAME} ${HEADERS} ${SOURCES} ${SYS_HEADERS} ${SYS_SOURCES}) 76 | -------------------------------------------------------------------------------- /dependencies/OpenCOLLADA/modules/COLLADASaxFrameworkLoader/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | 3 | set(PROJECT_NAME COLLADASaxFrameworkLoader) 4 | set(BASE_PATH ../../OpenCOLLADA) 5 | set(PROJECT_PATH ${BASE_PATH}/COLLADASaxFrameworkLoader) 6 | 7 | project(${PROJECT_NAME}) 8 | 9 | # COLLADASaxFrameworkLoader 10 | include_directories(${PROJECT_PATH}/include) 11 | file(GLOB HEADERS "${PROJECT_PATH}/include/*.h") 12 | file(GLOB SOURCES "${PROJECT_PATH}/src/*.cpp") 13 | 14 | # COLLADASaxFrameworkLoader Generated14 15 | include_directories(${PROJECT_PATH}/include/generated14) 16 | file(GLOB HEADERS_GENERATED14 "${PROJECT_PATH}/include/generated14/*.h") 17 | file(GLOB SOURCES_GENERATED14 "${PROJECT_PATH}/src/generated14/*.cpp") 18 | 19 | # COLLADASaxFrameworkLoader Generated15 20 | include_directories(${PROJECT_PATH}/include/generated15) 21 | file(GLOB HEADERS_GENERATED15 "${PROJECT_PATH}/include/generated15/*.h") 22 | file(GLOB SOURCES_GENERATED15 "${PROJECT_PATH}/src/generated15/*.cpp") 23 | 24 | # COLLADAFramework 25 | include_directories(${BASE_PATH}/COLLADAFramework/include) 26 | add_subdirectory(COLLADAFramework) 27 | 28 | # COLLADABaseUtils 29 | include_directories(${BASE_PATH}/COLLADABaseUtils/include) 30 | 31 | # MathMLSolver 32 | include_directories(${BASE_PATH}/Externals/MathMLSolver/include) 33 | 34 | # MathMLSolver AST 35 | include_directories(${BASE_PATH}/Externals/MathMLSolver/include/AST) 36 | 37 | # GeneratedSaxParser 38 | # Use LibXML by default 39 | add_definitions(-DGENERATEDSAXPARSER_XMLPARSER_LIBXML) 40 | include_directories(${BASE_PATH}/GeneratedSaxParser/include) 41 | add_subdirectory(GeneratedSaxParser) 42 | 43 | # LibXML 44 | if(NOT LIBXML2_FOUND) 45 | include_directories(${BASE_PATH}/Externals/LibXML/include) 46 | else() 47 | include_directories(${LIBXML2_INCLUDE_DIR}) 48 | endif() 49 | 50 | # expat 51 | include_directories(${BASE_PATH}/Externals/expat/lib) 52 | 53 | # pcre 54 | if(NOT PCRE_FOUND) 55 | add_definitions(-DPCRE_STATIC) 56 | include_directories(${BASE_PATH}/Externals/pcre/include) 57 | else() 58 | include_directories(${PCRE_INCLUDE_DIR}) 59 | endif() 60 | 61 | add_library(${PROJECT_NAME} ${HEADERS} ${SOURCES} ${HEADERS_GENERATED14} ${SOURCES_GENERATED14} ${HEADERS_GENERATED15} ${SOURCES_GENERATED15}) 62 | target_link_libraries(${PROJECT_NAME} COLLADAFramework GeneratedSaxParser) 63 | -------------------------------------------------------------------------------- /GLTF/include/GLTFNode.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "GLTFCamera.h" 9 | #include "GLTFMesh.h" 10 | #include "GLTFObject.h" 11 | #include "GLTFSkin.h" 12 | 13 | namespace GLTF { 14 | class Node : public GLTF::Object { 15 | public: 16 | class Transform { 17 | public: 18 | enum Type { TRS, MATRIX }; 19 | 20 | Type type; 21 | 22 | virtual Transform* clone() = 0; 23 | }; 24 | 25 | class TransformTRS; 26 | class TransformMatrix : public Transform { 27 | public: 28 | float matrix[16]; 29 | 30 | TransformMatrix(); 31 | TransformMatrix(float a00, float a01, float a02, float a03, float a10, 32 | float a11, float a12, float a13, float a20, float a21, 33 | float a22, float a23, float a30, float a31, float a32, 34 | float a33); 35 | 36 | void premultiply(TransformMatrix* transform); 37 | void premultiply(TransformMatrix* transform, TransformMatrix* destination); 38 | void scaleUniform(float scale); 39 | bool isIdentity(); 40 | void getTransformTRS(TransformTRS* out); 41 | TransformTRS* getTransformTRS(); 42 | 43 | Transform* clone(); 44 | }; 45 | 46 | class TransformTRS : public Transform { 47 | public: 48 | float translation[3]; 49 | float rotation[4]; 50 | float scale[3]; 51 | 52 | TransformTRS(); 53 | bool isIdentityTranslation(); 54 | bool isIdentityRotation(); 55 | bool isIdentityScale(); 56 | TransformMatrix* getTransformMatrix(); 57 | 58 | Transform* clone(); 59 | }; 60 | 61 | ~Node(); 62 | 63 | GLTF::Camera* camera = NULL; 64 | std::vector children; 65 | GLTF::Skin* skin = NULL; 66 | std::string jointName; 67 | GLTF::Mesh* mesh = NULL; 68 | GLTF::MaterialCommon::Light* light = NULL; 69 | 70 | Transform* transform = NULL; 71 | 72 | // Special implementation that takes a predicate so mappings can be 73 | // updated for all children. 74 | GLTF::Object* clone( 75 | GLTF::Node* node, 76 | const std::function& predicate); 77 | 78 | virtual std::string typeName(); 79 | virtual GLTF::Object* clone(GLTF::Object* clone); 80 | virtual void writeJSON(void* writer, GLTF::Options* options); 81 | }; 82 | } // namespace GLTF 83 | -------------------------------------------------------------------------------- /GLTF/test/src/GLTFAccessorTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFAccessorTest.h" 3 | 4 | #include "GLTFAccessor.h" 5 | 6 | TEST(GLTFAccessorTest, CreateFromData) { 7 | float points[12] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8 | 7.0, 8.0, 9.0, 10.0, 11.0, 12.0}; 9 | GLTF::Accessor* accessor = new GLTF::Accessor( 10 | GLTF::Accessor::Type::VEC3, GLTF::Constants::WebGL::FLOAT, 11 | (unsigned char*)points, 4, GLTF::Constants::WebGL::ARRAY_BUFFER); 12 | float* min = accessor->min; 13 | ASSERT_TRUE(min != NULL); 14 | EXPECT_EQ(min[0], 1.0); 15 | EXPECT_EQ(min[1], 2.0); 16 | EXPECT_EQ(min[2], 3.0); 17 | 18 | float* max = accessor->max; 19 | ASSERT_TRUE(max != NULL); 20 | EXPECT_EQ(max[0], 10.0); 21 | EXPECT_EQ(max[1], 11.0); 22 | EXPECT_EQ(max[2], 12.0); 23 | 24 | float component[3]; 25 | for (int i = 0; i < accessor->count; i++) { 26 | accessor->getComponentAtIndex(i, component); 27 | EXPECT_EQ(component[0], i * 3 + 1); 28 | EXPECT_EQ(component[1], i * 3 + 2); 29 | EXPECT_EQ(component[2], i * 3 + 3); 30 | } 31 | delete accessor; 32 | } 33 | 34 | TEST(GLTFAccessorTest, CreateOnBuffer) { 35 | float pointsBuffer[12] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 36 | 7.0, 8.0, 9.0, 10.0, 11.0, 12.0}; 37 | GLTF::Accessor* accessorFromData = new GLTF::Accessor( 38 | GLTF::Accessor::Type::VEC3, GLTF::Constants::WebGL::FLOAT, 39 | (unsigned char*)pointsBuffer, 4, GLTF::Constants::WebGL::ARRAY_BUFFER); 40 | GLTF::BufferView* bufferView = accessorFromData->bufferView; 41 | float points[6] = {13.0, 14.0, 15.0, 16.0, 17.0, 18.0}; 42 | GLTF::Accessor* accessor = new GLTF::Accessor( 43 | GLTF::Accessor::Type::VEC3, GLTF::Constants::WebGL::FLOAT, 44 | (unsigned char*)points, 2, bufferView); 45 | 46 | float* min = accessor->min; 47 | ASSERT_TRUE(min != NULL); 48 | EXPECT_EQ(min[0], 13.0); 49 | EXPECT_EQ(min[1], 14.0); 50 | EXPECT_EQ(min[2], 15.0); 51 | 52 | float* max = accessor->max; 53 | ASSERT_TRUE(max != NULL); 54 | EXPECT_EQ(max[0], 16.0); 55 | EXPECT_EQ(max[1], 17.0); 56 | EXPECT_EQ(max[2], 18.0); 57 | 58 | float component[3]; 59 | for (int i = 0; i < accessor->count; i++) { 60 | accessor->getComponentAtIndex(i, component); 61 | EXPECT_EQ(component[0], (i + 4) * 3 + 1); 62 | EXPECT_EQ(component[1], (i + 4) * 3 + 2); 63 | EXPECT_EQ(component[2], (i + 4) * 3 + 3); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /GLTF/include/GLTFAsset.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "GLTFAnimation.h" 9 | #include "GLTFDracoExtension.h" 10 | #include "GLTFObject.h" 11 | #include "GLTFScene.h" 12 | #include "draco/compression/encode.h" 13 | 14 | namespace GLTF { 15 | class Asset : public GLTF::Object { 16 | private: 17 | std::vector _ambientLights; 18 | 19 | public: 20 | class Metadata : public GLTF::Object { 21 | public: 22 | std::string copyright; 23 | std::string generator = "COLLADA2GLTF"; 24 | std::string version = "2.0"; 25 | virtual void writeJSON(void* writer, GLTF::Options* options); 26 | }; 27 | 28 | GLTF::Sampler* globalSampler = NULL; 29 | 30 | Metadata* metadata = NULL; 31 | std::set extensionsUsed; 32 | std::set extensionsRequired; 33 | 34 | std::vector scenes; 35 | std::vector animations; 36 | int scene = -1; 37 | 38 | Asset(); 39 | virtual ~Asset(); 40 | 41 | GLTF::Scene* getDefaultScene(); 42 | std::vector getAllAccessors(); 43 | std::vector getAllNodes(); 44 | std::vector getAllMeshes(); 45 | std::vector getAllPrimitives(); 46 | std::vector getAllSkins(); 47 | std::vector getAllMaterials(); 48 | std::vector getAllTechniques(); 49 | std::vector getAllPrograms(); 50 | std::vector getAllShaders(); 51 | std::vector getAllTextures(); 52 | std::vector getAllImages(); 53 | std::vector getAllPrimitiveAccessors( 54 | GLTF::Primitive* primitive) const; 55 | std::vector getAllBufferViews(); 56 | std::vector getAllBuffers(); 57 | std::vector getAllCameras(); 58 | std::vector getAllLights(); 59 | 60 | void mergeAnimations(); 61 | void mergeAnimations(std::vector> groups); 62 | void removeUnusedSemantics(); 63 | void removeUnusedNodes(GLTF::Options* options); 64 | GLTF::Buffer* packAccessors(); 65 | 66 | // Functions for Draco compression extension. 67 | std::vector getAllCompressedBufferView(); 68 | bool compressPrimitives(GLTF::Options* options); 69 | void removeUncompressedBufferViews(); 70 | void removeAttributeFromDracoExtension(GLTF::Primitive* primitive, 71 | const std::string& semantic); 72 | 73 | void requireExtension(std::string extension); 74 | void useExtension(std::string extension); 75 | virtual void writeJSON(void* writer, GLTF::Options* options); 76 | }; 77 | } // namespace GLTF 78 | -------------------------------------------------------------------------------- /src/COLLADA2GLTFExtrasHandler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "COLLADA2GLTFExtrasHandler.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | bool COLLADA2GLTF::ExtrasHandler::elementBegin( 9 | const COLLADASaxFWL::ParserChar* elementName, 10 | const GeneratedSaxParser::xmlChar** attributes) { 11 | std::string name = std::string(elementName); 12 | if (name == "ambient_diffuse_lock") { 13 | lockAmbientDiffuse.insert(_currentId); 14 | } else if (name == "bump") { 15 | _inBump = true; 16 | } else if (_inBump && name == "texture" && attributes != NULL) { 17 | const COLLADASaxFWL::FileLoader* fileLoader = _loader->getFileLoader(); 18 | COLLADASaxFWL::LibraryEffectsLoader* effectsLoader = 19 | (COLLADASaxFWL::LibraryEffectsLoader*)fileLoader->getPartLoader(); 20 | COLLADAFW::Effect* bumpEffect = 21 | (COLLADAFW::Effect*)effectsLoader->getObject(); 22 | COLLADAFW::TextureAttributes* bumpTexture = 23 | bumpEffect->createExtraTextureAttributes(); 24 | bumpTextures[_currentId] = bumpTexture; 25 | 26 | size_t index = 0; 27 | const GeneratedSaxParser::xmlChar* attributeKey = attributes[index++]; 28 | const GeneratedSaxParser::xmlChar* attributeValue = 0; 29 | while (attributeKey != 0) { 30 | std::string key = std::string(attributeKey); 31 | attributeValue = attributes[index++]; 32 | if (attributeValue != 0) { 33 | std::string value = std::string(attributeValue); 34 | if (key == "texture") { 35 | bumpTexture->textureSampler = std::string(attributeValue); 36 | } else if (key == "texcoord") { 37 | bumpTexture->texCoord = std::string(attributeValue); 38 | } 39 | } 40 | attributeKey = attributes[index++]; 41 | } 42 | } else if (name == "double_sided") { 43 | _inDoubleSided = true; 44 | } 45 | return true; 46 | } 47 | 48 | bool COLLADA2GLTF::ExtrasHandler::elementEnd( 49 | const COLLADASaxFWL::ParserChar* elementName) { 50 | std::string name = std::string(elementName); 51 | if (name == "bump") { 52 | _inBump = false; 53 | } else if (name == "double_sided") { 54 | _inDoubleSided = false; 55 | } 56 | return true; 57 | } 58 | 59 | bool COLLADA2GLTF::ExtrasHandler::parseElement( 60 | const COLLADASaxFWL::ParserChar* profileName, 61 | const COLLADASaxFWL::StringHash& elementHash, 62 | const COLLADAFW::UniqueId& uniqueId, COLLADAFW::Object* object) { 63 | _currentId = uniqueId; 64 | return true; 65 | } 66 | 67 | bool COLLADA2GLTF::ExtrasHandler::textData( 68 | const COLLADASaxFWL::ParserChar* text, size_t textLength) { 69 | if (_inDoubleSided) { 70 | std::string flag = std::string(text, textLength); 71 | if (flag == "1" || flag == "true") { 72 | doubleSided.insert(_currentId); 73 | } 74 | } 75 | return true; 76 | } 77 | -------------------------------------------------------------------------------- /GLTF/src/Base64.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "Base64.h" 3 | 4 | #include 5 | 6 | static inline bool is_base64(unsigned char c) { 7 | return (isalnum(c) || (c == '+') || (c == '/')); 8 | } 9 | 10 | static const char base64CharSet[] = 11 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 12 | char* Base64::encode(unsigned char* data, size_t length) { 13 | size_t base64Length = (size_t)(ceil(length / 3.0) * 4) + 1; 14 | char* base64 = new char[base64Length]; 15 | int index; 16 | int j = 0; 17 | for (size_t i = 0; i < length; i += 3) { 18 | index = (data[i] & 0xFC) >> 2; 19 | base64[j] = base64CharSet[index]; 20 | j++; 21 | index = (data[i] & 0x03) << 4; 22 | if (i + 1 < length) { 23 | index |= (data[i + 1] & 0xF0) >> 4; 24 | base64[j] = base64CharSet[index]; 25 | j++; 26 | index = (data[i + 1] & 0x0F) << 2; 27 | if (i + 2 < length) { 28 | index |= (data[i + 2] & 0xC0) >> 6; 29 | base64[j] = base64CharSet[index]; 30 | j++; 31 | index = data[i + 2] & 0x3F; 32 | base64[j] = base64CharSet[index]; 33 | j++; 34 | } else { 35 | base64[j] = base64CharSet[index]; 36 | base64[j + 1] = '='; 37 | } 38 | } else { 39 | base64[j] = base64CharSet[index]; 40 | base64[j + 1] = '='; 41 | base64[j + 2] = '='; 42 | } 43 | } 44 | base64[base64Length - 1] = '\0'; 45 | return base64; 46 | } 47 | 48 | std::string Base64::decode(std::string string) { 49 | size_t length = string.size(); 50 | size_t i = 0; 51 | size_t j = 0; 52 | int in_ = 0; 53 | unsigned char char_array_4[4], char_array_3[3]; 54 | std::string ret; 55 | 56 | while (length-- && (string[in_] != '=') && is_base64(string[in_])) { 57 | char_array_4[i++] = string[in_]; 58 | in_++; 59 | if (i == 4) { 60 | for (i = 0; i < 4; i++) { 61 | char_array_4[i] = static_cast( 62 | std::string(base64CharSet).find(char_array_4[i])); 63 | } 64 | 65 | char_array_3[0] = 66 | (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 67 | char_array_3[1] = 68 | ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 69 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 70 | 71 | for (i = 0; i < 3; i++) { 72 | ret += char_array_3[i]; 73 | } 74 | i = 0; 75 | } 76 | } 77 | 78 | if (i) { 79 | for (j = i; j < 4; j++) { 80 | char_array_4[j] = 0; 81 | } 82 | for (j = 0; j < 4; j++) { 83 | char_array_4[j] = static_cast( 84 | std::string(base64CharSet).find(char_array_4[j])); 85 | } 86 | 87 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 88 | char_array_3[1] = 89 | ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 90 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 91 | 92 | for (j = 0; (j < i - 1); j++) { 93 | ret += char_array_3[j]; 94 | } 95 | } 96 | return ret; 97 | } 98 | -------------------------------------------------------------------------------- /GLTF/src/GLTFPrimitive.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFPrimitive.h" 3 | 4 | #include 5 | 6 | #include "rapidjson/stringbuffer.h" 7 | #include "rapidjson/writer.h" 8 | 9 | GLTF::Primitive::~Primitive() { 10 | std::for_each(targets.begin(), targets.end(), std::default_delete()); 11 | } 12 | 13 | GLTF::Object* GLTF::Primitive::clone(GLTF::Object* clone) { 14 | GLTF::Primitive* primitive = dynamic_cast(clone); 15 | if (primitive != NULL) { 16 | primitive->attributes = std::map(); 17 | for (const auto& attribute : this->attributes) { 18 | primitive->attributes.insert(attribute); 19 | } 20 | primitive->indices = this->indices; 21 | primitive->material = this->material; 22 | primitive->mode = this->mode; 23 | primitive->targets = std::vector(); 24 | for (auto* target : this->targets) { 25 | primitive->targets.push_back(target); 26 | } 27 | } 28 | Object::clone(clone); 29 | return primitive; 30 | } 31 | 32 | void GLTF::Primitive::writeJSON(void* writer, Options* options) { 33 | auto* jsonWriter = 34 | static_cast*>(writer); 35 | jsonWriter->Key("attributes"); 36 | jsonWriter->StartObject(); 37 | for (const auto& attribute : this->attributes) { 38 | jsonWriter->Key(attribute.first.c_str()); 39 | if (options->version == "1.0") { 40 | jsonWriter->String(attribute.second->getStringId().c_str()); 41 | } else { 42 | jsonWriter->Int(attribute.second->id); 43 | } 44 | } 45 | jsonWriter->EndObject(); 46 | if (this->indices) { 47 | jsonWriter->Key("indices"); 48 | if (options->version == "1.0") { 49 | jsonWriter->String(indices->getStringId().c_str()); 50 | } else { 51 | jsonWriter->Int(indices->id); 52 | } 53 | } 54 | jsonWriter->Key("mode"); 55 | jsonWriter->Int(static_cast(this->mode)); 56 | if (this->material) { 57 | jsonWriter->Key("material"); 58 | if (options->version == "1.0") { 59 | jsonWriter->String(material->getStringId().c_str()); 60 | } else { 61 | jsonWriter->Int(material->id); 62 | } 63 | } 64 | if (!this->targets.empty()) { 65 | jsonWriter->Key("targets"); 66 | jsonWriter->StartArray(); 67 | for (auto* target : this->targets) { 68 | target->writeJSON(writer, options); 69 | } 70 | jsonWriter->EndArray(); 71 | } 72 | Object::writeJSON(writer, options); 73 | } 74 | 75 | GLTF::Primitive::Target* GLTF::Primitive::Target::clone(Object* clone) { 76 | Target* target = dynamic_cast(clone); 77 | if (target != nullptr) { 78 | target->attributes = std::map(); 79 | for (const auto& attribute : this->attributes) { 80 | target->attributes.insert(attribute); 81 | } 82 | } 83 | return target; 84 | } 85 | 86 | void GLTF::Primitive::Target::writeJSON(void* writer, Options* options) { 87 | auto* jsonWriter = 88 | static_cast*>(writer); 89 | jsonWriter->StartObject(); 90 | for (const auto& attribute : this->attributes) { 91 | jsonWriter->Key(attribute.first.c_str()); 92 | jsonWriter->Int(attribute.second->id); 93 | } 94 | jsonWriter->EndObject(); 95 | } 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # COLLADA2GLTF 6 | [![Build Status](https://github.com/KhronosGroup/COLLADA2GLTF/workflows/CI/badge.svg)](https://github.com/KhronosGroup/COLLADA2GLTF/actions?query=workflow%3ACI) 7 | 8 | # COLLADA to glTF converter 9 | 10 | A command-line tool to convert COLLADA (`.dae`) files to [glTF](https://github.com/KhronosGroup/glTF). 11 | 12 | ## Releases 13 | 14 | Compiled binaries for Windows, MacOS, and Linux can be found under [releases](https://github.com/KhronosGroup/COLLADA2GLTF/releases). It is recommended to use the last versioned release 15 | 16 | ## Compile from source 17 | 18 | 1. Clone repository 19 | 20 | ```bash 21 | git clone --recursive https://github.com/KhronosGroup/COLLADA2GLTF.git 22 | ``` 23 | 2. Compile 24 | 25 | ```bash 26 | cd COLLADA2GLTF 27 | mkdir build 28 | cd build 29 | cmake .. #-Dtest=ON 30 | # Linux 31 | make 32 | # Windows 33 | ## Open the generated COLLADA2GLTF.sln in Visual Studio and build 34 | ``` 35 | 36 | 3. Run 37 | 38 | ```bash 39 | COLLADA2GLTF-bin[.exe] 40 | ``` 41 | 42 | 4. Run tests 43 | 44 | ```bash 45 | COLLADA2GLTF-test[.exe] 46 | GLTF-test[.exe] 47 | ``` 48 | 49 | ## Usage 50 | 51 | ```bash 52 | COLLADA2GLTF[.exe] [input] [output] [options] 53 | ``` 54 | ### Options 55 | | Flag | Default | Required | Description | 56 | | --- | --- | --- | --- | 57 | | -i, --input | | Yes :white_check_mark: | Path to the input COLLADA file | 58 | | -o, --output | output/${input}.gltf | No | Path to the output glTF file | 59 | | --basepath | Parent of input path | No | Resolve external uris using this as the reference path | 60 | | -s, --separate | false | No | Output separate binary buffer, shaders, and textures | 61 | | -t, --separateTextures | false | No | Output textures separately | 62 | | -b, --binary | false | No | Output Binary glTF | 63 | | -m, --materialsCommon | false | No | Output materials using the KHR_materials_common extension | 64 | | -v, --version | | No | glTF version to output (e.g. '1.0', '2.0') | 65 | | -d, --dracoCompression | false | No | Output meshes using Draco compression extension | 66 | | --qp | | No | Quantization bits used for position attributes in Draco compression extension | 67 | | --qn | | No | Quantization bits used for normal attributes in Draco compression extension | 68 | | --qt | | No | Quantization bits used for texcoord attributes in Draco compression extension | 69 | | --qc | | No | Quantization bits used for color attributes in Draco compression extension | 70 | | --qj | | No | Quantization bits used for joint indice and weight attributes in Draco compression extension | 71 | | --metallicRoughnessTextures | | No | Paths to images to use as the PBR metallicRoughness textures | 72 | | --specularGlossiness | false | No | output PBR materials with the KHR_materials_pbrSpecularGlossiness extension | 73 | | --lockOcclusionMetallicRoughness | false | No | Set `metallicRoughnessTexture` to be the same as the `occlusionTexture` in materials where an ambient texture is defined | 74 | | --doubleSided | false | No | Force all materials to be double sided. When this value is true, back-face culling is disabled and double sided lighting is enabled | 75 | | --preserveUnusedSemantics | false | No | Don't optimize out primitive semantics and their data, even if they aren't used. | 76 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | ### Next Release 4 | ##### Additions :tada: 5 | * Switch CI builds to GitHub actions [#264](https://github.com/KhronosGroup/COLLADA2GLTF/pull/264) 6 | * Large-scale code cleanup and add cppcheck to CI to prevent backsliding [#265](https://github.com/KhronosGroup/COLLADA2GLTF/pull/265) 7 | 8 | ##### Fixes :wrench: 9 | * De-duplicate GLTF generated materials [#251](https://github.com/KhronosGroup/COLLADA2GLTF/issues/251) 10 | * Fix seg-fault exporting GLTF 1.0 [#261](https://github.com/KhronosGroup/COLLADA2GLTF/issues/261) 11 | 12 | ### v2.1.5 - 2019-05-22 13 | 14 | ##### Additions :tada: 15 | * Added support for morph targets [#166](https://github.com/KhronosGroup/COLLADA2GLTF/issues/166) 16 | * Support converting models with transparency [#168](https://github.com/KhronosGroup/COLLADA2GLTF/issues/168) 17 | * Added support for exporting COLLADA animation clips as groups [#227](https://github.com/KhronosGroup/COLLADA2GLTF/pull/227) 18 | * Clean up glTF tree when the asset is freed [#229](https://github.com/KhronosGroup/COLLADA2GLTF/issues/229) 19 | * Added `--preserveUnusedSemantics` option [#245](https://github.com/KhronosGroup/COLLADA2GLTF/pull/245) 20 | * Added end-to-end validation to CI [#235](https://github.com/KhronosGroup/COLLADA2GLTF/pull/235) 21 | 22 | ##### Fixes :wrench: 23 | * Library nodes should only be written as part of a scene [#236](https://github.com/KhronosGroup/COLLADA2GLTF/issues/236) 24 | 25 | ### v2.1.4 - 2018-08-29 26 | 27 | ##### Additions :tada: 28 | * Added support for multiple maps [#169](https://github.com/KhronosGroup/COLLADA2GLTF/issues/169) 29 | 30 | ##### Fixes :wrench: 31 | * Fixed issue with relative path resolution on Windows [#200](https://github.com/KhronosGroup/COLLADA2GLTF/issues/200) 32 | * Updated to OpenCOLLADA 1.6.63 33 | * Resolves issue where image elements declared in profile_COMMON are not written [#129](https://github.com/KhronosGroup/COLLADA2GLTF/issues/129) and [#114](https://github.com/KhronosGroup/COLLADA2GLTF/issues/114) 34 | * Remove Windows debug builds from CI - as per [Microsoft](https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-6.0/aa260978(v=vs.60)#a-list-of-redistributable-files), they are not to be redistributed 35 | 36 | ### v2.1.3 - 2018-07-01 37 | 38 | ##### Additions :tada: 39 | * Enable OS X builds in CI [#191](https://github.com/KhronosGroup/COLLADA2GLTF/pull/191) 40 | 41 | ##### Fixes :wrench: 42 | * Bone weights are normalized, resolving some rendering issues using logarithmic depth buffers [#187](https://github.com/KhronosGroup/COLLADA2GLTF/pull/187) 43 | 44 | ### v2.1.2 - 2018-04-22 45 | 46 | ##### Fixes :wrench: 47 | * Upgrade to Draco 1.2.5 [#179](https://github.com/KhronosGroup/COLLADA2GLTF/pull/179) 48 | * Fix issue with exporting normal maps [#182](https://github.com/KhronosGroup/COLLADA2GLTF/pull/182) 49 | * Fix issue with COLLADA textures with no backing image [#183](https://github.com/KhronosGroup/COLLADA2GLTF/pull/183) 50 | 51 | ### v2.1.1 - 2018-04-04 52 | 53 | ##### Additions :tada: 54 | * Add support for exporting double sided materials [#133](https://github.com/KhronosGroup/COLLADA2GLTF/pull/133) 55 | 56 | ##### Fixes :wrench: 57 | * Fixed issue where incorrect mime types were being generated [#162](https://github.com/KhronosGroup/COLLADA2GLTF/pull/164) 58 | 59 | ### v2.1.0 - 2018-03-15 60 | 61 | ##### Additions :tada: 62 | * Add support for exporting glTF 1.0 [#69](https://github.com/KhronosGroup/COLLADA2GLTF/issues/69) 63 | 64 | ##### Fixes :wrench: 65 | * Export UNSIGNED_INT indices for large primitives [#123](https://github.com/KhronosGroup/COLLADA2GLTF/issues/123) 66 | * Fixed issues with mesh instancing [#135](https://github.com/KhronosGroup/COLLADA2GLTF/issues/135) 67 | 68 | ### v2.0.0 - 2017-08-27 69 | 70 | ##### Highlights :sparkler: 71 | * Rewrite of legacy converter for exporting glTF 2.0 [#3](https://github.com/KhronosGroup/COLLADA2GLTF/issues/3) 72 | -------------------------------------------------------------------------------- /GLTF/src/GLTFTechnique.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFTechnique.h" 3 | 4 | #include "rapidjson/stringbuffer.h" 5 | #include "rapidjson/writer.h" 6 | 7 | GLTF::Technique::~Technique() { 8 | for (auto& kv : parameters) { 9 | delete kv.second; 10 | } 11 | } 12 | 13 | std::string GLTF::Technique::typeName() { return "technique"; } 14 | 15 | void GLTF::Technique::writeJSON(void* writer, GLTF::Options* options) { 16 | rapidjson::Writer* jsonWriter = 17 | (rapidjson::Writer*)writer; 18 | 19 | jsonWriter->Key("attributes"); 20 | jsonWriter->StartObject(); 21 | for (auto attribute : attributes) { 22 | jsonWriter->Key(attribute.first.c_str()); 23 | jsonWriter->String(attribute.second.c_str()); 24 | } 25 | jsonWriter->EndObject(); 26 | 27 | jsonWriter->Key("parameters"); 28 | jsonWriter->StartObject(); 29 | for (auto parameter : parameters) { 30 | jsonWriter->Key(parameter.first.c_str()); 31 | jsonWriter->StartObject(); 32 | GLTF::Technique::Parameter* parameterValue = parameter.second; 33 | std::string semantic = parameterValue->semantic; 34 | if (semantic != "") { 35 | jsonWriter->Key("semantic"); 36 | jsonWriter->String(semantic.c_str()); 37 | } 38 | if (options->version == "1.0") { 39 | if (parameterValue->nodeString != "") { 40 | jsonWriter->Key("node"); 41 | jsonWriter->String(parameterValue->nodeString.c_str()); 42 | } 43 | } else { 44 | if (parameterValue->node >= 0) { 45 | jsonWriter->Key("node"); 46 | jsonWriter->Int(parameterValue->node); 47 | } 48 | } 49 | if (parameterValue->value != NULL) { 50 | jsonWriter->Key("value"); 51 | jsonWriter->StartArray(); 52 | for (int i = 0; i < parameterValue->valueLength; i++) { 53 | jsonWriter->Double(parameterValue->value[i]); 54 | } 55 | jsonWriter->EndArray(); 56 | } 57 | if (parameterValue->count >= 0) { 58 | jsonWriter->Key("count"); 59 | jsonWriter->Int(parameterValue->count); 60 | } 61 | jsonWriter->Key("type"); 62 | jsonWriter->Int(static_cast(parameterValue->type)); 63 | jsonWriter->EndObject(); 64 | } 65 | jsonWriter->EndObject(); 66 | 67 | if (program != NULL) { 68 | jsonWriter->Key("program"); 69 | if (options->version == "1.0") { 70 | jsonWriter->String(program->getStringId().c_str()); 71 | } else { 72 | jsonWriter->Int(program->id); 73 | } 74 | } 75 | 76 | if (enableStates.size() > 0 || depthMask != NULL || 77 | blendEquationSeparate.size() > 0 || blendFuncSeparate.size() > 0) { 78 | jsonWriter->Key("states"); 79 | jsonWriter->StartObject(); 80 | if (enableStates.size() > 0) { 81 | jsonWriter->Key("enable"); 82 | jsonWriter->StartArray(); 83 | for (GLTF::Constants::WebGL constant : enableStates) { 84 | jsonWriter->Int(static_cast(constant)); 85 | } 86 | jsonWriter->EndArray(); 87 | } 88 | if (depthMask != NULL) { 89 | jsonWriter->Key("depthMask"); 90 | jsonWriter->Bool(depthMask[0]); 91 | } 92 | if (blendEquationSeparate.size() > 0 || blendFuncSeparate.size() > 0) { 93 | jsonWriter->Key("functions"); 94 | jsonWriter->StartObject(); 95 | if (blendEquationSeparate.size() > 0) { 96 | jsonWriter->Key("blendEquationSeparate"); 97 | jsonWriter->StartArray(); 98 | for (GLTF::Constants::WebGL constant : blendEquationSeparate) { 99 | jsonWriter->Int(static_cast(constant)); 100 | } 101 | jsonWriter->EndArray(); 102 | } 103 | if (blendFuncSeparate.size() > 0) { 104 | jsonWriter->Key("blendFuncSeparate"); 105 | jsonWriter->StartArray(); 106 | for (GLTF::Constants::WebGL constant : blendFuncSeparate) { 107 | jsonWriter->Int(static_cast(constant)); 108 | } 109 | jsonWriter->EndArray(); 110 | } 111 | jsonWriter->EndObject(); 112 | } 113 | jsonWriter->EndObject(); 114 | } 115 | 116 | if (uniforms.size() > 0) { 117 | jsonWriter->Key("uniforms"); 118 | jsonWriter->StartObject(); 119 | for (auto uniform : uniforms) { 120 | jsonWriter->Key(uniform.first.c_str()); 121 | jsonWriter->String(uniform.second.c_str()); 122 | } 123 | jsonWriter->EndObject(); 124 | } 125 | GLTF::Object::writeJSON(writer, options); 126 | } 127 | -------------------------------------------------------------------------------- /GLTF/include/GLTFMaterial.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "GLTFObject.h" 9 | #include "GLTFTechnique.h" 10 | #include "GLTFTexture.h" 11 | 12 | namespace GLTF { 13 | class Material : public GLTF::Object { 14 | public: 15 | enum Type { MATERIAL, MATERIAL_COMMON, PBR_METALLIC_ROUGHNESS, UNKNOWN }; 16 | 17 | class Values { 18 | public: 19 | float* ambient = NULL; 20 | GLTF::Texture* ambientTexture = NULL; 21 | int ambientTexCoord = 0; 22 | float* diffuse = NULL; 23 | GLTF::Texture* diffuseTexture = NULL; 24 | int diffuseTexCoord = 0; 25 | float* emission = NULL; 26 | GLTF::Texture* emissionTexture = NULL; 27 | int emissionTexCoord = 0; 28 | float* specular = NULL; 29 | GLTF::Texture* specularTexture = NULL; 30 | int specularTexCoord = 0; 31 | float* shininess = NULL; 32 | float* transparency = NULL; 33 | GLTF::Texture* bumpTexture = NULL; 34 | 35 | void writeJSON(void* writer, GLTF::Options* options); 36 | }; 37 | 38 | GLTF::Technique* technique = NULL; 39 | Type type = Type::UNKNOWN; 40 | Values* values = NULL; 41 | bool doubleSided = false; 42 | 43 | Material(); 44 | virtual ~Material(); 45 | 46 | Material(const Material&) = delete; 47 | Material& operator=(const Material&) = delete; 48 | Material(Material&&) = delete; 49 | Material& operator=(Material&&) = delete; 50 | 51 | bool hasTexture(); 52 | virtual std::string typeName(); 53 | virtual void writeJSON(void* writer, GLTF::Options* options); 54 | }; 55 | 56 | class MaterialPBR : public GLTF::Material { 57 | public: 58 | virtual ~MaterialPBR(); 59 | 60 | MaterialPBR(const MaterialPBR&) = delete; 61 | MaterialPBR& operator=(const MaterialPBR&) = delete; 62 | MaterialPBR(MaterialPBR&&) = delete; 63 | MaterialPBR& operator=(MaterialPBR&&) = delete; 64 | 65 | class Texture : public GLTF::Object { 66 | public: 67 | float scale = 1; 68 | GLTF::Texture* texture = NULL; 69 | int texCoord = -1; 70 | 71 | void writeJSON(void* writer, GLTF::Options* options); 72 | }; 73 | 74 | class MetallicRoughness : public GLTF::Object { 75 | public: 76 | virtual ~MetallicRoughness(); 77 | float* baseColorFactor = NULL; 78 | Texture* baseColorTexture = NULL; 79 | float metallicFactor = -1.0; 80 | float roughnessFactor = -1.0; 81 | Texture* metallicRoughnessTexture = NULL; 82 | 83 | void writeJSON(void* writer, GLTF::Options* options); 84 | }; 85 | 86 | class SpecularGlossiness : public GLTF::Object { 87 | public: 88 | virtual ~SpecularGlossiness(); 89 | float* diffuseFactor = NULL; 90 | Texture* diffuseTexture = NULL; 91 | float* specularFactor = NULL; 92 | Texture* specularGlossinessTexture = NULL; 93 | float* glossinessFactor = NULL; 94 | 95 | void writeJSON(void* writer, GLTF::Options* options); 96 | }; 97 | 98 | MetallicRoughness* metallicRoughness = NULL; 99 | Texture* normalTexture = NULL; 100 | Texture* occlusionTexture = NULL; 101 | float* emissiveFactor = NULL; 102 | Texture* emissiveTexture = NULL; 103 | SpecularGlossiness* specularGlossiness = NULL; 104 | 105 | /** Either "OPAQUE", "BLEND" or "MASK". Default = "OPAQUE" */ 106 | std::string alphaMode; 107 | 108 | /** Only when alphaMode == "MASK". Default = 0.5 */ 109 | float alphaCutoff = NAN; 110 | 111 | bool doubleSided = false; 112 | 113 | MaterialPBR(); 114 | void writeJSON(void* writer, GLTF::Options* options); 115 | }; 116 | 117 | class MaterialCommon : public GLTF::Material { 118 | public: 119 | enum Technique { BLINN, PHONG, LAMBERT, CONSTANT, UNKNOWN }; 120 | 121 | class Light : public GLTF::Object { 122 | public: 123 | enum Type { AMBIENT, DIRECTIONAL, POINT, SPOT, UNKOWN }; 124 | 125 | Type type = Type::UNKOWN; 126 | float color[4]; 127 | float constantAttenuation; 128 | float linearAttenuation; 129 | float quadraticAttenuation; 130 | void* node = NULL; 131 | 132 | virtual std::string typeName(); 133 | virtual void writeJSON(void* writer, GLTF::Options* options); 134 | }; 135 | 136 | int jointCount = 0; 137 | bool transparent = false; 138 | 139 | MaterialCommon::Technique technique = MaterialCommon::Technique::UNKNOWN; 140 | 141 | MaterialCommon(); 142 | 143 | MaterialCommon(const MaterialCommon&) = delete; 144 | MaterialCommon& operator=(const MaterialCommon&) = delete; 145 | MaterialCommon(MaterialCommon&&) = delete; 146 | MaterialCommon& operator=(MaterialCommon&&) = delete; 147 | 148 | const char* getTechniqueName(); 149 | GLTF::Material* getMaterial(std::vector lights, 150 | GLTF::Options* options); 151 | GLTF::Material* getMaterial(std::vector lights, 152 | bool hasColorAttribute, GLTF::Options* options); 153 | std::string getTechniqueKey(GLTF::Options* options); 154 | GLTF::MaterialPBR* getMaterialPBR(GLTF::Options* options); 155 | virtual void writeJSON(void* writer, GLTF::Options* options); 156 | }; 157 | } // namespace GLTF 158 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.1.0) 2 | 3 | set(PROJECT_NAME COLLADA2GLTF) 4 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 5 | set(CMAKE_CXX_STANDARD 11) 6 | set(CMAKE_CXX_EXTENSIONS OFF) 7 | include(ExternalProject) 8 | 9 | project(${PROJECT_NAME}) 10 | 11 | # cmake -Dtest=ON to build with tests 12 | option(test "Build all tests." OFF) 13 | 14 | # GLTF 15 | include_directories(GLTF/include) 16 | add_subdirectory(GLTF) 17 | 18 | # Disable for other modules 19 | set(TEST_ENABLED ${test}) 20 | set(test OFF) 21 | 22 | # RapidJSON 23 | if(DEFINED RAPIDJSON_INCLUDE_DIR) 24 | include_directories(${RAPIDJSON_INCLUDE_DIR}) 25 | else() 26 | include_directories(GLTF/dependencies/rapidjson/include) 27 | endif() 28 | 29 | # Draco 30 | if(DEFINED DRACO_INCLUDE_DIR) 31 | include_directories(${DRACO_INCLUDE_DIR}) 32 | else() 33 | include_directories(GLTF/dependencies/draco/src) 34 | endif() 35 | 36 | # Pre-compiled 37 | if(precompiled MATCHES "X86") 38 | if(precompiled MATCHES "DEBUG") 39 | if(MSVC) 40 | ExternalProject_Add( 41 | precompiled 42 | URL https://github.com/KhronosGroup/COLLADA2GLTF/releases/download/dependencies/dependencies-windows-Debug-Win32.zip 43 | CONFIGURE_COMMAND "" 44 | BUILD_COMMAND "" 45 | INSTALL_COMMAND "" 46 | ) 47 | set(PRECOMPILED_DIR ${CMAKE_CURRENT_BINARY_DIR}/precompiled-prefix/src/precompiled) 48 | endif() 49 | elseif(precompiled MATCHES "RELEASE") 50 | if(MSVC) 51 | ExternalProject_Add( 52 | precompiled 53 | URL https://github.com/KhronosGroup/COLLADA2GLTF/releases/download/dependencies/dependencies-windows-Release-Win32.zip 54 | CONFIGURE_COMMAND "" 55 | BUILD_COMMAND "" 56 | INSTALL_COMMAND "" 57 | ) 58 | set(PRECOMPILED_DIR ${CMAKE_CURRENT_BINARY_DIR}/precompiled-prefix/src/precompiled) 59 | endif() 60 | endif() 61 | elseif(precompiled MATCHES "X64") 62 | if(precompiled MATCHES "DEBUG") 63 | if(MSVC) 64 | ExternalProject_Add( 65 | precompiled 66 | URL https://github.com/KhronosGroup/COLLADA2GLTF/releases/download/dependencies/dependencies-windows-Debug-x64.zip 67 | CONFIGURE_COMMAND "" 68 | BUILD_COMMAND "" 69 | INSTALL_COMMAND "" 70 | ) 71 | set(PRECOMPILED_DIR ${CMAKE_CURRENT_BINARY_DIR}/precompiled-prefix/src/precompiled) 72 | endif() 73 | elseif(precompiled MATCHES "RELEASE") 74 | if(MSVC) 75 | ExternalProject_Add( 76 | precompiled 77 | URL https://github.com/KhronosGroup/COLLADA2GLTF/releases/download/dependencies/dependencies-windows-Release-x64.zip 78 | CONFIGURE_COMMAND "" 79 | BUILD_COMMAND "" 80 | INSTALL_COMMAND "" 81 | ) 82 | set(PRECOMPILED_DIR ${CMAKE_CURRENT_BINARY_DIR}/precompiled-prefix/src/precompiled) 83 | endif() 84 | endif() 85 | endif() 86 | 87 | if(PRECOMPILED_DIR) 88 | set(OpenCOLLADA 89 | ${PRECOMPILED_DIR}/COLLADABaseUtils.lib 90 | ${PRECOMPILED_DIR}/COLLADAFramework.lib 91 | ${PRECOMPILED_DIR}/COLLADASaxFrameworkLoader.lib 92 | ${PRECOMPILED_DIR}/expat.lib 93 | ${PRECOMPILED_DIR}/GeneratedSaxParser.lib 94 | ${PRECOMPILED_DIR}/LibXML.lib 95 | ${PRECOMPILED_DIR}/MathMLSolver.lib 96 | ${PRECOMPILED_DIR}/pcre.lib) 97 | endif() 98 | 99 | # COLLADASaxFrameworkLoader/BaseUtils/Framework, GeneratedSaxParser 100 | include_directories(dependencies/OpenCOLLADA/OpenCOLLADA/COLLADASaxFrameworkLoader/include) 101 | include_directories(dependencies/OpenCOLLADA/OpenCOLLADA/COLLADABaseUtils/include) 102 | include_directories(dependencies/OpenCOLLADA/OpenCOLLADA/COLLADAFramework/include) 103 | include_directories(dependencies/OpenCOLLADA/OpenCOLLADA/GeneratedSaxParser/include) 104 | include_directories(dependencies/OpenCOLLADA/OpenCOLLADA/Externals/pcre/include) 105 | if(NOT OpenCOLLADA) 106 | add_subdirectory(dependencies/OpenCOLLADA/modules/COLLADASaxFrameworkLoader) 107 | set(OpenCOLLADA COLLADASaxFrameworkLoader) 108 | endif() 109 | 110 | # COLLADA2GLTF 111 | include_directories(include) 112 | file(GLOB LIB_HEADERS "include/*.h") 113 | set(LIB_SOURCES src/COLLADA2GLTFWriter.cpp src/COLLADA2GLTFExtrasHandler.cpp) 114 | add_library(${PROJECT_NAME} ${LIB_HEADERS} ${LIB_SOURCES}) 115 | target_link_libraries(${PROJECT_NAME} GLTF ${OpenCOLLADA}) 116 | 117 | # ahoy 118 | include_directories(dependencies/ahoy/include) 119 | add_subdirectory(dependencies/ahoy) 120 | 121 | if(NOT NO_COLLADA2GLTF_BIN) 122 | add_executable(${PROJECT_NAME}-bin src/main.cpp) 123 | target_link_libraries(${PROJECT_NAME}-bin ${PROJECT_NAME} ahoy draco) 124 | endif() 125 | 126 | if(TEST_ENABLED) 127 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 128 | enable_testing() 129 | 130 | # gtest 131 | set(GTEST_LINKED 1) 132 | include_directories(GLTF/dependencies/googletest/googletest/include) 133 | add_subdirectory(GLTF/dependencies/googletest/googletest) 134 | 135 | # Unit Tests 136 | include_directories(test/include) 137 | file(GLOB TEST_HEADERS "test/include/*.h") 138 | file(GLOB TEST_SOURCES "test/src/*.cpp") 139 | 140 | add_executable(${PROJECT_NAME}-test ${TEST_HEADERS} ${TEST_SOURCES}) 141 | target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME} GLTF gtest) 142 | 143 | add_test(COLLADA2GLTFWriterTest ${PROJECT_NAME}-test) 144 | endif() 145 | -------------------------------------------------------------------------------- /dependencies/OpenCOLLADA/modules/COLLADASaxFrameworkLoader/GeneratedSaxParser/LibXML/include/config-mac.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | /* config.h generated manually for macos. */ 5 | 6 | /* Define if you have the strftime function. */ 7 | #define HAVE_STRFTIME 8 | 9 | /* Define if you have the ANSI C header files. */ 10 | #define STDC_HEADERS 11 | 12 | #define PACKAGE 13 | #define VERSION 14 | 15 | #undef HAVE_LIBZ 16 | #undef HAVE_LIBM 17 | #undef HAVE_ISINF 18 | #undef HAVE_ISNAN 19 | #undef HAVE_LIBHISTORY 20 | #undef HAVE_LIBREADLINE 21 | 22 | #define XML_SOCKLEN_T socklen_t 23 | #define HAVE_LIBPTHREAD 24 | #define HAVE_PTHREAD_H 25 | #define LIBXML_THREAD_ENABLED 26 | 27 | /* Define if you have the _stat function. */ 28 | #define HAVE__STAT 29 | 30 | /* Define if you have the class function. */ 31 | #undef HAVE_CLASS 32 | 33 | /* Define if you have the finite function. */ 34 | #undef HAVE_FINITE 35 | 36 | /* Define if you have the fp_class function. */ 37 | #undef HAVE_FP_CLASS 38 | 39 | /* Define if you have the fpclass function. */ 40 | #undef HAVE_FPCLASS 41 | 42 | /* Define if you have the fprintf function. */ 43 | #define HAVE_FPRINTF 44 | 45 | /* Define if you have the isnand function. */ 46 | #undef HAVE_ISNAND 47 | 48 | /* Define if you have the localtime function. */ 49 | #define HAVE_LOCALTIME 50 | 51 | /* Define if you have the printf function. */ 52 | #define HAVE_PRINTF 53 | 54 | /* Define if you have the signal function. */ 55 | #define HAVE_SIGNAL 56 | 57 | /* Define if you have the snprintf function. */ 58 | #define HAVE_SNPRINTF 59 | 60 | /* Define if you have the sprintf function. */ 61 | #define HAVE_SPRINTF 62 | 63 | /* Define if you have the sscanf function. */ 64 | #define HAVE_SSCANF 65 | 66 | /* Define if you have the stat function. */ 67 | #define HAVE_STAT 68 | 69 | /* Define if you have the strdup function. */ 70 | #define HAVE_STRDUP 71 | 72 | /* Define if you have the strerror function. */ 73 | #define HAVE_STRERROR 74 | 75 | /* Define if you have the strftime function. */ 76 | #define HAVE_STRFTIME 77 | 78 | /* Define if you have the strndup function. */ 79 | #define HAVE_STRNDUP 80 | 81 | /* Define if you have the vfprintf function. */ 82 | #define HAVE_VFPRINTF 83 | 84 | /* Define if you have the vsnprintf function. */ 85 | #define HAVE_VSNPRINTF 86 | 87 | /* Define if you have the vsprintf function. */ 88 | #define HAVE_VSPRINTF 89 | 90 | /* Define if you have the header file. */ 91 | #define HAVE_ARPA_INET_H 92 | 93 | /* Define if you have the header file. */ 94 | #define HAVE_CTYPE_H 95 | 96 | /* Define if you have the header file. */ 97 | #define HAVE_DIRENT_H 98 | 99 | /* Define if you have the header file. */ 100 | #define HAVE_DLFCN_H 101 | 102 | /* Define if you have the header file. */ 103 | #define HAVE_ERRNO_H 104 | 105 | /* Define if you have the header file. */ 106 | #define HAVE_FCNTL_H 107 | 108 | /* Define if you have the header file. */ 109 | #define HAVE_FLOAT_H 110 | 111 | /* Define if you have the header file. */ 112 | #define HAVE_FP_CLASS_H 113 | 114 | /* Define if you have the header file. */ 115 | #define HAVE_IEEEFP_H 116 | 117 | /* Define if you have the header file. */ 118 | #undef HAVE_MALLOC_H 119 | 120 | /* Define if you have the header file. */ 121 | #define HAVE_MATH_H 122 | 123 | /* Define if you have the header file. */ 124 | #define HAVE_NAN_H 125 | 126 | /* Define if you have the header file. */ 127 | #define HAVE_NDIR_H 128 | 129 | /* Define if you have the header file. */ 130 | #define HAVE_NETDB_H 131 | 132 | /* Define if you have the header file. */ 133 | #define HAVE_NETINET_IN_H 134 | 135 | /* Define if you have the header file. */ 136 | #define HAVE_SIGNAL_H 137 | 138 | /* Define if you have the header file. */ 139 | #define HAVE_STDARG_H 140 | 141 | /* Define if you have the header file. */ 142 | #define HAVE_STDLIB_H 143 | 144 | /* Define if you have the header file. */ 145 | #define HAVE_STRING_H 146 | 147 | /* Define if you have the header file. */ 148 | #define HAVE_SYS_DIR_H 149 | 150 | /* Define if you have the header file. */ 151 | #undef HAVE_SYS_MMAN_H 152 | 153 | /* Define if you have the header file. */ 154 | #undef HAVE_SYS_NDIR_H 155 | 156 | /* Define if you have the header file. */ 157 | #define HAVE_SYS_SELECT_H 158 | 159 | /* Define if you have the header file. */ 160 | #define HAVE_SYS_SOCKET_H 161 | 162 | /* Define if you have the header file. */ 163 | #define HAVE_SYS_STAT_H 164 | 165 | /* Define if you have the header file. */ 166 | #define HAVE_SYS_TIME_H 167 | 168 | /* Define if you have the header file. */ 169 | #define HAVE_SYS_TYPES_H 170 | 171 | /* Define if you have the header file. */ 172 | #define HAVE_TIME_H 173 | 174 | /* Define if you have the header file. */ 175 | #define HAVE_UNISTD_H 176 | 177 | /* Define if you have the header file. */ 178 | #undef HAVE_ZLIB_H 179 | 180 | /* Name of package */ 181 | #define PACKAGE 182 | 183 | /* Version number of package */ 184 | #define VERSION 185 | 186 | /* Define if compiler has function prototypes */ 187 | #define PROTOTYPES 188 | 189 | #include 190 | #include 191 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Original work Copyright (c) 2012, Motorola Mobility, Inc. 2 | All Rights Reserved. 3 | BSD License. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | - Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | - Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | - Neither the name of Motorola Mobility nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | 29 | Additional contributions copyright their respective contributors (also BSD License): 30 | * Fabrice Robinet 31 | * Analytical Graphics, Inc. 32 | 33 | Third-Party Libraries 34 | ===================== 35 | 36 | 37 | OpenCOLLADA 38 | ----------- 39 | 40 | Copyright (c) 2008-2009 NetAllied Systems GmbH 41 | 42 | This file is part of COLLADAMax. 43 | 44 | Portions of the code are: 45 | Copyright (c) 2005-2007 Feeling Software Inc. 46 | Copyright (c) 2005-2007 Sony Computer Entertainment America 47 | 48 | Based on the 3dsMax COLLADASW Tools: 49 | Copyright (c) 2005-2006 Autodesk Media Entertainment 50 | 51 | Licensed under the MIT Open Source License, 52 | for details please see LICENSE file or the website 53 | http://www.opensource.org/licenses/mit-license.php 54 | 55 | RapidJSON 56 | --------- 57 | 58 | Copyright (C) 2011 Milo Yip 59 | 60 | Permission is hereby granted, free of charge, to any person obtaining a copy 61 | of this software and associated documentation files (the "Software"), to deal 62 | in the Software without restriction, including without limitation the rights 63 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 64 | copies of the Software, and to permit persons to whom the Software is 65 | furnished to do so, subject to the following conditions: 66 | 67 | The above copyright notice and this permission notice shall be included in 68 | all copies or substantial portions of the Software. 69 | 70 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 71 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 72 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 73 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 74 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 75 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 76 | THE SOFTWARE. 77 | 78 | get_opt 79 | ------- 80 | 81 | /* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ 82 | /* $FreeBSD: src/include/getopt.h,v 1.1 2002/09/29 04:14:30 eric Exp $ */ 83 | 84 | /*- 85 | * Copyright (c) 2000 The NetBSD Foundation, Inc. 86 | * All rights reserved. 87 | * 88 | * This code is derived from software contributed to The NetBSD Foundation 89 | * by Dieter Baron and Thomas Klausner. 90 | * 91 | * Redistribution and use in source and binary forms, with or without 92 | * modification, are permitted provided that the following conditions 93 | * are met: 94 | * 1. Redistributions of source code must retain the above copyright 95 | * notice, this list of conditions and the following disclaimer. 96 | * 2. Redistributions in binary form must reproduce the above copyright 97 | * notice, this list of conditions and the following disclaimer in the 98 | * documentation and/or other materials provided with the distribution. 99 | * 3. All advertising materials mentioning features or use of this software 100 | * must display the following acknowledgement: 101 | * This product includes software developed by the NetBSD 102 | * Foundation, Inc. and its contributors. 103 | * 4. Neither the name of The NetBSD Foundation nor the names of its 104 | * contributors may be used to endorse or promote products derived 105 | * from this software without specific prior written permission. 106 | * 107 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 108 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 109 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 110 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 111 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 112 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 113 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 114 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 115 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 116 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 117 | * POSSIBILITY OF SUCH DAMAGE. 118 | */ 119 | 120 | -------------------------------------------------------------------------------- /GLTF/src/GLTFAnimation.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFAnimation.h" 3 | 4 | #include 5 | 6 | #include "rapidjson/stringbuffer.h" 7 | #include "rapidjson/writer.h" 8 | 9 | std::string pathString(GLTF::Animation::Path path) { 10 | switch (path) { 11 | case GLTF::Animation::Path::TRANSLATION: 12 | return "translation"; 13 | case GLTF::Animation::Path::ROTATION: 14 | return "rotation"; 15 | case GLTF::Animation::Path::SCALE: 16 | return "scale"; 17 | case GLTF::Animation::Path::WEIGHTS: 18 | return "weights"; 19 | } 20 | return "unknown"; 21 | } 22 | 23 | GLTF::Animation::~Animation() { 24 | std::for_each(channels.begin(), channels.end(), 25 | std::default_delete()); 26 | } 27 | 28 | std::string GLTF::Animation::typeName() { return "animation"; } 29 | 30 | void GLTF::Animation::writeJSON(void* writer, GLTF::Options* options) { 31 | rapidjson::Writer* jsonWriter = 32 | static_cast*>(writer); 33 | 34 | jsonWriter->Key("channels"); 35 | jsonWriter->StartArray(); 36 | 37 | std::vector samplers; 38 | for (GLTF::Animation::Channel* channel : channels) { 39 | if (channel->target->node->id >= 0) { 40 | if (channel->sampler->id < 0) { 41 | channel->sampler->id = samplers.size(); 42 | channel->sampler->path = channel->target->path; 43 | samplers.push_back(channel->sampler); 44 | } 45 | jsonWriter->StartObject(); 46 | channel->writeJSON(writer, options); 47 | jsonWriter->EndObject(); 48 | } 49 | } 50 | jsonWriter->EndArray(); 51 | 52 | std::map parameterMap; 53 | if (options->version == "1.0") { 54 | int timeIndex = 0; 55 | std::map::iterator findParameter; 56 | std::map pathCounts; 57 | std::map::iterator findPathCount; 58 | for (GLTF::Animation::Sampler* sampler : samplers) { 59 | std::string inputString = "TIME"; 60 | std::string inputAccessorId = sampler->input->getStringId(); 61 | if (timeIndex > 0) { 62 | findParameter = parameterMap.find(inputAccessorId); 63 | if (findParameter == parameterMap.end()) { 64 | inputString = "TIME_" + std::to_string(timeIndex); 65 | parameterMap[inputAccessorId] = inputString; 66 | timeIndex++; 67 | } 68 | } else { 69 | parameterMap[inputAccessorId] = inputString; 70 | timeIndex++; 71 | } 72 | sampler->inputString = inputString; 73 | std::string path = pathString(sampler->path); 74 | int count = 0; 75 | findPathCount = pathCounts.find(path); 76 | if (findPathCount == pathCounts.end()) { 77 | pathCounts[path] = 1; 78 | } else { 79 | count = findPathCount->second; 80 | pathCounts[path]++; 81 | } 82 | std::string outputString = 83 | path + (count > 0 ? "_" + std::to_string(count) : ""); 84 | std::string outputAccessorId = sampler->output->getStringId(); 85 | parameterMap[outputAccessorId] = outputString; 86 | sampler->outputString = outputString; 87 | } 88 | jsonWriter->Key("parameters"); 89 | jsonWriter->StartObject(); 90 | for (auto parameter : parameterMap) { 91 | std::string accessorId = parameter.first; 92 | std::string parameterName = parameter.second; 93 | jsonWriter->Key(parameterName.c_str()); 94 | jsonWriter->String(accessorId.c_str()); 95 | } 96 | jsonWriter->EndObject(); 97 | } 98 | 99 | jsonWriter->Key("samplers"); 100 | if (options->version == "1.0") { 101 | jsonWriter->StartObject(); 102 | } else { 103 | jsonWriter->StartArray(); 104 | } 105 | for (GLTF::Animation::Sampler* sampler : samplers) { 106 | if (options->version == "1.0") { 107 | jsonWriter->Key(sampler->getStringId().c_str()); 108 | } 109 | jsonWriter->StartObject(); 110 | sampler->writeJSON(writer, options); 111 | jsonWriter->EndObject(); 112 | } 113 | if (options->version == "1.0") { 114 | jsonWriter->EndObject(); 115 | } else { 116 | jsonWriter->EndArray(); 117 | } 118 | samplers.clear(); 119 | GLTF::Object::writeJSON(writer, options); 120 | } 121 | 122 | std::string GLTF::Animation::Sampler::typeName() { return "sampler"; } 123 | 124 | void GLTF::Animation::Sampler::writeJSON(void* writer, GLTF::Options* options) { 125 | rapidjson::Writer* jsonWriter = 126 | static_cast*>(writer); 127 | 128 | jsonWriter->Key("input"); 129 | if (options->version == "1.0") { 130 | jsonWriter->String(inputString.c_str()); 131 | } else { 132 | jsonWriter->Int(input->id); 133 | } 134 | jsonWriter->Key("interpolation"); 135 | jsonWriter->String(interpolation.c_str()); 136 | jsonWriter->Key("output"); 137 | if (options->version == "1.0") { 138 | jsonWriter->String(outputString.c_str()); 139 | } else { 140 | jsonWriter->Int(output->id); 141 | } 142 | GLTF::Object::writeJSON(writer, options); 143 | } 144 | 145 | GLTF::Animation::Channel::~Channel() { 146 | delete sampler; 147 | delete target; 148 | } 149 | 150 | void GLTF::Animation::Channel::writeJSON(void* writer, GLTF::Options* options) { 151 | rapidjson::Writer* jsonWriter = 152 | static_cast*>(writer); 153 | 154 | jsonWriter->Key("sampler"); 155 | if (options->version == "1.0") { 156 | jsonWriter->String(sampler->getStringId().c_str()); 157 | } else { 158 | jsonWriter->Int(sampler->id); 159 | } 160 | jsonWriter->Key("target"); 161 | jsonWriter->StartObject(); 162 | target->writeJSON(writer, options); 163 | jsonWriter->EndObject(); 164 | 165 | GLTF::Object::writeJSON(writer, options); 166 | } 167 | 168 | void GLTF::Animation::Channel::Target::writeJSON(void* writer, 169 | GLTF::Options* options) { 170 | rapidjson::Writer* jsonWriter = 171 | static_cast*>(writer); 172 | 173 | if (options->version == "1.0") { 174 | jsonWriter->Key("id"); 175 | jsonWriter->String(node->getStringId().c_str()); 176 | } else { 177 | jsonWriter->Key("node"); 178 | jsonWriter->Int(node->id); 179 | } 180 | jsonWriter->Key("path"); 181 | jsonWriter->String(pathString(path).c_str()); 182 | 183 | GLTF::Object::writeJSON(writer, options); 184 | } 185 | -------------------------------------------------------------------------------- /GLTF/src/GLTFImage.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFImage.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Base64.h" 9 | #include "rapidjson/stringbuffer.h" 10 | #include "rapidjson/writer.h" 11 | 12 | std::map _imageCache; 13 | 14 | GLTF::Image::Image(std::string uri, std::string cacheKey) 15 | : uri(uri), cacheKey(cacheKey) {} 16 | 17 | GLTF::Image::Image(std::string uri) : Image(uri, "") {} 18 | 19 | GLTF::Image::Image(std::string uri, std::string cacheKey, unsigned char* data, 20 | size_t byteLength, std::string fileExtension) 21 | : uri(uri), data(data), byteLength(byteLength), cacheKey(cacheKey) { 22 | std::string dataSubstring(reinterpret_cast(data), 8); 23 | if (dataSubstring.substr(1, 7) == "PNG\r\n\x1a\n") { 24 | mimeType = "image/png"; 25 | } else if (data[0] == 255 && data[1] == 216) { 26 | mimeType = "image/jpeg"; 27 | } else { 28 | mimeType = "image/" + fileExtension; 29 | } 30 | } 31 | 32 | GLTF::Image::Image(std::string uri, unsigned char* data, size_t byteLength, 33 | std::string fileExtension) 34 | : Image(uri, "", data, byteLength, fileExtension) {} 35 | 36 | GLTF::Image::~Image() { 37 | if (!cacheKey.empty()) { 38 | _imageCache.erase(cacheKey); 39 | } 40 | 41 | free(this->data); 42 | } 43 | 44 | GLTF::Image* GLTF::Image::load(std::string imagePath, bool writeAbsoluteUris) { 45 | std::map::iterator imageCacheIt = 46 | _imageCache.find(imagePath); 47 | if (imageCacheIt != _imageCache.end()) { 48 | return imageCacheIt->second; 49 | } 50 | 51 | GLTF::Image* image = NULL; 52 | if (writeAbsoluteUris) { 53 | std::string absoluteUri = "file://"; 54 | if (imagePath[0] != '/') { 55 | absoluteUri += '/'; 56 | } 57 | absoluteUri += imagePath; 58 | 59 | std::replace(absoluteUri.begin(), absoluteUri.end(), '\\', '/'); 60 | image = new GLTF::Image(absoluteUri, imagePath); 61 | _imageCache[imagePath] = image; 62 | return image; 63 | } 64 | 65 | size_t fileExtensionStart = imagePath.find_last_of("."); 66 | std::string fileExtension = ""; 67 | if (fileExtensionStart != std::string::npos) { 68 | fileExtension = imagePath.substr(fileExtensionStart + 1); 69 | } 70 | 71 | size_t lastSlash = imagePath.find_last_of("/"); 72 | std::string fileName = imagePath; 73 | if (lastSlash == std::string::npos) { 74 | lastSlash = imagePath.find_last_of("\\"); 75 | } 76 | if (lastSlash != std::string::npos) { 77 | fileName = imagePath.substr(lastSlash + 1); 78 | } 79 | 80 | FILE* file = fopen(imagePath.c_str(), "rb"); 81 | if (file == NULL) { 82 | std::cout << "WARNING: Image uri: " << imagePath 83 | << " could not be resolved " << std::endl; 84 | image = new GLTF::Image(fileName, imagePath); 85 | } else { 86 | fseek(file, 0, SEEK_END); 87 | size_t size = ftell(file); 88 | fclose(file); 89 | file = fopen(imagePath.c_str(), "rb"); 90 | unsigned char* buffer = (unsigned char*)malloc(size); 91 | size_t bytesRead = fread(buffer, sizeof(unsigned char), size, file); 92 | fclose(file); 93 | image = 94 | new GLTF::Image(fileName, imagePath, buffer, bytesRead, fileExtension); 95 | } 96 | _imageCache[imagePath] = image; 97 | return image; 98 | } 99 | 100 | uint16_t endianSwap16(uint16_t x) { return (x >> 8) | (x << 8); } 101 | 102 | uint32_t endianSwap32(uint32_t x) { 103 | return ((x >> 24) & 0xFF) | ((x << 8) & 0xFF0000) | ((x >> 8) & 0xFF00) | 104 | ((x << 24) & 0xFF000000); 105 | } 106 | 107 | /** 108 | * Get the dimensions of this image based on the mimeType. 109 | * Based on code from: https://github.com/image-size/image-size. 110 | */ 111 | std::pair GLTF::Image::getDimensions() { 112 | int width = -1; 113 | int height = -1; 114 | if (mimeType == "image/png") { 115 | uint32_t* readUInt32 = reinterpret_cast(data + 16); 116 | height = endianSwap32(readUInt32[0]); 117 | width = endianSwap32(readUInt32[1]); 118 | } else if (mimeType == "image/jpeg") { 119 | // Skip signature chars 120 | size_t offset = 4; 121 | 122 | uint16_t i; 123 | unsigned char next; 124 | while (offset < byteLength) { 125 | uint16_t* readUint16 = reinterpret_cast(data + offset); 126 | i = endianSwap16(readUint16[0]); 127 | next = (data + offset)[i + 1]; 128 | 129 | // 0xFFC0 is baseline(SOF) 130 | // 0xFFC2 is progressive(SOF2) 131 | if (next == 0xC0 || next == 0xC2) { 132 | readUint16 = reinterpret_cast(data + offset + i + 5); 133 | height = endianSwap16(readUint16[0]); 134 | width = endianSwap16(readUint16[1]); 135 | break; 136 | } 137 | 138 | // next block 139 | offset += i + 2; 140 | } 141 | } 142 | return std::pair(width, height); 143 | } 144 | 145 | std::string GLTF::Image::typeName() { return "image"; } 146 | 147 | void GLTF::Image::writeJSON(void* writer, GLTF::Options* options) { 148 | rapidjson::Writer* jsonWriter = 149 | (rapidjson::Writer*)writer; 150 | 151 | if (options->embeddedTextures && data != NULL) { 152 | if (!options->binary) { 153 | jsonWriter->Key("uri"); 154 | std::string embeddedUri = 155 | "data:" + mimeType + ";base64," + Base64::encode(data, byteLength); 156 | jsonWriter->String(embeddedUri.c_str()); 157 | } else { 158 | if (options->version == "1.0") { 159 | jsonWriter->Key("extensions"); 160 | jsonWriter->StartObject(); 161 | jsonWriter->Key("KHR_binary_glTF"); 162 | jsonWriter->StartObject(); 163 | jsonWriter->Key("bufferView"); 164 | jsonWriter->String(bufferView->getStringId().c_str()); 165 | jsonWriter->Key("mimeType"); 166 | jsonWriter->String(mimeType.c_str()); 167 | jsonWriter->Key("width"); 168 | jsonWriter->Int(getDimensions().first); 169 | jsonWriter->Key("height"); 170 | jsonWriter->Int(getDimensions().second); 171 | jsonWriter->EndObject(); 172 | jsonWriter->EndObject(); 173 | } else { 174 | jsonWriter->Key("bufferView"); 175 | jsonWriter->Int(bufferView->id); 176 | jsonWriter->Key("mimeType"); 177 | jsonWriter->String(mimeType.c_str()); 178 | } 179 | } 180 | } else { 181 | jsonWriter->Key("uri"); 182 | jsonWriter->String(uri.c_str()); 183 | } 184 | GLTF::Object::writeJSON(writer, options); 185 | } 186 | -------------------------------------------------------------------------------- /test/src/COLLADA2GLTFWriterTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "COLLADA2GLTFWriterTest.h" 3 | 4 | #include 5 | 6 | #include "COLLADABU.h" 7 | #include "COLLADAFW.h" 8 | 9 | COLLADA2GLTFWriterTest::COLLADA2GLTFWriterTest() { 10 | asset = new GLTF::Asset(); 11 | options = new COLLADA2GLTF::Options(); 12 | COLLADASaxFWL::Loader* loader = new COLLADASaxFWL::Loader(); 13 | extrasHandler = new COLLADA2GLTF::ExtrasHandler(loader); 14 | writer = new COLLADA2GLTF::Writer(loader, asset, options, extrasHandler); 15 | } 16 | 17 | COLLADA2GLTFWriterTest::~COLLADA2GLTFWriterTest() { 18 | delete writer; 19 | delete asset; 20 | } 21 | 22 | TEST_F(COLLADA2GLTFWriterTest, WriteVisualScene_SingleNode) { 23 | COLLADAFW::VisualScene* visualScene = new COLLADAFW::VisualScene( 24 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::VISUAL_SCENE, 0, 0)); 25 | COLLADAFW::Node* node = new COLLADAFW::Node( 26 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::NODE, 0, 0)); 27 | visualScene->getRootNodes().append(node); 28 | this->writer->writeVisualScene(visualScene); 29 | GLTF::Scene* scene = this->asset->getDefaultScene(); 30 | ASSERT_TRUE(scene != NULL); 31 | std::vector sceneNodes = scene->nodes; 32 | EXPECT_EQ(sceneNodes.size(), 1); 33 | } 34 | 35 | TEST_F(COLLADA2GLTFWriterTest, WriteVisualScene_MultipleNodes) { 36 | COLLADAFW::VisualScene* visualScene = new COLLADAFW::VisualScene( 37 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::VISUAL_SCENE, 0, 0)); 38 | COLLADAFW::Node* nodeOne = new COLLADAFW::Node( 39 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::NODE, 0, 0)); 40 | COLLADAFW::Node* nodeTwo = new COLLADAFW::Node( 41 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::NODE, 1, 0)); 42 | visualScene->getRootNodes().append(nodeOne); 43 | visualScene->getRootNodes().append(nodeTwo); 44 | this->writer->writeVisualScene(visualScene); 45 | GLTF::Scene* scene = this->asset->getDefaultScene(); 46 | ASSERT_TRUE(scene != NULL); 47 | std::vector sceneNodes = scene->nodes; 48 | EXPECT_EQ(sceneNodes.size(), 2); 49 | } 50 | 51 | TEST_F(COLLADA2GLTFWriterTest, WriteVisualScene_MeshDoesNotExist) { 52 | COLLADAFW::VisualScene* visualScene = new COLLADAFW::VisualScene( 53 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::VISUAL_SCENE, 0, 0)); 54 | COLLADAFW::Node* node = new COLLADAFW::Node( 55 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::NODE, 0, 0)); 56 | COLLADAFW::InstanceGeometry* instanceGeometry = 57 | new COLLADAFW::InstanceGeometry( 58 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::MESH, 0, 0), 59 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::MESH, 1, 0)); 60 | node->getInstanceGeometries().append(instanceGeometry); 61 | visualScene->getRootNodes().append(node); 62 | this->writer->writeVisualScene(visualScene); 63 | GLTF::Scene* scene = this->asset->getDefaultScene(); 64 | ASSERT_TRUE(scene != NULL); 65 | std::vector sceneNodes = scene->nodes; 66 | ASSERT_EQ(sceneNodes.size(), 1); 67 | GLTF::Node* sceneNode = sceneNodes[0]; 68 | GLTF::Mesh* mesh = sceneNode->mesh; 69 | ASSERT_TRUE(mesh == NULL); 70 | } 71 | 72 | TEST_F(COLLADA2GLTFWriterTest, WriteVisualScene_MeshDoesExist) { 73 | COLLADAFW::VisualScene* visualScene = new COLLADAFW::VisualScene( 74 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::VISUAL_SCENE, 0, 0)); 75 | COLLADAFW::Node* node = new COLLADAFW::Node( 76 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::NODE, 0, 0)); 77 | COLLADAFW::InstanceGeometry* instanceGeometry = 78 | new COLLADAFW::InstanceGeometry( 79 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::MESH, 1, 0), 80 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::MESH, 0, 0)); 81 | COLLADAFW::Geometry* geometry = new COLLADAFW::Mesh( 82 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::MESH, 0, 0)); 83 | this->writer->writeGeometry(geometry); 84 | node->getInstanceGeometries().append(instanceGeometry); 85 | visualScene->getRootNodes().append(node); 86 | this->writer->writeVisualScene(visualScene); 87 | GLTF::Scene* scene = this->asset->getDefaultScene(); 88 | ASSERT_TRUE(scene != NULL); 89 | std::vector sceneNodes = scene->nodes; 90 | ASSERT_EQ(sceneNodes.size(), 1); 91 | GLTF::Node* sceneNode = sceneNodes[0]; 92 | GLTF::Mesh* mesh = sceneNode->mesh; 93 | ASSERT_TRUE(mesh != NULL); 94 | } 95 | 96 | TEST_F(COLLADA2GLTFWriterTest, WriteVisualScene_MultipleLevelsOfInstanceNodes) { 97 | COLLADAFW::VisualScene* visualScene = new COLLADAFW::VisualScene( 98 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::VISUAL_SCENE, 0, 0)); 99 | 100 | // Create 2 root nodes 101 | COLLADAFW::Node* vsNode1 = new COLLADAFW::Node( 102 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::NODE, 0, 0)); 103 | COLLADAFW::Node* vsNode2 = new COLLADAFW::Node( 104 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::NODE, 1, 0)); 105 | visualScene->getRootNodes().append(vsNode1); 106 | visualScene->getRootNodes().append(vsNode2); 107 | 108 | // Create 2 instance_nodes that refers to the same node that is under 109 | // library_nodes 110 | COLLADAFW::UniqueId instanceId1(COLLADAFW::COLLADA_TYPE::NODE, 2, 0); 111 | COLLADAFW::InstanceNode* instance_libNode1_1 = new COLLADAFW::InstanceNode( 112 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::INSTANCE_NODE, 0, 0), 113 | instanceId1); 114 | COLLADAFW::InstanceNode* instance_libNode1_2 = new COLLADAFW::InstanceNode( 115 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::INSTANCE_NODE, 1, 0), 116 | instanceId1); 117 | vsNode1->getInstanceNodes().append(instance_libNode1_1); 118 | vsNode2->getInstanceNodes().append(instance_libNode1_2); 119 | 120 | // Create node under libary_node that is refered to by the 2 root nodes 121 | COLLADAFW::Node* libNode1 = new COLLADAFW::Node(instanceId1); 122 | 123 | // Create node under libary_node that is refered to by the previous node 124 | COLLADAFW::UniqueId instanceId2(COLLADAFW::COLLADA_TYPE::NODE, 3, 0); 125 | COLLADAFW::Node* libNode2 = new COLLADAFW::Node(instanceId2); 126 | 127 | COLLADAFW::InstanceNode* instance_libNode2 = new COLLADAFW::InstanceNode( 128 | COLLADAFW::UniqueId(COLLADAFW::COLLADA_TYPE::INSTANCE_NODE, 2, 0), 129 | instanceId2); 130 | libNode1->getInstanceNodes().append(instance_libNode2); 131 | 132 | COLLADAFW::LibraryNodes* libraryNodes = new COLLADAFW::LibraryNodes(); 133 | libraryNodes->getNodes().append(libNode1); 134 | libraryNodes->getNodes().append(libNode2); 135 | 136 | this->writer->writeVisualScene(visualScene); 137 | this->writer->writeLibraryNodes(libraryNodes); 138 | 139 | GLTF::Scene* scene = this->asset->getDefaultScene(); 140 | ASSERT_TRUE(scene != NULL); 141 | 142 | // We should have 2 root nodes 143 | std::vector sceneNodes = scene->nodes; 144 | ASSERT_EQ(sceneNodes.size(), 2); 145 | 146 | // Each of these nodes should have 1 child (clones of libNode1) 147 | ASSERT_EQ(sceneNodes[0]->children.size(), 1); 148 | ASSERT_EQ(sceneNodes[1]->children.size(), 1); 149 | 150 | // Each of those children should have 1 child (clones of libNode2) 151 | ASSERT_EQ(sceneNodes[0]->children[0]->children.size(), 1); 152 | ASSERT_EQ(sceneNodes[1]->children[0]->children.size(), 1); 153 | } 154 | -------------------------------------------------------------------------------- /include/COLLADA2GLTFWriter.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "COLLADA2GLTFExtrasHandler.h" 11 | #include "COLLADA2GLTFOptions.h" 12 | #include "COLLADABU.h" 13 | #include "COLLADAFW.h" 14 | #include "GLTFAsset.h" 15 | #include "draco/compression/encode.h" 16 | 17 | namespace COLLADA2GLTF { 18 | class Writer : public COLLADAFW::IWriter { 19 | private: 20 | COLLADASaxFWL::Loader* _loader; 21 | GLTF::Asset* _asset; 22 | COLLADA2GLTF::Options* _options; 23 | COLLADA2GLTF::ExtrasHandler* _extrasHandler; 24 | GLTF::Node* _rootNode = NULL; 25 | float _assetScale; 26 | std::map _materialEffects; 27 | std::map _effectInstances; 28 | std::map _cameraInstances; 29 | std::map _meshInstances; 30 | std::map _nodeInstances; 31 | std::map _animationInstances; 32 | std::map> _nodeInstanceTargets; 33 | std::map> 34 | _nodeInstanceTargetMapping; 35 | std::map>> 36 | _meshMaterialPrimitiveMapping; 37 | std::map _lightInstances; 38 | std::map>> 40 | _meshPositionMapping; 41 | std::map> 42 | _meshTexCoordSetMapping; 43 | std::map _skinInstances; 44 | std::map _animatedNodes; 45 | std::map _originalRotationAngles; 46 | std::map*> _unboundSkeletonNodes; 47 | std::map _nodes; 48 | std::map> 49 | _skinJointNodes; 50 | std::map< 51 | COLLADAFW::UniqueId, 52 | std::tuple, std::vector>> 53 | _skinData; 54 | std::map _skinnedMeshes; 55 | std::map _images; 56 | std::map, std::vector>> 58 | _animationData; 59 | std::map> 60 | _effectTextureMapping; 61 | std::map _meshMorphTargets; 62 | std::map> _animationClips; 63 | 64 | bool writeNodeToGroup(std::vector* group, 65 | const COLLADAFW::Node* node); 66 | bool writeNodesToGroup(std::vector* group, 67 | const COLLADAFW::NodePointerArray& nodes); 68 | void nodeClonePredicate(GLTF::Node* node, GLTF::Node* clonedNode); 69 | GLTF::Texture* fromColladaTexture(const COLLADAFW::EffectCommon* effectCommon, 70 | COLLADAFW::SamplerID samplerId); 71 | GLTF::Texture* fromColladaTexture(const COLLADAFW::EffectCommon* effectCommon, 72 | COLLADAFW::Texture texture); 73 | 74 | public: 75 | Writer(COLLADASaxFWL::Loader* loader, GLTF::Asset* asset, 76 | COLLADA2GLTF::Options* options, COLLADA2GLTF::ExtrasHandler* handler); 77 | 78 | /** 79 | * Get animation groupings (from animation clips) by index. 80 | */ 81 | std::vector> getAnimationGroups(); 82 | 83 | /** 84 | * Deletes the entire scene. 85 | * @param errorMessage A message containing informations about the error that 86 | * occurred. 87 | */ 88 | void cancel(const std::string& errorMessage); 89 | 90 | /** Prepare to receive data.*/ 91 | void start(); 92 | 93 | /** Remove all objects that don't have an object. Deletes unused visual 94 | * scenes.*/ 95 | void finish(); 96 | 97 | /** When this method is called, the writer must write the global document 98 | asset-> 99 | @return The writer should return true, if writing succeeded, false 100 | otherwise.*/ 101 | virtual bool writeGlobalAsset(const COLLADAFW::FileInfo* asset); 102 | 103 | /** Writes the entire visual scene. 104 | @return True on succeeded, false otherwise.*/ 105 | virtual bool writeVisualScene(const COLLADAFW::VisualScene* visualScene); 106 | 107 | /** Writes the scene. 108 | @return True on succeeded, false otherwise.*/ 109 | virtual bool writeScene(const COLLADAFW::Scene* scene); 110 | 111 | /** Handles all nodes in the library nodes. 112 | @return True on succeeded, false otherwise.*/ 113 | virtual bool writeLibraryNodes(const COLLADAFW::LibraryNodes* libraryNodes); 114 | 115 | bool writeMesh(const COLLADAFW::Mesh* mesh); 116 | 117 | /** Writes the geometry. 118 | @return True on succeeded, false otherwise.*/ 119 | virtual bool writeGeometry(const COLLADAFW::Geometry* geometry); 120 | 121 | /** Writes the material. 122 | @return True on succeeded, false otherwise.*/ 123 | virtual bool writeMaterial(const COLLADAFW::Material* material); 124 | 125 | /** Writes the effect. 126 | @return True on succeeded, false otherwise.*/ 127 | virtual bool writeEffect(const COLLADAFW::Effect* effect); 128 | 129 | /** Writes the camera. 130 | @return True on succeeded, false otherwise.*/ 131 | virtual bool writeCamera(const COLLADAFW::Camera* camera); 132 | 133 | /** Writes the image. 134 | @return True on succeeded, false otherwise.*/ 135 | virtual bool writeImage(const COLLADAFW::Image* image); 136 | 137 | /** Writes the light. 138 | @return True on succeeded, false otherwise.*/ 139 | virtual bool writeLight(const COLLADAFW::Light* light); 140 | 141 | /** Writes the animation. 142 | @return True on succeeded, false otherwise.*/ 143 | virtual bool writeAnimation(const COLLADAFW::Animation* animation); 144 | 145 | /** Writes the animation clip. 146 | @return True on succeeded, false otherwise.*/ 147 | virtual bool writeAnimationClip( 148 | const COLLADAFW::AnimationClip* animationClip); 149 | 150 | /** Writes the animation. 151 | @return True on succeeded, false otherwise.*/ 152 | virtual bool writeAnimationList( 153 | const COLLADAFW::AnimationList* animationList); 154 | 155 | /** Writes the skin controller data. 156 | @return True on succeeded, false otherwise.*/ 157 | virtual bool writeSkinControllerData( 158 | const COLLADAFW::SkinControllerData* skinControllerData); 159 | 160 | /** Writes the controller. 161 | @return True on succeeded, false otherwise.*/ 162 | virtual bool writeController(const COLLADAFW::Controller* Controller); 163 | 164 | /** When this method is called, the writer must write the formulas. All the 165 | formulas of the entire COLLADA file are contained in @a formulas. 166 | @return The writer should return true, if writing succeeded, false 167 | otherwise.*/ 168 | virtual bool writeFormulas(const COLLADAFW::Formulas* formulas); 169 | 170 | /** When this method is called, the writer must write the kinematics scene. 171 | @return The writer should return true, if writing succeeded, false 172 | otherwise.*/ 173 | virtual bool writeKinematicsScene( 174 | const COLLADAFW::KinematicsScene* kinematicsScene); 175 | 176 | /** Add attributes of mesh to draco compression extension.*/ 177 | bool addAttributesToDracoMesh( 178 | GLTF::Primitive* primitive, 179 | const std::map>& buildAttributes, 180 | const std::vector& buildIndices); 181 | 182 | /** Add joint indices and joint weights to draco compression extension.*/ 183 | bool addControllerDataToDracoMesh(GLTF::Primitive* primitive, 184 | uint16_t* jointArray, float* weightArray); 185 | }; 186 | } // namespace COLLADA2GLTF 187 | -------------------------------------------------------------------------------- /.github/workflows/continuous-integration-workflow.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | cpplint: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v1 9 | - uses: actions/setup-python@v1 10 | - run: pip install cpplint 11 | - run: cpplint --recursive . 12 | 13 | clang-format: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: DoozyX/clang-format-lint-action@v0.6 18 | with: 19 | style: Google 20 | 21 | build-windows: 22 | name: build-windows 23 | runs-on: windows-latest 24 | needs: [cpplint, clang-format] 25 | 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v1 29 | with: 30 | submodules: recursive 31 | 32 | - name: Add msbuild to PATH 33 | uses: microsoft/setup-msbuild@v1.0.0 34 | 35 | - name: Run cmake 36 | run: | 37 | cd ${{ github.workspace }} 38 | mkdir build 39 | cd build 40 | cmake .. -Dtest=ON 41 | 42 | - name: Build 43 | run: | 44 | cd build 45 | MSBuild.exe COLLADA2GLTF.sln /property:Configuration=Release 46 | 47 | - name: Run unit tests 48 | run: | 49 | build/Release/COLLADA2GLTF-test.exe 50 | build/GLTF/Release/GLTF-test.exe 51 | 52 | - name: Upload built executable 53 | uses: actions/upload-artifact@v1 54 | with: 55 | name: COLLADA2GLTF-windows.exe 56 | path: ./build/Release/COLLADA2GLTF-bin.exe 57 | 58 | - if: startsWith(github.ref, 'refs/tags/') # Release step, only runs on tag builds 59 | name: (Release) Get current date 60 | id: date 61 | run: echo "::set-output name=date::$(date +'%Y-%m-%d')" 62 | 63 | - if: startsWith(github.ref, 'refs/tags/') # Release step, only runs on tag builds 64 | name: (Release) Get version 65 | id: get_version 66 | run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} 67 | 68 | - if: startsWith(github.ref, 'refs/tags/') # Release step, only runs on tag builds 69 | name: (Release) Create release 70 | id: create_release 71 | uses: actions/create-release@v1 72 | env: 73 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 74 | with: 75 | tag_name: ${{ github.ref }} 76 | release_name: ${{ steps.get_version.outputs.VERSION }} 77 | body: | 78 | Release ${{ steps.get_version.outputs.VERSION }} ${{ steps.date.outputs.date }} 79 | draft: false 80 | prerelease: false 81 | 82 | - if: startsWith(github.ref, 'refs/tags/') # Release step, only runs on tag builds 83 | name: (Release) Upload release asset 84 | uses: actions/upload-release-asset@v1 85 | env: 86 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 87 | with: 88 | upload_url: ${{ steps.create_release.outputs.upload_url }} 89 | asset_path: ./build/Release/COLLADA2GLTF.exe 90 | asset_name: COLLADA2GLTF-windows-${{ steps.get_version.outputs.VERSION }}.exe 91 | asset_content_type: application/vnd.microsoft.portable-executable 92 | 93 | build-linux: 94 | name: build-linux 95 | runs-on: ubuntu-latest 96 | needs: [cpplint, clang-format] 97 | 98 | steps: 99 | - uses: actions/setup-node@v1 100 | with: 101 | node-version: 8 102 | 103 | - name: Checkout 104 | uses: actions/checkout@v1 105 | with: 106 | submodules: recursive 107 | 108 | - name: Run cmake 109 | run: | 110 | cd ${{ github.workspace }} 111 | mkdir build 112 | cd build 113 | cmake .. -Dtest=ON 114 | 115 | - name: Build 116 | run: | 117 | cd build 118 | make 119 | 120 | - name: Run unit tests 121 | run: | 122 | cd build 123 | make test 124 | 125 | - name: Run integration tests 126 | run: | 127 | cd test/js 128 | npm install 129 | npm run test 130 | 131 | - name: Upload built executable 132 | uses: actions/upload-artifact@v1 133 | with: 134 | name: COLLADA2GLTF-linux 135 | path: ./build/COLLADA2GLTF-bin 136 | 137 | - if: startsWith(github.ref, 'refs/tags/') # Release step, only runs on tag builds 138 | name: (Release) Get current date 139 | id: date 140 | run: echo "::set-output name=date::$(date +'%Y-%m-%d')" 141 | 142 | - if: startsWith(github.ref, 'refs/tags/') # Release step, only runs on tag builds 143 | name: (Release) Get version 144 | id: get_version 145 | run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} 146 | 147 | - if: startsWith(github.ref, 'refs/tags/') # Release step, only runs on tag builds 148 | name: (Release) Create release 149 | id: create_release 150 | uses: actions/create-release@v1 151 | env: 152 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 153 | with: 154 | tag_name: ${{ github.ref }} 155 | release_name: ${{ steps.get_version.outputs.VERSION }} 156 | body: | 157 | Release ${{ steps.get_version.outputs.VERSION }} ${{ steps.date.outputs.date }} 158 | draft: false 159 | prerelease: false 160 | 161 | - if: startsWith(github.ref, 'refs/tags/') # Release step, only runs on tag builds 162 | name: (Release) Upload release asset 163 | uses: actions/upload-release-asset@v1 164 | env: 165 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 166 | with: 167 | upload_url: ${{ steps.create_release.outputs.upload_url }} 168 | asset_path: ./build/COLLADA2GLTF-bin 169 | asset_name: COLLADA2GLTF-linux-${{ steps.get_version.outputs.VERSION }} 170 | asset_content_type: application/octet-stream 171 | 172 | build-osx: 173 | name: build-osx 174 | runs-on: macos-latest 175 | needs: [cpplint, clang-format] 176 | 177 | steps: 178 | - name: Checkout 179 | uses: actions/checkout@v1 180 | with: 181 | submodules: recursive 182 | 183 | - name: Run cmake 184 | run: | 185 | cd ${{ github.workspace }} 186 | mkdir build 187 | cd build 188 | cmake .. -Dtest=ON 189 | 190 | - name: Build 191 | run: | 192 | cd build 193 | make 194 | 195 | - name: Run unit tests 196 | run: | 197 | cd build 198 | make test 199 | 200 | - name: Upload built executable 201 | uses: actions/upload-artifact@v1 202 | with: 203 | name: COLLADA2GLTF-osx 204 | path: ./build/COLLADA2GLTF-bin 205 | 206 | - if: startsWith(github.ref, 'refs/tags/') # Release step, only runs on tag builds 207 | name: (Release) Get current date 208 | id: date 209 | run: echo "::set-output name=date::$(date +'%Y-%m-%d')" 210 | 211 | - if: startsWith(github.ref, 'refs/tags/') # Release step, only runs on tag builds 212 | name: (Release) Get version 213 | id: get_version 214 | run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} 215 | 216 | - if: startsWith(github.ref, 'refs/tags/') # Release step, only runs on tag builds 217 | name: (Release) Create release 218 | id: create_release 219 | uses: actions/create-release@v1 220 | env: 221 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 222 | with: 223 | tag_name: ${{ github.ref }} 224 | release_name: ${{ steps.get_version.outputs.VERSION }} 225 | body: | 226 | Release ${{ steps.get_version.outputs.VERSION }} ${{ steps.date.outputs.date }} 227 | draft: false 228 | prerelease: false 229 | 230 | - if: startsWith(github.ref, 'refs/tags/') # Release step, only runs on tag builds 231 | name: (Release) Upload release asset 232 | uses: actions/upload-release-asset@v1 233 | env: 234 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 235 | with: 236 | upload_url: ${{ steps.create_release.outputs.upload_url }} 237 | asset_path: ./build/COLLADA2GLTF-bin 238 | asset_name: COLLADA2GLTF-osx-${{ steps.get_version.outputs.VERSION }} 239 | asset_content_type: application/octet-stream 240 | -------------------------------------------------------------------------------- /test/js/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "collada2gltf-test", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "assertion-error": { 8 | "version": "1.1.0", 9 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 10 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" 11 | }, 12 | "balanced-match": { 13 | "version": "1.0.0", 14 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 15 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 16 | }, 17 | "bluebird": { 18 | "version": "3.5.3", 19 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", 20 | "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==" 21 | }, 22 | "brace-expansion": { 23 | "version": "1.1.11", 24 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 25 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 26 | "requires": { 27 | "balanced-match": "^1.0.0", 28 | "concat-map": "0.0.1" 29 | } 30 | }, 31 | "browser-stdout": { 32 | "version": "1.3.1", 33 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 34 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" 35 | }, 36 | "chai": { 37 | "version": "4.2.0", 38 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", 39 | "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", 40 | "requires": { 41 | "assertion-error": "^1.1.0", 42 | "check-error": "^1.0.2", 43 | "deep-eql": "^3.0.1", 44 | "get-func-name": "^2.0.0", 45 | "pathval": "^1.1.0", 46 | "type-detect": "^4.0.5" 47 | } 48 | }, 49 | "chai-subset": { 50 | "version": "1.6.0", 51 | "resolved": "https://registry.npmjs.org/chai-subset/-/chai-subset-1.6.0.tgz", 52 | "integrity": "sha1-pdDKFOMpp5WW7XAFi2ZGvWmIz+k=" 53 | }, 54 | "check-error": { 55 | "version": "1.0.2", 56 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 57 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" 58 | }, 59 | "commander": { 60 | "version": "2.15.1", 61 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", 62 | "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" 63 | }, 64 | "concat-map": { 65 | "version": "0.0.1", 66 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 67 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 68 | }, 69 | "debug": { 70 | "version": "3.1.0", 71 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 72 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 73 | "requires": { 74 | "ms": "2.0.0" 75 | } 76 | }, 77 | "deep-eql": { 78 | "version": "3.0.1", 79 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 80 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 81 | "requires": { 82 | "type-detect": "^4.0.0" 83 | } 84 | }, 85 | "diff": { 86 | "version": "3.5.0", 87 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 88 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" 89 | }, 90 | "escape-string-regexp": { 91 | "version": "1.0.5", 92 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 93 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 94 | }, 95 | "esm": { 96 | "version": "3.1.0", 97 | "resolved": "https://registry.npmjs.org/esm/-/esm-3.1.0.tgz", 98 | "integrity": "sha512-r4Go7Wh7Wh0WPinRXeeM9PIajRsUdt8SAyki5R1obVc0+BwtqvtjbngVSSdXg0jCe2xZkY8hyBMx6q/uymUkPw==" 99 | }, 100 | "fs.realpath": { 101 | "version": "1.0.0", 102 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 103 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 104 | }, 105 | "get-func-name": { 106 | "version": "2.0.0", 107 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 108 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" 109 | }, 110 | "glob": { 111 | "version": "7.1.2", 112 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 113 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 114 | "requires": { 115 | "fs.realpath": "^1.0.0", 116 | "inflight": "^1.0.4", 117 | "inherits": "2", 118 | "minimatch": "^3.0.4", 119 | "once": "^1.3.0", 120 | "path-is-absolute": "^1.0.0" 121 | } 122 | }, 123 | "gltf-validator": { 124 | "version": "2.0.0-dev.2.7", 125 | "resolved": "https://registry.npmjs.org/gltf-validator/-/gltf-validator-2.0.0-dev.2.7.tgz", 126 | "integrity": "sha512-OmI/inmNEL9uDTxj6KNF+/oZ7df2okhi1HKMV7cjakzo2k/MFKpa95R4dMG6WQx1KaiZAG7hvdpSxvjtkTyb2A==" 127 | }, 128 | "growl": { 129 | "version": "1.10.5", 130 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 131 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" 132 | }, 133 | "has-flag": { 134 | "version": "3.0.0", 135 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 136 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 137 | }, 138 | "he": { 139 | "version": "1.1.1", 140 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 141 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" 142 | }, 143 | "inflight": { 144 | "version": "1.0.6", 145 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 146 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 147 | "requires": { 148 | "once": "^1.3.0", 149 | "wrappy": "1" 150 | } 151 | }, 152 | "inherits": { 153 | "version": "2.0.3", 154 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 155 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 156 | }, 157 | "minimatch": { 158 | "version": "3.0.4", 159 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 160 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 161 | "requires": { 162 | "brace-expansion": "^1.1.7" 163 | } 164 | }, 165 | "minimist": { 166 | "version": "0.0.8", 167 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 168 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 169 | }, 170 | "mkdirp": { 171 | "version": "0.5.1", 172 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 173 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 174 | "requires": { 175 | "minimist": "0.0.8" 176 | } 177 | }, 178 | "mocha": { 179 | "version": "5.2.0", 180 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", 181 | "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", 182 | "requires": { 183 | "browser-stdout": "1.3.1", 184 | "commander": "2.15.1", 185 | "debug": "3.1.0", 186 | "diff": "3.5.0", 187 | "escape-string-regexp": "1.0.5", 188 | "glob": "7.1.2", 189 | "growl": "1.10.5", 190 | "he": "1.1.1", 191 | "minimatch": "3.0.4", 192 | "mkdirp": "0.5.1", 193 | "supports-color": "5.4.0" 194 | } 195 | }, 196 | "ms": { 197 | "version": "2.0.0", 198 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 199 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 200 | }, 201 | "once": { 202 | "version": "1.4.0", 203 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 204 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 205 | "requires": { 206 | "wrappy": "1" 207 | } 208 | }, 209 | "os-tmpdir": { 210 | "version": "1.0.2", 211 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 212 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" 213 | }, 214 | "path-is-absolute": { 215 | "version": "1.0.1", 216 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 217 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 218 | }, 219 | "pathval": { 220 | "version": "1.1.0", 221 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", 222 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" 223 | }, 224 | "supports-color": { 225 | "version": "5.4.0", 226 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 227 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 228 | "requires": { 229 | "has-flag": "^3.0.0" 230 | } 231 | }, 232 | "tmp": { 233 | "version": "0.0.33", 234 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", 235 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", 236 | "requires": { 237 | "os-tmpdir": "~1.0.2" 238 | } 239 | }, 240 | "type-detect": { 241 | "version": "4.0.8", 242 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 243 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" 244 | }, 245 | "wrappy": { 246 | "version": "1.0.2", 247 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 248 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 249 | } 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /GLTF/src/GLTFAccessor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFAccessor.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "rapidjson/stringbuffer.h" 12 | #include "rapidjson/writer.h" 13 | 14 | GLTF::Accessor::Accessor(GLTF::Accessor::Type type, 15 | GLTF::Constants::WebGL componentType) 16 | : type(type), componentType(componentType), byteOffset(0) {} 17 | 18 | GLTF::Accessor::Accessor(GLTF::Accessor::Type type, 19 | GLTF::Constants::WebGL componentType, 20 | unsigned char* data, int count, 21 | GLTF::Constants::WebGL target) 22 | : Accessor(type, componentType) { 23 | int byteLength = 24 | count * this->getNumberOfComponents() * this->getComponentByteLength(); 25 | unsigned char* allocatedData = (unsigned char*)malloc(byteLength); 26 | std::memcpy(allocatedData, data, byteLength); 27 | this->bufferView = new GLTF::BufferView(allocatedData, byteLength, target); 28 | this->count = count; 29 | this->computeMinMax(); 30 | } 31 | 32 | GLTF::Accessor::Accessor(GLTF::Accessor::Type type, 33 | GLTF::Constants::WebGL componentType, 34 | unsigned char* data, int count, 35 | GLTF::BufferView* bufferView) 36 | : Accessor(type, componentType) { 37 | GLTF::Buffer* buffer = bufferView->buffer; 38 | this->bufferView = bufferView; 39 | this->byteOffset = bufferView->byteLength; 40 | this->count = count; 41 | int componentByteLength = this->getComponentByteLength(); 42 | int byteLength = count * this->getNumberOfComponents() * componentByteLength; 43 | 44 | int padding = byteOffset % componentByteLength; 45 | if (padding != 0) { 46 | padding = componentByteLength - padding; 47 | } 48 | this->byteOffset += padding; 49 | 50 | buffer->data = (unsigned char*)realloc( 51 | buffer->data, buffer->byteLength + padding + byteLength); 52 | std::memcpy(buffer->data + buffer->byteLength + padding, data, byteLength); 53 | buffer->byteLength += byteLength + padding; 54 | bufferView->byteLength += byteLength + padding; 55 | this->computeMinMax(); 56 | } 57 | 58 | GLTF::Accessor::Accessor(GLTF::Accessor::Type type, 59 | GLTF::Constants::WebGL componentType, int byteOffset, 60 | int count, GLTF::BufferView* bufferView) 61 | : Accessor(type, componentType) { 62 | this->byteOffset = byteOffset; 63 | this->count = count; 64 | this->bufferView = bufferView; 65 | } 66 | 67 | GLTF::Accessor::Accessor(GLTF::Accessor* accessor) 68 | : Accessor(accessor->type, accessor->componentType, 69 | &(accessor->bufferView->buffer 70 | ->data[accessor->byteOffset + 71 | accessor->bufferView->byteOffset]), 72 | accessor->count, accessor->bufferView->target) {} 73 | 74 | bool GLTF::Accessor::computeMinMax() { 75 | int numberOfComponents = this->getNumberOfComponents(); 76 | int count = this->count; 77 | if (count > 0) { 78 | if (max == NULL) { 79 | max = new float[numberOfComponents]; 80 | } 81 | if (min == NULL) { 82 | min = new float[numberOfComponents]; 83 | } 84 | float* component = new float[numberOfComponents]; 85 | this->getComponentAtIndex(0, component); 86 | for (int i = 0; i < numberOfComponents; i++) { 87 | min[i] = component[i]; 88 | max[i] = component[i]; 89 | } 90 | for (int i = 1; i < this->count; i++) { 91 | this->getComponentAtIndex(i, component); 92 | for (int j = 0; j < numberOfComponents; j++) { 93 | min[j] = std::min(component[j], min[j]); 94 | max[j] = std::max(component[j], max[j]); 95 | } 96 | } 97 | } 98 | return true; 99 | } 100 | 101 | int GLTF::Accessor::getByteStride() { 102 | if (this->bufferView == NULL || this->bufferView->byteStride == 0) { 103 | return this->getNumberOfComponents() * this->getComponentByteLength(); 104 | } 105 | return this->bufferView->byteStride; 106 | } 107 | 108 | bool GLTF::Accessor::getComponentAtIndex(int index, float* component) { 109 | int byteOffset = this->byteOffset + this->bufferView->byteOffset; 110 | int numberOfComponents = this->getNumberOfComponents(); 111 | byteOffset += this->getByteStride() * index; 112 | unsigned char* buf = this->bufferView->buffer->data + byteOffset; 113 | 114 | for (int i = 0; i < numberOfComponents; i++) { 115 | switch (this->componentType) { 116 | case GLTF::Constants::WebGL::BYTE: 117 | component[i] = static_cast(reinterpret_cast(buf)[i]); 118 | break; 119 | case GLTF::Constants::WebGL::UNSIGNED_BYTE: 120 | component[i] = static_cast(buf[i]); 121 | break; 122 | case GLTF::Constants::WebGL::SHORT: 123 | component[i] = static_cast(reinterpret_cast(buf)[i]); 124 | break; 125 | case GLTF::Constants::WebGL::UNSIGNED_SHORT: 126 | component[i] = static_cast(reinterpret_cast(buf)[i]); 127 | break; 128 | case GLTF::Constants::WebGL::FLOAT: 129 | component[i] = reinterpret_cast(buf)[i]; 130 | break; 131 | case GLTF::Constants::WebGL::UNSIGNED_INT: 132 | component[i] = 133 | static_cast(reinterpret_cast(buf)[i]); 134 | break; 135 | default: 136 | return false; 137 | } 138 | } 139 | return true; 140 | } 141 | 142 | bool GLTF::Accessor::writeComponentAtIndex(int index, float* component) { 143 | int byteOffset = this->byteOffset + this->bufferView->byteOffset; 144 | int numberOfComponents = this->getNumberOfComponents(); 145 | byteOffset += this->getByteStride() * index; 146 | unsigned char* buf = this->bufferView->buffer->data + byteOffset; 147 | 148 | for (int i = 0; i < numberOfComponents; i++) { 149 | switch (this->componentType) { 150 | case GLTF::Constants::WebGL::BYTE: 151 | buf[i] = static_cast(component[i]); 152 | break; 153 | case GLTF::Constants::WebGL::UNSIGNED_BYTE: 154 | buf[i] = static_cast(component[i]); 155 | break; 156 | case GLTF::Constants::WebGL::SHORT: 157 | reinterpret_cast(buf)[i] = static_cast(component[i]); 158 | break; 159 | case GLTF::Constants::WebGL::UNSIGNED_SHORT: 160 | reinterpret_cast(buf)[i] = 161 | static_cast(component[i]); 162 | break; 163 | case GLTF::Constants::WebGL::FLOAT: 164 | reinterpret_cast(buf)[i] = static_cast(component[i]); 165 | break; 166 | case GLTF::Constants::WebGL::UNSIGNED_INT: 167 | reinterpret_cast(buf)[i] = 168 | static_cast(component[i]); 169 | break; 170 | default: 171 | return false; 172 | } 173 | } 174 | return true; 175 | } 176 | 177 | int GLTF::Accessor::getComponentByteLength( 178 | GLTF::Constants::WebGL componentType) { 179 | switch (componentType) { 180 | case GLTF::Constants::WebGL::BYTE: 181 | case GLTF::Constants::WebGL::UNSIGNED_BYTE: 182 | return 1; 183 | case GLTF::Constants::WebGL::SHORT: 184 | case GLTF::Constants::WebGL::UNSIGNED_SHORT: 185 | return 2; 186 | case GLTF::Constants::WebGL::FLOAT: 187 | case GLTF::Constants::WebGL::UNSIGNED_INT: 188 | return 4; 189 | } 190 | return 0; 191 | } 192 | 193 | int GLTF::Accessor::getComponentByteLength() { 194 | return GLTF::Accessor::getComponentByteLength(this->componentType); 195 | } 196 | 197 | int GLTF::Accessor::getNumberOfComponents(GLTF::Accessor::Type type) { 198 | switch (type) { 199 | case GLTF::Accessor::Type::SCALAR: 200 | return 1; 201 | case GLTF::Accessor::Type::VEC2: 202 | return 2; 203 | case GLTF::Accessor::Type::VEC3: 204 | return 3; 205 | case GLTF::Accessor::Type::VEC4: 206 | case GLTF::Accessor::Type::MAT2: 207 | return 4; 208 | case GLTF::Accessor::Type::MAT3: 209 | return 9; 210 | case GLTF::Accessor::Type::MAT4: 211 | return 16; 212 | } 213 | return 0; 214 | } 215 | 216 | int GLTF::Accessor::getNumberOfComponents() { 217 | return GLTF::Accessor::getNumberOfComponents(this->type); 218 | } 219 | 220 | const char* GLTF::Accessor::getTypeName() { 221 | switch (this->type) { 222 | case GLTF::Accessor::Type::SCALAR: 223 | return "SCALAR"; 224 | case GLTF::Accessor::Type::VEC2: 225 | return "VEC2"; 226 | case GLTF::Accessor::Type::VEC3: 227 | return "VEC3"; 228 | case GLTF::Accessor::Type::VEC4: 229 | return "VEC4"; 230 | case GLTF::Accessor::Type::MAT2: 231 | return "MAT2"; 232 | case GLTF::Accessor::Type::MAT3: 233 | return "MAT3"; 234 | case GLTF::Accessor::Type::MAT4: 235 | return "MAT4"; 236 | } 237 | return ""; 238 | } 239 | 240 | bool GLTF::Accessor::equals(GLTF::Accessor* accessor) { 241 | if (type != accessor->type || componentType != accessor->componentType || 242 | count != accessor->count) { 243 | return false; 244 | } 245 | int numberOfComponents = getNumberOfComponents(); 246 | float* componentOne = new float[numberOfComponents]; 247 | float* componentTwo = new float[numberOfComponents]; 248 | for (int i = 0; i < count; i++) { 249 | this->getComponentAtIndex(i, componentOne); 250 | accessor->getComponentAtIndex(i, componentTwo); 251 | for (int j = 0; j < numberOfComponents; j++) { 252 | if (componentOne[j] != componentTwo[j]) { 253 | return false; 254 | } 255 | } 256 | } 257 | return true; 258 | } 259 | 260 | std::string GLTF::Accessor::typeName() { return "accessor"; } 261 | 262 | void GLTF::Accessor::writeJSON(void* writer, GLTF::Options* options) { 263 | rapidjson::Writer* jsonWriter = 264 | static_cast*>(writer); 265 | if (this->bufferView) { 266 | jsonWriter->Key("bufferView"); 267 | if (options->version == "1.0") { 268 | jsonWriter->String(this->bufferView->getStringId().c_str()); 269 | } else { 270 | jsonWriter->Int(this->bufferView->id); 271 | } 272 | jsonWriter->Key("byteOffset"); 273 | jsonWriter->Int(this->byteOffset); 274 | } 275 | if (options->version == "1.0") { 276 | int byteStride = bufferView->byteStride; 277 | if (byteStride != 0) { 278 | jsonWriter->Key("byteStride"); 279 | jsonWriter->Int(bufferView->byteStride); 280 | } 281 | } 282 | jsonWriter->Key("componentType"); 283 | jsonWriter->Int(static_cast(this->componentType)); 284 | jsonWriter->Key("count"); 285 | jsonWriter->Int(this->count); 286 | if (this->max) { 287 | jsonWriter->Key("max"); 288 | jsonWriter->StartArray(); 289 | for (int i = 0; i < this->getNumberOfComponents(); i++) { 290 | if (componentType == GLTF::Constants::WebGL::FLOAT) { 291 | jsonWriter->Double(this->max[i]); 292 | } else { 293 | jsonWriter->Int(static_cast(this->max[i])); 294 | } 295 | } 296 | jsonWriter->EndArray(); 297 | } 298 | if (this->min) { 299 | jsonWriter->Key("min"); 300 | jsonWriter->StartArray(); 301 | for (int i = 0; i < this->getNumberOfComponents(); i++) { 302 | if (componentType == GLTF::Constants::WebGL::FLOAT) { 303 | jsonWriter->Double(this->min[i]); 304 | } else { 305 | jsonWriter->Int(static_cast(this->min[i])); 306 | } 307 | } 308 | jsonWriter->EndArray(); 309 | } 310 | jsonWriter->Key("type"); 311 | jsonWriter->String(this->getTypeName()); 312 | } 313 | -------------------------------------------------------------------------------- /test/js/test/validate.js: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai' 2 | import chai from 'chai' 3 | import chaiSubset from 'chai-subset' 4 | import child_process from 'child_process' 5 | import fs from 'fs' 6 | import https from 'https' 7 | import path from 'path' 8 | import Promise from 'bluebird' 9 | import tmp from 'tmp' 10 | import validator from 'gltf-validator' 11 | 12 | chai.use(chaiSubset) 13 | 14 | const execPromise = Promise.promisify(child_process.execFile) 15 | 16 | const SAMPLE_MODELS_BASE_URL = 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/sourceModels/' 17 | const COLLADA2GLTF_PATH = '../../build/COLLADA2GLTF-bin' 18 | 19 | async function convert(collada_url) { 20 | const tmpDir = tmp.dirSync({ 21 | unsafeCleanup: true 22 | }) 23 | try { 24 | const dirName = tmpDir.name 25 | const fileName = collada_url.split('/').pop() 26 | const targetFileName = path.join(dirName, fileName) 27 | const targetStream = fs.createWriteStream(targetFileName) 28 | https.get(collada_url, (res) => { 29 | res.pipe(targetStream) 30 | }) 31 | await new Promise((resolve, reject) => { 32 | targetStream.on('finish', () => { 33 | targetStream.close() 34 | resolve() 35 | }) 36 | }) 37 | const collada = fs.readFileSync(targetFileName, 'UTF-8') 38 | const stdout = await execPromise(COLLADA2GLTF_PATH, [targetFileName]) 39 | const outputFileName = path.join(dirName, 'output', fileName.replace('.dae', '.gltf')) 40 | return fs.readFileSync(outputFileName) 41 | } finally { 42 | tmpDir.removeCallback() 43 | } 44 | } 45 | 46 | async function validate(asset, expected) { 47 | const report = await validator.validateBytes(new Uint8Array(asset)) 48 | expect(report).to.containSubset(expected) 49 | return report 50 | } 51 | 52 | describe('Test and validate model conversions', () => { 53 | it('AnimatedMorphCube', async () => { 54 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'AnimatedMorphCube/AnimatedMorphCube.dae') 55 | await validate(asset, { 56 | info: { 57 | hasAnimations: true, 58 | hasMorphTargets: true, 59 | primitivesCount: 1 60 | }, issues: { 61 | numErrors: 0, 62 | numInfos: 0, 63 | numWarnings: 0 64 | } 65 | }) 66 | }) 67 | 68 | it('Box', async () => { 69 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'Box/Box.dae') 70 | await validate(asset, { 71 | info: { 72 | primitivesCount: 1 73 | }, issues: { 74 | numErrors: 0, 75 | numInfos: 0, 76 | numWarnings: 0 77 | } 78 | }) 79 | }) 80 | 81 | it('BoxAnimated', async () => { 82 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'BoxAnimated/BoxAnimated.dae') 83 | await validate(asset, { 84 | info: { 85 | hasAnimations: true, 86 | primitivesCount: 2 87 | }, issues: { 88 | numErrors: 0, 89 | numInfos: 0, 90 | numWarnings: 0 91 | } 92 | }) 93 | }) 94 | 95 | it('BoxTextured', async () => { 96 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'BoxTextured/BoxTextured.dae') 97 | await validate(asset, { 98 | info: { 99 | hasTextures: true, 100 | primitivesCount: 1, 101 | resources: [{ 102 | uri: 'CesiumLogoFlat.png' 103 | }] 104 | }, issues: { 105 | numErrors: 0, 106 | numInfos: 0, 107 | numWarnings: 0 108 | } 109 | }) 110 | }) 111 | 112 | it('BrainStem', async () => { 113 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'BrainStem/BrainStem.dae') 114 | await validate(asset, { 115 | info: { 116 | hasAnimations: true, 117 | hasSkins: true, 118 | primitivesCount: 59 119 | }, issues: { 120 | numErrors: 0, 121 | numInfos: 0, 122 | numWarnings: 0 123 | } 124 | }) 125 | }).timeout(10000) 126 | 127 | it('Buggy', async () => { 128 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'Buggy/Buggy.dae') 129 | await validate(asset, { 130 | info: { 131 | primitivesCount: 148 132 | }, issues: { 133 | numErrors: 0, 134 | /** 135 | * Buggy has degenerate triangles which generates infos. 136 | * Once we optimize them out, this should be uncommented. 137 | */ 138 | //numInfos: 0, 139 | numWarnings: 0 140 | } 141 | }) 142 | }).timeout(10000) 143 | 144 | it('CesiumMan', async () => { 145 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'CesiumMan/CesiumMan.dae') 146 | await validate(asset, { 147 | info: { 148 | hasAnimations: true, 149 | hasSkins: true, 150 | hasTextures: true, 151 | primitivesCount: 1, 152 | resources: [{ 153 | uri: 'CesiumMan.jpg' 154 | }] 155 | }, issues: { 156 | numErrors: 0, 157 | numInfos: 0, 158 | numWarnings: 0 159 | } 160 | }) 161 | }) 162 | 163 | it('CesiumMilkTruck', async () => { 164 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'CesiumMilkTruck/CesiumMilkTruck.dae') 165 | await validate(asset, { 166 | info: { 167 | hasAnimations: true, 168 | hasTextures: true, 169 | primitivesCount: 4, 170 | resources: [{ 171 | uri: 'CesiumMilkTruck.png' 172 | }] 173 | }, issues: { 174 | numErrors: 0, 175 | numInfos: 0, 176 | numWarnings: 0 177 | } 178 | }) 179 | }) 180 | 181 | it('Duck', async () => { 182 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'Duck/Duck.dae') 183 | await validate(asset, { 184 | info: { 185 | hasTextures: true, 186 | primitivesCount: 1, 187 | resources: [{ 188 | uri: 'DuckCM.png' 189 | }] 190 | }, issues: { 191 | numErrors: 0, 192 | numInfos: 0, 193 | numWarnings: 0 194 | } 195 | }) 196 | }) 197 | 198 | it('GearboxAssy', async () => { 199 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'GearboxAssy/GearboxAssy.dae') 200 | await validate(asset, { 201 | info: { 202 | primitivesCount: 42 203 | }, issues: { 204 | numErrors: 0, 205 | numInfos: 0, 206 | numWarnings: 0 207 | } 208 | }) 209 | }).timeout(10000) 210 | 211 | it('Monster', async () => { 212 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'Monster/Monster.dae') 213 | await validate(asset, { 214 | info: { 215 | hasAnimations: true, 216 | hasSkins: true, 217 | hasTextures: true, 218 | primitivesCount: 1, 219 | resources: [{ 220 | uri: 'Monster.jpg' 221 | }] 222 | }, issues: { 223 | numErrors: 0, 224 | numInfos: 0, 225 | numWarnings: 0 226 | } 227 | }) 228 | }) 229 | 230 | it('ReciprocatingSaw', async () => { 231 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'ReciprocatingSaw/ReciprocatingSaw.dae') 232 | await validate(asset, { 233 | info: { 234 | primitivesCount: 65 235 | }, issues: { 236 | numErrors: 0, 237 | numInfos: 0, 238 | numWarnings: 0 239 | } 240 | }) 241 | }).timeout(10000) 242 | 243 | it('RiggedFigure', async () => { 244 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'RiggedFigure/RiggedFigure.dae') 245 | await validate(asset, { 246 | info: { 247 | hasAnimations: true, 248 | hasSkins: true, 249 | primitivesCount: 1, 250 | }, issues: { 251 | numErrors: 0, 252 | numInfos: 0, 253 | numWarnings: 0 254 | } 255 | }) 256 | }) 257 | 258 | it('RiggedSimple', async () => { 259 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'RiggedSimple/RiggedSimple.dae') 260 | await validate(asset, { 261 | info: { 262 | hasAnimations: true, 263 | hasSkins: true, 264 | primitivesCount: 1, 265 | }, issues: { 266 | numErrors: 0, 267 | numInfos: 0, 268 | numWarnings: 0 269 | } 270 | }) 271 | }) 272 | 273 | it('VC', async () => { 274 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'VC/VC.dae') 275 | await validate(asset, { 276 | info: { 277 | hasAnimations: true, 278 | hasTextures: true, 279 | primitivesCount: 167, 280 | resources: [{ 281 | uri: '001.jpg' 282 | }, { 283 | uri: 'cockpit-map.jpg' 284 | }, { 285 | uri: 's_08.jpg' 286 | }, { 287 | uri: 's_06.jpg' 288 | }, { 289 | uri: 's_04.jpg' 290 | }, { 291 | uri: 's_02.jpg' 292 | }, { 293 | uri: 's_07.jpg' 294 | }, { 295 | uri: 's_03.jpg' 296 | }, { 297 | uri: 's_05.jpg' 298 | }, { 299 | uri: 's_01.jpg' 300 | }, { 301 | uri: '002.jpg' 302 | }, { 303 | uri: '11.jpg' 304 | },, { 305 | uri: 'machine.jpg' 306 | }, { 307 | uri: 'prop128.png' 308 | }, { 309 | uri: 'scrapsurf03-red.jpg' 310 | }, { 311 | uri: 'f22.jpg' 312 | }, { 313 | uri: 'heli.jpg' 314 | }, { 315 | uri: 'O21.jpg' 316 | }, { 317 | uri: '5.jpg' 318 | }, { 319 | uri: 'surface01.jpg' 320 | }] 321 | }, issues: { 322 | numErrors: 0, 323 | numInfos: 0, 324 | numWarnings: 0 325 | } 326 | }) 327 | }) 328 | 329 | it('WalkingLady', async () => { 330 | const asset = await convert(SAMPLE_MODELS_BASE_URL + 'WalkingLady/WalkingLady.dae') 331 | await validate(asset, { 332 | info: { 333 | hasAnimations: true, 334 | hasSkins: true, 335 | primitivesCount: 1 336 | }, issues: { 337 | numErrors: 0, 338 | numInfos: 0, 339 | numWarnings: 0 340 | } 341 | }) 342 | }) 343 | }) -------------------------------------------------------------------------------- /GLTF/src/GLTFNode.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include "GLTFNode.h" 3 | 4 | #include 5 | 6 | #include "rapidjson/stringbuffer.h" 7 | #include "rapidjson/writer.h" 8 | 9 | GLTF::Node::TransformMatrix::TransformMatrix() { 10 | this->type = GLTF::Node::Transform::MATRIX; 11 | this->matrix[0] = 1; 12 | this->matrix[1] = 0; 13 | this->matrix[2] = 0; 14 | this->matrix[3] = 0; 15 | this->matrix[4] = 0; 16 | this->matrix[5] = 1; 17 | this->matrix[6] = 0; 18 | this->matrix[7] = 0; 19 | this->matrix[8] = 0; 20 | this->matrix[9] = 0; 21 | this->matrix[10] = 1; 22 | this->matrix[11] = 0; 23 | this->matrix[12] = 0; 24 | this->matrix[13] = 0; 25 | this->matrix[14] = 0; 26 | this->matrix[15] = 1; 27 | } 28 | 29 | GLTF::Node::TransformMatrix::TransformMatrix(float a00, float a01, float a02, 30 | float a03, float a10, float a11, 31 | float a12, float a13, float a20, 32 | float a21, float a22, float a23, 33 | float a30, float a31, float a32, 34 | float a33) 35 | : GLTF::Node::TransformMatrix() { 36 | this->matrix[0] = a00; 37 | this->matrix[1] = a10; 38 | this->matrix[2] = a20; 39 | this->matrix[3] = a30; 40 | this->matrix[4] = a01; 41 | this->matrix[5] = a11; 42 | this->matrix[6] = a21; 43 | this->matrix[7] = a31; 44 | this->matrix[8] = a02; 45 | this->matrix[9] = a12; 46 | this->matrix[10] = a22; 47 | this->matrix[11] = a32; 48 | this->matrix[12] = a03; 49 | this->matrix[13] = a13; 50 | this->matrix[14] = a23; 51 | this->matrix[15] = a33; 52 | } 53 | 54 | GLTF::Node::TransformTRS::TransformTRS() { 55 | this->type = GLTF::Node::Transform::TRS; 56 | } 57 | 58 | void GLTF::Node::TransformMatrix::premultiply( 59 | GLTF::Node::TransformMatrix* transform) { 60 | premultiply(transform, this); 61 | } 62 | 63 | void GLTF::Node::TransformMatrix::premultiply( 64 | GLTF::Node::TransformMatrix* transform, 65 | GLTF::Node::TransformMatrix* destination) { 66 | float matrix[16]; 67 | for (int i = 0; i < 4; i++) { 68 | for (int j = 0; j < 4; j++) { 69 | float sum = 0; 70 | for (int k = 0; k < 4; k++) { 71 | sum += this->matrix[i * 4 + k] * transform->matrix[k * 4 + j]; 72 | } 73 | matrix[i * 4 + j] = sum; 74 | } 75 | } 76 | for (int i = 0; i < 16; i++) { 77 | destination->matrix[i] = matrix[i]; 78 | } 79 | } 80 | 81 | void GLTF::Node::TransformMatrix::scaleUniform(float scale) { 82 | for (int i = 0; i < 11; i++) { 83 | this->matrix[i] *= scale; 84 | } 85 | } 86 | 87 | bool GLTF::Node::TransformMatrix::isIdentity() { 88 | return matrix[0] == 1 && matrix[1] == 0 && matrix[2] == 0 && matrix[3] == 0 && 89 | matrix[4] == 0 && matrix[5] == 1 && matrix[6] == 0 && matrix[7] == 0 && 90 | matrix[8] == 0 && matrix[9] == 0 && matrix[10] == 1 && 91 | matrix[11] == 0 && matrix[12] == 0 && matrix[13] == 0 && 92 | matrix[14] == 0 && matrix[15] == 1; 93 | } 94 | 95 | bool GLTF::Node::TransformTRS::isIdentityTranslation() { 96 | return translation[0] == 0 && translation[1] == 0 && translation[2] == 0; 97 | } 98 | 99 | bool GLTF::Node::TransformTRS::isIdentityRotation() { 100 | return rotation[0] == 0 && rotation[1] == 0 && rotation[2] == 0 && 101 | rotation[3] == 1; 102 | } 103 | 104 | bool GLTF::Node::TransformTRS::isIdentityScale() { 105 | return scale[0] == 1 && scale[1] == 1 && scale[2] == 1; 106 | } 107 | 108 | GLTF::Node::TransformTRS* GLTF::Node::TransformMatrix::getTransformTRS() { 109 | GLTF::Node::TransformTRS* trs = new GLTF::Node::TransformTRS(); 110 | getTransformTRS(trs); 111 | return trs; 112 | } 113 | 114 | GLTF::Node::Transform* GLTF::Node::TransformMatrix::clone() { 115 | auto result = new TransformMatrix(); 116 | memcpy(result->matrix, matrix, sizeof(float) * 16); 117 | return result; 118 | } 119 | 120 | const int rotationMatrixNext[3] = {1, 2, 0}; 121 | void GLTF::Node::TransformMatrix::getTransformTRS( 122 | GLTF::Node::TransformTRS* trs) { 123 | // get translation 124 | trs->translation[0] = matrix[12]; 125 | trs->translation[1] = matrix[13]; 126 | trs->translation[2] = matrix[14]; 127 | 128 | // get rotation 129 | float root; 130 | float m00 = matrix[0]; 131 | float m11 = matrix[5]; 132 | float m22 = matrix[10]; 133 | float trace = m00 + m11 + m22; 134 | 135 | float x, y, z, w; 136 | 137 | if (trace > 0) { 138 | root = sqrtf(static_cast(trace + 1.0)); 139 | w = static_cast(0.5 * root); 140 | root = static_cast(0.5 / root); 141 | 142 | x = (matrix[6] - matrix[9]) * root; 143 | y = (matrix[8] - matrix[2]) * root; 144 | z = (matrix[1] - matrix[4]) * root; 145 | } else { 146 | int i = 0; 147 | if (m11 > m00) { 148 | i = 1; 149 | } 150 | if (m22 > m00 && m22 > m11) { 151 | i = 2; 152 | } 153 | int j = rotationMatrixNext[i]; 154 | int k = rotationMatrixNext[j]; 155 | 156 | root = sqrtf(static_cast(matrix[i * 4 + i] - matrix[j * 4 + j] - 157 | matrix[k * 4 + k] + 1.0)); 158 | trs->rotation[i] = static_cast(0.5 * root); 159 | root = static_cast(0.5 / root); 160 | w = (matrix[k * 4 + j] - matrix[j * 4 + k]) * root; 161 | trs->rotation[j] = (matrix[j * 4 + i] + matrix[i * 4 + j]) * root; 162 | trs->rotation[k] = (matrix[k * 4 + i] + matrix[i * 4 + k]) * root; 163 | 164 | x = -trs->rotation[0]; 165 | y = -trs->rotation[1]; 166 | z = -trs->rotation[2]; 167 | } 168 | trs->rotation[0] = -x; 169 | trs->rotation[1] = -y; 170 | trs->rotation[2] = -z; 171 | trs->rotation[3] = -w; 172 | 173 | // get scale 174 | trs->scale[0] = sqrtf(matrix[0] * matrix[0] + matrix[1] * matrix[1] + 175 | matrix[2] * matrix[2]); 176 | trs->scale[1] = sqrtf(matrix[4] * matrix[4] + matrix[5] * matrix[5] + 177 | matrix[6] * matrix[6]); 178 | trs->scale[2] = sqrtf(matrix[8] * matrix[8] + matrix[9] * matrix[9] + 179 | matrix[10] * matrix[10]); 180 | } 181 | 182 | GLTF::Node::TransformMatrix* GLTF::Node::TransformTRS::getTransformMatrix() { 183 | GLTF::Node::TransformMatrix* result = new GLTF::Node::TransformMatrix(); 184 | float scaleX = scale[0]; 185 | float scaleY = scale[1]; 186 | float scaleZ = scale[2]; 187 | 188 | float rotationX = rotation[0]; 189 | float rotationY = rotation[1]; 190 | float rotationZ = rotation[2]; 191 | float rotationW = rotation[3]; 192 | 193 | float x2 = rotationX * rotationX; 194 | float xy = rotationX * rotationY; 195 | float xz = rotationX * rotationZ; 196 | float xw = rotationX * rotationW; 197 | float y2 = rotationY * rotationY; 198 | float yz = rotationY * rotationZ; 199 | float yw = rotationY * rotationW; 200 | float z2 = rotationZ * rotationZ; 201 | float zw = rotationZ * rotationW; 202 | float w2 = rotationW * rotationW; 203 | 204 | float m00 = x2 - y2 - z2 + w2; 205 | float m01 = static_cast(2.0 * (xy - zw)); 206 | float m02 = static_cast(2.0 * (xz + yw)); 207 | 208 | float m10 = static_cast(2.0 * (xy + zw)); 209 | float m11 = -x2 + y2 - z2 + w2; 210 | float m12 = static_cast(2.0 * (yz - xw)); 211 | 212 | float m20 = static_cast(2.0 * (xz - yw)); 213 | float m21 = static_cast(2.0 * (yz + xw)); 214 | float m22 = -x2 - y2 + z2 + w2; 215 | 216 | result->matrix[0] = m00 * scaleX; 217 | result->matrix[1] = m10 * scaleX; 218 | result->matrix[2] = m20 * scaleX; 219 | result->matrix[3] = 0.0; 220 | result->matrix[4] = m01 * scaleY; 221 | result->matrix[5] = m11 * scaleY; 222 | result->matrix[6] = m21 * scaleY; 223 | result->matrix[7] = 0.0; 224 | result->matrix[8] = m02 * scaleZ; 225 | result->matrix[9] = m12 * scaleZ; 226 | result->matrix[10] = m22 * scaleZ; 227 | result->matrix[11] = 0.0; 228 | result->matrix[12] = translation[0]; 229 | result->matrix[13] = translation[1]; 230 | result->matrix[14] = translation[2]; 231 | result->matrix[15] = 1.0; 232 | 233 | return result; 234 | } 235 | 236 | GLTF::Node::Transform* GLTF::Node::TransformTRS::clone() { 237 | auto result = new TransformTRS(); 238 | 239 | memcpy(result->translation, translation, sizeof(float) * 3); 240 | memcpy(result->rotation, rotation, sizeof(float) * 4); 241 | memcpy(result->scale, scale, sizeof(float) * 3); 242 | 243 | return result; 244 | } 245 | 246 | GLTF::Node::~Node() { delete transform; } 247 | 248 | std::string GLTF::Node::typeName() { return "node"; } 249 | 250 | GLTF::Object* GLTF::Node::clone(GLTF::Object* clone) { 251 | static std::function noop = 252 | [](GLTF::Node*, GLTF::Node*) {}; 253 | 254 | return this->clone(dynamic_cast(clone), noop); 255 | } 256 | 257 | GLTF::Object* GLTF::Node::clone( 258 | GLTF::Node* node, 259 | const std::function& predicate) { 260 | if (node != NULL) { 261 | predicate(this, node); 262 | node->camera = camera; 263 | for (GLTF::Node* child : children) { 264 | GLTF::Node* cloneChild = new GLTF::Node(); 265 | child->clone(cloneChild, predicate); 266 | node->children.push_back(cloneChild); 267 | } 268 | node->skin = skin; 269 | node->jointName = jointName; 270 | node->mesh = mesh; 271 | node->light = light; 272 | node->transform = transform->clone(); 273 | GLTF::Object::clone(node); 274 | } 275 | return node; 276 | } 277 | 278 | void GLTF::Node::writeJSON(void* writer, GLTF::Options* options) { 279 | rapidjson::Writer* jsonWriter = 280 | (rapidjson::Writer*)writer; 281 | 282 | if (mesh != NULL) { 283 | if (options->version == "1.0") { 284 | jsonWriter->Key("meshes"); 285 | jsonWriter->StartArray(); 286 | jsonWriter->String(mesh->getStringId().c_str()); 287 | jsonWriter->EndArray(); 288 | } else { 289 | jsonWriter->Key("mesh"); 290 | jsonWriter->Int(mesh->id); 291 | } 292 | } 293 | if (children.size() > 0) { 294 | jsonWriter->Key("children"); 295 | jsonWriter->StartArray(); 296 | for (GLTF::Node* child : children) { 297 | if (options->version == "1.0") { 298 | jsonWriter->String(child->getStringId().c_str()); 299 | } else { 300 | jsonWriter->Int(child->id); 301 | } 302 | } 303 | jsonWriter->EndArray(); 304 | } 305 | if (options->version == "1.0" && jointName != "") { 306 | jsonWriter->Key("jointName"); 307 | jsonWriter->String(jointName.c_str()); 308 | } 309 | if (options->materialsCommon && light != NULL) { 310 | jsonWriter->Key("extensions"); 311 | jsonWriter->StartObject(); 312 | jsonWriter->Key("KHR_materials_common"); 313 | jsonWriter->StartObject(); 314 | jsonWriter->Key("light"); 315 | if (options->version == "1.0") { 316 | jsonWriter->String(light->getStringId().c_str()); 317 | } else { 318 | jsonWriter->Int(light->id); 319 | } 320 | jsonWriter->EndObject(); 321 | jsonWriter->EndObject(); 322 | } 323 | if (transform != NULL) { 324 | if (transform->type == GLTF::Node::Transform::MATRIX) { 325 | GLTF::Node::TransformMatrix* transformMatrix = 326 | (GLTF::Node::TransformMatrix*)transform; 327 | if (!transformMatrix->isIdentity()) { 328 | jsonWriter->Key("matrix"); 329 | jsonWriter->StartArray(); 330 | for (int i = 0; i < 16; i++) { 331 | jsonWriter->Double(transformMatrix->matrix[i]); 332 | } 333 | jsonWriter->EndArray(); 334 | } 335 | } else if (transform->type == GLTF::Node::Transform::TRS) { 336 | GLTF::Node::TransformTRS* transformTRS = 337 | (GLTF::Node::TransformTRS*)transform; 338 | if (!transformTRS->isIdentityTranslation()) { 339 | jsonWriter->Key("translation"); 340 | jsonWriter->StartArray(); 341 | for (int i = 0; i < 3; i++) { 342 | jsonWriter->Double(transformTRS->translation[i]); 343 | } 344 | jsonWriter->EndArray(); 345 | } 346 | 347 | if (!transformTRS->isIdentityRotation()) { 348 | jsonWriter->Key("rotation"); 349 | jsonWriter->StartArray(); 350 | for (int i = 0; i < 4; i++) { 351 | jsonWriter->Double(transformTRS->rotation[i]); 352 | } 353 | jsonWriter->EndArray(); 354 | } 355 | 356 | if (!transformTRS->isIdentityScale()) { 357 | jsonWriter->Key("scale"); 358 | jsonWriter->StartArray(); 359 | for (int i = 0; i < 3; i++) { 360 | jsonWriter->Double(transformTRS->scale[i]); 361 | } 362 | jsonWriter->EndArray(); 363 | } 364 | } 365 | } 366 | if (skin != NULL) { 367 | jsonWriter->Key("skin"); 368 | if (options->version == "1.0") { 369 | jsonWriter->String(skin->getStringId().c_str()); 370 | if (skin->skeleton != NULL) { 371 | jsonWriter->Key("skeletons"); 372 | jsonWriter->StartArray(); 373 | jsonWriter->String(skin->skeleton->getStringId().c_str()); 374 | jsonWriter->EndArray(); 375 | } 376 | } else { 377 | jsonWriter->Int(skin->id); 378 | } 379 | } 380 | if (camera != NULL) { 381 | jsonWriter->Key("camera"); 382 | if (options->version == "1.0") { 383 | jsonWriter->String(camera->getStringId().c_str()); 384 | } else { 385 | jsonWriter->Int(camera->id); 386 | } 387 | } 388 | GLTF::Object::writeJSON(writer, options); 389 | } 390 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Khronos® Group Inc. 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "COLLADA2GLTFExtrasHandler.h" 9 | #include "COLLADA2GLTFWriter.h" 10 | #include "COLLADASaxFWLLoader.h" 11 | #include "ahoy/ahoy.h" 12 | #include "rapidjson/document.h" 13 | #include "rapidjson/prettywriter.h" 14 | #include "rapidjson/stringbuffer.h" 15 | #include "rapidjson/writer.h" 16 | 17 | const int HEADER_LENGTH = 12; 18 | const int CHUNK_HEADER_LENGTH = 8; 19 | 20 | int main(int argc, const char** argv) { 21 | COLLADA2GLTF::Options* options = new COLLADA2GLTF::Options(); 22 | 23 | bool separate; 24 | bool separateTextures; 25 | 26 | ahoy::Parser* parser = new ahoy::Parser(); 27 | parser->name("COLLADA2GLTF") 28 | ->usage("./COLLADA2GLTF input.dae output.gltf [options]"); 29 | 30 | parser->define("i", &options->inputPath) 31 | ->alias("input") 32 | ->description("path of the input COLLADA file") 33 | ->index(0) 34 | ->require(); 35 | 36 | parser->define("o", &options->outputPath) 37 | ->alias("output") 38 | ->description("path of the output glTF file") 39 | ->index(1); 40 | 41 | parser->define("basePath", &options->basePath) 42 | ->description("resolve external uris using this as the reference path"); 43 | 44 | parser->define("s", &separate) 45 | ->alias("separate") 46 | ->defaults(false) 47 | ->description("output separate binary buffer, shaders, and textures"); 48 | 49 | parser->define("t", &separateTextures) 50 | ->alias("separateTextures") 51 | ->defaults(false) 52 | ->description("output images separately, but embed buffers and shaders"); 53 | 54 | parser->define("b", &options->binary) 55 | ->alias("binary") 56 | ->defaults(false) 57 | ->description("output binary glTF"); 58 | 59 | parser->define("g", &options->glsl) 60 | ->alias("glsl") 61 | ->defaults(false) 62 | ->description( 63 | "output materials with glsl shaders using the KHR_technique_webgl " 64 | "extension"); 65 | 66 | parser->define("m", &options->materialsCommon) 67 | ->alias("materialsCommon") 68 | ->defaults(false) 69 | ->description( 70 | "output materials using the KHR_materials_common extension"); 71 | 72 | parser->define("doubleSided", &options->doubleSided) 73 | ->defaults(false) 74 | ->description( 75 | "Force all materials to be double sided. When this value is true, " 76 | "back-face culling is disabled and double sided lighting is enabled"); 77 | 78 | parser->define("v", &options->version) 79 | ->alias("version") 80 | ->description("glTF version to output (e.g. '1.0', '2.0')"); 81 | 82 | parser->define("p", &options->preserveUnusedSemantics) 83 | ->alias("preserveUnusedSemantics") 84 | ->defaults(false) 85 | ->description( 86 | "should unused semantics be preserved. When this value is true, all " 87 | "mesh data is left intact even if it's not used."); 88 | 89 | parser 90 | ->define("metallicRoughnessTextures", 91 | &options->metallicRoughnessTexturePaths) 92 | ->description( 93 | "paths to images to use as the PBR metallicRoughness textures"); 94 | 95 | parser->define("specularGlossiness", &options->specularGlossiness) 96 | ->defaults(false) 97 | ->description( 98 | "output PBR materials with the KHR_materials_pbrSpecularGlossiness " 99 | "extension"); 100 | 101 | parser 102 | ->define("lockOcclusionMetallicRoughness", 103 | &options->lockOcclusionMetallicRoughness) 104 | ->defaults(false) 105 | ->description( 106 | "set metallicRoughnessTexture to be the same as the occlusionTexture " 107 | "in materials where an ambient texture is defined"); 108 | 109 | parser->define("d", &options->dracoCompression) 110 | ->alias("dracoCompression") 111 | ->defaults(false) 112 | ->description( 113 | "compress the geometries using Draco compression extension"); 114 | 115 | parser->define("qp", &options->positionQuantizationBits) 116 | ->description( 117 | "position quantization bits used in Draco compression extension"); 118 | 119 | parser->define("qn", &options->normalQuantizationBits) 120 | ->description( 121 | "normal quantization bits used in Draco compression extension"); 122 | 123 | parser->define("qt", &options->texcoordQuantizationBits) 124 | ->description( 125 | "texture coordinate quantization bits used in Draco compression " 126 | "extension"); 127 | 128 | parser->define("qc", &options->colorQuantizationBits) 129 | ->description( 130 | "color quantization bits used in Draco compression extension"); 131 | 132 | parser->define("qj", &options->jointQuantizationBits) 133 | ->description( 134 | "joint indices and weights quantization bits used in Draco " 135 | "compression extension"); 136 | 137 | if (parser->parse(argc, argv)) { 138 | // Resolve and sanitize paths 139 | COLLADABU::URI inputPathURI = 140 | COLLADABU::URI::nativePathToUri(options->inputPath); 141 | std::string inputPathDir; 142 | std::string inputPathBaseName; 143 | std::string inputPathExtension; 144 | inputPathURI.pathComponents(inputPathDir, inputPathBaseName, 145 | inputPathExtension); 146 | COLLADABU::URI inputPathDirURI = 147 | COLLADABU::URI::nativePathToUri(inputPathDir); 148 | inputPathDir = 149 | inputPathDirURI.toNativePath(COLLADABU::Utils::getSystemType()); 150 | 151 | options->name = inputPathBaseName; 152 | 153 | COLLADABU::URI outputPathURI; 154 | if (options->outputPath == "") { 155 | outputPathURI = COLLADABU::URI::nativePathToUri( 156 | inputPathDir + "output/" + inputPathBaseName + ".gltf"); 157 | options->outputPath = 158 | outputPathURI.toNativePath(COLLADABU::Utils::getSystemType()); 159 | } 160 | outputPathURI = COLLADABU::URI::nativePathToUri(options->outputPath); 161 | std::string outputPathDir; 162 | std::string outputPathBaseName; 163 | std::string outputPathExtension; 164 | outputPathURI.pathComponents(outputPathDir, outputPathBaseName, 165 | outputPathExtension); 166 | COLLADABU::URI outputPathDirURI = 167 | COLLADABU::URI::nativePathToUri(outputPathDir); 168 | outputPathDir = 169 | outputPathDirURI.toNativePath(COLLADABU::Utils::getSystemType()); 170 | 171 | if (options->binary && outputPathExtension != "glb") { 172 | outputPathURI = COLLADABU::URI::nativePathToUri( 173 | outputPathDir + outputPathBaseName + ".glb"); 174 | options->outputPath = 175 | outputPathURI.toNativePath(COLLADABU::Utils::getSystemType()); 176 | } 177 | 178 | if (options->basePath == "") { 179 | options->basePath = inputPathDir; 180 | } else { 181 | COLLADABU::URI basePathURI = 182 | COLLADABU::URI::nativePathToUri(options->basePath); 183 | options->basePath = 184 | basePathURI.toNativePath(COLLADABU::Utils::getSystemType()); 185 | } 186 | 187 | // Export flags 188 | if (separate != 0) { 189 | options->embeddedBuffers = false; 190 | options->embeddedShaders = false; 191 | options->embeddedTextures = false; 192 | } 193 | if (separateTextures != 0) { 194 | options->embeddedTextures = false; 195 | } 196 | 197 | if (options->version == "1.0" && !options->materialsCommon) { 198 | options->glsl = true; 199 | } 200 | 201 | if (options->glsl && options->materialsCommon) { 202 | std::cout 203 | << "ERROR: Cannot export with both glsl and materialsCommon enabled" 204 | << std::endl; 205 | return -1; 206 | } 207 | if ((options->glsl || options->materialsCommon) && 208 | options->specularGlossiness) { 209 | std::cout << "ERROR: Cannot enable specularGlossiness unless the " 210 | "materials are exported as PBR" 211 | << std::endl; 212 | return -1; 213 | } 214 | if ((options->glsl || options->materialsCommon) && 215 | options->lockOcclusionMetallicRoughness) { 216 | std::cout << "ERROR: Cannot enable lockOcclusionMetallicRoughness unless " 217 | "the materials are exported as PBR" 218 | << std::endl; 219 | return -1; 220 | } 221 | 222 | // Create the output directory if it does not exist 223 | 224 | if (!COLLADABU::Utils::directoryExists(outputPathDir)) { 225 | COLLADABU::Utils::createDirectoryIfNeeded(outputPathDir); 226 | } 227 | 228 | std::cout << "Converting " << options->inputPath << " -> " 229 | << options->outputPath << std::endl; 230 | std::clock_t start = std::clock(); 231 | 232 | GLTF::Asset* asset = new GLTF::Asset(); 233 | COLLADASaxFWL::Loader* loader = new COLLADASaxFWL::Loader(); 234 | COLLADA2GLTF::ExtrasHandler* extrasHandler = 235 | new COLLADA2GLTF::ExtrasHandler(loader); 236 | COLLADA2GLTF::Writer* writer = 237 | new COLLADA2GLTF::Writer(loader, asset, options, extrasHandler); 238 | loader->registerExtraDataCallbackHandler( 239 | (COLLADASaxFWL::IExtraDataCallbackHandler*)extrasHandler); 240 | COLLADAFW::Root root(loader, writer); 241 | if (!root.loadDocument(options->inputPath)) { 242 | std::cout << "ERROR: Unable to load input from path '" 243 | << options->inputPath << "'" << std::endl; 244 | return -1; 245 | } 246 | 247 | asset->mergeAnimations(writer->getAnimationGroups()); 248 | asset->removeUnusedNodes(options); 249 | 250 | if (!options->preserveUnusedSemantics) { 251 | asset->removeUnusedSemantics(); 252 | } 253 | 254 | if (options->dracoCompression) { 255 | asset->removeUncompressedBufferViews(); 256 | asset->compressPrimitives(options); 257 | } 258 | 259 | GLTF::Buffer* buffer = asset->packAccessors(); 260 | if (options->binary && options->version == "1.0") { 261 | buffer->stringId = "binary_glTF"; 262 | } 263 | 264 | // Create image bufferViews for binary glTF 265 | if (options->binary && options->embeddedTextures) { 266 | size_t imageBufferLength = 0; 267 | std::vector images = asset->getAllImages(); 268 | for (GLTF::Image* image : images) { 269 | imageBufferLength += image->byteLength; 270 | } 271 | unsigned char* bufferData = buffer->data; 272 | bufferData = (unsigned char*)realloc( 273 | bufferData, buffer->byteLength + imageBufferLength); 274 | size_t byteOffset = buffer->byteLength; 275 | for (GLTF::Image* image : images) { 276 | GLTF::BufferView* bufferView = 277 | new GLTF::BufferView(byteOffset, image->byteLength, buffer); 278 | image->bufferView = bufferView; 279 | std::memcpy(bufferData + byteOffset, image->data, image->byteLength); 280 | byteOffset += image->byteLength; 281 | } 282 | buffer->data = bufferData; 283 | buffer->byteLength += imageBufferLength; 284 | } 285 | 286 | rapidjson::StringBuffer s; 287 | rapidjson::Writer jsonWriter = 288 | rapidjson::Writer(s); 289 | jsonWriter.StartObject(); 290 | asset->writeJSON(&jsonWriter, options); 291 | jsonWriter.EndObject(); 292 | 293 | if (!options->embeddedTextures) { 294 | for (GLTF::Image* image : asset->getAllImages()) { 295 | COLLADABU::URI imageURI = 296 | COLLADABU::URI::nativePathToUri(outputPathDir + image->uri); 297 | std::string imageString = 298 | imageURI.toNativePath(COLLADABU::Utils::getSystemType()); 299 | FILE* file = fopen(imageString.c_str(), "wb"); 300 | if (file != NULL) { 301 | fwrite(image->data, sizeof(unsigned char), image->byteLength, file); 302 | fclose(file); 303 | } else { 304 | std::cout << "ERROR: Couldn't write image to path '" << imageString 305 | << "'" << std::endl; 306 | } 307 | } 308 | } 309 | 310 | if (!options->embeddedBuffers) { 311 | COLLADABU::URI bufferURI = 312 | COLLADABU::URI::nativePathToUri(outputPathDir + buffer->uri); 313 | std::string bufferString = 314 | bufferURI.toNativePath(COLLADABU::Utils::getSystemType()); 315 | FILE* file = fopen(bufferString.c_str(), "wb"); 316 | if (file != NULL) { 317 | fwrite(buffer->data, sizeof(unsigned char), buffer->byteLength, file); 318 | fclose(file); 319 | } else { 320 | std::cout << "ERROR: Couldn't write buffer to path '" << bufferString 321 | << "'" << std::endl; 322 | } 323 | } 324 | 325 | if (!options->embeddedShaders) { 326 | for (GLTF::Shader* shader : asset->getAllShaders()) { 327 | COLLADABU::URI shaderURI = 328 | COLLADABU::URI::nativePathToUri(outputPathDir + shader->uri); 329 | std::string shaderString = 330 | shaderURI.toNativePath(COLLADABU::Utils::getSystemType()); 331 | FILE* file = fopen(shaderString.c_str(), "wb"); 332 | if (file != NULL) { 333 | fwrite(shader->source.c_str(), sizeof(unsigned char), 334 | shader->source.length(), file); 335 | fclose(file); 336 | } else { 337 | std::cout << "ERROR: Couldn't write shader to path '" << shaderString 338 | << "'" << std::endl; 339 | } 340 | } 341 | } 342 | 343 | std::string jsonString = s.GetString(); 344 | if (!options->binary) { 345 | rapidjson::Document jsonDocument; 346 | jsonDocument.Parse(jsonString.c_str()); 347 | 348 | rapidjson::StringBuffer buffer; 349 | rapidjson::PrettyWriter writer(buffer); 350 | jsonDocument.Accept(writer); 351 | 352 | std::ofstream file(options->outputPath); 353 | if (file.is_open()) { 354 | file << buffer.GetString() << std::endl; 355 | file.close(); 356 | } else { 357 | std::cout << "ERROR: couldn't write glTF to path '" 358 | << options->outputPath << "'" << std::endl; 359 | } 360 | } else { 361 | FILE* file = fopen(options->outputPath.c_str(), "wb"); 362 | if (file != NULL) { 363 | fwrite("glTF", sizeof(char), 4, file); // magic 364 | 365 | uint32_t* writeHeader = new uint32_t[2]; 366 | // version 367 | if (options->version == "1.0") { 368 | writeHeader[0] = 1; 369 | } else { 370 | writeHeader[0] = 2; 371 | } 372 | 373 | int jsonPadding = (4 - (jsonString.length() & 3)) & 3; 374 | int binPadding = (4 - (buffer->byteLength & 3)) & 3; 375 | 376 | writeHeader[1] = 377 | HEADER_LENGTH + 378 | (CHUNK_HEADER_LENGTH + jsonString.length() + jsonPadding + 379 | buffer->byteLength + binPadding); // length 380 | if (options->version != "1.0") { 381 | writeHeader[1] += CHUNK_HEADER_LENGTH; 382 | } 383 | fwrite(writeHeader, sizeof(uint32_t), 2, file); // GLB header 384 | 385 | writeHeader[0] = 386 | jsonString.length() + 387 | jsonPadding; // 2.0 - chunkLength / 1.0 - contentLength 388 | if (options->version == "1.0") { 389 | writeHeader[1] = 0; // 1.0 - contentFormat 390 | } else { 391 | writeHeader[1] = 0x4E4F534A; // 2.0 - chunkType JSON 392 | } 393 | fwrite(writeHeader, sizeof(uint32_t), 2, file); 394 | fwrite(jsonString.c_str(), sizeof(char), jsonString.length(), file); 395 | for (int i = 0; i < jsonPadding; i++) { 396 | fwrite(" ", sizeof(char), 1, file); 397 | } 398 | if (options->version != "1.0") { 399 | writeHeader[0] = buffer->byteLength + binPadding; // chunkLength 400 | writeHeader[1] = 0x004E4942; // chunkType BIN 401 | fwrite(writeHeader, sizeof(uint32_t), 2, file); 402 | } 403 | fwrite(buffer->data, sizeof(unsigned char), buffer->byteLength, file); 404 | for (int i = 0; i < binPadding; i++) { 405 | fwrite("\0", sizeof(char), 1, file); 406 | } 407 | delete[] writeHeader; 408 | 409 | fclose(file); 410 | } else { 411 | std::cout << "ERROR couldn't write binary glTF to path '" 412 | << options->outputPath << "'" << std::endl; 413 | } 414 | } 415 | 416 | std::clock_t end = std::clock(); 417 | std::cout << "Time: " 418 | << ((end - start) / static_cast(CLOCKS_PER_SEC / 1000)) 419 | << " ms" << std::endl; 420 | delete asset; 421 | return 0; 422 | } else { 423 | return -1; 424 | } 425 | } 426 | --------------------------------------------------------------------------------