diff --git a/cmake/ports/cgltf/portfile.cmake b/cmake/ports/cgltf/portfile.cmake new file mode 100644 index 00000000000..95cc008a209 --- /dev/null +++ b/cmake/ports/cgltf/portfile.cmake @@ -0,0 +1,15 @@ +# header-only library + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO jkuhlmann/cgltf + REF de399881c65c438a635627c749440eeea7e05599 + SHA512 753923116b92642848ff2bda70695ddd0e7be6db43ed3cfc37aff4cba90a29a92e3dbda139a5f2c80cad1d2cdaf81e1383e4ea7a12195f61fe8cfeb105e53ea2 + HEAD_REF master +) + +file(COPY "${SOURCE_PATH}/cgltf.h" DESTINATION "${CURRENT_PACKAGES_DIR}/include") +file(COPY "${SOURCE_PATH}/cgltf_write.h" DESTINATION "${CURRENT_PACKAGES_DIR}/include") + +# Handle copyright +configure_file("${SOURCE_PATH}/LICENSE" "${CURRENT_PACKAGES_DIR}/share/${PORT}/copyright" COPYONLY) \ No newline at end of file diff --git a/cmake/ports/cgltf/vcpkg.json b/cmake/ports/cgltf/vcpkg.json new file mode 100644 index 00000000000..a57db71cb4d --- /dev/null +++ b/cmake/ports/cgltf/vcpkg.json @@ -0,0 +1,7 @@ +{ + "name": "cgltf", + "version": "1.13", + "description": "Single-file glTF 2.0 loader and writer written in C99", + "homepage": "https://github.com/jkuhlmann/cgltf", + "license": "MIT" +} \ No newline at end of file diff --git a/cmake/ports/hifi-deps/CONTROL b/cmake/ports/hifi-deps/CONTROL index ee9f4cf1b3c..c4f55f71689 100644 --- a/cmake/ports/hifi-deps/CONTROL +++ b/cmake/ports/hifi-deps/CONTROL @@ -5,4 +5,4 @@ Source: hifi-deps Version: 0.1.5-github-actions Description: Collected dependencies for High Fidelity applications -Build-Depends: bullet3, draco, etc2comp, glad, glm, node, nvtt, openexr (!android), openssl (windows), opus, polyvox, tbb (!android), vhacd, webrtc (!android|!(linux&arm)), zlib +Build-Depends: bullet3, cgltf, draco, etc2comp, glad, glm, node, nvtt, openexr (!android), openssl (windows), opus, polyvox, tbb (!android), vhacd, webrtc (!android|!(linux&arm)), zlib diff --git a/libraries/model-networking/src/model-networking/ModelLoader.cpp b/libraries/model-networking/src/model-networking/ModelLoader.cpp index 65314633c96..6e71f6bb279 100644 --- a/libraries/model-networking/src/model-networking/ModelLoader.cpp +++ b/libraries/model-networking/src/model-networking/ModelLoader.cpp @@ -20,5 +20,6 @@ hfm::Model::Pointer ModelLoader::load(const hifi::ByteArray& data, const hifi::V if (!serializer) { return hfm::Model::Pointer(); } + qDebug() << "ModelLoader::load: " << url; return serializer->read(data, mapping, url); } diff --git a/libraries/model-serializers/src/GLTFSerializer.cpp b/libraries/model-serializers/src/GLTFSerializer.cpp index 488a59d7cbb..68a2fcecd91 100644 --- a/libraries/model-serializers/src/GLTFSerializer.cpp +++ b/libraries/model-serializers/src/GLTFSerializer.cpp @@ -10,6 +10,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#define CGLTF_IMPLEMENTATION + #include "GLTFSerializer.h" #include @@ -59,749 +61,30 @@ newArray.append(oldArray[index1]); newArray.append(oldArray[index1 + 1]); newArr newArray.append(oldArray[index2]); newArray.append(oldArray[index2 + 1]); newArray.append(oldArray[index2 + 2]); newArray.append(oldArray[index2 + 3]); \ newArray.append(oldArray[index3]); newArray.append(oldArray[index3 + 1]); newArray.append(oldArray[index3 + 2]); newArray.append(oldArray[index3 + 3]); -bool GLTFSerializer::getStringVal(const QJsonObject& object, const QString& fieldname, - QString& value, QMap& defined) { - bool _defined = (object.contains(fieldname) && object[fieldname].isString()); - if (_defined) { - value = object[fieldname].toString(); - } - defined.insert(fieldname, _defined); - return _defined; -} - -bool GLTFSerializer::getBoolVal(const QJsonObject& object, const QString& fieldname, - bool& value, QMap& defined) { - bool _defined = (object.contains(fieldname) && object[fieldname].isBool()); - if (_defined) { - value = object[fieldname].toBool(); - } - defined.insert(fieldname, _defined); - return _defined; -} - -bool GLTFSerializer::getIntVal(const QJsonObject& object, const QString& fieldname, - int& value, QMap& defined) { - bool _defined = (object.contains(fieldname) && !object[fieldname].isNull()); - if (_defined) { - value = object[fieldname].toInt(); - } - defined.insert(fieldname, _defined); - return _defined; -} - -bool GLTFSerializer::getDoubleVal(const QJsonObject& object, const QString& fieldname, - double& value, QMap& defined) { - bool _defined = (object.contains(fieldname) && object[fieldname].isDouble()); - if (_defined) { - value = object[fieldname].toDouble(); - } - defined.insert(fieldname, _defined); - return _defined; -} -bool GLTFSerializer::getObjectVal(const QJsonObject& object, const QString& fieldname, - QJsonObject& value, QMap& defined) { - bool _defined = (object.contains(fieldname) && object[fieldname].isObject()); - if (_defined) { - value = object[fieldname].toObject(); - } - defined.insert(fieldname, _defined); - return _defined; -} - -bool GLTFSerializer::getIntArrayVal(const QJsonObject& object, const QString& fieldname, - QVector& values, QMap& defined) { - bool _defined = (object.contains(fieldname) && object[fieldname].isArray()); - if (_defined) { - QJsonArray arr = object[fieldname].toArray(); - foreach(const QJsonValue & v, arr) { - if (!v.isNull()) { - values.push_back(v.toInt()); - } - } - } - defined.insert(fieldname, _defined); - return _defined; -} - -bool GLTFSerializer::getDoubleArrayVal(const QJsonObject& object, const QString& fieldname, - QVector& values, QMap& defined) { - bool _defined = (object.contains(fieldname) && object[fieldname].isArray()); - if (_defined) { - QJsonArray arr = object[fieldname].toArray(); - foreach(const QJsonValue & v, arr) { - if (v.isDouble()) { - values.push_back(v.toDouble()); - } - } - } - defined.insert(fieldname, _defined); - return _defined; -} - -bool GLTFSerializer::getObjectArrayVal(const QJsonObject& object, const QString& fieldname, - QJsonArray& objects, QMap& defined) { - bool _defined = (object.contains(fieldname) && object[fieldname].isArray()); - if (_defined) { - objects = object[fieldname].toArray(); - } - defined.insert(fieldname, _defined); - return _defined; -} - -hifi::ByteArray GLTFSerializer::setGLBChunks(const hifi::ByteArray& data) { - int byte = 4; - int jsonStart = data.indexOf("JSON", Qt::CaseSensitive); - int binStart = data.indexOf("BIN", Qt::CaseSensitive); - int jsonLength, binLength; - hifi::ByteArray jsonLengthChunk, binLengthChunk; - - jsonLengthChunk = data.mid(jsonStart - byte, byte); - QDataStream tempJsonLen(jsonLengthChunk); - tempJsonLen.setByteOrder(QDataStream::LittleEndian); - tempJsonLen >> jsonLength; - hifi::ByteArray jsonChunk = data.mid(jsonStart + byte, jsonLength); - - if (binStart != -1) { - binLengthChunk = data.mid(binStart - byte, byte); - - QDataStream tempBinLen(binLengthChunk); - tempBinLen.setByteOrder(QDataStream::LittleEndian); - tempBinLen >> binLength; - - _glbBinary = data.mid(binStart + byte, binLength); - } - return jsonChunk; -} - -int GLTFSerializer::getMeshPrimitiveRenderingMode(const QString& type) -{ - if (type == "POINTS") { - return GLTFMeshPrimitivesRenderingMode::POINTS; - } - if (type == "LINES") { - return GLTFMeshPrimitivesRenderingMode::LINES; - } - if (type == "LINE_LOOP") { - return GLTFMeshPrimitivesRenderingMode::LINE_LOOP; - } - if (type == "LINE_STRIP") { - return GLTFMeshPrimitivesRenderingMode::LINE_STRIP; - } - if (type == "TRIANGLES") { - return GLTFMeshPrimitivesRenderingMode::TRIANGLES; - } - if (type == "TRIANGLE_STRIP") { - return GLTFMeshPrimitivesRenderingMode::TRIANGLE_STRIP; - } - if (type == "TRIANGLE_FAN") { - return GLTFMeshPrimitivesRenderingMode::TRIANGLE_FAN; - } - return GLTFMeshPrimitivesRenderingMode::TRIANGLES; -} - -int GLTFSerializer::getAccessorType(const QString& type) -{ - if (type == "SCALAR") { - return GLTFAccessorType::SCALAR; - } - if (type == "VEC2") { - return GLTFAccessorType::VEC2; - } - if (type == "VEC3") { - return GLTFAccessorType::VEC3; - } - if (type == "VEC4") { - return GLTFAccessorType::VEC4; - } - if (type == "MAT2") { - return GLTFAccessorType::MAT2; - } - if (type == "MAT3") { - return GLTFAccessorType::MAT3; - } - if (type == "MAT4") { - return GLTFAccessorType::MAT4; - } - return GLTFAccessorType::SCALAR; -} - -graphics::MaterialKey::OpacityMapMode GLTFSerializer::getMaterialAlphaMode(const QString& type) { - if (type == "OPAQUE") { - return graphics::MaterialKey::OPACITY_MAP_OPAQUE; - } - if (type == "MASK") { - return graphics::MaterialKey::OPACITY_MAP_MASK; - } - if (type == "BLEND") { - return graphics::MaterialKey::OPACITY_MAP_BLEND; - } - return graphics::MaterialKey::OPACITY_MAP_BLEND; -} - -int GLTFSerializer::getCameraType(const QString& type) -{ - if (type == "orthographic") { - return GLTFCameraTypes::ORTHOGRAPHIC; - } - if (type == "perspective") { - return GLTFCameraTypes::PERSPECTIVE; - } - return GLTFCameraTypes::PERSPECTIVE; -} - -int GLTFSerializer::getImageMimeType(const QString& mime) -{ - if (mime == "image/jpeg") { - return GLTFImageMimetype::JPEG; - } - if (mime == "image/png") { - return GLTFImageMimetype::PNG; - } - return GLTFImageMimetype::JPEG; -} - -int GLTFSerializer::getAnimationSamplerInterpolation(const QString& interpolation) -{ - if (interpolation == "LINEAR") { - return GLTFAnimationSamplerInterpolation::LINEAR; - } - return GLTFAnimationSamplerInterpolation::LINEAR; -} - -bool GLTFSerializer::setAsset(const QJsonObject& object) { - QJsonObject jsAsset; - bool isAssetDefined = getObjectVal(object, "asset", jsAsset, _file.defined); - if (isAssetDefined) { - if (!getStringVal(jsAsset, "version", _file.asset.version, - _file.asset.defined) || _file.asset.version != "2.0") { - return false; - } - getStringVal(jsAsset, "generator", _file.asset.generator, _file.asset.defined); - getStringVal(jsAsset, "copyright", _file.asset.copyright, _file.asset.defined); - } - return isAssetDefined; -} - -GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices GLTFSerializer::createAccessorSparseIndices(const QJsonObject& object) { - GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices accessorSparseIndices; - - getIntVal(object, "bufferView", accessorSparseIndices.bufferView, accessorSparseIndices.defined); - getIntVal(object, "byteOffset", accessorSparseIndices.byteOffset, accessorSparseIndices.defined); - getIntVal(object, "componentType", accessorSparseIndices.componentType, accessorSparseIndices.defined); - - return accessorSparseIndices; -} - -GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseValues GLTFSerializer::createAccessorSparseValues(const QJsonObject& object) { - GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseValues accessorSparseValues; - - getIntVal(object, "bufferView", accessorSparseValues.bufferView, accessorSparseValues.defined); - getIntVal(object, "byteOffset", accessorSparseValues.byteOffset, accessorSparseValues.defined); - - return accessorSparseValues; -} - -GLTFAccessor::GLTFAccessorSparse GLTFSerializer::createAccessorSparse(const QJsonObject& object) { - GLTFAccessor::GLTFAccessorSparse accessorSparse; - - getIntVal(object, "count", accessorSparse.count, accessorSparse.defined); - QJsonObject sparseIndicesObject; - if (getObjectVal(object, "indices", sparseIndicesObject, accessorSparse.defined)) { - accessorSparse.indices = createAccessorSparseIndices(sparseIndicesObject); - } - QJsonObject sparseValuesObject; - if (getObjectVal(object, "values", sparseValuesObject, accessorSparse.defined)) { - accessorSparse.values = createAccessorSparseValues(sparseValuesObject); - } - - return accessorSparse; -} - -bool GLTFSerializer::addAccessor(const QJsonObject& object) { - GLTFAccessor accessor; - - getIntVal(object, "bufferView", accessor.bufferView, accessor.defined); - getIntVal(object, "byteOffset", accessor.byteOffset, accessor.defined); - getIntVal(object, "componentType", accessor.componentType, accessor.defined); - getIntVal(object, "count", accessor.count, accessor.defined); - getBoolVal(object, "normalized", accessor.normalized, accessor.defined); - QString type; - if (getStringVal(object, "type", type, accessor.defined)) { - accessor.type = getAccessorType(type); - } - - QJsonObject sparseObject; - if (getObjectVal(object, "sparse", sparseObject, accessor.defined)) { - accessor.sparse = createAccessorSparse(sparseObject); - } - - getDoubleArrayVal(object, "max", accessor.max, accessor.defined); - getDoubleArrayVal(object, "min", accessor.min, accessor.defined); - - _file.accessors.push_back(accessor); - - return true; -} - -bool GLTFSerializer::addAnimation(const QJsonObject& object) { - GLTFAnimation animation; - - QJsonArray channels; - if (getObjectArrayVal(object, "channels", channels, animation.defined)) { - foreach(const QJsonValue & v, channels) { - if (v.isObject()) { - GLTFChannel channel; - getIntVal(v.toObject(), "sampler", channel.sampler, channel.defined); - QJsonObject jsChannel; - if (getObjectVal(v.toObject(), "target", jsChannel, channel.defined)) { - getIntVal(jsChannel, "node", channel.target.node, channel.target.defined); - getIntVal(jsChannel, "path", channel.target.path, channel.target.defined); - } - } - } - } - - QJsonArray samplers; - if (getObjectArrayVal(object, "samplers", samplers, animation.defined)) { - foreach(const QJsonValue & v, samplers) { - if (v.isObject()) { - GLTFAnimationSampler sampler; - getIntVal(v.toObject(), "input", sampler.input, sampler.defined); - getIntVal(v.toObject(), "output", sampler.input, sampler.defined); - QString interpolation; - if (getStringVal(v.toObject(), "interpolation", interpolation, sampler.defined)) { - sampler.interpolation = getAnimationSamplerInterpolation(interpolation); - } - } - } - } - - _file.animations.push_back(animation); - - return true; -} - -bool GLTFSerializer::addBufferView(const QJsonObject& object) { - GLTFBufferView bufferview; - - getIntVal(object, "buffer", bufferview.buffer, bufferview.defined); - getIntVal(object, "byteLength", bufferview.byteLength, bufferview.defined); - getIntVal(object, "byteOffset", bufferview.byteOffset, bufferview.defined); - getIntVal(object, "target", bufferview.target, bufferview.defined); - - _file.bufferviews.push_back(bufferview); - - return true; -} - -bool GLTFSerializer::addBuffer(const QJsonObject& object) { - GLTFBuffer buffer; - - getIntVal(object, "byteLength", buffer.byteLength, buffer.defined); - - if (_url.path().endsWith("glb")) { - if (!_glbBinary.isEmpty()) { - buffer.blob = _glbBinary; - } else { - return false; - } - } - if (getStringVal(object, "uri", buffer.uri, buffer.defined)) { - if (!readBinary(buffer.uri, buffer.blob)) { - return false; - } - } - _file.buffers.push_back(buffer); - - return true; -} - -bool GLTFSerializer::addCamera(const QJsonObject& object) { - GLTFCamera camera; - - QJsonObject jsPerspective; - QJsonObject jsOrthographic; - QString type; - getStringVal(object, "name", camera.name, camera.defined); - if (getObjectVal(object, "perspective", jsPerspective, camera.defined)) { - getDoubleVal(jsPerspective, "aspectRatio", camera.perspective.aspectRatio, camera.perspective.defined); - getDoubleVal(jsPerspective, "yfov", camera.perspective.yfov, camera.perspective.defined); - getDoubleVal(jsPerspective, "zfar", camera.perspective.zfar, camera.perspective.defined); - getDoubleVal(jsPerspective, "znear", camera.perspective.znear, camera.perspective.defined); - camera.type = GLTFCameraTypes::PERSPECTIVE; - } else if (getObjectVal(object, "orthographic", jsOrthographic, camera.defined)) { - getDoubleVal(jsOrthographic, "zfar", camera.orthographic.zfar, camera.orthographic.defined); - getDoubleVal(jsOrthographic, "znear", camera.orthographic.znear, camera.orthographic.defined); - getDoubleVal(jsOrthographic, "xmag", camera.orthographic.xmag, camera.orthographic.defined); - getDoubleVal(jsOrthographic, "ymag", camera.orthographic.ymag, camera.orthographic.defined); - camera.type = GLTFCameraTypes::ORTHOGRAPHIC; - } else if (getStringVal(object, "type", type, camera.defined)) { - camera.type = getCameraType(type); - } - - _file.cameras.push_back(camera); - - return true; -} - -bool GLTFSerializer::addImage(const QJsonObject& object) { - GLTFImage image; - - QString mime; - getStringVal(object, "uri", image.uri, image.defined); - if (image.uri.contains("data:image/png;base64,")) { - image.mimeType = getImageMimeType("image/png"); - } else if (image.uri.contains("data:image/jpeg;base64,")) { - image.mimeType = getImageMimeType("image/jpeg"); - } - if (getStringVal(object, "mimeType", mime, image.defined)) { - image.mimeType = getImageMimeType(mime); - } - getIntVal(object, "bufferView", image.bufferView, image.defined); - - _file.images.push_back(image); - - return true; -} - -bool GLTFSerializer::getIndexFromObject(const QJsonObject& object, const QString& field, - int& outidx, QMap& defined) { - QJsonObject subobject; - if (getObjectVal(object, field, subobject, defined)) { - QMap tmpdefined = QMap(); - return getIntVal(subobject, "index", outidx, tmpdefined); - } - return false; -} - -bool GLTFSerializer::addMaterial(const QJsonObject& object) { - GLTFMaterial material; - - getStringVal(object, "name", material.name, material.defined); - getDoubleArrayVal(object, "emissiveFactor", material.emissiveFactor, material.defined); - getIndexFromObject(object, "emissiveTexture", material.emissiveTexture, material.defined); - getIndexFromObject(object, "normalTexture", material.normalTexture, material.defined); - getIndexFromObject(object, "occlusionTexture", material.occlusionTexture, material.defined); - getBoolVal(object, "doubleSided", material.doubleSided, material.defined); - QString alphaMode; - if (getStringVal(object, "alphaMode", alphaMode, material.defined)) { - material.alphaMode = getMaterialAlphaMode(alphaMode); - } - getDoubleVal(object, "alphaCutoff", material.alphaCutoff, material.defined); - QJsonObject jsMetallicRoughness; - if (getObjectVal(object, "pbrMetallicRoughness", jsMetallicRoughness, material.defined)) { - getDoubleArrayVal(jsMetallicRoughness, "baseColorFactor", - material.pbrMetallicRoughness.baseColorFactor, - material.pbrMetallicRoughness.defined); - getIndexFromObject(jsMetallicRoughness, "baseColorTexture", - material.pbrMetallicRoughness.baseColorTexture, - material.pbrMetallicRoughness.defined); - // Undefined metallicFactor used with pbrMetallicRoughness means metallicFactor == 1.0 - if (!getDoubleVal(jsMetallicRoughness, "metallicFactor", - material.pbrMetallicRoughness.metallicFactor, - material.pbrMetallicRoughness.defined)) { - material.pbrMetallicRoughness.metallicFactor = 1.0; - material.pbrMetallicRoughness.defined["metallicFactor"] = true; - } - getDoubleVal(jsMetallicRoughness, "roughnessFactor", - material.pbrMetallicRoughness.roughnessFactor, - material.pbrMetallicRoughness.defined); - getIndexFromObject(jsMetallicRoughness, "metallicRoughnessTexture", - material.pbrMetallicRoughness.metallicRoughnessTexture, - material.pbrMetallicRoughness.defined); - } - _file.materials.push_back(material); - return true; -} - -bool GLTFSerializer::addMesh(const QJsonObject& object) { - GLTFMesh mesh; - - getStringVal(object, "name", mesh.name, mesh.defined); - getDoubleArrayVal(object, "weights", mesh.weights, mesh.defined); - QJsonArray jsPrimitives; - object.keys(); - if (getObjectArrayVal(object, "primitives", jsPrimitives, mesh.defined)) { - foreach(const QJsonValue & prim, jsPrimitives) { - if (prim.isObject()) { - GLTFMeshPrimitive primitive; - QJsonObject jsPrimitive = prim.toObject(); - getIntVal(jsPrimitive, "mode", primitive.mode, primitive.defined); - getIntVal(jsPrimitive, "indices", primitive.indices, primitive.defined); - getIntVal(jsPrimitive, "material", primitive.material, primitive.defined); - - QJsonObject jsAttributes; - if (getObjectVal(jsPrimitive, "attributes", jsAttributes, primitive.defined)) { - QStringList attrKeys = jsAttributes.keys(); - foreach(const QString & attrKey, attrKeys) { - int attrVal; - getIntVal(jsAttributes, attrKey, attrVal, primitive.attributes.defined); - primitive.attributes.values.insert(attrKey, attrVal); - } - } - - QJsonArray jsTargets; - if (getObjectArrayVal(jsPrimitive, "targets", jsTargets, primitive.defined)) { - foreach(const QJsonValue & tar, jsTargets) { - if (tar.isObject()) { - QJsonObject jsTarget = tar.toObject(); - QStringList tarKeys = jsTarget.keys(); - GLTFMeshPrimitiveAttr target; - foreach(const QString & tarKey, tarKeys) { - int tarVal; - getIntVal(jsTarget, tarKey, tarVal, target.defined); - target.values.insert(tarKey, tarVal); - } - primitive.targets.push_back(target); - } - } - } - mesh.primitives.push_back(primitive); - } - } - } - - QJsonObject jsExtras; - GLTFMeshExtra extras; - if (getObjectVal(object, "extras", jsExtras, mesh.defined)) { - QJsonArray jsTargetNames; - if (getObjectArrayVal(jsExtras, "targetNames", jsTargetNames, extras.defined)) { - foreach (const QJsonValue& tarName, jsTargetNames) { - extras.targetNames.push_back(tarName.toString()); - } - } - mesh.extras = extras; - } - - _file.meshes.push_back(mesh); - - return true; -} - -bool GLTFSerializer::addNode(const QJsonObject& object) { - GLTFNode node; - - getStringVal(object, "name", node.name, node.defined); - getIntVal(object, "camera", node.camera, node.defined); - getIntVal(object, "mesh", node.mesh, node.defined); - getIntArrayVal(object, "children", node.children, node.defined); - getDoubleArrayVal(object, "translation", node.translation, node.defined); - getDoubleArrayVal(object, "rotation", node.rotation, node.defined); - getDoubleArrayVal(object, "scale", node.scale, node.defined); - getDoubleArrayVal(object, "matrix", node.matrix, node.defined); - getIntVal(object, "skin", node.skin, node.defined); - getStringVal(object, "jointName", node.jointName, node.defined); - getIntArrayVal(object, "skeletons", node.skeletons, node.defined); - - _file.nodes.push_back(node); - - return true; -} - -bool GLTFSerializer::addSampler(const QJsonObject& object) { - GLTFSampler sampler; - - getIntVal(object, "magFilter", sampler.magFilter, sampler.defined); - getIntVal(object, "minFilter", sampler.minFilter, sampler.defined); - getIntVal(object, "wrapS", sampler.wrapS, sampler.defined); - getIntVal(object, "wrapT", sampler.wrapT, sampler.defined); - - _file.samplers.push_back(sampler); - - return true; - -} - -bool GLTFSerializer::addScene(const QJsonObject& object) { - GLTFScene scene; - - getStringVal(object, "name", scene.name, scene.defined); - getIntArrayVal(object, "nodes", scene.nodes, scene.defined); - - _file.scenes.push_back(scene); - return true; -} - -bool GLTFSerializer::addSkin(const QJsonObject& object) { - GLTFSkin skin; - - getIntVal(object, "inverseBindMatrices", skin.inverseBindMatrices, skin.defined); - getIntVal(object, "skeleton", skin.skeleton, skin.defined); - getIntArrayVal(object, "joints", skin.joints, skin.defined); - - _file.skins.push_back(skin); - - return true; -} - -bool GLTFSerializer::addTexture(const QJsonObject& object) { - GLTFTexture texture; - getIntVal(object, "sampler", texture.sampler, texture.defined); - getIntVal(object, "source", texture.source, texture.defined); - - _file.textures.push_back(texture); - - return true; -} - -bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { - PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xffff0000, nullptr); - - hifi::ByteArray jsonChunk = data; - - if (_url.path().endsWith("glb") && data.indexOf("glTF") == 0 && data.contains("JSON")) { - jsonChunk = setGLBChunks(data); - } - - QJsonDocument d = QJsonDocument::fromJson(jsonChunk); - QJsonObject jsFile = d.object(); - - bool success = setAsset(jsFile); - if (success) { - QJsonArray accessors; - if (getObjectArrayVal(jsFile, "accessors", accessors, _file.defined)) { - foreach(const QJsonValue & accVal, accessors) { - if (accVal.isObject()) { - success = success && addAccessor(accVal.toObject()); - } - } - } - - QJsonArray animations; - if (getObjectArrayVal(jsFile, "animations", animations, _file.defined)) { - foreach(const QJsonValue & animVal, accessors) { - if (animVal.isObject()) { - success = success && addAnimation(animVal.toObject()); - } - } - } - - QJsonArray bufferViews; - if (getObjectArrayVal(jsFile, "bufferViews", bufferViews, _file.defined)) { - foreach(const QJsonValue & bufviewVal, bufferViews) { - if (bufviewVal.isObject()) { - success = success && addBufferView(bufviewVal.toObject()); - } - } - } - - QJsonArray buffers; - if (getObjectArrayVal(jsFile, "buffers", buffers, _file.defined)) { - foreach(const QJsonValue & bufVal, buffers) { - if (bufVal.isObject()) { - success = success && addBuffer(bufVal.toObject()); - } - } - } - - QJsonArray cameras; - if (getObjectArrayVal(jsFile, "cameras", cameras, _file.defined)) { - foreach(const QJsonValue & camVal, cameras) { - if (camVal.isObject()) { - success = success && addCamera(camVal.toObject()); - } - } - } - - QJsonArray images; - if (getObjectArrayVal(jsFile, "images", images, _file.defined)) { - foreach(const QJsonValue & imgVal, images) { - if (imgVal.isObject()) { - success = success && addImage(imgVal.toObject()); - } - } - } - - QJsonArray materials; - if (getObjectArrayVal(jsFile, "materials", materials, _file.defined)) { - foreach(const QJsonValue & matVal, materials) { - if (matVal.isObject()) { - success = success && addMaterial(matVal.toObject()); - } - } - } - - QJsonArray meshes; - if (getObjectArrayVal(jsFile, "meshes", meshes, _file.defined)) { - foreach(const QJsonValue & meshVal, meshes) { - if (meshVal.isObject()) { - success = success && addMesh(meshVal.toObject()); - } - } - } - - QJsonArray nodes; - if (getObjectArrayVal(jsFile, "nodes", nodes, _file.defined)) { - foreach(const QJsonValue & nodeVal, nodes) { - if (nodeVal.isObject()) { - success = success && addNode(nodeVal.toObject()); - } - } - } - - QJsonArray samplers; - if (getObjectArrayVal(jsFile, "samplers", samplers, _file.defined)) { - foreach(const QJsonValue & samVal, samplers) { - if (samVal.isObject()) { - success = success && addSampler(samVal.toObject()); - } - } - } - - QJsonArray scenes; - if (getObjectArrayVal(jsFile, "scenes", scenes, _file.defined)) { - foreach(const QJsonValue & sceneVal, scenes) { - if (sceneVal.isObject()) { - success = success && addScene(sceneVal.toObject()); - } - } - } - - QJsonArray skins; - if (getObjectArrayVal(jsFile, "skins", skins, _file.defined)) { - foreach(const QJsonValue & skinVal, skins) { - if (skinVal.isObject()) { - success = success && addSkin(skinVal.toObject()); - } - } - } - - QJsonArray textures; - if (getObjectArrayVal(jsFile, "textures", textures, _file.defined)) { - foreach(const QJsonValue & texVal, textures) { - if (texVal.isObject()) { - success = success && addTexture(texVal.toObject()); - } - } - } - } - return success; -} - -glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) { +glm::mat4 GLTFSerializer::getModelTransform(const cgltf_node& node) { glm::mat4 tmat = glm::mat4(1.0); - if (node.defined["matrix"] && node.matrix.size() == 16) { + if (node.has_matrix) { tmat = glm::mat4(node.matrix[0], node.matrix[1], node.matrix[2], node.matrix[3], node.matrix[4], node.matrix[5], node.matrix[6], node.matrix[7], node.matrix[8], node.matrix[9], node.matrix[10], node.matrix[11], node.matrix[12], node.matrix[13], node.matrix[14], node.matrix[15]); } else { - if (node.defined["scale"] && node.scale.size() == 3) { + if (node.has_scale) { glm::vec3 scale = glm::vec3(node.scale[0], node.scale[1], node.scale[2]); glm::mat4 s = glm::mat4(1.0); s = glm::scale(s, scale); tmat = s * tmat; } - if (node.defined["rotation"] && node.rotation.size() == 4) { + if (node.has_rotation) { //quat(x,y,z,w) to quat(w,x,y,z) glm::quat rotquat = glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]); tmat = glm::mat4_cast(rotquat) * tmat; } - if (node.defined["translation"] && node.translation.size() == 3) { + if (node.has_translation) { glm::vec3 trans = glm::vec3(node.translation[0], node.translation[1], node.translation[2]); glm::mat4 t = glm::mat4(1.0); t = glm::translate(t, trans); @@ -811,65 +94,125 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) { return tmat; } -void GLTFSerializer::getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues) { - for (auto &skin : _file.skins) { - GLTFAccessor& indicesAccessor = _file.accessors[skin.inverseBindMatrices]; +bool GLTFSerializer::getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues) { + for (size_t i = 0; i < _data->skins_count; i++) { + auto &skin = _data->skins[i]; + + if (skin.inverse_bind_matrices == NULL) { + return false; + } + + cgltf_accessor &matricesAccessor = *skin.inverse_bind_matrices; QVector matrices; - addArrayFromAccessor(indicesAccessor, matrices); + if (matricesAccessor.type != cgltf_type_mat4) { + return false; + } + matrices.resize(matricesAccessor.count * 16); + size_t numFloats = cgltf_accessor_unpack_floats(&matricesAccessor, matrices.data(), matricesAccessor.count * 16); + Q_ASSERT(numFloats == matricesAccessor.count * 16); inverseBindMatrixValues.push_back(std::vector(matrices.begin(), matrices.end())); } + return true; } -void GLTFSerializer::generateTargetData(int index, float weight, QVector& returnVector) { - GLTFAccessor& accessor = _file.accessors[index]; +bool GLTFSerializer::generateTargetData(cgltf_accessor *accessor, float weight, QVector& returnVector) { QVector storedValues; - addArrayFromAccessor(accessor, storedValues); + if(accessor == nullptr) { + return false; + } + if (accessor->type != cgltf_type_vec3) { + return false; + } + storedValues.resize(accessor->count * 3); + size_t numFloats = cgltf_accessor_unpack_floats(accessor, storedValues.data(), accessor->count * 3); + if (numFloats == accessor->count * 3) { + return false; + } + for (int n = 0; n + 2 < storedValues.size(); n = n + 3) { returnVector.push_back(glm::vec3(weight * storedValues[n], weight * storedValues[n + 1], weight * storedValues[n + 2])); } + return true; +} + +bool findNodeInPointerArray(const cgltf_node *nodePointer, cgltf_node **nodes, size_t arraySize, size_t &index) { + for (size_t i = 0; i < arraySize; i++) { + if (nodes[i] == nodePointer) { + index = i; + return true; + } + } + return false; +} + +template bool findPointerInArray(const T *pointer, const T *array, size_t arraySize, size_t &index) { + for (size_t i = 0; i < arraySize; i++) { + if (&array[i] == pointer) { + index = i; + return true; + } + } + return false; +} + +bool findAttribute(const QString &name, const cgltf_attribute *attributes, size_t numAttributes, size_t &index) { + std::string nameString = name.toStdString(); + for (size_t i = 0; i < numAttributes; i++) { + if (strcmp(nameString.c_str(), attributes->name) != 0) { + index = i; + return true; + } + } + return false; } bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mapping, const hifi::URL& url) { hfmModel.originalURL = url.toString(); - int numNodes = _file.nodes.size(); + size_t numNodes = _data->nodes_count; //Build dependencies QVector parents; QVector sortedNodes; parents.fill(-1, numNodes); sortedNodes.reserve(numNodes); - int nodecount = 0; - foreach(auto &node, _file.nodes) { - foreach(int child, node.children) { - parents[child] = nodecount; + for(size_t index = 0; index < numNodes; index++) { + auto &node = _data->nodes[index]; + for(size_t childIndexInParent = 0; childIndexInParent < node.children_count; childIndexInParent++) { + cgltf_node *child = node.children[childIndexInParent]; + size_t childIndex = 0; + if (!findPointerInArray(child, _data->nodes, _data->nodes_count, childIndex)) { + qDebug(modelformat) << "findPointerInArray failed for model: " << _url; + hfmModel.loadErrorCount++; + return false; + } + parents[childIndex] = index; } - sortedNodes.push_back(nodecount); - ++nodecount; + sortedNodes.push_back(index); } - // Build transforms - nodecount = 0; - foreach(auto &node, _file.nodes) { + typedef QVector NodeTransforms; + QVector transforms; + transforms.resize(numNodes); + for (size_t index = 0; index < numNodes; index++) { // collect node transform - _file.nodes[nodecount].transforms.push_back(getModelTransform(node)); - int parentIndex = parents[nodecount]; + auto &node = _data->nodes[index]; + transforms[index].push_back(getModelTransform(node)); + int parentIndex = parents[index]; while (parentIndex != -1) { - const auto& parentNode = _file.nodes[parentIndex]; + const auto& parentNode = _data->nodes[parentIndex]; // collect transforms for a node's parents, grandparents, etc. - _file.nodes[nodecount].transforms.push_back(getModelTransform(parentNode)); + transforms[index].push_back(getModelTransform(parentNode)); parentIndex = parents[parentIndex]; } - ++nodecount; } - // since parent indices must exist in the sorted list before any of their children, sortedNodes might not be initialized in the correct order // therefore we need to re-initialize the order in which nodes will be parsed QVector hasBeenSorted; hasBeenSorted.fill(false, numNodes); - int i = 0; // initial index + size_t i = 0; // initial index while (i < numNodes) { int currentNode = sortedNodes[i]; int parentIndex = parents[currentNode]; @@ -877,7 +220,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& hasBeenSorted[currentNode] = true; ++i; } else { - int j = i + 1; // index of node to be sorted + size_t j = i + 1; // index of node to be sorted while (j < numNodes) { int nextNode = sortedNodes[j]; parentIndex = parents[nextNode]; @@ -898,7 +241,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& // Build map from original to new indices QVector originalToNewNodeIndexMap; originalToNewNodeIndexMap.fill(-1, numNodes); - for (int i = 0; i < numNodes; ++i) { + for (size_t i = 0; i < numNodes; ++i) { originalToNewNodeIndexMap[sortedNodes[i]] = i; } @@ -911,13 +254,13 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& globalTransforms.resize(numNodes); for (int nodeIndex : sortedNodes) { - auto& node = _file.nodes[nodeIndex]; + auto& node = _data->nodes[nodeIndex]; joint.parentIndex = parents[nodeIndex]; if (joint.parentIndex != -1) { joint.parentIndex = originalToNewNodeIndexMap[joint.parentIndex]; } - joint.transform = node.transforms.first(); + joint.transform = transforms[nodeIndex].first(); joint.translation = extractTranslation(joint.transform); joint.rotation = glmExtractRotation(joint.transform); glm::vec3 scale = extractScale(joint.transform); @@ -951,24 +294,34 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& jointInverseBindTransforms.resize(numNodes); globalBindTransforms.resize(numNodes); - hfmModel.hasSkeletonJoints = !_file.skins.isEmpty(); + hfmModel.hasSkeletonJoints = _data->skins_count > 0; if (hfmModel.hasSkeletonJoints) { std::vector> inverseBindValues; - getSkinInverseBindMatrices(inverseBindValues); + if (!getSkinInverseBindMatrices(inverseBindValues)) { + qDebug(modelformat) << "GLTFSerializer::getSkinInverseBindMatrices: wrong matrices accessor type for model: " << _url; + hfmModel.loadErrorCount++; + return false; + } - for (int jointIndex = 0; jointIndex < numNodes; ++jointIndex) { + for (size_t jointIndex = 0; jointIndex < numNodes; ++jointIndex) { int nodeIndex = sortedNodes[jointIndex]; auto joint = hfmModel.joints[jointIndex]; - for (int s = 0; s < _file.skins.size(); ++s) { - const auto& skin = _file.skins[s]; - int matrixIndex = skin.joints.indexOf(nodeIndex); - joint.isSkeletonJoint = skin.joints.contains(nodeIndex); + for (size_t s = 0; s < _data->skins_count; ++s) { + const auto& skin = _data->skins[s]; + size_t jointNodeIndex = 0; + joint.isSkeletonJoint = findNodeInPointerArray(&_data->nodes[nodeIndex], skin.joints, skin.joints_count, jointNodeIndex); // build inverse bind matrices if (joint.isSkeletonJoint) { + size_t matrixIndex = jointNodeIndex; std::vector& value = inverseBindValues[s]; - int matrixCount = 16 * matrixIndex; + size_t matrixCount = 16 * matrixIndex; + if (matrixCount + 15 >= value.size()) { + qDebug(modelformat) << "GLTFSerializer::buildGeometry: not enough entries in jointInverseBindTransforms: " << _url; + hfmModel.loadErrorCount++; + return false; + } jointInverseBindTransforms[jointIndex] = glm::mat4(value[matrixCount], value[matrixCount + 1], value[matrixCount + 2], value[matrixCount + 3], value[matrixCount + 4], value[matrixCount + 5], value[matrixCount + 6], value[matrixCount + 7], @@ -992,35 +345,35 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& // Build materials QVector materialIDs; QString unknown = "Default"; - int ukcount = 0; - foreach(auto material, _file.materials) { - if (!material.defined["name"]) { - QString name = unknown + QString::number(++ukcount); - material.name = name; - material.defined.insert("name", true); + for (size_t i = 0; i < _data->materials_count; i++) { + auto &material = _data->materials[i]; + QString mid; + if (material.name != nullptr) { + mid = QString(material.name); + }else{ + mid = QString::number(i); } - QString mid = material.name; materialIDs.push_back(mid); } - for (int i = 0; i < materialIDs.size(); ++i) { + for (size_t i = 0; i < (size_t)materialIDs.size(); ++i) { QString& matid = materialIDs[i]; hfmModel.materials[matid] = HFMMaterial(); HFMMaterial& hfmMaterial = hfmModel.materials[matid]; hfmMaterial._material = std::make_shared(); hfmMaterial.name = hfmMaterial.materialID = matid; - setHFMMaterial(hfmMaterial, _file.materials[i]); + setHFMMaterial(hfmMaterial, _data->materials[i]); } // Build meshes - nodecount = 0; + size_t nodeCount = 0; hfmModel.meshExtents.reset(); for (int nodeIndex : sortedNodes) { - auto& node = _file.nodes[nodeIndex]; + auto& node = _data->nodes[nodeIndex]; - if (node.defined["mesh"]) { + if (node.mesh != nullptr) { hfmModel.meshes.append(HFMMesh()); HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1]; @@ -1028,12 +381,12 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& if (!hfmModel.hasSkeletonJoints) { HFMCluster cluster; - cluster.jointIndex = nodecount; + cluster.jointIndex = nodeCount; cluster.inverseBindMatrix = glm::mat4(); cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix); mesh.clusters.append(cluster); } else { // skinned model - for (int j = 0; j < numNodes; ++j) { + for (size_t j = 0; j < numNodes; ++j) { HFMCluster cluster; cluster.jointIndex = j; cluster.inverseBindMatrix = jointInverseBindTransforms[j]; @@ -1048,27 +401,27 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mesh.clusters.append(root); QList meshAttributes; - foreach(auto &primitive, _file.meshes[node.mesh].primitives) { - QList keys = primitive.attributes.values.keys(); - foreach (auto &key, keys) { + for (size_t primitiveIndex = 0; primitiveIndex < node.mesh->primitives_count; primitiveIndex++) { + auto &primitive = node.mesh->primitives[primitiveIndex]; + for (size_t attributeIndex = 0; attributeIndex < primitive.attributes_count; attributeIndex++) { + auto &attribute = primitive.attributes[attributeIndex]; + QString key(attribute.name); if (!meshAttributes.contains(key)) { meshAttributes.push_back(key); } } } - foreach(auto &primitive, _file.meshes[node.mesh].primitives) { + for (size_t primitiveIndex = 0; primitiveIndex < node.mesh->primitives_count; primitiveIndex++) { + auto &primitive = node.mesh->primitives[primitiveIndex]; HFMMeshPart part = HFMMeshPart(); - int indicesAccessorIdx = primitive.indices; - - if (indicesAccessorIdx > _file.accessors.size()) { - qWarning(modelformat) << "Indices accessor index is out of bounds for model " << _url; + if (primitive.indices == nullptr) { + qDebug() << "No indices accessor for mesh: " << _url; hfmModel.loadErrorCount++; - continue; + return false; } - - GLTFAccessor& indicesAccessor = _file.accessors[indicesAccessorIdx]; + auto &indicesAccessor = primitive.indices; // Buffers QVector indices; @@ -1089,9 +442,10 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& QVector weights; int weightStride = 4; - bool success = addArrayFromAccessor(indicesAccessor, indices); + indices.resize(indicesAccessor->count); + size_t readIndicesCount = cgltf_accessor_unpack_indices(indicesAccessor, indices.data(), sizeof(unsigned int), indicesAccessor->count); - if (!success) { + if (readIndicesCount != indicesAccessor->count) { qWarning(modelformat) << "There was a problem reading glTF INDICES data for model " << _url; hfmModel.loadErrorCount++; continue; @@ -1100,51 +454,56 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& // Increment the triangle indices by the current mesh vertex count so each mesh part can all reference the same buffers within the mesh int prevMeshVerticesCount = mesh.vertices.count(); - QList keys = primitive.attributes.values.keys(); QVector clusterJoints; QVector clusterWeights; - foreach(auto &key, keys) { - int accessorIdx = primitive.attributes.values[key]; - - if (accessorIdx > _file.accessors.size()) { - qWarning(modelformat) << "Accessor index is out of bounds for model " << _url; + for (size_t attributeIndex = 0; attributeIndex < primitive.attributes_count; attributeIndex++) { + if (primitive.attributes[attributeIndex].name == nullptr) { + qDebug() << "Inalid accessor name for mesh: " << _url; hfmModel.loadErrorCount++; - continue; + return false; } + QString key(primitive.attributes[attributeIndex].name); - GLTFAccessor& accessor = _file.accessors[accessorIdx]; + if (primitive.attributes[attributeIndex].data == nullptr) { + qDebug() << "Inalid accessor for mesh: " << _url; + hfmModel.loadErrorCount++; + return false; + } + auto accessor = primitive.attributes[attributeIndex].data; if (key == "POSITION") { - if (accessor.type != GLTFAccessorType::VEC3) { + if (accessor->type != cgltf_type_vec3) { qWarning(modelformat) << "Invalid accessor type on glTF POSITION data for model " << _url; hfmModel.loadErrorCount++; continue; } - success = addArrayFromAccessor(accessor, vertices); - if (!success) { + vertices.resize(accessor->count * 3); + size_t floatCount = cgltf_accessor_unpack_floats(accessor, vertices.data(), accessor->count * 3); + if (floatCount != accessor->count * 3) { qWarning(modelformat) << "There was a problem reading glTF POSITION data for model " << _url; hfmModel.loadErrorCount++; continue; } } else if (key == "NORMAL") { - if (accessor.type != GLTFAccessorType::VEC3) { + if (accessor->type != cgltf_type_vec3) { qWarning(modelformat) << "Invalid accessor type on glTF NORMAL data for model " << _url; hfmModel.loadErrorCount++; continue; } - success = addArrayFromAccessor(accessor, normals); - if (!success) { + normals.resize(accessor->count * 3); + size_t floatCount = cgltf_accessor_unpack_floats(accessor, normals.data(), accessor->count * 3); + if (floatCount != accessor->count * 3) { qWarning(modelformat) << "There was a problem reading glTF NORMAL data for model " << _url; hfmModel.loadErrorCount++; continue; } } else if (key == "TANGENT") { - if (accessor.type == GLTFAccessorType::VEC4) { + if (accessor->type == cgltf_type_vec4) { tangentStride = 4; - } else if (accessor.type == GLTFAccessorType::VEC3) { + } else if (accessor->type == cgltf_type_vec3) { tangentStride = 3; } else { qWarning(modelformat) << "Invalid accessor type on glTF TANGENT data for model " << _url; @@ -1152,43 +511,46 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& continue; } - success = addArrayFromAccessor(accessor, tangents); - if (!success) { + tangents.resize(accessor->count * tangentStride); + size_t floatCount = cgltf_accessor_unpack_floats(accessor, tangents.data(), accessor->count * tangentStride); + if (floatCount != accessor->count * tangentStride) { qWarning(modelformat) << "There was a problem reading glTF TANGENT data for model " << _url; hfmModel.loadErrorCount++; tangentStride = 0; continue; } } else if (key == "TEXCOORD_0") { - success = addArrayFromAccessor(accessor, texcoords); - if (!success) { - qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_0 data for model " << _url; + if (accessor->type != cgltf_type_vec2) { + qWarning(modelformat) << "Invalid accessor type on glTF TEXCOORD_0 data for model " << _url; hfmModel.loadErrorCount++; continue; } - if (accessor.type != GLTFAccessorType::VEC2) { - qWarning(modelformat) << "Invalid accessor type on glTF TEXCOORD_0 data for model " << _url; + texcoords.resize(accessor->count * 2); + size_t floatCount = cgltf_accessor_unpack_floats(accessor, texcoords.data(), accessor->count * 2); + if (floatCount != accessor->count * 2) { + qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_0 data for model " << _url; hfmModel.loadErrorCount++; continue; } } else if (key == "TEXCOORD_1") { - success = addArrayFromAccessor(accessor, texcoords2); - if (!success) { - qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_1 data for model " << _url; + if (accessor->type != cgltf_type_vec2) { + qWarning(modelformat) << "Invalid accessor type on glTF TEXCOORD_1 data for model " << _url; hfmModel.loadErrorCount++; continue; } - if (accessor.type != GLTFAccessorType::VEC2) { - qWarning(modelformat) << "Invalid accessor type on glTF TEXCOORD_1 data for model " << _url; + texcoords2.resize(accessor->count * 2); + size_t floatCount = cgltf_accessor_unpack_floats(accessor, texcoords2.data(), accessor->count * 2); + if (floatCount != accessor->count * 2) { + qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_1 data for model " << _url; hfmModel.loadErrorCount++; continue; } } else if (key == "COLOR_0") { - if (accessor.type == GLTFAccessorType::VEC4) { + if (accessor->type == cgltf_type_vec4) { colorStride = 4; - } else if (accessor.type == GLTFAccessorType::VEC3) { + } else if (accessor->type == cgltf_type_vec3) { colorStride = 3; } else { qWarning(modelformat) << "Invalid accessor type on glTF COLOR_0 data for model " << _url; @@ -1196,20 +558,21 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& continue; } - success = addArrayFromAccessor(accessor, colors); - if (!success) { + colors.resize(accessor->count * colorStride); + size_t floatCount = cgltf_accessor_unpack_floats(accessor, colors.data(), accessor->count * colorStride); + if (floatCount != accessor->count * colorStride) { qWarning(modelformat) << "There was a problem reading glTF COLOR_0 data for model " << _url; hfmModel.loadErrorCount++; continue; } } else if (key == "JOINTS_0") { - if (accessor.type == GLTFAccessorType::VEC4) { + if (accessor->type == cgltf_type_vec4) { jointStride = 4; - } else if (accessor.type == GLTFAccessorType::VEC3) { + } else if (accessor->type == cgltf_type_vec3) { jointStride = 3; - } else if (accessor.type == GLTFAccessorType::VEC2) { + } else if (accessor->type == cgltf_type_vec2) { jointStride = 2; - } else if (accessor.type == GLTFAccessorType::SCALAR) { + } else if (accessor->type == cgltf_type_scalar) { jointStride = 1; } else { qWarning(modelformat) << "Invalid accessor type on glTF JOINTS_0 data for model " << _url; @@ -1217,20 +580,23 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& continue; } - success = addArrayFromAccessor(accessor, joints); - if (!success) { - qWarning(modelformat) << "There was a problem reading glTF JOINTS_0 data for model " << _url; - hfmModel.loadErrorCount++; - continue; + joints.resize(accessor->count * jointStride); + cgltf_uint jointIndices[4]; + for (size_t i = 0; i < accessor->count; i++) { + cgltf_accessor_read_uint(accessor, i, jointIndices, jointStride); + for (int component = 0; component < jointStride; component++) { + joints[i * jointStride + component] = (uint16_t)jointIndices[component]; + } } + } else if (key == "WEIGHTS_0") { - if (accessor.type == GLTFAccessorType::VEC4) { + if (accessor->type == cgltf_type_vec4) { weightStride = 4; - } else if (accessor.type == GLTFAccessorType::VEC3) { + } else if (accessor->type == cgltf_type_vec3) { weightStride = 3; - } else if (accessor.type == GLTFAccessorType::VEC2) { + } else if (accessor->type == cgltf_type_vec2) { weightStride = 2; - } else if (accessor.type == GLTFAccessorType::SCALAR) { + } else if (accessor->type == cgltf_type_scalar) { weightStride = 1; } else { qWarning(modelformat) << "Invalid accessor type on glTF WEIGHTS_0 data for model " << _url; @@ -1238,8 +604,9 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& continue; } - success = addArrayFromAccessor(accessor, weights); - if (!success) { + weights.resize(accessor->count * weightStride); + size_t floatCount = cgltf_accessor_unpack_floats(accessor, weights.data(), accessor->count * weightStride); + if (floatCount != accessor->count * weightStride) { qWarning(modelformat) << "There was a problem reading glTF WEIGHTS_0 data for model " << _url; hfmModel.loadErrorCount++; continue; @@ -1280,7 +647,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& if (v1_index + 2 >= vertices.size() || v2_index + 2 >= vertices.size() || v3_index + 2 >= vertices.size()) { qWarning(modelformat) << "Indices out of range for model " << _url; hfmModel.loadErrorCount++; - break; + return false; } glm::vec3 v1 = glm::vec3(vertices[v1_index], vertices[v1_index + 1], vertices[v1_index + 2]); @@ -1534,22 +901,21 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& continue; } - if ( _file.skins.length() <= node.skin ) { - qCWarning(modelformat) << "Trying to read past end of _file.skins at" << node.skin; + if ( node.skin->joints_count <= clusterJoints[c]) { + qCWarning(modelformat) << "Trying to read past end of _file.skins[node.skin].joints at" << clusterJoints[c] + << "; there are only" << node.skin->joints_count << "for skin" << node.skin->name; hfmModel.loadErrorCount++; continue; } - if ( _file.skins[node.skin].joints.length() <= clusterJoints[c]) { - qCWarning(modelformat) << "Trying to read past end of _file.skins[node.skin].joints at" << clusterJoints[c] - << "; there are only" << _file.skins[node.skin].joints.length() << "for skin" << node.skin; + size_t jointIndex = 0; + if (!findPointerInArray(node.skin->joints[clusterJoints[c]], _data->nodes, _data->nodes_count, jointIndex)) { + qCWarning(modelformat) << "Cannot find the joint " << node.skin->joints[clusterJoints[c]]->name <<" in joint array"; hfmModel.loadErrorCount++; continue; } - - mesh.clusterIndices[prevMeshClusterIndexCount + c] = - originalToNewNodeIndexMap[_file.skins[node.skin].joints[clusterJoints[c]]]; + originalToNewNodeIndexMap[jointIndex]; } // normalize and compress to 16-bits @@ -1586,8 +952,14 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& } } - if (primitive.defined["material"]) { - part.materialID = materialIDs[primitive.material]; + size_t materialIndex = 0; + if (primitive.material != nullptr && !findPointerInArray(primitive.material, _data->materials, _data->materials_count, materialIndex)) { + qCWarning(modelformat) << "GLTFSerializer::buildGeometry: Invalid material pointer"; + hfmModel.loadErrorCount++; + return false; + } + if (primitive.material != nullptr) { + part.materialID = materialIDs[materialIndex]; } mesh.parts.push_back(part); @@ -1599,7 +971,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& } // Build morph targets (blend shapes) - if (!primitive.targets.isEmpty()) { + if (!primitive.targets_count) { // Build list of blendshapes from FST and model. typedef QPair WeightedIndex; @@ -1613,21 +985,30 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& auto mappings = blendshapeMappings.values(blendshapeName); if (mappings.count() > 0) { // Use blendshape from mapping. - foreach(const QVariant& mapping, mappings) { - auto blendshapeMapping = mapping.toList(); + foreach(const QVariant& mappingVariant, mappings) { + auto blendshapeMapping = mappingVariant.toList(); blendshapeIndices.insert(blendshapeMapping.at(0).toString(), WeightedIndex(i, blendshapeMapping.at(1).toFloat())); } } else { // Use blendshape from model. - if (_file.meshes[node.mesh].extras.targetNames.contains(blendshapeName)) { - blendshapeIndices.insert(blendshapeName, WeightedIndex(i, 1.0f)); + std::string blendshapeNameString = blendshapeName.toStdString(); + for (size_t i = 0; i < node.mesh->target_names_count; i++) { + if (strcmp(node.mesh->target_names[i], blendshapeNameString.c_str()) == 0) { + blendshapeIndices.insert(blendshapeName, WeightedIndex(i, 1.0f)); + break; + } } } } // If an FST isn't being used and the model is likely from ReadyPlayerMe, add blendshape synonyms. - auto fileTargetNames = _file.meshes[node.mesh].extras.targetNames; + QVector fileTargetNames; + fileTargetNames.reserve(node.mesh->target_names_count); + for (size_t i = 0; i < node.mesh->target_names_count; i++) { + fileTargetNames.push_back(QString(node.mesh->target_names[i])); + } + bool likelyReadyPlayerMeFile = fileTargetNames.contains("browOuterUpLeft") && fileTargetNames.contains("browInnerUp") @@ -1658,7 +1039,11 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& } auto keys = blendshapeIndices.keys(); auto values = blendshapeIndices.values(); - auto names = _file.meshes[node.mesh].extras.targetNames; + QVector names; + names.reserve(node.mesh->target_names_count); + for (size_t i = 0; i < node.mesh->target_names_count; i++) { + names.push_back(QString(node.mesh->target_names[i])); + } for (int weightedIndex = 0; weightedIndex < keys.size(); ++weightedIndex) { float weight = 1.0f; @@ -1682,11 +1067,21 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& QVector normals; QVector vertices; - if (target.values.contains((QString) "NORMAL")) { - generateTargetData(target.values.value((QString) "NORMAL"), weight, normals); + size_t normalAttributeIndex = 0; + if (findAttribute("NORMAL", target.attributes, target.attributes_count, normalAttributeIndex)) { + if (!generateTargetData(target.attributes[normalAttributeIndex].data, weight, normals)) { + qWarning(modelformat) << "Invalid accessor type on generateTargetData vertices for model " << _url; + hfmModel.loadErrorCount++; + return false; + } } - if (target.values.contains((QString) "POSITION")) { - generateTargetData(target.values.value((QString) "POSITION"), weight, vertices); + size_t positionAttributeIndex = 0; + if (findAttribute("POSITION", target.attributes, target.attributes_count, positionAttributeIndex)) { + if (!generateTargetData(target.attributes[positionAttributeIndex].data, weight, vertices)) { + qWarning(modelformat) << "Invalid accessor type on generateTargetData vertices for model " << _url; + hfmModel.loadErrorCount++; + return false; + } } if (blendshape.indices.size() < prevMeshVerticesCount + vertices.size()) { @@ -1720,7 +1115,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mesh.meshIndex = hfmModel.meshes.size(); } - ++nodecount; + ++nodeCount; } return true; @@ -1752,45 +1147,58 @@ HFMModel::Pointer GLTFSerializer::read(const hifi::ByteArray& data, const hifi:: _url = hifi::URL(QFileInfo(localFileName).absoluteFilePath()); } - if (parseGLTF(data)) { - //_file.dump(); - auto hfmModelPtr = std::make_shared(); - HFMModel& hfmModel = *hfmModelPtr; - buildGeometry(hfmModel, mapping, _url); - - //hfmModel.debugDump(); - //glTFDebugDump(); - - return hfmModelPtr; - } else { + cgltf_options options = {}; + cgltf_result result = cgltf_parse(&options, data.data(), data.size(), &_data); + if (result != cgltf_result_success) { qCDebug(modelformat) << "Error parsing GLTF file."; + return nullptr; + } + cgltf_load_buffers(&options, _data, NULL); + for (size_t i = 0; i < _data->buffers_count; i++) { + cgltf_buffer &buffer = _data->buffers[i]; + if (buffer.data == nullptr) { + if (!readBinary(buffer.uri, buffer)) { + qCDebug(modelformat) << "Error parsing GLTF file."; + return nullptr; + } + } } - return nullptr; + + auto hfmModelPtr = std::make_shared(); + HFMModel& hfmModel = *hfmModelPtr; + buildGeometry(hfmModel, mapping, _url); + + return hfmModelPtr; } -bool GLTFSerializer::readBinary(const QString& url, hifi::ByteArray& outdata) { +bool GLTFSerializer::readBinary(const QString& url, cgltf_buffer &buffer) { bool success; + hifi::ByteArray outdata; + // Is this part already done by cgltf? if (url.contains("data:application/octet-stream;base64,")) { + qDebug() << "GLTFSerializer::readBinary: base64"; outdata = requestEmbeddedData(url); success = !outdata.isEmpty(); } else { hifi::URL binaryUrl = _url.resolved(url); std::tie(success, outdata) = requestData(binaryUrl); } + if (success) { + if(buffer.size == (size_t)outdata.size()) { + _externalData.push_back(outdata); + buffer.data = _externalData.last().data(); + buffer.data_free_method = cgltf_data_free_method_none; + } else { + qDebug() << "Buffer size mismatch for model: " << _url; + success = false; + } + } return success; } -bool GLTFSerializer::doesResourceExist(const QString& url) { - if (_url.isEmpty()) { - return false; - } - hifi::URL candidateUrl = _url.resolved(url); - return DependencyManager::get()->resourceExists(candidateUrl); -} - std::tuple GLTFSerializer::requestData(hifi::URL& url) { auto request = DependencyManager::get()->createResourceRequest( nullptr, url, true, -1, "GLTFSerializer::requestData"); @@ -1841,263 +1249,113 @@ QNetworkReply* GLTFSerializer::request(hifi::URL& url, bool isTest) { return netReply; // trying to sync later on. } -HFMTexture GLTFSerializer::getHFMTexture(const GLTFTexture& texture) { - HFMTexture fbxtex = HFMTexture(); - fbxtex.texcoordSet = 0; +HFMTexture GLTFSerializer::getHFMTexture(const cgltf_texture *texture) { + HFMTexture hfmTex = HFMTexture(); + hfmTex.texcoordSet = 0; - if (texture.defined["source"]) { - QString url = _file.images[texture.source].uri; + if (texture->image) { + QString url = texture->image->uri; - QString fname = hifi::URL(url).fileName(); + QString fileName = hifi::URL(url).fileName(); hifi::URL textureUrl = _url.resolved(url); - fbxtex.name = fname; - fbxtex.filename = textureUrl.toEncoded(); + hfmTex.name = fileName; + hfmTex.filename = textureUrl.toEncoded(); + + if (_url.path().endsWith("glb")) { + cgltf_buffer_view *bufferView = texture->image->buffer_view; - if (_url.path().endsWith("glb") && !_glbBinary.isEmpty()) { - int bufferView = _file.images[texture.source].bufferView; + size_t offset = bufferView->offset; + size_t length = bufferView->size; - GLTFBufferView& imagesBufferview = _file.bufferviews[bufferView]; - int offset = imagesBufferview.byteOffset; - int length = imagesBufferview.byteLength; + size_t imageIndex = 0; + if (!findPointerInArray(texture->image, _data->images, _data->images_count, imageIndex)) { + // This should never happen. It would mean a bug in cgltf library. + qDebug(modelformat) << "GLTFSerializer::getHFMTexture: can't find texture in the array"; + return hfmTex; + } - fbxtex.content = _glbBinary.mid(offset, length); - fbxtex.filename = textureUrl.toEncoded().append(texture.source); + if (offset + length > bufferView->buffer->size) { + qDebug(modelformat) << "GLTFSerializer::getHFMTexture: texture data to short"; + return hfmTex; + } + hfmTex.content = QByteArray(static_cast(bufferView->buffer->data) + offset, length); + hfmTex.filename = textureUrl.toEncoded().append(imageIndex); } - if (url.contains("data:image/jpeg;base64,") || url.contains("data:image/png;base64,")) { - fbxtex.content = requestEmbeddedData(url); + if (url.contains("data:image/jpeg;base64,") || url.contains("data:image/png;base64,") || url.contains("data:image/webp;base64,")) { + hfmTex.content = requestEmbeddedData(url); } } - return fbxtex; + return hfmTex; } -void GLTFSerializer::setHFMMaterial(HFMMaterial& hfmMat, const GLTFMaterial& material) { - if (material.defined["alphaMode"]) { - hfmMat._material->setOpacityMapMode(material.alphaMode); +void GLTFSerializer::setHFMMaterial(HFMMaterial& hfmMat, const cgltf_material& material) { + if (material.alpha_mode == cgltf_alpha_mode_opaque) { + hfmMat._material->setOpacityMapMode(graphics::MaterialKey::OPACITY_MAP_OPAQUE); + } else if (material.alpha_mode == cgltf_alpha_mode_mask) { + hfmMat._material->setOpacityMapMode(graphics::MaterialKey::OPACITY_MAP_MASK); + } else if (material.alpha_mode == cgltf_alpha_mode_blend) { + hfmMat._material->setOpacityMapMode(graphics::MaterialKey::OPACITY_MAP_BLEND); } else { hfmMat._material->setOpacityMapMode(graphics::MaterialKey::OPACITY_MAP_OPAQUE); // GLTF defaults to opaque } - if (material.defined["alphaCutoff"]) { - hfmMat._material->setOpacityCutoff(material.alphaCutoff); - } + hfmMat._material->setOpacityCutoff(material.alpha_cutoff); - if (material.defined["doubleSided"] && material.doubleSided) { + if (material.double_sided) { hfmMat._material->setCullFaceMode(graphics::MaterialKey::CullFaceMode::CULL_NONE); } - if (material.defined["emissiveFactor"] && material.emissiveFactor.size() == 3) { - glm::vec3 emissiveLinear = glm::vec3(material.emissiveFactor[0], material.emissiveFactor[1], material.emissiveFactor[2]); - glm::vec3 emissive = ColorUtils::tosRGBVec3(emissiveLinear); - hfmMat._material->setEmissive(emissive); - } + glm::vec3 emissiveLinear = glm::vec3(material.emissive_factor[0], material.emissive_factor[1], material.emissive_factor[2]); + glm::vec3 emissive = ColorUtils::tosRGBVec3(emissiveLinear); + hfmMat._material->setEmissive(emissive); - if (material.defined["emissiveTexture"]) { - hfmMat.emissiveTexture = getHFMTexture(_file.textures[material.emissiveTexture]); + if (material.emissive_texture.texture != nullptr) { + hfmMat.emissiveTexture = getHFMTexture(material.emissive_texture.texture); hfmMat.useEmissiveMap = true; } - if (material.defined["normalTexture"]) { - hfmMat.normalTexture = getHFMTexture(_file.textures[material.normalTexture]); + if (material.normal_texture.texture != nullptr) { + hfmMat.normalTexture = getHFMTexture(material.normal_texture.texture); hfmMat.useNormalMap = true; } - if (material.defined["occlusionTexture"]) { - hfmMat.occlusionTexture = getHFMTexture(_file.textures[material.occlusionTexture]); + if (material.occlusion_texture.texture != nullptr) { + hfmMat.occlusionTexture = getHFMTexture(material.occlusion_texture.texture); hfmMat.useOcclusionMap = true; } - if (material.defined["pbrMetallicRoughness"]) { + if (material.has_pbr_metallic_roughness) { hfmMat.isPBSMaterial = true; - if (material.pbrMetallicRoughness.defined["metallicFactor"]) { - hfmMat.metallic = material.pbrMetallicRoughness.metallicFactor; - hfmMat._material->setMetallic(hfmMat.metallic); - } - if (material.pbrMetallicRoughness.defined["baseColorTexture"]) { - hfmMat.opacityTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); - hfmMat.albedoTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); + hfmMat.metallic = material.pbr_metallic_roughness.metallic_factor; + hfmMat._material->setMetallic(hfmMat.metallic); + + if (material.pbr_metallic_roughness.base_color_texture.texture != nullptr) { + hfmMat.opacityTexture = getHFMTexture(material.pbr_metallic_roughness.base_color_texture.texture); + hfmMat.albedoTexture = getHFMTexture(material.pbr_metallic_roughness.base_color_texture.texture); hfmMat.useAlbedoMap = true; } - if (material.pbrMetallicRoughness.defined["metallicRoughnessTexture"]) { - hfmMat.roughnessTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); + if (material.pbr_metallic_roughness.metallic_roughness_texture.texture) { + hfmMat.roughnessTexture = getHFMTexture(material.pbr_metallic_roughness.metallic_roughness_texture.texture); hfmMat.roughnessTexture.sourceChannel = image::ColorChannel::GREEN; hfmMat.useRoughnessMap = true; - hfmMat.metallicTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); + hfmMat.metallicTexture = getHFMTexture(material.pbr_metallic_roughness.metallic_roughness_texture.texture); hfmMat.metallicTexture.sourceChannel = image::ColorChannel::BLUE; hfmMat.useMetallicMap = true; } - if (material.pbrMetallicRoughness.defined["roughnessFactor"]) { - hfmMat._material->setRoughness(material.pbrMetallicRoughness.roughnessFactor); - } - if (material.pbrMetallicRoughness.defined["baseColorFactor"] && - material.pbrMetallicRoughness.baseColorFactor.size() == 4) { - glm::vec3 lcolor = glm::vec3(material.pbrMetallicRoughness.baseColorFactor[0], material.pbrMetallicRoughness.baseColorFactor[1], - material.pbrMetallicRoughness.baseColorFactor[2]); - glm::vec3 dcolor = ColorUtils::tosRGBVec3(lcolor); - hfmMat.diffuseColor = dcolor; - hfmMat._material->setAlbedo(dcolor); - hfmMat._material->setOpacity(material.pbrMetallicRoughness.baseColorFactor[3]); - } - } - -} - -template -bool GLTFSerializer::readArray(const hifi::ByteArray& bin, int byteOffset, int count, - QVector& outarray, int accessorType, bool normalized) { - - QDataStream blobstream(bin); - blobstream.setByteOrder(QDataStream::LittleEndian); - blobstream.setVersion(QDataStream::Qt_5_9); - blobstream.setFloatingPointPrecision(QDataStream::FloatingPointPrecision::SinglePrecision); - blobstream.skipRawData(byteOffset); - - int bufferCount = 0; - switch (accessorType) { - case GLTFAccessorType::SCALAR: - bufferCount = 1; - break; - case GLTFAccessorType::VEC2: - bufferCount = 2; - break; - case GLTFAccessorType::VEC3: - bufferCount = 3; - break; - case GLTFAccessorType::VEC4: - bufferCount = 4; - break; - case GLTFAccessorType::MAT2: - bufferCount = 4; - break; - case GLTFAccessorType::MAT3: - bufferCount = 9; - break; - case GLTFAccessorType::MAT4: - bufferCount = 16; - break; - default: - qWarning(modelformat) << "Unknown accessorType: " << accessorType; - blobstream.setDevice(nullptr); - return false; - } - - float scale = 1.0f; // Normalized output values should always be floats. - if (normalized) { - scale = (float)(std::numeric_limits::max)(); - } - - for (int i = 0; i < count; ++i) { - for (int j = 0; j < bufferCount; ++j) { - if (!blobstream.atEnd()) { - T value; - blobstream >> value; - if (normalized) { - outarray.push_back(std::max((float)value / scale, -1.0f)); - } else { - outarray.push_back(value); - } - } else { - blobstream.setDevice(nullptr); - return false; - } - } - } - - blobstream.setDevice(nullptr); - return true; -} -template -bool GLTFSerializer::addArrayOfType(const hifi::ByteArray& bin, int byteOffset, int count, - QVector& outarray, int accessorType, int componentType, bool normalized) { - - switch (componentType) { - case GLTFAccessorComponentType::BYTE: {} - case GLTFAccessorComponentType::UNSIGNED_BYTE: { - return readArray(bin, byteOffset, count, outarray, accessorType, normalized); - } - case GLTFAccessorComponentType::SHORT: { - return readArray(bin, byteOffset, count, outarray, accessorType, normalized); - } - case GLTFAccessorComponentType::UNSIGNED_INT: { - return readArray(bin, byteOffset, count, outarray, accessorType, normalized); - } - case GLTFAccessorComponentType::UNSIGNED_SHORT: { - return readArray(bin, byteOffset, count, outarray, accessorType, normalized); - } - case GLTFAccessorComponentType::FLOAT: { - return readArray(bin, byteOffset, count, outarray, accessorType, normalized); - } - } - return false; -} - -template -bool GLTFSerializer::addArrayFromAccessor(GLTFAccessor& accessor, QVector& outarray) { - bool success = true; - - if (accessor.defined["bufferView"]) { - GLTFBufferView& bufferview = _file.bufferviews[accessor.bufferView]; - GLTFBuffer& buffer = _file.buffers[bufferview.buffer]; - - int accBoffset = accessor.defined["byteOffset"] ? accessor.byteOffset : 0; - - success = addArrayOfType(buffer.blob, bufferview.byteOffset + accBoffset, accessor.count, outarray, accessor.type, - accessor.componentType, accessor.normalized); - } else { - for (int i = 0; i < accessor.count; ++i) { - T value; - memset(&value, 0, sizeof(T)); // Make sure the dummy array is initialized to zero. - outarray.push_back(value); - } - } - - if (success) { - if (accessor.defined["sparse"]) { - QVector out_sparse_indices_array; - - GLTFBufferView& sparseIndicesBufferview = _file.bufferviews[accessor.sparse.indices.bufferView]; - GLTFBuffer& sparseIndicesBuffer = _file.buffers[sparseIndicesBufferview.buffer]; - - int accSIBoffset = accessor.sparse.indices.defined["byteOffset"] ? accessor.sparse.indices.byteOffset : 0; - - success = addArrayOfType(sparseIndicesBuffer.blob, sparseIndicesBufferview.byteOffset + accSIBoffset, - accessor.sparse.count, out_sparse_indices_array, GLTFAccessorType::SCALAR, - accessor.sparse.indices.componentType, false); - if (success) { - QVector out_sparse_values_array; - GLTFBufferView& sparseValuesBufferview = _file.bufferviews[accessor.sparse.values.bufferView]; - GLTFBuffer& sparseValuesBuffer = _file.buffers[sparseValuesBufferview.buffer]; + hfmMat._material->setRoughness(material.pbr_metallic_roughness.roughness_factor); - int accSVBoffset = accessor.sparse.values.defined["byteOffset"] ? accessor.sparse.values.byteOffset : 0; - - success = addArrayOfType(sparseValuesBuffer.blob, sparseValuesBufferview.byteOffset + accSVBoffset, - accessor.sparse.count, out_sparse_values_array, accessor.type, accessor.componentType, - accessor.normalized); - - if (success) { - for (int i = 0; i < accessor.sparse.count; ++i) { - if ((i * 3) + 2 < out_sparse_values_array.size()) { - if ((out_sparse_indices_array[i] * 3) + 2 < outarray.length()) { - for (int j = 0; j < 3; ++j) { - outarray[(out_sparse_indices_array[i] * 3) + j] = out_sparse_values_array[(i * 3) + j]; - } - } else { - success = false; - break; - } - } else { - success = false; - break; - } - } - } - } - } + glm::vec3 lcolor = glm::vec3(material.pbr_metallic_roughness.base_color_factor[0], + material.pbr_metallic_roughness.base_color_factor[1], + material.pbr_metallic_roughness.base_color_factor[2]); + glm::vec3 dcolor = ColorUtils::tosRGBVec3(lcolor); + hfmMat.diffuseColor = dcolor; + hfmMat._material->setAlbedo(dcolor); + hfmMat._material->setOpacity(material.pbr_metallic_roughness.base_color_factor[3]); } - return success; } void GLTFSerializer::retriangulate(const QVector& inIndices, const QVector& in_vertices, @@ -2123,29 +1381,6 @@ void GLTFSerializer::retriangulate(const QVector& inIndices, const QVector< } } -void GLTFSerializer::glTFDebugDump() { - qCDebug(modelformat) << "\n"; - qCDebug(modelformat) << "---------------- GLTF Model ----------------"; - - qCDebug(modelformat) << "---------------- Nodes ----------------"; - for (GLTFNode node : _file.nodes) { - if (node.defined["mesh"]) { - qCDebug(modelformat) << " node_transforms" << node.transforms; - } - } - - qCDebug(modelformat) << "---------------- Accessors ----------------"; - for (GLTFAccessor accessor : _file.accessors) { - qCDebug(modelformat) << "count: " << accessor.count; - qCDebug(modelformat) << "byteOffset: " << accessor.byteOffset; - } - - qCDebug(modelformat) << "---------------- Textures ----------------"; - for (GLTFTexture texture : _file.textures) { - if (texture.defined["source"]) { - QString url = _file.images[texture.source].uri; - QString fname = hifi::URL(url).fileName(); - qCDebug(modelformat) << "fname: " << fname; - } - } -} +GLTFSerializer::~GLTFSerializer() { + cgltf_free(_data); +} \ No newline at end of file diff --git a/libraries/model-serializers/src/GLTFSerializer.h b/libraries/model-serializers/src/GLTFSerializer.h index da5284d0b32..5d31a5618a4 100644 --- a/libraries/model-serializers/src/GLTFSerializer.h +++ b/libraries/model-serializers/src/GLTFSerializer.h @@ -4,7 +4,7 @@ // // Created by Luis Cuenca on 8/30/17. // Copyright 2017 High Fidelity, Inc. -// Copyright 2023 Overte e.V. +// Copyright 2023-2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -14,754 +14,14 @@ #ifndef hifi_GLTFSerializer_h #define hifi_GLTFSerializer_h +#include "cgltf.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; - int mesh; - QVector children; - QVector translation; - QVector rotation; - QVector scale; - QVector matrix; - QVector transforms; - int skin; - 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; - } - } -}; - -// 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; - } - } -}; - -struct GLTFMaterial { - QString name; - QVector emissiveFactor; - int emissiveTexture; - int normalTexture; - int occlusionTexture; - graphics::MaterialKey::OpacityMapMode alphaMode { graphics::MaterialKey::OPACITY_MAP_OPAQUE }; - double alphaCutoff; - bool doubleSided { false }; - 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["alphaMode"]) { - qCDebug(modelformat) << "alphaMode: " << alphaMode; - } - if (defined["alphaCutoff"]) { - qCDebug(modelformat) << "alphaCutoff: " << alphaCutoff; - } - if (defined["pbrMetallicRoughness"]) { - pbrMetallicRoughness.dump(); - } - } -}; - -// Accesors - -namespace GLTFAccessorType { - enum Values { - SCALAR = 0, - VEC2, - VEC3, - VEC4, - MAT2, - MAT3, - MAT4 - }; -} -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(); - } - } -}; - class GLTFSerializer : public QObject, public HFMSerializer { Q_OBJECT public: @@ -769,79 +29,19 @@ class GLTFSerializer : public QObject, public HFMSerializer { std::unique_ptr getFactory() const override; HFMModel::Pointer read(const hifi::ByteArray& data, const hifi::VariantHash& mapping, const hifi::URL& url = hifi::URL()) override; + ~GLTFSerializer(); private: - GLTFFile _file; + cgltf_data* _data {nullptr}; hifi::URL _url; - hifi::ByteArray _glbBinary; + QVector _externalData; - glm::mat4 getModelTransform(const GLTFNode& node); - void getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues); - void generateTargetData(int index, float weight, QVector& returnVector); + glm::mat4 getModelTransform(const cgltf_node& node); + bool getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues); + bool generateTargetData(cgltf_accessor *accessor, 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); - - graphics::MaterialKey::OpacityMapMode 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, bool normalized); - - template - bool addArrayOfType(const hifi::ByteArray& bin, int byteOffset, int count, - QVector& outarray, int accessorType, int componentType, bool normalized); - - template - bool addArrayFromAccessor(GLTFAccessor& accessor, QVector& outarray); + bool readBinary(const QString& url, cgltf_buffer &buffer); void retriangulate(const QVector& in_indices, const QVector& in_vertices, const QVector& in_normals, QVector& out_indices, @@ -851,12 +51,9 @@ class GLTFSerializer : public QObject, public HFMSerializer { hifi::ByteArray requestEmbeddedData(const QString& url); QNetworkReply* request(hifi::URL& url, bool isTest); - bool doesResourceExist(const QString& url); - - void setHFMMaterial(HFMMaterial& hfmMat, const GLTFMaterial& material); - HFMTexture getHFMTexture(const GLTFTexture& texture); - void glTFDebugDump(); + void setHFMMaterial(HFMMaterial& hfmMat, const cgltf_material& material); + HFMTexture getHFMTexture(const cgltf_texture *texture); }; #endif // hifi_GLTFSerializer_h diff --git a/tests/model-serializers/CMakeLists.txt b/tests/model-serializers/CMakeLists.txt index 1072a870522..b951f11eadf 100644 --- a/tests/model-serializers/CMakeLists.txt +++ b/tests/model-serializers/CMakeLists.txt @@ -77,7 +77,7 @@ macro (setup_testcase_dependencies) gltf_samples PREFIX "models" GIT_REPOSITORY "https://github.com/KhronosGroup/glTF-Sample-models" - GIT_TAG "master" + GIT_TAG "main" DOWNLOAD_NO_EXTRACT true CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" ) diff --git a/tests/model-serializers/src/ModelSerializersTests.cpp b/tests/model-serializers/src/ModelSerializersTests.cpp index 6988b3761ed..05e3633d7aa 100644 --- a/tests/model-serializers/src/ModelSerializersTests.cpp +++ b/tests/model-serializers/src/ModelSerializersTests.cpp @@ -74,17 +74,17 @@ void ModelSerializersTests::loadGLTF_data() { QTest::newRow("ready-player-me-good3") << "models/src/Franny.glb.gz" << false << false << false; QTest::newRow("ready-player-me-good4") << "models/src/womanInTShirt.glb.gz" << false << false << false; QTest::newRow("ready-player-me-good5") << "models/src/female-avatar-with-swords.glb.gz" << false << false << false; - QTest::newRow("ready-player-me-broken1") << "models/src/broken-2022-11-27.glb.gz" << false << true; + QTest::newRow("ready-player-me-broken1") << "models/src/broken-2022-11-27.glb.gz" << false << false << false; // We can't parse GLTF 1.0 at present, and probably not ever. We're expecting all these to fail. - QDirIterator it("models/src/gltf_samples/1.0", QStringList() << "*.glb", QDir::Files, QDirIterator::Subdirectories); + /*QDirIterator it("models/src/gltf_samples/1.0", QStringList() << "*.glb", QDir::Files, QDirIterator::Subdirectories); while(it.hasNext()) { QString filename = it.next(); QFileInfo fi(filename); QString testname = "gltf1.0-" + fi.fileName(); QTest::newRow(testname.toUtf8().data()) << filename << true << false << false; - } + }*/ QDirIterator it2("models/src/gltf_samples/2.0", QStringList() << "*.glb", QDir::Files, QDirIterator::Subdirectories); while(it2.hasNext()) {