FBX2glTF/src/gltf/GltfModel.hpp

197 lines
6.8 KiB
C++

/**
* 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 <fstream>
#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<T> 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<T> 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 <typename T>
class Holder {
public:
std::shared_ptr<T> hold(T* ptr) {
ptr->ix = to_uint32(ptrs.size());
ptrs.emplace_back(ptr);
return ptrs.back();
}
std::vector<std::shared_ptr<T>> ptrs;
};
class GltfModel {
public:
explicit GltfModel(const GltfOptions& options)
: binary(new std::vector<uint8_t>),
isGlb(options.outputBinary),
defaultSampler(nullptr),
defaultBuffer(buffers.hold(buildDefaultBuffer(options))) {
defaultSampler = samplers.hold(buildDefaultSampler());
}
std::shared_ptr<BufferViewData> GetAlignedBufferView(
BufferData& buffer,
const BufferViewData::GL_ArrayType target);
std::shared_ptr<BufferViewData>
AddRawBufferView(BufferData& buffer, const char* source, uint32_t bytes);
std::shared_ptr<BufferViewData> AddBufferViewForFile(
BufferData& buffer,
const std::string& filename);
template <class T>
std::shared_ptr<AccessorData> AddAccessorWithView(
BufferViewData& bufferView,
const GLType& type,
const std::vector<T>& source,
std::string name) {
auto accessor = accessors.hold(new AccessorData(bufferView, type, name));
accessor->appendAsBinaryArray(source, *binary);
bufferView.byteLength = accessor->byteLength();
return accessor;
}
template <class T>
std::shared_ptr<AccessorData>
AddAccessorAndView(BufferData& buffer, const GLType& type, const std::vector<T>& source) {
auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE);
return AddAccessorWithView(*bufferView, type, source, std::string(""));
}
template <class T>
std::shared_ptr<AccessorData> AddAccessorAndView(
BufferData& buffer,
const GLType& type,
const std::vector<T>& source,
std::string name) {
auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE);
return AddAccessorWithView(*bufferView, type, source, name);
}
template <class T>
std::shared_ptr<AccessorData> AddAttributeToPrimitive(
BufferData& buffer,
const RawModel& surfaceModel,
PrimitiveData& primitive,
const AttributeDefinition<T>& attrDef) {
// copy attribute data into vector
std::vector<T> attribArr;
surfaceModel.GetAttributeArray<T>(attribArr, attrDef.rawAttributeIx);
std::shared_ptr<AccessorData> 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 <class T>
std::shared_ptr<AccessorData> AddAttributeArrayToPrimitive(
BufferData& buffer,
const RawModel& surfaceModel,
PrimitiveData& primitive,
const AttributeArrayDefinition<T>& attrDef) {
// copy attribute data into vector
std::vector<T> attribArr;
surfaceModel.GetArrayAttributeArray<T>(attribArr, attrDef.rawAttributeIx, attrDef.arrayOffset);
std::shared_ptr<AccessorData> accessor;
if (attrDef.dracoComponentType != draco::DT_INVALID && primitive.dracoMesh != nullptr) {
primitive.AddDracoArrayAttrib(attrDef, attribArr);
accessor = accessors.hold(new AccessorData(attrDef.glType));
accessor->count = 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 <class T>
void serializeHolder(json& glTFJson, std::string key, const Holder<T> holder) {
if (!holder.ptrs.empty()) {
std::vector<json> 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<std::string, std::shared_ptr<BufferViewData>> filenameToBufferView;
std::shared_ptr<std::vector<uint8_t>> binary;
Holder<BufferData> buffers;
Holder<BufferViewData> bufferViews;
Holder<AccessorData> accessors;
Holder<ImageData> images;
Holder<SamplerData> samplers;
Holder<TextureData> textures;
Holder<MaterialData> materials;
Holder<MeshData> meshes;
Holder<SkinData> skins;
Holder<AnimationData> animations;
Holder<CameraData> cameras;
Holder<NodeData> nodes;
Holder<SceneData> scenes;
Holder<LightData> lights;
std::shared_ptr<SamplerData> defaultSampler;
std::shared_ptr<BufferData> defaultBuffer;
private:
SamplerData* buildDefaultSampler() {
return new SamplerData();
}
BufferData* buildDefaultBuffer(const GltfOptions& options) {
return options.outputBinary ? new BufferData(binary)
: new BufferData(extBufferFilename, binary, options.embedResources);
}
};