/** * Copyright (c) Facebook, Inc. and its affiliates. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include #include "FBX2glTF.h" #include "gltf/properties/AccessorData.hpp" #include "gltf/properties/AnimationData.hpp" #include "gltf/properties/BufferData.hpp" #include "gltf/properties/BufferViewData.hpp" #include "gltf/properties/CameraData.hpp" #include "gltf/properties/ImageData.hpp" #include "gltf/properties/LightData.hpp" #include "gltf/properties/MaterialData.hpp" #include "gltf/properties/MeshData.hpp" #include "gltf/properties/NodeData.hpp" #include "gltf/properties/PrimitiveData.hpp" #include "gltf/properties/SamplerData.hpp" #include "gltf/properties/SceneData.hpp" #include "gltf/properties/SkinData.hpp" #include "gltf/properties/TextureData.hpp" /** * glTF 2.0 is based on the idea that data structs within a file are referenced by index; an * accessor will point to the n:th buffer view, and so on. The Holder class takes a freshly * instantiated class, and then creates, stored, and returns a shared_ptr for it. * * The idea is that every glTF resource in the file will live as long as the Holder does, and the * Holders are all kept in the GLTFData struct. Clients may certainly cnhoose to perpetuate the full * shared_ptr reference counting type, but generally speaking we pass around simple T& and T* * types because the GLTFData struct will, by design, outlive all other activity that takes place * during in a single conversion run. */ template class Holder { public: std::shared_ptr hold(T* ptr) { ptr->ix = to_uint32(ptrs.size()); ptrs.emplace_back(ptr); return ptrs.back(); } std::vector> ptrs; }; class GltfModel { public: explicit GltfModel(const GltfOptions& options) : binary(new std::vector), isGlb(options.outputBinary), defaultSampler(nullptr), defaultBuffer(buffers.hold(buildDefaultBuffer(options))) { defaultSampler = samplers.hold(buildDefaultSampler()); } std::shared_ptr GetAlignedBufferView( BufferData& buffer, const BufferViewData::GL_ArrayType target); std::shared_ptr AddRawBufferView(BufferData& buffer, const char* source, uint32_t bytes); std::shared_ptr AddBufferViewForFile( BufferData& buffer, const std::string& filename); template std::shared_ptr AddAccessorWithView( BufferViewData& bufferView, const GLType& type, const std::vector& source, std::string name) { auto accessor = accessors.hold(new AccessorData(bufferView, type, name)); accessor->appendAsBinaryArray(source, *binary); bufferView.byteLength = accessor->byteLength(); return accessor; } template std::shared_ptr AddAccessorAndView(BufferData& buffer, const GLType& type, const std::vector& source) { auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE); return AddAccessorWithView(*bufferView, type, source, std::string("")); } template std::shared_ptr AddAccessorAndView( BufferData& buffer, const GLType& type, const std::vector& source, std::string name) { auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE); return AddAccessorWithView(*bufferView, type, source, name); } template std::shared_ptr AddAttributeToPrimitive( BufferData& buffer, const RawModel& surfaceModel, PrimitiveData& primitive, const AttributeDefinition& attrDef) { // copy attribute data into vector std::vector attribArr; surfaceModel.GetAttributeArray(attribArr, attrDef.rawAttributeIx); std::shared_ptr accessor; if (attrDef.dracoComponentType != draco::DT_INVALID && primitive.dracoMesh != nullptr) { primitive.AddDracoAttrib(attrDef, attribArr); accessor = accessors.hold(new AccessorData(attrDef.glType)); accessor->count = to_uint32(attribArr.size()); } else { auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER); accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr, std::string("")); } primitive.AddAttrib(attrDef.gltfName, *accessor); return accessor; }; template std::shared_ptr AddTimestampsToAnimation( BufferData& buffer, AnimationData& animationData, const std::vector& timestamps, const GLType& glType, const draco::DataType dracoComponentType) { std::shared_ptr accessor; if (dracoComponentType != draco::DT_INVALID && animationData.dracoKeyframeAnimation != nullptr) { accessor = accessors.hold(new AccessorData(glType)); accessor->count = to_uint32(timestamps.size()); animationData.AddDracoTimestamps(*accessor, timestamps); } else { accessor = AddAccessorAndView(buffer, glType, timestamps); animationData.AddTimestamps(*accessor); } return accessor; }; template std::shared_ptr AddChannelToAnimation( BufferData& buffer, AnimationData& animationData, const NodeData& nDat, const ChannelDefinition& channelDef) { std::shared_ptr accessor; if (channelDef.dracoComponentType != draco::DT_INVALID && animationData.dracoKeyframeAnimation != nullptr) { accessor = accessors.hold(new AccessorData(channelDef.glType)); accessor->count = to_uint32(channelDef.channelData.size()); animationData.AddDracoNodeChannel( nDat, *accessor, channelDef.path, channelDef); } else { accessor = AddAccessorAndView(buffer, channelDef.glType, channelDef.channelData); animationData.AddNodeChannel(nDat, *accessor, channelDef.path); } return accessor; }; template void serializeHolder(json& glTFJson, std::string key, const Holder holder) { if (!holder.ptrs.empty()) { std::vector bits; for (const auto& ptr : holder.ptrs) { bits.push_back(ptr->serialize()); } glTFJson[key] = bits; } } void serializeHolders(json& glTFJson); const bool isGlb; // cache BufferViewData instances that've already been created from a given filename std::map> filenameToBufferView; std::shared_ptr> binary; Holder buffers; Holder bufferViews; Holder accessors; Holder images; Holder samplers; Holder textures; Holder materials; Holder meshes; Holder skins; Holder animations; Holder cameras; Holder nodes; Holder scenes; Holder lights; std::shared_ptr defaultSampler; std::shared_ptr defaultBuffer; private: SamplerData* buildDefaultSampler() { return new SamplerData(); } BufferData* buildDefaultBuffer(const GltfOptions& options) { return options.outputBinary ? new BufferData(binary) : new BufferData(extBufferFilename, binary, options.embedResources); } };