443static void setMaterialProperties(QSSGSceneDesc::Material &target,
const aiMaterial &source,
const SceneInfo &sceneInfo, QSSGSceneDesc::Material::RuntimeType type)
445 if (target.name.isNull()) {
446 aiString materialName = source.GetName();
447 target.name = fromAiString(materialName);
450 const auto createTextureNode = [&sceneInfo, &target](
const aiMaterial &material, aiTextureType textureType,
unsigned int index) {
451 const auto &srcScene = sceneInfo
.scene;
452 QSSGSceneDesc::Texture *tex =
nullptr;
453 aiString texturePath;
456 if (material.GetTexture(textureType, index, &texturePath, &texInfo
.mapping, &texInfo
.uvIndex,
nullptr,
nullptr, texInfo
.modes) == aiReturn_SUCCESS) {
457 if (texturePath.length > 0) {
458 aiUVTransform transform;
459 if (material.Get(AI_MATKEY_UVTRANSFORM(textureType, index), transform) == aiReturn_SUCCESS)
460 texInfo.transform = transform;
462 material.Get(AI_MATKEY_UVWSRC(textureType, index), texInfo
.uvIndex);
463 material.Get(AI_MATKEY_GLTF_MAPPINGFILTER_MIN(textureType, index), texInfo
.minFilter);
464 material.Get(AI_MATKEY_GLTF_MAPPINGFILTER_MAG(textureType, index), texInfo
.magFilter);
466 auto &textureMap = sceneInfo.textureMap;
468 QByteArray texName = QByteArray(texturePath.C_Str(), texturePath.length);
470 const auto it = textureMap.constFind(
TextureEntry{texName, texInfo});
471 if (it != textureMap.cend()) {
472 Q_ASSERT(it->texture);
477 tex =
new QSSGSceneDesc::Texture(QSSGSceneDesc::Texture::RuntimeType::Image2D, texName);
478 textureMap.insert(TextureEntry{fromAiString(texturePath), texInfo, tex});
479 QSSGSceneDesc::addNode(target, *tex);
480 setTextureProperties(*tex, texInfo, sceneInfo);
482 auto aEmbeddedTex = srcScene.GetEmbeddedTextureAndIndex(texturePath.C_Str());
483 const auto &embeddedTexId = aEmbeddedTex.second;
484 if (embeddedTexId > -1) {
485 QSSGSceneDesc::TextureData *textureData =
nullptr;
486 auto &embeddedTextures = sceneInfo.embeddedTextureMap;
487 textureData = embeddedTextures[embeddedTexId];
489 const auto *sourceTexture = aEmbeddedTex.first;
490 Q_ASSERT(sourceTexture->pcData);
492 const bool isCompressed = (sourceTexture->mHeight == 0);
495 const qsizetype asize = (isCompressed) ? sourceTexture->mWidth : (sourceTexture->mHeight * sourceTexture->mWidth) *
sizeof(aiTexel);
496 const QSize size = (!isCompressed) ? QSize(
int(sourceTexture->mWidth),
int(sourceTexture->mHeight)) : QSize();
497 QByteArray imageData {
reinterpret_cast<
const char *>(sourceTexture->pcData), asize };
498 const auto format = (isCompressed) ? QByteArray(sourceTexture->achFormatHint) : QByteArrayLiteral(
"rgba8888");
499 const quint8 flags = isCompressed ? quint8(QSSGSceneDesc::TextureData::Flags::Compressed) : 0;
500 textureData =
new QSSGSceneDesc::TextureData(imageData, size, format, flags);
501 QSSGSceneDesc::addNode(*tex, *textureData);
502 embeddedTextures[embeddedTexId] = textureData;
506 QSSGSceneDesc::setProperty(*tex,
"textureData", &QQuick3DTexture::setTextureData, textureData);
508 auto relativePath = QString::fromUtf8(texturePath.C_Str());
511 relativePath.replace(
"\\",
"/");
512 const auto path = sceneInfo.workingDir.absoluteFilePath(relativePath);
513 QSSGSceneDesc::setProperty(*tex,
"source", &QQuick3DTexture::setSource, QUrl{ path });
524 if (type == QSSGSceneDesc::Material::RuntimeType::PrincipledMaterial) {
526 aiColor4D baseColorFactor;
527 result = source.Get(AI_MATKEY_BASE_COLOR, baseColorFactor);
528 if (result == aiReturn_SUCCESS) {
529 QSSGSceneDesc::setProperty(target,
"baseColor", &QQuick3DPrincipledMaterial::setBaseColor, aiColorToQColor(baseColorFactor));
533 aiColor3D diffuseColor;
534 result = source.Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor);
535 if (result == aiReturn_SUCCESS)
536 QSSGSceneDesc::setProperty(target,
"baseColor", &QQuick3DPrincipledMaterial::setBaseColor, aiColorToQColor(diffuseColor));
540 if (
auto baseColorTexture = createTextureNode(source, AI_MATKEY_BASE_COLOR_TEXTURE)) {
541 QSSGSceneDesc::setProperty(target,
"baseColorMap", &QQuick3DPrincipledMaterial::setBaseColorMap, baseColorTexture);
542 QSSGSceneDesc::setProperty(target,
"opacityChannel", &QQuick3DPrincipledMaterial::setOpacityChannel, QQuick3DPrincipledMaterial::TextureChannelMapping::A);
543 }
else if (
auto diffuseMapTexture = createTextureNode(source, aiTextureType_DIFFUSE, 0)) {
545 QSSGSceneDesc::setProperty(target,
"baseColorMap", &QQuick3DPrincipledMaterial::setBaseColorMap, diffuseMapTexture);
548 if (
auto metalicRoughnessTexture = createTextureNode(source, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE)) {
549 QSSGSceneDesc::setProperty(target,
"metalnessMap", &QQuick3DPrincipledMaterial::setMetalnessMap, metalicRoughnessTexture);
550 QSSGSceneDesc::setProperty(target,
"metalnessChannel", &QQuick3DPrincipledMaterial::setMetalnessChannel, QQuick3DPrincipledMaterial::TextureChannelMapping::B);
551 QSSGSceneDesc::setProperty(target,
"roughnessMap", &QQuick3DPrincipledMaterial::setRoughnessMap, metalicRoughnessTexture);
552 QSSGSceneDesc::setProperty(target,
"roughnessChannel", &QQuick3DPrincipledMaterial::setRoughnessChannel, QQuick3DPrincipledMaterial::TextureChannelMapping::G);
556 ai_real metallicFactor;
557 result = source.Get(AI_MATKEY_METALLIC_FACTOR, metallicFactor);
558 if (result == aiReturn_SUCCESS)
559 QSSGSceneDesc::setProperty(target,
"metalness", &QQuick3DPrincipledMaterial::setMetalness,
float(metallicFactor));
563 ai_real roughnessFactor;
564 result = source.Get(AI_MATKEY_ROUGHNESS_FACTOR, roughnessFactor);
565 if (result == aiReturn_SUCCESS)
566 QSSGSceneDesc::setProperty(target,
"roughness", &QQuick3DPrincipledMaterial::setRoughness,
float(roughnessFactor));
569 if (
auto normalTexture = createTextureNode(source, aiTextureType_NORMALS, 0)) {
570 QSSGSceneDesc::setProperty(target,
"normalMap", &QQuick3DPrincipledMaterial::setNormalMap, normalTexture);
573 result = source.Get(AI_MATKEY_GLTF_TEXTURE_SCALE(aiTextureType_NORMALS, 0), normalScale);
574 if (result == aiReturn_SUCCESS)
575 QSSGSceneDesc::setProperty(target,
"normalStrength", &QQuick3DPrincipledMaterial::setNormalStrength,
float(normalScale));
580 if (
auto occlusionTexture = createTextureNode(source, aiTextureType_LIGHTMAP, 0)) {
581 QSSGSceneDesc::setProperty(target,
"occlusionMap", &QQuick3DPrincipledMaterial::setOcclusionMap, occlusionTexture);
582 QSSGSceneDesc::setProperty(target,
"occlusionChannel", &QQuick3DPrincipledMaterial::setOcclusionChannel, QQuick3DPrincipledMaterial::TextureChannelMapping::R);
584 ai_real occlusionAmount;
585 result = source.Get(AI_MATKEY_GLTF_TEXTURE_STRENGTH(aiTextureType_LIGHTMAP, 0), occlusionAmount);
586 if (result == aiReturn_SUCCESS)
587 QSSGSceneDesc::setProperty(target,
"occlusionAmount", &QQuick3DPrincipledMaterial::setOcclusionAmount,
float(occlusionAmount));
591 if (
auto emissiveTexture = createTextureNode(source, aiTextureType_EMISSIVE, 0))
592 QSSGSceneDesc::setProperty(target,
"emissiveMap", &QQuick3DPrincipledMaterial::setEmissiveMap, emissiveTexture);
595 aiColor3D emissiveColorFactor;
596 result = source.Get(AI_MATKEY_COLOR_EMISSIVE, emissiveColorFactor);
597 if (result == aiReturn_SUCCESS)
598 QSSGSceneDesc::setProperty(target,
"emissiveFactor", &QQuick3DPrincipledMaterial::setEmissiveFactor, QVector3D { emissiveColorFactor.r, emissiveColorFactor.g, emissiveColorFactor.b });
603 result = source.Get(AI_MATKEY_TWOSIDED, isDoubleSided);
604 if (result == aiReturn_SUCCESS && isDoubleSided)
605 QSSGSceneDesc::setProperty(target,
"cullMode", &QQuick3DPrincipledMaterial::setCullMode, QQuick3DPrincipledMaterial::CullMode::NoCulling);
610 result = source.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode);
611 if (result == aiReturn_SUCCESS) {
612 auto mode = QQuick3DPrincipledMaterial::AlphaMode::Default;
613 if (QByteArrayView(alphaMode.C_Str()) ==
"OPAQUE")
614 mode = QQuick3DPrincipledMaterial::AlphaMode::Opaque;
615 else if (QByteArrayView(alphaMode.C_Str()) ==
"MASK")
616 mode = QQuick3DPrincipledMaterial::AlphaMode::Mask;
617 else if (QByteArrayView(alphaMode.C_Str()) ==
"BLEND")
618 mode = QQuick3DPrincipledMaterial::AlphaMode::Blend;
620 if (mode != QQuick3DPrincipledMaterial::AlphaMode::Default) {
621 QSSGSceneDesc::setProperty(target,
"alphaMode", &QQuick3DPrincipledMaterial::setAlphaMode, mode);
623 if (mode == QQuick3DPrincipledMaterial::AlphaMode::Mask)
624 QSSGSceneDesc::setProperty(target,
"depthDrawMode", &QQuick3DPrincipledMaterial::setDepthDrawMode, QQuick3DMaterial::OpaquePrePassDepthDraw);
631 result = source.Get(AI_MATKEY_GLTF_ALPHACUTOFF, alphaCutoff);
632 if (result == aiReturn_SUCCESS)
633 QSSGSceneDesc::setProperty(target,
"alphaCutoff", &QQuick3DPrincipledMaterial::setAlphaCutoff,
float(alphaCutoff));
637 int shadingModel = 0;
638 result = source.Get(AI_MATKEY_SHADING_MODEL, shadingModel);
639 if (result == aiReturn_SUCCESS && shadingModel == aiShadingMode_Unlit)
640 QSSGSceneDesc::setProperty(target,
"lighting", &QQuick3DPrincipledMaterial::setLighting, QQuick3DPrincipledMaterial::Lighting::NoLighting);
648 ai_real clearcoatFactor = 0.0f;
649 result = source.Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoatFactor);
650 if (result == aiReturn_SUCCESS)
651 QSSGSceneDesc::setProperty(target,
653 &QQuick3DPrincipledMaterial::setClearcoatAmount,
654 float(clearcoatFactor));
659 ai_real clearcoatRoughnessFactor = 0.0f;
660 result = source.Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoatRoughnessFactor);
661 if (result == aiReturn_SUCCESS)
662 QSSGSceneDesc::setProperty(target,
663 "clearcoatRoughnessAmount",
664 &QQuick3DPrincipledMaterial::setClearcoatRoughnessAmount,
665 float(clearcoatRoughnessFactor));
669 if (
auto clearcoatTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_TEXTURE))
670 QSSGSceneDesc::setProperty(target,
"clearcoatMap", &QQuick3DPrincipledMaterial::setClearcoatMap, clearcoatTexture);
673 if (
auto clearcoatRoughnessTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE))
674 QSSGSceneDesc::setProperty(target,
675 "clearcoatRoughnessMap",
676 &QQuick3DPrincipledMaterial::setClearcoatRoughnessMap,
677 clearcoatRoughnessTexture);
680 if (
auto clearcoatNormalTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE))
681 QSSGSceneDesc::setProperty(target,
"clearcoatNormalMap", &QQuick3DPrincipledMaterial::setClearcoatNormalMap, clearcoatNormalTexture);
688 ai_real transmissionFactor = 0.0f;
689 result = source.Get(AI_MATKEY_TRANSMISSION_FACTOR, transmissionFactor);
690 if (result == aiReturn_SUCCESS)
691 QSSGSceneDesc::setProperty(target,
692 "transmissionFactor",
693 &QQuick3DPrincipledMaterial::setTransmissionFactor,
694 float(transmissionFactor));
699 if (
auto transmissionImage = createTextureNode(source, AI_MATKEY_TRANSMISSION_TEXTURE))
700 QSSGSceneDesc::setProperty(target,
702 &QQuick3DPrincipledMaterial::setTransmissionMap,
712 ai_real thicknessFactor = 0.0f;
713 result = source.Get(AI_MATKEY_VOLUME_THICKNESS_FACTOR, thicknessFactor);
714 if (result == aiReturn_SUCCESS)
715 QSSGSceneDesc::setProperty(target,
"thicknessFactor", &QQuick3DPrincipledMaterial::setThicknessFactor,
float(thicknessFactor));
720 if (
auto thicknessImage = createTextureNode(source, AI_MATKEY_VOLUME_THICKNESS_TEXTURE))
721 QSSGSceneDesc::setProperty(target,
"thicknessMap", &QQuick3DPrincipledMaterial::setThicknessMap, thicknessImage);
726 ai_real attenuationDistance = 0.0f;
727 result = source.Get(AI_MATKEY_VOLUME_ATTENUATION_DISTANCE, attenuationDistance);
728 if (result == aiReturn_SUCCESS)
729 QSSGSceneDesc::setProperty(target,
730 "attenuationDistance",
731 &QQuick3DPrincipledMaterial::setAttenuationDistance,
732 float(attenuationDistance));
737 aiColor3D attenuationColor;
738 result = source.Get(AI_MATKEY_VOLUME_ATTENUATION_COLOR, attenuationColor);
739 if (result == aiReturn_SUCCESS)
740 QSSGSceneDesc::setProperty(target,
742 &QQuick3DPrincipledMaterial::setAttenuationColor,
743 aiColorToQColor(attenuationColor));
751 result = source.Get(AI_MATKEY_REFRACTI, ior);
752 if (result == aiReturn_SUCCESS)
753 QSSGSceneDesc::setProperty(target,
755 &QQuick3DPrincipledMaterial::setIndexOfRefraction,
759 }
else if (type == QSSGSceneDesc::Material::RuntimeType::DefaultMaterial) {
760 int shadingModel = 0;
761 auto material = &source;
762 result = material->Get(AI_MATKEY_SHADING_MODEL, shadingModel);
764 if (result == aiReturn_SUCCESS && (shadingModel == aiShadingMode_NoShading))
765 QSSGSceneDesc::setProperty(target,
"lighting", &QQuick3DDefaultMaterial::setLighting, QQuick3DDefaultMaterial::Lighting::NoLighting);
767 if (
auto diffuseMapTexture = createTextureNode(source, aiTextureType_DIFFUSE, 0)) {
768 QSSGSceneDesc::setProperty(target,
"diffuseMap", &QQuick3DDefaultMaterial::setDiffuseMap, diffuseMapTexture);
772 aiColor3D diffuseColor;
773 result = material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor);
774 if (result == aiReturn_SUCCESS)
775 QSSGSceneDesc::setProperty(target,
"diffuseColor", &QQuick3DDefaultMaterial::setDiffuseColor, aiColorToQColor(diffuseColor));
778 if (
auto emissiveTexture = createTextureNode(source, aiTextureType_EMISSIVE, 0))
779 QSSGSceneDesc::setProperty(target,
"emissiveMap", &QQuick3DDefaultMaterial::setEmissiveMap, emissiveTexture);
782 if (
auto specularTexture = createTextureNode(source, aiTextureType_SPECULAR, 0))
783 QSSGSceneDesc::setProperty(target,
"specularMap", &QQuick3DDefaultMaterial::setSpecularMap, specularTexture);
787 result = material->Get(AI_MATKEY_OPACITY, opacity);
788 if (result == aiReturn_SUCCESS)
789 QSSGSceneDesc::setProperty(target,
"opacity", &QQuick3DDefaultMaterial::setOpacity,
float(opacity));
792 if (
auto opacityTexture = createTextureNode(source, aiTextureType_OPACITY, 0))
793 QSSGSceneDesc::setProperty(target,
"opacityMap", &QQuick3DDefaultMaterial::setOpacityMap, opacityTexture);
796 if (
auto bumpTexture = createTextureNode(source, aiTextureType_HEIGHT, 0)) {
797 QSSGSceneDesc::setProperty(target,
"bumpMap", &QQuick3DDefaultMaterial::setBumpMap, bumpTexture);
800 result = material->Get(AI_MATKEY_BUMPSCALING, bumpAmount);
801 if (result == aiReturn_SUCCESS)
802 QSSGSceneDesc::setProperty(target,
"bumpAmount", &QQuick3DDefaultMaterial::setBumpAmount,
float(bumpAmount));
806 if (
auto normalTexture = createTextureNode(source, aiTextureType_NORMALS, 0))
807 QSSGSceneDesc::setProperty(target,
"normalMap", &QQuick3DDefaultMaterial::setNormalMap, normalTexture);
808 }
else if (type == QSSGSceneDesc::Material::RuntimeType::SpecularGlossyMaterial) {
810 aiColor4D albedoFactor;
811 result = source.Get(AI_MATKEY_COLOR_DIFFUSE, albedoFactor);
812 if (result == aiReturn_SUCCESS)
813 QSSGSceneDesc::setProperty(target,
"albedoColor", &QQuick3DSpecularGlossyMaterial::setAlbedoColor, aiColorToQColor(albedoFactor));
816 if (
auto albedoTexture = createTextureNode(source, aiTextureType_DIFFUSE, 0)) {
817 QSSGSceneDesc::setProperty(target,
"albedoMap", &QQuick3DSpecularGlossyMaterial::setAlbedoMap, albedoTexture);
818 QSSGSceneDesc::setProperty(target,
"opacityChannel", &QQuick3DSpecularGlossyMaterial::setOpacityChannel, QQuick3DSpecularGlossyMaterial::TextureChannelMapping::A);
821 if (
auto specularGlossinessTexture = createTextureNode(source, aiTextureType_SPECULAR, 0)) {
822 QSSGSceneDesc::setProperty(target,
"specularMap", &QQuick3DSpecularGlossyMaterial::setSpecularMap, specularGlossinessTexture);
823 QSSGSceneDesc::setProperty(target,
"glossinessMap", &QQuick3DSpecularGlossyMaterial::setGlossinessMap, specularGlossinessTexture);
824 QSSGSceneDesc::setProperty(target,
"glossinessChannel", &QQuick3DSpecularGlossyMaterial::setGlossinessChannel, QQuick3DSpecularGlossyMaterial::TextureChannelMapping::A);
828 aiColor4D specularColorFactor;
829 result = source.Get(AI_MATKEY_COLOR_SPECULAR, specularColorFactor);
830 if (result == aiReturn_SUCCESS)
831 QSSGSceneDesc::setProperty(target,
"specularColor", &QQuick3DSpecularGlossyMaterial::setSpecularColor, aiColorToQColor(specularColorFactor));
835 ai_real glossinessFactor;
836 result = source.Get(AI_MATKEY_GLOSSINESS_FACTOR, glossinessFactor);
837 if (result == aiReturn_SUCCESS)
838 QSSGSceneDesc::setProperty(target,
"glossiness", &QQuick3DSpecularGlossyMaterial::setGlossiness,
float(glossinessFactor));
841 if (
auto normalTexture = createTextureNode(source, aiTextureType_NORMALS, 0)) {
842 QSSGSceneDesc::setProperty(target,
"normalMap", &QQuick3DSpecularGlossyMaterial::setNormalMap, normalTexture);
845 result = source.Get(AI_MATKEY_GLTF_TEXTURE_SCALE(aiTextureType_NORMALS, 0), normalScale);
846 if (result == aiReturn_SUCCESS)
847 QSSGSceneDesc::setProperty(target,
"normalStrength", &QQuick3DSpecularGlossyMaterial::setNormalStrength,
float(normalScale));
852 if (
auto occlusionTexture = createTextureNode(source, aiTextureType_LIGHTMAP, 0)) {
853 QSSGSceneDesc::setProperty(target,
"occlusionMap", &QQuick3DSpecularGlossyMaterial::setOcclusionMap, occlusionTexture);
854 QSSGSceneDesc::setProperty(target,
"occlusionChannel", &QQuick3DSpecularGlossyMaterial::setOcclusionChannel, QQuick3DSpecularGlossyMaterial::TextureChannelMapping::R);
856 ai_real occlusionAmount;
857 result = source.Get(AI_MATKEY_GLTF_TEXTURE_STRENGTH(aiTextureType_LIGHTMAP, 0), occlusionAmount);
858 if (result == aiReturn_SUCCESS)
859 QSSGSceneDesc::setProperty(target,
"occlusionAmount", &QQuick3DSpecularGlossyMaterial::setOcclusionAmount,
float(occlusionAmount));
863 if (
auto emissiveTexture = createTextureNode(source, aiTextureType_EMISSIVE, 0))
864 QSSGSceneDesc::setProperty(target,
"emissiveMap", &QQuick3DSpecularGlossyMaterial::setEmissiveMap, emissiveTexture);
867 aiColor3D emissiveColorFactor;
868 result = source.Get(AI_MATKEY_COLOR_EMISSIVE, emissiveColorFactor);
869 if (result == aiReturn_SUCCESS)
870 QSSGSceneDesc::setProperty(target,
"emissiveFactor", &QQuick3DSpecularGlossyMaterial::setEmissiveFactor, QVector3D { emissiveColorFactor.r, emissiveColorFactor.g, emissiveColorFactor.b });
875 result = source.Get(AI_MATKEY_TWOSIDED, isDoubleSided);
876 if (result == aiReturn_SUCCESS && isDoubleSided)
877 QSSGSceneDesc::setProperty(target,
"cullMode", &QQuick3DSpecularGlossyMaterial::setCullMode, QQuick3DSpecularGlossyMaterial::CullMode::NoCulling);
882 result = source.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode);
883 if (result == aiReturn_SUCCESS) {
884 auto mode = QQuick3DSpecularGlossyMaterial::AlphaMode::Default;
885 if (QByteArrayView(alphaMode.C_Str()) ==
"OPAQUE")
886 mode = QQuick3DSpecularGlossyMaterial::AlphaMode::Opaque;
887 else if (QByteArrayView(alphaMode.C_Str()) ==
"MASK")
888 mode = QQuick3DSpecularGlossyMaterial::AlphaMode::Mask;
889 else if (QByteArrayView(alphaMode.C_Str()) ==
"BLEND")
890 mode = QQuick3DSpecularGlossyMaterial::AlphaMode::Blend;
892 if (mode != QQuick3DSpecularGlossyMaterial::AlphaMode::Default) {
893 QSSGSceneDesc::setProperty(target,
"alphaMode", &QQuick3DSpecularGlossyMaterial::setAlphaMode, mode);
895 if (mode == QQuick3DSpecularGlossyMaterial::AlphaMode::Mask)
896 QSSGSceneDesc::setProperty(target,
"depthDrawMode", &QQuick3DSpecularGlossyMaterial::setDepthDrawMode, QQuick3DMaterial::OpaquePrePassDepthDraw);
903 result = source.Get(AI_MATKEY_GLTF_ALPHACUTOFF, alphaCutoff);
904 if (result == aiReturn_SUCCESS)
905 QSSGSceneDesc::setProperty(target,
"alphaCutoff", &QQuick3DSpecularGlossyMaterial::setAlphaCutoff,
float(alphaCutoff));
909 int shadingModel = 0;
910 result = source.Get(AI_MATKEY_SHADING_MODEL, shadingModel);
911 if (result == aiReturn_SUCCESS && shadingModel == aiShadingMode_Unlit)
912 QSSGSceneDesc::setProperty(target,
"lighting", &QQuick3DSpecularGlossyMaterial::setLighting, QQuick3DSpecularGlossyMaterial::Lighting::NoLighting);
920 ai_real clearcoatFactor = 0.0f;
921 result = source.Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoatFactor);
922 if (result == aiReturn_SUCCESS)
923 QSSGSceneDesc::setProperty(target,
925 &QQuick3DSpecularGlossyMaterial::setClearcoatAmount,
926 float(clearcoatFactor));
931 ai_real clearcoatRoughnessFactor = 0.0f;
932 result = source.Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoatRoughnessFactor);
933 if (result == aiReturn_SUCCESS)
934 QSSGSceneDesc::setProperty(target,
935 "clearcoatRoughnessAmount",
936 &QQuick3DSpecularGlossyMaterial::setClearcoatRoughnessAmount,
937 float(clearcoatRoughnessFactor));
941 if (
auto clearcoatTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_TEXTURE))
942 QSSGSceneDesc::setProperty(target,
"clearcoatMap", &QQuick3DSpecularGlossyMaterial::setClearcoatMap, clearcoatTexture);
945 if (
auto clearcoatRoughnessTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE))
946 QSSGSceneDesc::setProperty(target,
947 "clearcoatRoughnessMap",
948 &QQuick3DSpecularGlossyMaterial::setClearcoatRoughnessMap,
949 clearcoatRoughnessTexture);
952 if (
auto clearcoatNormalTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE))
953 QSSGSceneDesc::setProperty(target,
"clearcoatNormalMap", &QQuick3DSpecularGlossyMaterial::setClearcoatNormalMap, clearcoatNormalTexture);
960 ai_real transmissionFactor = 0.0f;
961 result = source.Get(AI_MATKEY_TRANSMISSION_FACTOR, transmissionFactor);
962 if (result == aiReturn_SUCCESS)
963 QSSGSceneDesc::setProperty(target,
964 "transmissionFactor",
965 &QQuick3DSpecularGlossyMaterial::setTransmissionFactor,
966 float(transmissionFactor));
971 if (
auto transmissionImage = createTextureNode(source, AI_MATKEY_TRANSMISSION_TEXTURE))
972 QSSGSceneDesc::setProperty(target,
974 &QQuick3DSpecularGlossyMaterial::setTransmissionMap,
984 ai_real thicknessFactor = 0.0f;
985 result = source.Get(AI_MATKEY_VOLUME_THICKNESS_FACTOR, thicknessFactor);
986 if (result == aiReturn_SUCCESS)
987 QSSGSceneDesc::setProperty(target,
"thicknessFactor", &QQuick3DSpecularGlossyMaterial::setThicknessFactor,
float(thicknessFactor));
992 if (
auto thicknessImage = createTextureNode(source, AI_MATKEY_VOLUME_THICKNESS_TEXTURE))
993 QSSGSceneDesc::setProperty(target,
"thicknessMap", &QQuick3DSpecularGlossyMaterial::setThicknessMap, thicknessImage);
998 ai_real attenuationDistance = 0.0f;
999 result = source.Get(AI_MATKEY_VOLUME_ATTENUATION_DISTANCE, attenuationDistance);
1000 if (result == aiReturn_SUCCESS)
1001 QSSGSceneDesc::setProperty(target,
1002 "attenuationDistance",
1003 &QQuick3DSpecularGlossyMaterial::setAttenuationDistance,
1004 float(attenuationDistance));
1009 aiColor3D attenuationColor;
1010 result = source.Get(AI_MATKEY_VOLUME_ATTENUATION_COLOR, attenuationColor);
1011 if (result == aiReturn_SUCCESS)
1012 QSSGSceneDesc::setProperty(target,
1014 &QQuick3DSpecularGlossyMaterial::setAttenuationColor,
1015 aiColorToQColor(attenuationColor));
1676static QString
importImp(
const QUrl &url,
const QJsonObject &options, QSSGSceneDesc::Scene &targetScene)
1678 auto filePath = url.path();
1680 const bool maybeLocalFile = QQmlFile::isLocalFile(url);
1681 if (maybeLocalFile && !QFileInfo::exists(filePath))
1682 filePath = QQmlFile::urlToLocalFileOrQrc(url);
1684 auto sourceFile = QFileInfo(filePath);
1685 if (!sourceFile.exists())
1686 return QLatin1String(
"File not found");
1687 targetScene.sourceDir = sourceFile.path();
1689 std::unique_ptr<Assimp::Importer> importer(
new Assimp::Importer());
1692 aiPostProcessSteps postProcessSteps;
1693 if (options.isEmpty())
1696 postProcessSteps = processOptions(options, importer);
1699 importer->SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE);
1700 importer->SetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES, 1);
1702 if (filePath.startsWith(
":"))
1703 importer->SetIOHandler(
new ResourceIOSystem);
1705 auto sourceScene = importer->ReadFile(filePath.toStdString(), postProcessSteps);
1708 return QString::fromLocal8Bit(importer->GetErrorString());
1714 targetScene.id = sourceFile.canonicalFilePath();
1717 using It =
decltype(sourceScene->mNumMeshes);
1721 const auto &srcRootNode = *sourceScene->mRootNode;
1725 AnimationNodeMap animatingNodes;
1727 if (sourceScene->HasLights()) {
1728 for (It i = 0, end = sourceScene->mNumLights; i != end; ++i) {
1729 const auto &type = *sourceScene->mLights[i];
1730 if (
auto node = srcRootNode.FindNode(type.mName))
1731 nodeMap[node] = { i, NodeInfo::Type::Light };
1735 if (sourceScene->HasCameras()) {
1736 for (It i = 0, end = sourceScene->mNumCameras; i != end; ++i) {
1737 const auto &srcCam = *sourceScene->mCameras[i];
1738 if (
auto node = srcRootNode.FindNode(srcCam.mName))
1739 nodeMap[node] = { i, NodeInfo::Type::Camera };
1743 if (sourceScene->HasAnimations()) {
1744 for (It i = 0, end = sourceScene->mNumAnimations; i != end; ++i) {
1745 const auto &srcAnim = *sourceScene->mAnimations[i];
1746 const auto channelCount = srcAnim.mNumChannels;
1747 for (It cIdx = 0; cIdx != channelCount; ++cIdx) {
1748 const auto &srcChannel = srcAnim.mChannels[cIdx];
1749 const auto &nodeName = srcChannel->mNodeName;
1750 if (nodeName.length > 0) {
1752 QByteArray name(nodeName.C_Str(), qsizetype(nodeName.length));
1753 if (!animatingNodes.contains(name))
1754 animatingNodes.insert(name,
nullptr);
1757 const auto morphChannelCount = srcAnim.mNumMorphMeshChannels;
1758 for (It cIdx = 0; cIdx != morphChannelCount; ++cIdx) {
1759 const auto &srcChannel = srcAnim.mMorphMeshChannels[cIdx];
1760 const auto &nodeName = srcChannel->mName;
1761 if (nodeName.length > 0) {
1762 const auto morphKeys = srcChannel->mKeys;
1763 const auto numMorphTargets = qMin(morphKeys[0].mNumValuesAndWeights, 8U);
1765 for (It j = 0; j < numMorphTargets; ++j) {
1766 QString morphTargetName(nodeName.C_Str());
1767 morphTargetName += QStringLiteral(
"_morph") + QString::number(j);
1768 animatingNodes.insert(morphTargetName.toUtf8(),
nullptr);
1777 const auto materialCount = sourceScene->mNumMaterials;
1778 SceneInfo::MaterialMap materials;
1779 materials.reserve(materialCount);
1781 const auto meshCount = sourceScene->mNumMeshes;
1782 SceneInfo::MeshMap meshes;
1783 meshes.reserve(meshCount);
1784 SceneInfo::Mesh2SkinMap mesh2skin;
1785 mesh2skin.reserve(meshCount);
1787 const auto embeddedTextureCount = sourceScene->mNumTextures;
1788 SceneInfo::EmbeddedTextureMap embeddedTextures;
1790 SceneInfo::SkinMap skins;
1792 for (It i = 0; i != materialCount; ++i)
1793 materials.push_back({sourceScene->mMaterials[i],
nullptr});
1795 for (It i = 0; i != meshCount; ++i) {
1796 meshes.push_back({sourceScene->mMeshes[i],
nullptr});
1797 if (sourceScene->mMeshes[i]->HasBones()) {
1798 mesh2skin.push_back(skins.size());
1799 const auto boneCount = sourceScene->mMeshes[i]->mNumBones;
1800 auto bones = sourceScene->mMeshes[i]->mBones;
1806 for (It j = 0; j != boneCount; ++j) {
1807 const auto &nodeName = bones[j]->mName;
1808 if (nodeName.length > 0) {
1809 animatingNodes.insert(QByteArray{ nodeName.C_Str(),
1810 qsizetype(nodeName.length) },
1815 mesh2skin.push_back(-1);
1819 for (It i = 0; i != embeddedTextureCount; ++i)
1820 embeddedTextures.push_back(
nullptr);
1822 SceneInfo::TextureMap textureMap;
1824 if (!targetScene.root) {
1825 auto root =
new QSSGSceneDesc::Node(QSSGSceneDesc::Node::Type::Transform, QSSGSceneDesc::Node::RuntimeType::Node);
1826 QSSGSceneDesc::addNode(targetScene, *root);
1830 auto opt = processSceneOptions(options);
1832 const auto extension = sourceFile.suffix().toLower();
1833 if (extension == QStringLiteral(
"gltf") || extension == QStringLiteral(
"glb"))
1834 opt.gltfMode =
true;
1835 else if (extension == QStringLiteral(
"fbx"))
1838 SceneInfo sceneInfo { *sourceScene, materials, meshes, embeddedTextures,
1839 textureMap, skins, mesh2skin, sourceFile.dir(), opt };
1841 if (!qFuzzyCompare(opt.globalScaleValue, 1.0f) && !qFuzzyCompare(opt.globalScaleValue, 0.0f)) {
1842 const auto gscale = opt.globalScaleValue;
1843 QSSGSceneDesc::setProperty(*targetScene.root,
"scale", &QQuick3DNode::setScale, QVector3D { gscale, gscale, gscale });
1847 if (sourceScene->mRootNode)
1848 processNode(sceneInfo, *sourceScene->mRootNode, *targetScene.root, nodeMap, animatingNodes);
1850 for (It i = 0, endI = skins.size(); i != endI; ++i) {
1851 const auto &skin = skins[i];
1857 QList<QMatrix4x4> inverseBindPoses;
1858 QVarLengthArray<QSSGSceneDesc::Node *> joints;
1859 joints.reserve(skin.mNumBones);
1860 for (It j = 0, endJ = skin.mNumBones; j != endJ; ++j) {
1861 const auto &bone = *skin.mBones[j];
1862 const auto &nodeName = bone.mName;
1863 if (nodeName.length > 0) {
1864 auto targetNode = animatingNodes.value(QByteArray{ nodeName.C_Str(), qsizetype(nodeName.length) });
1865 joints.push_back(targetNode);
1866 const auto &osMat = bone.mOffsetMatrix;
1867 auto pose = QMatrix4x4(osMat[0][0], osMat[0][1], osMat[0][2], osMat[0][3],
1868 osMat[1][0], osMat[1][1], osMat[1][2], osMat[1][3],
1869 osMat[2][0], osMat[2][1], osMat[2][2], osMat[2][3],
1870 osMat[3][0], osMat[3][1], osMat[3][2], osMat[3][3]);
1871 inverseBindPoses.push_back(pose);
1874 QSSGSceneDesc::setProperty(*skin.node,
"joints", &QQuick3DSkin::joints, joints);
1875 QSSGSceneDesc::setProperty(*skin.node,
"inverseBindPoses", &QQuick3DSkin::setInverseBindPoses, inverseBindPoses);
1878 static const auto fuzzyComparePos = [](
const aiVectorKey *pos,
const aiVectorKey *prev){
1881 return qFuzzyCompare(pos->mValue.x, prev->mValue.x)
1882 && qFuzzyCompare(pos->mValue.y, prev->mValue.y)
1883 && qFuzzyCompare(pos->mValue.z, prev->mValue.z);
1886 static const auto fuzzyCompareRot = [](
const aiQuatKey *rot,
const aiQuatKey *prev){
1889 return qFuzzyCompare(rot->mValue.x, prev->mValue.x)
1890 && qFuzzyCompare(rot->mValue.y, prev->mValue.y)
1891 && qFuzzyCompare(rot->mValue.z, prev->mValue.z)
1892 && qFuzzyCompare(rot->mValue.w, prev->mValue.w);
1895 static const auto createAnimation = [](QSSGSceneDesc::Scene &targetScene,
const aiAnimation &srcAnim,
const AnimationNodeMap &animatingNodes) {
1896 using namespace QSSGSceneDesc;
1897 Animation targetAnimation;
1898 auto &channels = targetAnimation.channels;
1899 qreal freq = qFuzzyIsNull(srcAnim.mTicksPerSecond) ? 1.0
1900 : 1000.0 / srcAnim.mTicksPerSecond;
1901 targetAnimation.framesPerSecond = srcAnim.mTicksPerSecond;
1902 targetAnimation.name = fromAiString(srcAnim.mName);
1904 for (It i = 0, end = srcAnim.mNumChannels; i != end; ++i) {
1905 const auto &srcChannel = *srcAnim.mChannels[i];
1907 const auto &nodeName = srcChannel.mNodeName;
1908 if (nodeName.length > 0) {
1909 const auto aNodeEnd = animatingNodes.cend();
1910 const auto aNodeIt = animatingNodes.constFind(QByteArray{ nodeName.C_Str(), qsizetype(nodeName.length) });
1911 if (aNodeIt != aNodeEnd && aNodeIt.value() !=
nullptr) {
1912 auto targetNode = aNodeIt.value();
1915 const auto currentPropertyValue = [targetNode](
const char *propertyName) -> QVariant {
1916 for (
auto *p : targetNode->properties) {
1917 if (!qstrcmp(propertyName, p->name))
1924 const auto posKeyEnd = srcChannel.mNumPositionKeys;
1925 Animation::Channel targetChannel;
1926 targetChannel.targetProperty = Animation::Channel::TargetProperty::Position;
1927 targetChannel.target = targetNode;
1928 const aiVectorKey *prevPos =
nullptr;
1929 for (It posKeyIdx = 0; posKeyIdx != posKeyEnd; ++posKeyIdx) {
1930 const auto &posKey = srcChannel.mPositionKeys[posKeyIdx];
1931 if (fuzzyComparePos(&posKey, prevPos))
1933 targetChannel.keys.push_back(
new Animation::KeyPosition(toAnimationKey(posKey, freq)));
1937 const auto isUnchanged = [&targetChannel, currentPropertyValue]() {
1938 if (targetChannel.keys.count() != 1)
1940 auto currentPos = currentPropertyValue(
"position").value<QVector3D>();
1941 return qFuzzyCompare(targetChannel.keys[0]->value.toVector3D(), currentPos);
1943 if (!targetChannel.keys.isEmpty()) {
1944 if (!isUnchanged()) {
1945 channels.push_back(
new Animation::Channel(targetChannel));
1946 float endTime =
float(srcChannel.mPositionKeys[posKeyEnd - 1].mTime) * freq;
1947 if (targetAnimation.length < endTime)
1948 targetAnimation.length = endTime;
1951 qDeleteAll(targetChannel.keys);
1957 const auto rotKeyEnd = srcChannel.mNumRotationKeys;
1958 Animation::Channel targetChannel;
1959 targetChannel.targetProperty = Animation::Channel::TargetProperty::Rotation;
1960 targetChannel.target = targetNode;
1961 const aiQuatKey *prevRot =
nullptr;
1962 for (It rotKeyIdx = 0; rotKeyIdx != rotKeyEnd; ++rotKeyIdx) {
1963 const auto &rotKey = srcChannel.mRotationKeys[rotKeyIdx];
1964 if (fuzzyCompareRot(&rotKey, prevRot))
1966 targetChannel.keys.push_back(
new Animation::KeyPosition(toAnimationKey(rotKey, freq)));
1970 const auto isUnchanged = [&targetChannel, currentPropertyValue]() {
1971 if (targetChannel.keys.count() != 1)
1973 auto currentVal = currentPropertyValue(
"rotation");
1974 QQuaternion rot = currentVal.isValid() ? currentVal.value<QQuaternion>() : QQuaternion{};
1975 return qFuzzyCompare(QQuaternion(targetChannel.keys[0]->value), rot);
1977 if (!targetChannel.keys.isEmpty()) {
1978 if (!isUnchanged()) {
1979 channels.push_back(
new Animation::Channel(targetChannel));
1980 float endTime =
float(srcChannel.mRotationKeys[rotKeyEnd - 1].mTime) * freq;
1981 if (targetAnimation.length < endTime)
1982 targetAnimation.length = endTime;
1985 qDeleteAll(targetChannel.keys);
1991 const auto scaleKeyEnd = srcChannel.mNumScalingKeys;
1992 Animation::Channel targetChannel;
1993 targetChannel.targetProperty = Animation::Channel::TargetProperty::Scale;
1994 targetChannel.target = targetNode;
1995 const aiVectorKey *prevScale =
nullptr;
1996 for (It scaleKeyIdx = 0; scaleKeyIdx != scaleKeyEnd; ++scaleKeyIdx) {
1997 const auto &scaleKey = srcChannel.mScalingKeys[scaleKeyIdx];
1998 if (fuzzyComparePos(&scaleKey, prevScale))
2000 targetChannel.keys.push_back(
new Animation::KeyPosition(toAnimationKey(scaleKey, freq)));
2001 prevScale = &scaleKey;
2004 const auto isUnchanged = [&targetChannel, currentPropertyValue]() {
2005 if (targetChannel.keys.count() != 1)
2007 auto currentVal = currentPropertyValue(
"scale");
2008 QVector3D scale = currentVal.isValid() ? currentVal.value<QVector3D>() : QVector3D{ 1, 1, 1 };
2009 return qFuzzyCompare(targetChannel.keys[0]->value.toVector3D(), scale);
2012 if (!targetChannel.keys.isEmpty()) {
2013 if (!isUnchanged()) {
2014 channels.push_back(
new Animation::Channel(targetChannel));
2015 float endTime =
float(srcChannel.mScalingKeys[scaleKeyEnd - 1].mTime) * freq;
2016 if (targetAnimation.length < endTime)
2017 targetAnimation.length = endTime;
2020 qDeleteAll(targetChannel.keys);
2028 for (It i = 0, end = srcAnim.mNumMorphMeshChannels; i != end; ++i) {
2029 const auto &srcMorphChannel = *srcAnim.mMorphMeshChannels[i];
2030 const QString nodeName(srcMorphChannel.mName.C_Str());
2031 const auto *morphKeys = srcMorphChannel.mKeys;
2032 const auto numMorphTargets = qMin(morphKeys[0].mNumValuesAndWeights, 8U);
2033 for (It targetId = 0; targetId != numMorphTargets; ++targetId) {
2034 QString morphTargetName = nodeName + QStringLiteral(
"_morph") + QString::number(targetId);
2035 const auto aNodeEnd = animatingNodes.cend();
2036 const auto aNodeIt = animatingNodes.constFind(morphTargetName.toUtf8());
2037 if (aNodeIt != aNodeEnd && aNodeIt.value() !=
nullptr) {
2038 auto targetNode = aNodeIt.value();
2039 const auto weightKeyEnd = srcMorphChannel.mNumKeys;
2040 Animation::Channel targetChannel;
2041 targetChannel.targetProperty = Animation::Channel::TargetProperty::Weight;
2042 targetChannel.target = targetNode;
2043 for (It wId = 0; wId != weightKeyEnd; ++wId) {
2044 const auto &weightKey = srcMorphChannel.mKeys[wId];
2045 const auto animationKey =
new Animation::KeyPosition(toAnimationKey(weightKey, freq, targetId));
2046 targetChannel.keys.push_back(animationKey);
2048 if (!targetChannel.keys.isEmpty()) {
2049 channels.push_back(
new Animation::Channel(targetChannel));
2050 float endTime =
float(srcMorphChannel.mKeys[weightKeyEnd - 1].mTime) * freq;
2051 if (targetAnimation.length < endTime)
2052 targetAnimation.length = endTime;
2059 if (!targetAnimation.channels.isEmpty())
2060 targetScene.animations.push_back(
new Animation(targetAnimation));
2064 if (sourceScene->HasAnimations()) {
2065 const auto animationCount = sourceScene->mNumAnimations;
2066 targetScene.animations.reserve(animationCount);
2067 for (It i = 0, end = animationCount; i != end; ++i) {
2068 const auto &srcAnim = *sourceScene->mAnimations[i];
2069 createAnimation(targetScene, srcAnim, animatingNodes);
2078 QSSGQmlUtilities::applyEdit(&targetScene, options);