// // GLTFSerializer.h // libraries/fbx/src // // Created by Luis Cuenca on 8/30/17. // Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #ifndef hifi_GLTFSerializer_h #define hifi_GLTFSerializer_h #include #include #include #include struct GLTFAsset { QString generator; QString version; //required QString copyright; QMap defined; void dump() { if (defined["generator"]) { qCDebug(modelformat) << "generator: " << generator; } if (defined["version"]) { qCDebug(modelformat) << "version: " << version; } if (defined["copyright"]) { qCDebug(modelformat) << "copyright: " << copyright; } } }; struct GLTFNode { QString name; int camera{ -1 }; int mesh{ -1 }; QVector children; QVector translation; QVector rotation; QVector scale; QVector matrix; glm::mat4 transform; int skin { -1 }; QVector skeletons; QString jointName; QMap defined; void dump() { if (defined["name"]) { qCDebug(modelformat) << "name: " << name; } if (defined["camera"]) { qCDebug(modelformat) << "camera: " << camera; } if (defined["mesh"]) { qCDebug(modelformat) << "mesh: " << mesh; } if (defined["skin"]) { qCDebug(modelformat) << "skin: " << skin; } if (defined["jointName"]) { qCDebug(modelformat) << "jointName: " << jointName; } if (defined["children"]) { qCDebug(modelformat) << "children: " << children; } if (defined["translation"]) { qCDebug(modelformat) << "translation: " << translation; } if (defined["rotation"]) { qCDebug(modelformat) << "rotation: " << rotation; } if (defined["scale"]) { qCDebug(modelformat) << "scale: " << scale; } if (defined["matrix"]) { qCDebug(modelformat) << "matrix: " << matrix; } if (defined["skeletons"]) { qCDebug(modelformat) << "skeletons: " << skeletons; } } void normalizeTransform(); }; // Meshes struct GLTFMeshPrimitivesTarget { int normal; int position; int tangent; QMap defined; void dump() { if (defined["normal"]) { qCDebug(modelformat) << "normal: " << normal; } if (defined["position"]) { qCDebug(modelformat) << "position: " << position; } if (defined["tangent"]) { qCDebug(modelformat) << "tangent: " << tangent; } } }; namespace GLTFMeshPrimitivesRenderingMode { enum Values { POINTS = 0, LINES, LINE_LOOP, LINE_STRIP, TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN }; } struct GLTFMeshPrimitiveAttr { QMap values; QMap defined; void dump() { QList keys = values.keys(); qCDebug(modelformat) << "values: "; foreach(auto k, keys) { qCDebug(modelformat) << k << ": " << values[k]; } } }; struct GLTFMeshPrimitive { GLTFMeshPrimitiveAttr attributes; int indices; int material; int mode{ GLTFMeshPrimitivesRenderingMode::TRIANGLES }; QVector targets; QMap defined; void dump() { if (defined["attributes"]) { qCDebug(modelformat) << "attributes: "; attributes.dump(); } if (defined["indices"]) { qCDebug(modelformat) << "indices: " << indices; } if (defined["material"]) { qCDebug(modelformat) << "material: " << material; } if (defined["mode"]) { qCDebug(modelformat) << "mode: " << mode; } if (defined["targets"]) { qCDebug(modelformat) << "targets: "; foreach(auto t, targets) t.dump(); } } }; struct GLTFMeshExtra { QVector targetNames; QMap defined; void dump() { if (defined["targetNames"]) { qCDebug(modelformat) << "targetNames: " << targetNames; } } }; struct GLTFMesh { QString name; QVector primitives; GLTFMeshExtra extras; QVector weights; QMap defined; void dump() { if (defined["name"]) { qCDebug(modelformat) << "name: " << name; } if (defined["primitives"]) { qCDebug(modelformat) << "primitives: "; foreach(auto prim, primitives) prim.dump(); } if (defined["extras"]) { qCDebug(modelformat) << "extras: "; extras.dump(); } if (defined["weights"]) { qCDebug(modelformat) << "weights: " << weights; } } }; // BufferViews namespace GLTFBufferViewTarget { enum Values { ARRAY_BUFFER = 34962, ELEMENT_ARRAY_BUFFER = 34963 }; } struct GLTFBufferView { int buffer; //required int byteLength; //required int byteOffset { 0 }; int target; QMap defined; void dump() { if (defined["buffer"]) { qCDebug(modelformat) << "buffer: " << buffer; } if (defined["byteLength"]) { qCDebug(modelformat) << "byteLength: " << byteLength; } if (defined["byteOffset"]) { qCDebug(modelformat) << "byteOffset: " << byteOffset; } if (defined["target"]) { qCDebug(modelformat) << "target: " << target; } } }; // Buffers struct GLTFBuffer { int byteLength; //required QString uri; hifi::ByteArray blob; QMap defined; void dump() { if (defined["byteLength"]) { qCDebug(modelformat) << "byteLength: " << byteLength; } if (defined["uri"]) { qCDebug(modelformat) << "uri: " << uri; } if (defined["blob"]) { qCDebug(modelformat) << "blob: " << "DEFINED"; } } }; // Samplers namespace GLTFSamplerFilterType { enum Values { NEAREST = 9728, LINEAR = 9729, NEAREST_MIPMAP_NEAREST = 9984, LINEAR_MIPMAP_NEAREST = 9985, NEAREST_MIPMAP_LINEAR = 9986, LINEAR_MIPMAP_LINEAR = 9987 }; } namespace GLTFSamplerWrapType { enum Values { CLAMP_TO_EDGE = 33071, MIRRORED_REPEAT = 33648, REPEAT = 10497 }; } struct GLTFSampler { int magFilter; int minFilter; int wrapS; int wrapT; QMap defined; void dump() { if (defined["magFilter"]) { qCDebug(modelformat) << "magFilter: " << magFilter; } if (defined["minFilter"]) { qCDebug(modelformat) << "minFilter: " << minFilter; } if (defined["wrapS"]) { qCDebug(modelformat) << "wrapS: " << wrapS; } if (defined["wrapT"]) { qCDebug(modelformat) << "wrapT: " << wrapT; } } }; // Cameras struct GLTFCameraPerspective { double aspectRatio; double yfov; //required double zfar; double znear; //required QMap defined; void dump() { if (defined["zfar"]) { qCDebug(modelformat) << "zfar: " << zfar; } if (defined["znear"]) { qCDebug(modelformat) << "znear: " << znear; } if (defined["aspectRatio"]) { qCDebug(modelformat) << "aspectRatio: " << aspectRatio; } if (defined["yfov"]) { qCDebug(modelformat) << "yfov: " << yfov; } } }; struct GLTFCameraOrthographic { double zfar; //required double znear; //required double xmag; //required double ymag; //required QMap defined; void dump() { if (defined["zfar"]) { qCDebug(modelformat) << "zfar: " << zfar; } if (defined["znear"]) { qCDebug(modelformat) << "znear: " << znear; } if (defined["xmag"]) { qCDebug(modelformat) << "xmag: " << xmag; } if (defined["ymag"]) { qCDebug(modelformat) << "ymag: " << ymag; } } }; namespace GLTFCameraTypes { enum Values { ORTHOGRAPHIC = 0, PERSPECTIVE }; } struct GLTFCamera { QString name; GLTFCameraPerspective perspective; //required (or) GLTFCameraOrthographic orthographic; //required (or) int type; QMap defined; void dump() { if (defined["name"]) { qCDebug(modelformat) << "name: " << name; } if (defined["type"]) { qCDebug(modelformat) << "type: " << type; } if (defined["perspective"]) { perspective.dump(); } if (defined["orthographic"]) { orthographic.dump(); } } }; // Images namespace GLTFImageMimetype { enum Values { JPEG = 0, PNG }; }; struct GLTFImage { QString uri; //required (or) int mimeType; int bufferView; //required (or) QMap defined; void dump() { if (defined["mimeType"]) { qCDebug(modelformat) << "mimeType: " << mimeType; } if (defined["bufferView"]) { qCDebug(modelformat) << "bufferView: " << bufferView; } } }; // Materials struct GLTFpbrMetallicRoughness { QVector baseColorFactor; int baseColorTexture; int metallicRoughnessTexture; double metallicFactor; double roughnessFactor; QMap defined; void dump() { if (defined["baseColorFactor"]) { qCDebug(modelformat) << "baseColorFactor: " << baseColorFactor; } if (defined["baseColorTexture"]) { qCDebug(modelformat) << "baseColorTexture: " << baseColorTexture; } if (defined["metallicRoughnessTexture"]) { qCDebug(modelformat) << "metallicRoughnessTexture: " << metallicRoughnessTexture; } if (defined["metallicFactor"]) { qCDebug(modelformat) << "metallicFactor: " << metallicFactor; } if (defined["roughnessFactor"]) { qCDebug(modelformat) << "roughnessFactor: " << roughnessFactor; } if (defined["baseColorFactor"]) { qCDebug(modelformat) << "baseColorFactor: " << baseColorFactor; } } }; namespace GLTFMaterialAlphaMode { enum Values { OPAQUE = 0, MASK, BLEND }; }; struct GLTFMaterial { QString name; QVector emissiveFactor; int emissiveTexture; int normalTexture; int occlusionTexture; int alphaMode; double alphaCutoff; bool doubleSided; GLTFpbrMetallicRoughness pbrMetallicRoughness; QMap defined; void dump() { if (defined["name"]) { qCDebug(modelformat) << "name: " << name; } if (defined["emissiveTexture"]) { qCDebug(modelformat) << "emissiveTexture: " << emissiveTexture; } if (defined["normalTexture"]) { qCDebug(modelformat) << "normalTexture: " << normalTexture; } if (defined["occlusionTexture"]) { qCDebug(modelformat) << "occlusionTexture: " << occlusionTexture; } if (defined["emissiveFactor"]) { qCDebug(modelformat) << "emissiveFactor: " << emissiveFactor; } if (defined["pbrMetallicRoughness"]) { pbrMetallicRoughness.dump(); } } }; // Accesors namespace GLTFAccessorType { enum Value { SCALAR = 1, VEC2 = 2, VEC3 = 3, VEC4 = 4, MAT2 = 5, MAT3 = 9, MAT4 = 16 }; inline int count(Value value) { if (value == MAT2) { return 4; } return (int)value; } } namespace GLTFVertexAttribute { enum Value { UNKNOWN = -1, POSITION = 0, NORMAL, TANGENT, TEXCOORD_0, TEXCOORD_1, COLOR_0, JOINTS_0, WEIGHTS_0, }; inline Value fromString(const QString& key) { if (key == "POSITION") { return POSITION; } else if (key == "NORMAL") { return NORMAL; } else if (key == "TANGENT") { return TANGENT; } else if (key == "TEXCOORD_0") { return TEXCOORD_0; } else if (key == "TEXCOORD_1") { return TEXCOORD_1; } else if (key == "COLOR_0") { return COLOR_0; } else if (key == "JOINTS_0") { return JOINTS_0; } else if (key == "WEIGHTS_0") { return WEIGHTS_0; } return UNKNOWN; } } namespace GLTFAccessorComponentType { enum Values { BYTE = 5120, UNSIGNED_BYTE = 5121, SHORT = 5122, UNSIGNED_SHORT = 5123, UNSIGNED_INT = 5125, FLOAT = 5126 }; } struct GLTFAccessor { struct GLTFAccessorSparse { struct GLTFAccessorSparseIndices { int bufferView; int byteOffset{ 0 }; int componentType; QMap defined; void dump() { if (defined["bufferView"]) { qCDebug(modelformat) << "bufferView: " << bufferView; } if (defined["byteOffset"]) { qCDebug(modelformat) << "byteOffset: " << byteOffset; } if (defined["componentType"]) { qCDebug(modelformat) << "componentType: " << componentType; } } }; struct GLTFAccessorSparseValues { int bufferView; int byteOffset{ 0 }; QMap defined; void dump() { if (defined["bufferView"]) { qCDebug(modelformat) << "bufferView: " << bufferView; } if (defined["byteOffset"]) { qCDebug(modelformat) << "byteOffset: " << byteOffset; } } }; int count; GLTFAccessorSparseIndices indices; GLTFAccessorSparseValues values; QMap defined; void dump() { } }; int bufferView; int byteOffset { 0 }; int componentType; //required int count; //required int type; //required bool normalized{ false }; QVector max; QVector min; GLTFAccessorSparse sparse; QMap defined; void dump() { if (defined["bufferView"]) { qCDebug(modelformat) << "bufferView: " << bufferView; } if (defined["byteOffset"]) { qCDebug(modelformat) << "byteOffset: " << byteOffset; } if (defined["componentType"]) { qCDebug(modelformat) << "componentType: " << componentType; } if (defined["count"]) { qCDebug(modelformat) << "count: " << count; } if (defined["type"]) { qCDebug(modelformat) << "type: " << type; } if (defined["normalized"]) { qCDebug(modelformat) << "normalized: " << (normalized ? "TRUE" : "FALSE"); } if (defined["max"]) { qCDebug(modelformat) << "max: "; foreach(float m, max) { qCDebug(modelformat) << m; } } if (defined["min"]) { qCDebug(modelformat) << "min: "; foreach(float m, min) { qCDebug(modelformat) << m; } } if (defined["sparse"]) { qCDebug(modelformat) << "sparse: "; sparse.dump(); } } }; // Animation namespace GLTFChannelTargetPath { enum Values { TRANSLATION = 0, ROTATION, SCALE }; } struct GLTFChannelTarget { int node; int path; QMap defined; void dump() { if (defined["node"]) { qCDebug(modelformat) << "node: " << node; } if (defined["path"]) { qCDebug(modelformat) << "path: " << path; } } }; struct GLTFChannel { int sampler; GLTFChannelTarget target; QMap defined; void dump() { if (defined["sampler"]) { qCDebug(modelformat) << "sampler: " << sampler; } if (defined["target"]) { target.dump(); } } }; namespace GLTFAnimationSamplerInterpolation { enum Values{ LINEAR = 0 }; } struct GLTFAnimationSampler { int input; int output; int interpolation; QMap defined; void dump() { if (defined["input"]) { qCDebug(modelformat) << "input: " << input; } if (defined["output"]) { qCDebug(modelformat) << "output: " << output; } if (defined["interpolation"]) { qCDebug(modelformat) << "interpolation: " << interpolation; } } }; struct GLTFAnimation { QVector channels; QVector samplers; QMap defined; void dump() { if (defined["channels"]) { foreach(auto channel, channels) channel.dump(); } if (defined["samplers"]) { foreach(auto sampler, samplers) sampler.dump(); } } }; struct GLTFScene { QString name; QVector nodes; QMap defined; void dump() { if (defined["name"]) { qCDebug(modelformat) << "name: " << name; } if (defined["nodes"]) { qCDebug(modelformat) << "nodes: "; foreach(int node, nodes) qCDebug(modelformat) << node; } } }; struct GLTFSkin { int inverseBindMatrices; QVector joints; int skeleton; QMap defined; void dump() { if (defined["inverseBindMatrices"]) { qCDebug(modelformat) << "inverseBindMatrices: " << inverseBindMatrices; } if (defined["skeleton"]) { qCDebug(modelformat) << "skeleton: " << skeleton; } if (defined["joints"]) { qCDebug(modelformat) << "joints: "; foreach(int joint, joints) qCDebug(modelformat) << joint; } } }; struct GLTFTexture { int sampler; int source; QMap defined; void dump() { if (defined["sampler"]) { qCDebug(modelformat) << "sampler: " << sampler; } if (defined["source"]) { qCDebug(modelformat) << "source: " << sampler; } } }; struct GLTFFile { GLTFAsset asset; int scene = 0; QVector accessors; QVector animations; QVector bufferviews; QVector buffers; QVector cameras; QVector images; QVector materials; QVector meshes; QVector nodes; QVector samplers; QVector scenes; QVector skins; QVector textures; QMap defined; void dump() { if (defined["asset"]) { asset.dump(); } if (defined["scene"]) { qCDebug(modelformat) << "scene: " << scene; } if (defined["accessors"]) { foreach(auto acc, accessors) acc.dump(); } if (defined["animations"]) { foreach(auto ani, animations) ani.dump(); } if (defined["bufferviews"]) { foreach(auto bv, bufferviews) bv.dump(); } if (defined["buffers"]) { foreach(auto b, buffers) b.dump(); } if (defined["cameras"]) { foreach(auto c, cameras) c.dump(); } if (defined["images"]) { foreach(auto i, images) i.dump(); } if (defined["materials"]) { foreach(auto mat, materials) mat.dump(); } if (defined["meshes"]) { foreach(auto mes, meshes) mes.dump(); } if (defined["nodes"]) { foreach(auto nod, nodes) nod.dump(); } if (defined["samplers"]) { foreach(auto sa, samplers) sa.dump(); } if (defined["scenes"]) { foreach(auto sc, scenes) sc.dump(); } if (defined["skins"]) { foreach(auto sk, nodes) sk.dump(); } if (defined["textures"]) { foreach(auto tex, textures) tex.dump(); } } void populateMaterialNames(); void sortNodes(); void normalizeNodeTransforms(); private: void reorderNodes(const std::unordered_map& reorderMap); }; class GLTFSerializer : public QObject, public HFMSerializer { Q_OBJECT public: MediaType getMediaType() const override; std::unique_ptr getFactory() const override; HFMModel::Pointer read(const hifi::ByteArray& data, const hifi::VariantHash& mapping, const hifi::URL& url = hifi::URL()) override; private: GLTFFile _file; hifi::URL _url; hifi::ByteArray _glbBinary; const glm::mat4& getModelTransform(const GLTFNode& node); void getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues); void generateTargetData(int index, float weight, QVector& returnVector); bool buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mapping, const hifi::URL& url); bool parseGLTF(const hifi::ByteArray& data); bool getStringVal(const QJsonObject& object, const QString& fieldname, QString& value, QMap& defined); bool getBoolVal(const QJsonObject& object, const QString& fieldname, bool& value, QMap& defined); bool getIntVal(const QJsonObject& object, const QString& fieldname, int& value, QMap& defined); bool getDoubleVal(const QJsonObject& object, const QString& fieldname, double& value, QMap& defined); bool getObjectVal(const QJsonObject& object, const QString& fieldname, QJsonObject& value, QMap& defined); bool getIntArrayVal(const QJsonObject& object, const QString& fieldname, QVector& values, QMap& defined); bool getDoubleArrayVal(const QJsonObject& object, const QString& fieldname, QVector& values, QMap& defined); bool getObjectArrayVal(const QJsonObject& object, const QString& fieldname, QJsonArray& objects, QMap& defined); hifi::ByteArray setGLBChunks(const hifi::ByteArray& data); int getMaterialAlphaMode(const QString& type); int getAccessorType(const QString& type); int getAnimationSamplerInterpolation(const QString& interpolation); int getCameraType(const QString& type); int getImageMimeType(const QString& mime); int getMeshPrimitiveRenderingMode(const QString& type); bool getIndexFromObject(const QJsonObject& object, const QString& field, int& outidx, QMap& defined); bool setAsset(const QJsonObject& object); GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices createAccessorSparseIndices(const QJsonObject& object); GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseValues createAccessorSparseValues(const QJsonObject& object); GLTFAccessor::GLTFAccessorSparse createAccessorSparse(const QJsonObject& object); bool addAccessor(const QJsonObject& object); bool addAnimation(const QJsonObject& object); bool addBufferView(const QJsonObject& object); bool addBuffer(const QJsonObject& object); bool addCamera(const QJsonObject& object); bool addImage(const QJsonObject& object); bool addMaterial(const QJsonObject& object); bool addMesh(const QJsonObject& object); bool addNode(const QJsonObject& object); bool addSampler(const QJsonObject& object); bool addScene(const QJsonObject& object); bool addSkin(const QJsonObject& object); bool addTexture(const QJsonObject& object); bool readBinary(const QString& url, hifi::ByteArray& outdata); template bool readArray(const hifi::ByteArray& bin, int byteOffset, int count, QVector& outarray, int accessorType); template bool addArrayOfType(const hifi::ByteArray& bin, int byteOffset, int count, QVector& outarray, int accessorType, int componentType); template bool addArrayFromAccessor(GLTFAccessor& accessor, QVector& outarray); template bool addArrayFromAttribute(GLTFVertexAttribute::Value vertexAttribute, GLTFAccessor& accessor, QVector& outarray); void retriangulate(const QVector& in_indices, const QVector& in_vertices, const QVector& in_normals, QVector& out_indices, QVector& out_vertices, QVector& out_normals); std::tuple requestData(hifi::URL& url); hifi::ByteArray requestEmbeddedData(const QString& url); QNetworkReply* request(hifi::URL& url, bool isTest); bool doesResourceExist(const QString& url); void setHFMMaterial(HFMMaterial& fbxmat, const GLTFMaterial& material); HFMTexture getHFMTexture(const GLTFTexture& texture); void glTFDebugDump(); void hfmDebugDump(const HFMModel& hfmModel); }; #endif // hifi_GLTFSerializer_h