453static void setMaterialProperties(QSSGSceneDesc::Material &target,
const aiMaterial &source,
const SceneInfo &sceneInfo, QSSGSceneDesc::Material::RuntimeType type)
455 if (target.name.isNull()) {
456 aiString materialName = source.GetName();
457 if (materialName.length > 0)
458 target.name = fromAiString(materialName);
460 target.name = QSSGQmlUtilities::getQmlElementName(target);
463 const auto createTextureNode = [&sceneInfo, &target](
const aiMaterial &material, aiTextureType textureType,
unsigned int index) {
464 const auto &srcScene = sceneInfo
.scene;
465 QSSGSceneDesc::Texture *tex =
nullptr;
466 aiString texturePath;
469 if (material.GetTexture(textureType, index, &texturePath, &texInfo
.mapping, &texInfo
.uvIndex,
nullptr,
nullptr, texInfo
.modes) == aiReturn_SUCCESS) {
470 if (texturePath.length > 0) {
471 aiUVTransform transform;
472 if (material.Get(AI_MATKEY_UVTRANSFORM(textureType, index), transform) == aiReturn_SUCCESS)
473 texInfo.transform = transform;
475 material.Get(AI_MATKEY_UVWSRC(textureType, index), texInfo
.uvIndex);
476 material.Get(AI_MATKEY_GLTF_MAPPINGFILTER_MIN(textureType, index), texInfo
.minFilter);
477 material.Get(AI_MATKEY_GLTF_MAPPINGFILTER_MAG(textureType, index), texInfo
.magFilter);
479 auto &textureMap = sceneInfo.textureMap;
481 QByteArray texName = QByteArray(texturePath.C_Str(), texturePath.length);
483 const auto it = textureMap.constFind(
TextureEntry{texName, texInfo});
484 if (it != textureMap.cend()) {
485 Q_ASSERT(it->texture);
490 tex =
new QSSGSceneDesc::Texture(QSSGSceneDesc::Texture::RuntimeType::Image2D, texName);
491 textureMap.insert(TextureEntry{fromAiString(texturePath), texInfo, tex});
492 QSSGSceneDesc::addNode(target, *tex);
493 setTextureProperties(*tex, texInfo, sceneInfo);
495 auto aEmbeddedTex = srcScene.GetEmbeddedTextureAndIndex(texturePath.C_Str());
496 const auto &embeddedTexId = aEmbeddedTex.second;
497 if (embeddedTexId > -1) {
498 QSSGSceneDesc::TextureData *textureData =
nullptr;
499 auto &embeddedTextures = sceneInfo.embeddedTextureMap;
500 textureData = embeddedTextures[embeddedTexId];
502 const auto *sourceTexture = aEmbeddedTex.first;
503 Q_ASSERT(sourceTexture->pcData);
505 const bool isCompressed = (sourceTexture->mHeight == 0);
508 const qsizetype asize = (isCompressed) ? sourceTexture->mWidth : (sourceTexture->mHeight * sourceTexture->mWidth) *
sizeof(aiTexel);
509 const QSize size = (!isCompressed) ? QSize(
int(sourceTexture->mWidth),
int(sourceTexture->mHeight)) : QSize();
510 QByteArray imageData {
reinterpret_cast<
const char *>(sourceTexture->pcData), asize };
511 const auto format = (isCompressed) ? QByteArray(sourceTexture->achFormatHint) : QByteArrayLiteral(
"rgba8888");
512 const quint8 flags = isCompressed ? quint8(QSSGSceneDesc::TextureData::Flags::Compressed) : 0;
513 textureData =
new QSSGSceneDesc::TextureData(imageData, size, format, flags);
514 QSSGSceneDesc::addNode(*tex, *textureData);
515 embeddedTextures[embeddedTexId] = textureData;
519 QSSGSceneDesc::setProperty(*tex,
"textureData", &QQuick3DTexture::setTextureData, textureData);
521 auto relativePath = QString::fromUtf8(texturePath.C_Str());
524 relativePath.replace(
"\\",
"/");
525 const auto path = sceneInfo.workingDir.absoluteFilePath(relativePath);
526 QSSGSceneDesc::setProperty(*tex,
"source", &QQuick3DTexture::setSource, QUrl{ path });
537 if (type == QSSGSceneDesc::Material::RuntimeType::PrincipledMaterial) {
539 aiColor4D baseColorFactor;
540 result = source.Get(AI_MATKEY_BASE_COLOR, baseColorFactor);
541 if (result == aiReturn_SUCCESS) {
543 const QColor sRGBBaseColorFactor = QSSGUtils::color::linearTosRGB(QVector4D(baseColorFactor.r, baseColorFactor.g, baseColorFactor.b, baseColorFactor.a));
544 QSSGSceneDesc::setProperty(target,
"baseColor", &QQuick3DPrincipledMaterial::setBaseColor, sRGBBaseColorFactor);
547 aiColor3D diffuseColor;
548 result = source.Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor);
549 if (result == aiReturn_SUCCESS)
550 QSSGSceneDesc::setProperty(target,
"baseColor", &QQuick3DPrincipledMaterial::setBaseColor, aiColorToQColor(diffuseColor));
554 if (
auto baseColorTexture = createTextureNode(source, AI_MATKEY_BASE_COLOR_TEXTURE)) {
555 QSSGSceneDesc::setProperty(target,
"baseColorMap", &QQuick3DPrincipledMaterial::setBaseColorMap, baseColorTexture);
556 QSSGSceneDesc::setProperty(target,
"opacityChannel", &QQuick3DPrincipledMaterial::setOpacityChannel, QQuick3DPrincipledMaterial::TextureChannelMapping::A);
557 }
else if (
auto diffuseMapTexture = createTextureNode(source, aiTextureType_DIFFUSE, 0)) {
559 QSSGSceneDesc::setProperty(target,
"baseColorMap", &QQuick3DPrincipledMaterial::setBaseColorMap, diffuseMapTexture);
562 if (
auto metalicRoughnessTexture = createTextureNode(source, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE)) {
563 QSSGSceneDesc::setProperty(target,
"metalnessMap", &QQuick3DPrincipledMaterial::setMetalnessMap, metalicRoughnessTexture);
564 QSSGSceneDesc::setProperty(target,
"metalnessChannel", &QQuick3DPrincipledMaterial::setMetalnessChannel, QQuick3DPrincipledMaterial::TextureChannelMapping::B);
565 QSSGSceneDesc::setProperty(target,
"roughnessMap", &QQuick3DPrincipledMaterial::setRoughnessMap, metalicRoughnessTexture);
566 QSSGSceneDesc::setProperty(target,
"roughnessChannel", &QQuick3DPrincipledMaterial::setRoughnessChannel, QQuick3DPrincipledMaterial::TextureChannelMapping::G);
570 ai_real metallicFactor;
571 result = source.Get(AI_MATKEY_METALLIC_FACTOR, metallicFactor);
572 if (result == aiReturn_SUCCESS)
573 QSSGSceneDesc::setProperty(target,
"metalness", &QQuick3DPrincipledMaterial::setMetalness,
float(metallicFactor));
577 ai_real roughnessFactor;
578 result = source.Get(AI_MATKEY_ROUGHNESS_FACTOR, roughnessFactor);
579 if (result == aiReturn_SUCCESS)
580 QSSGSceneDesc::setProperty(target,
"roughness", &QQuick3DPrincipledMaterial::setRoughness,
float(roughnessFactor));
583 if (
auto normalTexture = createTextureNode(source, aiTextureType_NORMALS, 0)) {
584 QSSGSceneDesc::setProperty(target,
"normalMap", &QQuick3DPrincipledMaterial::setNormalMap, normalTexture);
587 result = source.Get(AI_MATKEY_GLTF_TEXTURE_SCALE(aiTextureType_NORMALS, 0), normalScale);
588 if (result == aiReturn_SUCCESS)
589 QSSGSceneDesc::setProperty(target,
"normalStrength", &QQuick3DPrincipledMaterial::setNormalStrength,
float(normalScale));
594 if (
auto occlusionTexture = createTextureNode(source, aiTextureType_LIGHTMAP, 0)) {
595 QSSGSceneDesc::setProperty(target,
"occlusionMap", &QQuick3DPrincipledMaterial::setOcclusionMap, occlusionTexture);
596 QSSGSceneDesc::setProperty(target,
"occlusionChannel", &QQuick3DPrincipledMaterial::setOcclusionChannel, QQuick3DPrincipledMaterial::TextureChannelMapping::R);
598 ai_real occlusionAmount;
599 result = source.Get(AI_MATKEY_GLTF_TEXTURE_STRENGTH(aiTextureType_LIGHTMAP, 0), occlusionAmount);
600 if (result == aiReturn_SUCCESS)
601 QSSGSceneDesc::setProperty(target,
"occlusionAmount", &QQuick3DPrincipledMaterial::setOcclusionAmount,
float(occlusionAmount));
605 if (
auto emissiveTexture = createTextureNode(source, aiTextureType_EMISSIVE, 0))
606 QSSGSceneDesc::setProperty(target,
"emissiveMap", &QQuick3DPrincipledMaterial::setEmissiveMap, emissiveTexture);
609 aiColor3D emissiveColorFactor;
610 result = source.Get(AI_MATKEY_COLOR_EMISSIVE, emissiveColorFactor);
611 if (result == aiReturn_SUCCESS)
612 QSSGSceneDesc::setProperty(target,
"emissiveFactor", &QQuick3DPrincipledMaterial::setEmissiveFactor, QVector3D { emissiveColorFactor.r, emissiveColorFactor.g, emissiveColorFactor.b });
617 result = source.Get(AI_MATKEY_TWOSIDED, isDoubleSided);
618 if (result == aiReturn_SUCCESS && isDoubleSided)
619 QSSGSceneDesc::setProperty(target,
"cullMode", &QQuick3DPrincipledMaterial::setCullMode, QQuick3DPrincipledMaterial::CullMode::NoCulling);
624 result = source.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode);
625 if (result == aiReturn_SUCCESS) {
626 auto mode = QQuick3DPrincipledMaterial::AlphaMode::Default;
627 if (QByteArrayView(alphaMode.C_Str()) ==
"OPAQUE")
628 mode = QQuick3DPrincipledMaterial::AlphaMode::Opaque;
629 else if (QByteArrayView(alphaMode.C_Str()) ==
"MASK")
630 mode = QQuick3DPrincipledMaterial::AlphaMode::Mask;
631 else if (QByteArrayView(alphaMode.C_Str()) ==
"BLEND")
632 mode = QQuick3DPrincipledMaterial::AlphaMode::Blend;
634 if (mode != QQuick3DPrincipledMaterial::AlphaMode::Default) {
635 QSSGSceneDesc::setProperty(target,
"alphaMode", &QQuick3DPrincipledMaterial::setAlphaMode, mode);
637 if (mode == QQuick3DPrincipledMaterial::AlphaMode::Mask)
638 QSSGSceneDesc::setProperty(target,
"depthDrawMode", &QQuick3DPrincipledMaterial::setDepthDrawMode, QQuick3DMaterial::OpaquePrePassDepthDraw);
645 result = source.Get(AI_MATKEY_GLTF_ALPHACUTOFF, alphaCutoff);
646 if (result == aiReturn_SUCCESS)
647 QSSGSceneDesc::setProperty(target,
"alphaCutoff", &QQuick3DPrincipledMaterial::setAlphaCutoff,
float(alphaCutoff));
651 int shadingModel = 0;
652 result = source.Get(AI_MATKEY_SHADING_MODEL, shadingModel);
653 if (result == aiReturn_SUCCESS && shadingModel == aiShadingMode_Unlit)
654 QSSGSceneDesc::setProperty(target,
"lighting", &QQuick3DPrincipledMaterial::setLighting, QQuick3DPrincipledMaterial::Lighting::NoLighting);
662 ai_real clearcoatFactor = 0.0f;
663 result = source.Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoatFactor);
664 if (result == aiReturn_SUCCESS)
665 QSSGSceneDesc::setProperty(target,
667 &QQuick3DPrincipledMaterial::setClearcoatAmount,
668 float(clearcoatFactor));
673 ai_real clearcoatRoughnessFactor = 0.0f;
674 result = source.Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoatRoughnessFactor);
675 if (result == aiReturn_SUCCESS)
676 QSSGSceneDesc::setProperty(target,
677 "clearcoatRoughnessAmount",
678 &QQuick3DPrincipledMaterial::setClearcoatRoughnessAmount,
679 float(clearcoatRoughnessFactor));
683 if (
auto clearcoatTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_TEXTURE))
684 QSSGSceneDesc::setProperty(target,
"clearcoatMap", &QQuick3DPrincipledMaterial::setClearcoatMap, clearcoatTexture);
687 if (
auto clearcoatRoughnessTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE))
688 QSSGSceneDesc::setProperty(target,
689 "clearcoatRoughnessMap",
690 &QQuick3DPrincipledMaterial::setClearcoatRoughnessMap,
691 clearcoatRoughnessTexture);
694 if (
auto clearcoatNormalTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE))
695 QSSGSceneDesc::setProperty(target,
"clearcoatNormalMap", &QQuick3DPrincipledMaterial::setClearcoatNormalMap, clearcoatNormalTexture);
702 ai_real transmissionFactor = 0.0f;
703 result = source.Get(AI_MATKEY_TRANSMISSION_FACTOR, transmissionFactor);
704 if (result == aiReturn_SUCCESS)
705 QSSGSceneDesc::setProperty(target,
706 "transmissionFactor",
707 &QQuick3DPrincipledMaterial::setTransmissionFactor,
708 float(transmissionFactor));
713 if (
auto transmissionImage = createTextureNode(source, AI_MATKEY_TRANSMISSION_TEXTURE))
714 QSSGSceneDesc::setProperty(target,
716 &QQuick3DPrincipledMaterial::setTransmissionMap,
726 ai_real thicknessFactor = 0.0f;
727 result = source.Get(AI_MATKEY_VOLUME_THICKNESS_FACTOR, thicknessFactor);
728 if (result == aiReturn_SUCCESS)
729 QSSGSceneDesc::setProperty(target,
"thicknessFactor", &QQuick3DPrincipledMaterial::setThicknessFactor,
float(thicknessFactor));
734 if (
auto thicknessImage = createTextureNode(source, AI_MATKEY_VOLUME_THICKNESS_TEXTURE))
735 QSSGSceneDesc::setProperty(target,
"thicknessMap", &QQuick3DPrincipledMaterial::setThicknessMap, thicknessImage);
740 ai_real attenuationDistance = 0.0f;
741 result = source.Get(AI_MATKEY_VOLUME_ATTENUATION_DISTANCE, attenuationDistance);
742 if (result == aiReturn_SUCCESS)
743 QSSGSceneDesc::setProperty(target,
744 "attenuationDistance",
745 &QQuick3DPrincipledMaterial::setAttenuationDistance,
746 float(attenuationDistance));
751 aiColor3D attenuationColor;
752 result = source.Get(AI_MATKEY_VOLUME_ATTENUATION_COLOR, attenuationColor);
753 if (result == aiReturn_SUCCESS)
754 QSSGSceneDesc::setProperty(target,
756 &QQuick3DPrincipledMaterial::setAttenuationColor,
757 aiColorToQColor(attenuationColor));
765 result = source.Get(AI_MATKEY_REFRACTI, ior);
766 if (result == aiReturn_SUCCESS)
767 QSSGSceneDesc::setProperty(target,
769 &QQuick3DPrincipledMaterial::setIndexOfRefraction,
775 ai_real opacity = 1.0f;
776 result = source.Get(AI_MATKEY_OPACITY, opacity);
777 if (result == aiReturn_SUCCESS)
778 QSSGSceneDesc::setProperty(target,
"opacity", &QQuick3DPrincipledMaterial::setOpacity,
float(opacity));
781 if (
auto opacityTexture = createTextureNode(source, aiTextureType_OPACITY, 0))
782 QSSGSceneDesc::setProperty(target,
"opacityMap", &QQuick3DPrincipledMaterial::setOpacityMap, opacityTexture);
785 }
else if (type == QSSGSceneDesc::Material::RuntimeType::DefaultMaterial) {
786 int shadingModel = 0;
787 auto material = &source;
788 result = material->Get(AI_MATKEY_SHADING_MODEL, shadingModel);
790 if (result == aiReturn_SUCCESS && (shadingModel == aiShadingMode_NoShading))
791 QSSGSceneDesc::setProperty(target,
"lighting", &QQuick3DDefaultMaterial::setLighting, QQuick3DDefaultMaterial::Lighting::NoLighting);
793 if (
auto diffuseMapTexture = createTextureNode(source, aiTextureType_DIFFUSE, 0)) {
794 QSSGSceneDesc::setProperty(target,
"diffuseMap", &QQuick3DDefaultMaterial::setDiffuseMap, diffuseMapTexture);
798 aiColor3D diffuseColor;
799 result = material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor);
800 if (result == aiReturn_SUCCESS)
801 QSSGSceneDesc::setProperty(target,
"diffuseColor", &QQuick3DDefaultMaterial::setDiffuseColor, aiColorToQColor(diffuseColor));
804 if (
auto emissiveTexture = createTextureNode(source, aiTextureType_EMISSIVE, 0))
805 QSSGSceneDesc::setProperty(target,
"emissiveMap", &QQuick3DDefaultMaterial::setEmissiveMap, emissiveTexture);
808 if (
auto specularTexture = createTextureNode(source, aiTextureType_SPECULAR, 0))
809 QSSGSceneDesc::setProperty(target,
"specularMap", &QQuick3DDefaultMaterial::setSpecularMap, specularTexture);
813 result = material->Get(AI_MATKEY_OPACITY, opacity);
814 if (result == aiReturn_SUCCESS)
815 QSSGSceneDesc::setProperty(target,
"opacity", &QQuick3DDefaultMaterial::setOpacity,
float(opacity));
818 if (
auto opacityTexture = createTextureNode(source, aiTextureType_OPACITY, 0))
819 QSSGSceneDesc::setProperty(target,
"opacityMap", &QQuick3DDefaultMaterial::setOpacityMap, opacityTexture);
822 if (
auto bumpTexture = createTextureNode(source, aiTextureType_HEIGHT, 0)) {
823 QSSGSceneDesc::setProperty(target,
"bumpMap", &QQuick3DDefaultMaterial::setBumpMap, bumpTexture);
826 result = material->Get(AI_MATKEY_BUMPSCALING, bumpAmount);
827 if (result == aiReturn_SUCCESS)
828 QSSGSceneDesc::setProperty(target,
"bumpAmount", &QQuick3DDefaultMaterial::setBumpAmount,
float(bumpAmount));
832 if (
auto normalTexture = createTextureNode(source, aiTextureType_NORMALS, 0))
833 QSSGSceneDesc::setProperty(target,
"normalMap", &QQuick3DDefaultMaterial::setNormalMap, normalTexture);
834 }
else if (type == QSSGSceneDesc::Material::RuntimeType::SpecularGlossyMaterial) {
836 aiColor4D albedoFactor;
837 result = source.Get(AI_MATKEY_COLOR_DIFFUSE, albedoFactor);
838 if (result == aiReturn_SUCCESS)
839 QSSGSceneDesc::setProperty(target,
"albedoColor", &QQuick3DSpecularGlossyMaterial::setAlbedoColor, aiColorToQColor(albedoFactor));
842 if (
auto albedoTexture = createTextureNode(source, aiTextureType_DIFFUSE, 0)) {
843 QSSGSceneDesc::setProperty(target,
"albedoMap", &QQuick3DSpecularGlossyMaterial::setAlbedoMap, albedoTexture);
844 QSSGSceneDesc::setProperty(target,
"opacityChannel", &QQuick3DSpecularGlossyMaterial::setOpacityChannel, QQuick3DSpecularGlossyMaterial::TextureChannelMapping::A);
847 if (
auto specularGlossinessTexture = createTextureNode(source, aiTextureType_SPECULAR, 0)) {
848 QSSGSceneDesc::setProperty(target,
"specularMap", &QQuick3DSpecularGlossyMaterial::setSpecularMap, specularGlossinessTexture);
849 QSSGSceneDesc::setProperty(target,
"glossinessMap", &QQuick3DSpecularGlossyMaterial::setGlossinessMap, specularGlossinessTexture);
850 QSSGSceneDesc::setProperty(target,
"glossinessChannel", &QQuick3DSpecularGlossyMaterial::setGlossinessChannel, QQuick3DSpecularGlossyMaterial::TextureChannelMapping::A);
854 aiColor4D specularColorFactor;
855 result = source.Get(AI_MATKEY_COLOR_SPECULAR, specularColorFactor);
856 if (result == aiReturn_SUCCESS)
857 QSSGSceneDesc::setProperty(target,
"specularColor", &QQuick3DSpecularGlossyMaterial::setSpecularColor, aiColorToQColor(specularColorFactor));
861 ai_real glossinessFactor;
862 result = source.Get(AI_MATKEY_GLOSSINESS_FACTOR, glossinessFactor);
863 if (result == aiReturn_SUCCESS)
864 QSSGSceneDesc::setProperty(target,
"glossiness", &QQuick3DSpecularGlossyMaterial::setGlossiness,
float(glossinessFactor));
867 if (
auto normalTexture = createTextureNode(source, aiTextureType_NORMALS, 0)) {
868 QSSGSceneDesc::setProperty(target,
"normalMap", &QQuick3DSpecularGlossyMaterial::setNormalMap, normalTexture);
871 result = source.Get(AI_MATKEY_GLTF_TEXTURE_SCALE(aiTextureType_NORMALS, 0), normalScale);
872 if (result == aiReturn_SUCCESS)
873 QSSGSceneDesc::setProperty(target,
"normalStrength", &QQuick3DSpecularGlossyMaterial::setNormalStrength,
float(normalScale));
878 if (
auto occlusionTexture = createTextureNode(source, aiTextureType_LIGHTMAP, 0)) {
879 QSSGSceneDesc::setProperty(target,
"occlusionMap", &QQuick3DSpecularGlossyMaterial::setOcclusionMap, occlusionTexture);
880 QSSGSceneDesc::setProperty(target,
"occlusionChannel", &QQuick3DSpecularGlossyMaterial::setOcclusionChannel, QQuick3DSpecularGlossyMaterial::TextureChannelMapping::R);
882 ai_real occlusionAmount;
883 result = source.Get(AI_MATKEY_GLTF_TEXTURE_STRENGTH(aiTextureType_LIGHTMAP, 0), occlusionAmount);
884 if (result == aiReturn_SUCCESS)
885 QSSGSceneDesc::setProperty(target,
"occlusionAmount", &QQuick3DSpecularGlossyMaterial::setOcclusionAmount,
float(occlusionAmount));
889 if (
auto emissiveTexture = createTextureNode(source, aiTextureType_EMISSIVE, 0))
890 QSSGSceneDesc::setProperty(target,
"emissiveMap", &QQuick3DSpecularGlossyMaterial::setEmissiveMap, emissiveTexture);
893 aiColor3D emissiveColorFactor;
894 result = source.Get(AI_MATKEY_COLOR_EMISSIVE, emissiveColorFactor);
895 if (result == aiReturn_SUCCESS)
896 QSSGSceneDesc::setProperty(target,
"emissiveFactor", &QQuick3DSpecularGlossyMaterial::setEmissiveFactor, QVector3D { emissiveColorFactor.r, emissiveColorFactor.g, emissiveColorFactor.b });
901 result = source.Get(AI_MATKEY_TWOSIDED, isDoubleSided);
902 if (result == aiReturn_SUCCESS && isDoubleSided)
903 QSSGSceneDesc::setProperty(target,
"cullMode", &QQuick3DSpecularGlossyMaterial::setCullMode, QQuick3DSpecularGlossyMaterial::CullMode::NoCulling);
908 result = source.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode);
909 if (result == aiReturn_SUCCESS) {
910 auto mode = QQuick3DSpecularGlossyMaterial::AlphaMode::Default;
911 if (QByteArrayView(alphaMode.C_Str()) ==
"OPAQUE")
912 mode = QQuick3DSpecularGlossyMaterial::AlphaMode::Opaque;
913 else if (QByteArrayView(alphaMode.C_Str()) ==
"MASK")
914 mode = QQuick3DSpecularGlossyMaterial::AlphaMode::Mask;
915 else if (QByteArrayView(alphaMode.C_Str()) ==
"BLEND")
916 mode = QQuick3DSpecularGlossyMaterial::AlphaMode::Blend;
918 if (mode != QQuick3DSpecularGlossyMaterial::AlphaMode::Default) {
919 QSSGSceneDesc::setProperty(target,
"alphaMode", &QQuick3DSpecularGlossyMaterial::setAlphaMode, mode);
921 if (mode == QQuick3DSpecularGlossyMaterial::AlphaMode::Mask)
922 QSSGSceneDesc::setProperty(target,
"depthDrawMode", &QQuick3DSpecularGlossyMaterial::setDepthDrawMode, QQuick3DMaterial::OpaquePrePassDepthDraw);
929 result = source.Get(AI_MATKEY_GLTF_ALPHACUTOFF, alphaCutoff);
930 if (result == aiReturn_SUCCESS)
931 QSSGSceneDesc::setProperty(target,
"alphaCutoff", &QQuick3DSpecularGlossyMaterial::setAlphaCutoff,
float(alphaCutoff));
935 int shadingModel = 0;
936 result = source.Get(AI_MATKEY_SHADING_MODEL, shadingModel);
937 if (result == aiReturn_SUCCESS && shadingModel == aiShadingMode_Unlit)
938 QSSGSceneDesc::setProperty(target,
"lighting", &QQuick3DSpecularGlossyMaterial::setLighting, QQuick3DSpecularGlossyMaterial::Lighting::NoLighting);
946 ai_real clearcoatFactor = 0.0f;
947 result = source.Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoatFactor);
948 if (result == aiReturn_SUCCESS)
949 QSSGSceneDesc::setProperty(target,
951 &QQuick3DSpecularGlossyMaterial::setClearcoatAmount,
952 float(clearcoatFactor));
957 ai_real clearcoatRoughnessFactor = 0.0f;
958 result = source.Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoatRoughnessFactor);
959 if (result == aiReturn_SUCCESS)
960 QSSGSceneDesc::setProperty(target,
961 "clearcoatRoughnessAmount",
962 &QQuick3DSpecularGlossyMaterial::setClearcoatRoughnessAmount,
963 float(clearcoatRoughnessFactor));
967 if (
auto clearcoatTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_TEXTURE))
968 QSSGSceneDesc::setProperty(target,
"clearcoatMap", &QQuick3DSpecularGlossyMaterial::setClearcoatMap, clearcoatTexture);
971 if (
auto clearcoatRoughnessTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE))
972 QSSGSceneDesc::setProperty(target,
973 "clearcoatRoughnessMap",
974 &QQuick3DSpecularGlossyMaterial::setClearcoatRoughnessMap,
975 clearcoatRoughnessTexture);
978 if (
auto clearcoatNormalTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE)) {
979 QSSGSceneDesc::setProperty(target,
"clearcoatNormalMap", &QQuick3DSpecularGlossyMaterial::setClearcoatNormalMap, clearcoatNormalTexture);
981 ai_real clearcoatNormalStrength = 0.0f;
982 result = source.Get(AI_MATKEY_GLTF_TEXTURE_SCALE(aiTextureType_CLEARCOAT, 2), clearcoatNormalTexture);
983 if (result == aiReturn_SUCCESS)
984 QSSGSceneDesc::setProperty(target,
"clearcoatNormalStrength", &QQuick3DPrincipledMaterial::setClearcoatNormalStrength,
float(clearcoatNormalStrength));
992 ai_real transmissionFactor = 0.0f;
993 result = source.Get(AI_MATKEY_TRANSMISSION_FACTOR, transmissionFactor);
994 if (result == aiReturn_SUCCESS)
995 QSSGSceneDesc::setProperty(target,
996 "transmissionFactor",
997 &QQuick3DSpecularGlossyMaterial::setTransmissionFactor,
998 float(transmissionFactor));
1003 if (
auto transmissionImage = createTextureNode(source, AI_MATKEY_TRANSMISSION_TEXTURE))
1004 QSSGSceneDesc::setProperty(target,
1006 &QQuick3DSpecularGlossyMaterial::setTransmissionMap,
1016 ai_real thicknessFactor = 0.0f;
1017 result = source.Get(AI_MATKEY_VOLUME_THICKNESS_FACTOR, thicknessFactor);
1018 if (result == aiReturn_SUCCESS)
1019 QSSGSceneDesc::setProperty(target,
"thicknessFactor", &QQuick3DSpecularGlossyMaterial::setThicknessFactor,
float(thicknessFactor));
1024 if (
auto thicknessImage = createTextureNode(source, AI_MATKEY_VOLUME_THICKNESS_TEXTURE))
1025 QSSGSceneDesc::setProperty(target,
"thicknessMap", &QQuick3DSpecularGlossyMaterial::setThicknessMap, thicknessImage);
1030 ai_real attenuationDistance = 0.0f;
1031 result = source.Get(AI_MATKEY_VOLUME_ATTENUATION_DISTANCE, attenuationDistance);
1032 if (result == aiReturn_SUCCESS)
1033 QSSGSceneDesc::setProperty(target,
1034 "attenuationDistance",
1035 &QQuick3DSpecularGlossyMaterial::setAttenuationDistance,
1036 float(attenuationDistance));
1041 aiColor3D attenuationColor;
1042 result = source.Get(AI_MATKEY_VOLUME_ATTENUATION_COLOR, attenuationColor);
1043 if (result == aiReturn_SUCCESS)
1044 QSSGSceneDesc::setProperty(target,
1046 &QQuick3DSpecularGlossyMaterial::setAttenuationColor,
1047 aiColorToQColor(attenuationColor));
1699static QString
importImp(
const QUrl &url,
const QJsonObject &options, QSSGSceneDesc::Scene &targetScene)
1701 auto filePath = url.path();
1703 const bool maybeLocalFile = QQmlFile::isLocalFile(url);
1704 if (maybeLocalFile && !QFileInfo::exists(filePath))
1705 filePath = QQmlFile::urlToLocalFileOrQrc(url);
1707 auto sourceFile = QFileInfo(filePath);
1708 if (!sourceFile.exists())
1709 return QLatin1String(
"File not found");
1710 targetScene.sourceDir = sourceFile.path();
1712 std::unique_ptr<Assimp::Importer> importer(
new Assimp::Importer());
1715 aiPostProcessSteps postProcessSteps;
1716 if (options.isEmpty())
1719 postProcessSteps = processOptions(options, importer);
1722 importer->SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE);
1723 importer->SetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES, 1);
1725 if (filePath.startsWith(
":"))
1726 importer->SetIOHandler(
new ResourceIOSystem);
1731 importer->SetIOHandler(
new ResourceIOSystem);
1734 auto sourceScene = importer->ReadFile(filePath.toStdString(), postProcessSteps);
1737 return QString::fromLocal8Bit(importer->GetErrorString());
1743 targetScene.id = sourceFile.canonicalFilePath();
1746 using It =
decltype(sourceScene->mNumMeshes);
1750 const auto &srcRootNode = *sourceScene->mRootNode;
1754 AnimationNodeMap animatingNodes;
1756 if (sourceScene->HasLights()) {
1757 for (It i = 0, end = sourceScene->mNumLights; i != end; ++i) {
1758 const auto &type = *sourceScene->mLights[i];
1759 if (
auto node = srcRootNode.FindNode(type.mName))
1760 nodeMap[node] = { i, NodeInfo::Type::Light };
1764 if (sourceScene->HasCameras()) {
1765 for (It i = 0, end = sourceScene->mNumCameras; i != end; ++i) {
1766 const auto &srcCam = *sourceScene->mCameras[i];
1767 if (
auto node = srcRootNode.FindNode(srcCam.mName))
1768 nodeMap[node] = { i, NodeInfo::Type::Camera };
1772 if (sourceScene->HasAnimations()) {
1773 for (It i = 0, end = sourceScene->mNumAnimations; i != end; ++i) {
1774 const auto &srcAnim = *sourceScene->mAnimations[i];
1775 const auto channelCount = srcAnim.mNumChannels;
1776 for (It cIdx = 0; cIdx != channelCount; ++cIdx) {
1777 const auto &srcChannel = srcAnim.mChannels[cIdx];
1778 const auto &nodeName = srcChannel->mNodeName;
1779 if (nodeName.length > 0) {
1781 QByteArray name(nodeName.C_Str(), qsizetype(nodeName.length));
1782 if (!animatingNodes.contains(name))
1783 animatingNodes.insert(name,
nullptr);
1786 const auto morphChannelCount = srcAnim.mNumMorphMeshChannels;
1787 for (It cIdx = 0; cIdx != morphChannelCount; ++cIdx) {
1788 const auto &srcChannel = srcAnim.mMorphMeshChannels[cIdx];
1789 const auto &nodeName = srcChannel->mName;
1790 if (nodeName.length > 0) {
1791 const auto morphKeys = srcChannel->mKeys;
1792 const auto numMorphTargets = morphKeys[0].mNumValuesAndWeights;
1795 for (It j = 0; j < numMorphTargets; ++j) {
1796 QString morphTargetName(nodeName.C_Str());
1797 morphTargetName += QStringLiteral(
"_morph") + QString::number(j);
1798 animatingNodes.insert(morphTargetName.toUtf8(),
nullptr);
1807 const auto materialCount = sourceScene->mNumMaterials;
1808 SceneInfo::MaterialMap materials;
1809 materials.reserve(materialCount);
1811 const auto meshCount = sourceScene->mNumMeshes;
1812 SceneInfo::MeshMap meshes;
1813 meshes.reserve(meshCount);
1814 SceneInfo::Mesh2SkinMap mesh2skin;
1815 mesh2skin.reserve(meshCount);
1817 const auto embeddedTextureCount = sourceScene->mNumTextures;
1818 SceneInfo::EmbeddedTextureMap embeddedTextures;
1820 SceneInfo::SkinMap skins;
1822 for (It i = 0; i != materialCount; ++i)
1823 materials.push_back({sourceScene->mMaterials[i],
nullptr});
1825 for (It i = 0; i != meshCount; ++i) {
1826 meshes.push_back({sourceScene->mMeshes[i],
nullptr});
1827 if (sourceScene->mMeshes[i]->HasBones()) {
1828 mesh2skin.push_back(skins.size());
1829 const auto boneCount = sourceScene->mMeshes[i]->mNumBones;
1830 auto bones = sourceScene->mMeshes[i]->mBones;
1836 for (It j = 0; j != boneCount; ++j) {
1837 const auto &nodeName = bones[j]->mName;
1838 if (nodeName.length > 0) {
1839 animatingNodes.insert(QByteArray{ nodeName.C_Str(),
1840 qsizetype(nodeName.length) },
1845 mesh2skin.push_back(-1);
1849 for (It i = 0; i != embeddedTextureCount; ++i)
1850 embeddedTextures.push_back(
nullptr);
1852 SceneInfo::TextureMap textureMap;
1854 if (!targetScene.root) {
1855 auto root =
new QSSGSceneDesc::Node(QByteArrayLiteral(
"Root"), QSSGSceneDesc::Node::Type::Transform, QSSGSceneDesc::Node::RuntimeType::Node);
1856 QSSGSceneDesc::addNode(targetScene, *root);
1860 auto opt = processSceneOptions(options);
1862 const auto extension = sourceFile.suffix().toLower();
1863 if (extension == QStringLiteral(
"gltf") || extension == QStringLiteral(
"glb"))
1864 opt.gltfMode =
true;
1865 else if (extension == QStringLiteral(
"fbx"))
1868 SceneInfo sceneInfo { *sourceScene, materials, meshes, embeddedTextures,
1869 textureMap, skins, mesh2skin, sourceFile.dir(), opt };
1871 if (!qFuzzyCompare(opt.globalScaleValue, 1.0f) && !qFuzzyCompare(opt.globalScaleValue, 0.0f)) {
1872 const auto gscale = opt.globalScaleValue;
1873 QSSGSceneDesc::setProperty(*targetScene.root,
"scale", &QQuick3DNode::setScale, QVector3D { gscale, gscale, gscale });
1877 if (sourceScene->mRootNode)
1878 processNode(sceneInfo, *sourceScene->mRootNode, *targetScene.root, nodeMap, animatingNodes);
1880 for (It i = 0, endI = skins.size(); i != endI; ++i) {
1881 const auto &skin = skins[i];
1887 QList<QMatrix4x4> inverseBindPoses;
1888 QVarLengthArray<QSSGSceneDesc::Node *> joints;
1889 joints.reserve(skin.mNumBones);
1890 for (It j = 0, endJ = skin.mNumBones; j != endJ; ++j) {
1891 const auto &bone = *skin.mBones[j];
1892 const auto &nodeName = bone.mName;
1893 if (nodeName.length > 0) {
1894 auto targetNode = animatingNodes.value(QByteArray{ nodeName.C_Str(), qsizetype(nodeName.length) });
1895 joints.push_back(targetNode);
1896 const auto &osMat = bone.mOffsetMatrix;
1897 auto pose = QMatrix4x4(osMat[0][0], osMat[0][1], osMat[0][2], osMat[0][3],
1898 osMat[1][0], osMat[1][1], osMat[1][2], osMat[1][3],
1899 osMat[2][0], osMat[2][1], osMat[2][2], osMat[2][3],
1900 osMat[3][0], osMat[3][1], osMat[3][2], osMat[3][3]);
1901 inverseBindPoses.push_back(pose);
1904 QSSGSceneDesc::setProperty(*skin.node,
"joints", &QQuick3DSkin::joints, joints);
1905 QSSGSceneDesc::setProperty(*skin.node,
"inverseBindPoses", &QQuick3DSkin::setInverseBindPoses, inverseBindPoses);
1908 static const auto fuzzyComparePos = [](
const aiVectorKey *pos,
const aiVectorKey *prev){
1911 return qFuzzyCompare(pos->mValue.x, prev->mValue.x)
1912 && qFuzzyCompare(pos->mValue.y, prev->mValue.y)
1913 && qFuzzyCompare(pos->mValue.z, prev->mValue.z);
1916 static const auto fuzzyCompareRot = [](
const aiQuatKey *rot,
const aiQuatKey *prev){
1919 return qFuzzyCompare(rot->mValue.x, prev->mValue.x)
1920 && qFuzzyCompare(rot->mValue.y, prev->mValue.y)
1921 && qFuzzyCompare(rot->mValue.z, prev->mValue.z)
1922 && qFuzzyCompare(rot->mValue.w, prev->mValue.w);
1925 static const auto createAnimation = [](QSSGSceneDesc::Scene &targetScene,
const aiAnimation &srcAnim,
const AnimationNodeMap &animatingNodes) {
1926 using namespace QSSGSceneDesc;
1927 Animation targetAnimation;
1928 auto &channels = targetAnimation.channels;
1929 qreal freq = qFuzzyIsNull(srcAnim.mTicksPerSecond) ? 1.0
1930 : 1000.0 / srcAnim.mTicksPerSecond;
1931 targetAnimation.framesPerSecond = srcAnim.mTicksPerSecond;
1932 targetAnimation.name = fromAiString(srcAnim.mName);
1934 for (It i = 0, end = srcAnim.mNumChannels; i != end; ++i) {
1935 const auto &srcChannel = *srcAnim.mChannels[i];
1937 const auto &nodeName = srcChannel.mNodeName;
1938 if (nodeName.length > 0) {
1939 const auto aNodeEnd = animatingNodes.cend();
1940 const auto aNodeIt = animatingNodes.constFind(QByteArray{ nodeName.C_Str(), qsizetype(nodeName.length) });
1941 if (aNodeIt != aNodeEnd && aNodeIt.value() !=
nullptr) {
1942 auto targetNode = aNodeIt.value();
1945 const auto currentPropertyValue = [targetNode](
const char *propertyName) -> QVariant {
1946 for (
const auto *p : std::as_const(targetNode->properties)) {
1947 if (!qstrcmp(propertyName, p->name))
1954 const auto posKeyEnd = srcChannel.mNumPositionKeys;
1955 Animation::Channel targetChannel;
1956 targetChannel.targetProperty = Animation::Channel::TargetProperty::Position;
1957 targetChannel.target = targetNode;
1958 const aiVectorKey *prevPos =
nullptr;
1959 for (It posKeyIdx = 0; posKeyIdx != posKeyEnd; ++posKeyIdx) {
1960 const auto &posKey = srcChannel.mPositionKeys[posKeyIdx];
1961 if (fuzzyComparePos(&posKey, prevPos))
1963 targetChannel.keys.push_back(
new Animation::KeyPosition(toAnimationKey(posKey, freq)));
1967 const auto isUnchanged = [&targetChannel, currentPropertyValue]() {
1968 if (targetChannel.keys.count() != 1)
1970 auto currentPos = currentPropertyValue(
"position").value<QVector3D>();
1971 return qFuzzyCompare(targetChannel.keys[0]->value.toVector3D(), currentPos);
1973 if (!targetChannel.keys.isEmpty()) {
1974 if (!isUnchanged()) {
1975 channels.push_back(
new Animation::Channel(targetChannel));
1976 float endTime =
float(srcChannel.mPositionKeys[posKeyEnd - 1].mTime) * freq;
1977 if (targetAnimation.length < endTime)
1978 targetAnimation.length = endTime;
1981 qDeleteAll(targetChannel.keys);
1987 const auto rotKeyEnd = srcChannel.mNumRotationKeys;
1988 Animation::Channel targetChannel;
1989 targetChannel.targetProperty = Animation::Channel::TargetProperty::Rotation;
1990 targetChannel.target = targetNode;
1991 const aiQuatKey *prevRot =
nullptr;
1992 for (It rotKeyIdx = 0; rotKeyIdx != rotKeyEnd; ++rotKeyIdx) {
1993 const auto &rotKey = srcChannel.mRotationKeys[rotKeyIdx];
1994 if (fuzzyCompareRot(&rotKey, prevRot))
1996 targetChannel.keys.push_back(
new Animation::KeyPosition(toAnimationKey(rotKey, freq)));
2000 const auto isUnchanged = [&targetChannel, currentPropertyValue]() {
2001 if (targetChannel.keys.count() != 1)
2003 auto currentVal = currentPropertyValue(
"rotation");
2004 QQuaternion rot = currentVal.isValid() ? currentVal.value<QQuaternion>() : QQuaternion{};
2005 return qFuzzyCompare(QQuaternion(targetChannel.keys[0]->value), rot);
2007 if (!targetChannel.keys.isEmpty()) {
2008 if (!isUnchanged()) {
2009 channels.push_back(
new Animation::Channel(targetChannel));
2010 float endTime =
float(srcChannel.mRotationKeys[rotKeyEnd - 1].mTime) * freq;
2011 if (targetAnimation.length < endTime)
2012 targetAnimation.length = endTime;
2015 qDeleteAll(targetChannel.keys);
2021 const auto scaleKeyEnd = srcChannel.mNumScalingKeys;
2022 Animation::Channel targetChannel;
2023 targetChannel.targetProperty = Animation::Channel::TargetProperty::Scale;
2024 targetChannel.target = targetNode;
2025 const aiVectorKey *prevScale =
nullptr;
2026 for (It scaleKeyIdx = 0; scaleKeyIdx != scaleKeyEnd; ++scaleKeyIdx) {
2027 const auto &scaleKey = srcChannel.mScalingKeys[scaleKeyIdx];
2028 if (fuzzyComparePos(&scaleKey, prevScale))
2030 targetChannel.keys.push_back(
new Animation::KeyPosition(toAnimationKey(scaleKey, freq)));
2031 prevScale = &scaleKey;
2034 const auto isUnchanged = [&targetChannel, currentPropertyValue]() {
2035 if (targetChannel.keys.count() != 1)
2037 auto currentVal = currentPropertyValue(
"scale");
2038 QVector3D scale = currentVal.isValid() ? currentVal.value<QVector3D>() : QVector3D{ 1, 1, 1 };
2039 return qFuzzyCompare(targetChannel.keys[0]->value.toVector3D(), scale);
2042 if (!targetChannel.keys.isEmpty()) {
2043 if (!isUnchanged()) {
2044 channels.push_back(
new Animation::Channel(targetChannel));
2045 float endTime =
float(srcChannel.mScalingKeys[scaleKeyEnd - 1].mTime) * freq;
2046 if (targetAnimation.length < endTime)
2047 targetAnimation.length = endTime;
2050 qDeleteAll(targetChannel.keys);
2058 for (It i = 0, end = srcAnim.mNumMorphMeshChannels; i != end; ++i) {
2059 const auto &srcMorphChannel = *srcAnim.mMorphMeshChannels[i];
2060 const QString nodeName(srcMorphChannel.mName.C_Str());
2061 const auto *morphKeys = srcMorphChannel.mKeys;
2062 const auto numMorphTargets = qMin(morphKeys[0].mNumValuesAndWeights, 8U);
2063 for (It targetId = 0; targetId != numMorphTargets; ++targetId) {
2064 QString morphTargetName = nodeName + QStringLiteral(
"_morph") + QString::number(targetId);
2065 const auto aNodeEnd = animatingNodes.cend();
2066 const auto aNodeIt = animatingNodes.constFind(morphTargetName.toUtf8());
2067 if (aNodeIt != aNodeEnd && aNodeIt.value() !=
nullptr) {
2068 auto targetNode = aNodeIt.value();
2069 const auto weightKeyEnd = srcMorphChannel.mNumKeys;
2070 Animation::Channel targetChannel;
2071 targetChannel.targetProperty = Animation::Channel::TargetProperty::Weight;
2072 targetChannel.target = targetNode;
2073 for (It wId = 0; wId != weightKeyEnd; ++wId) {
2074 const auto &weightKey = srcMorphChannel.mKeys[wId];
2075 const auto animationKey =
new Animation::KeyPosition(toAnimationKey(weightKey, freq, targetId));
2076 targetChannel.keys.push_back(animationKey);
2078 if (!targetChannel.keys.isEmpty()) {
2079 channels.push_back(
new Animation::Channel(targetChannel));
2080 float endTime =
float(srcMorphChannel.mKeys[weightKeyEnd - 1].mTime) * freq;
2081 if (targetAnimation.length < endTime)
2082 targetAnimation.length = endTime;
2089 if (!targetAnimation.channels.isEmpty())
2090 targetScene.animations.push_back(
new Animation(targetAnimation));
2094 if (sourceScene->HasAnimations()) {
2095 const auto animationCount = sourceScene->mNumAnimations;
2096 targetScene.animations.reserve(animationCount);
2097 for (It i = 0, end = animationCount; i != end; ++i) {
2098 const auto &srcAnim = *sourceScene->mAnimations[i];
2099 createAnimation(targetScene, srcAnim, animatingNodes);
2108 QSSGQmlUtilities::applyEdit(&targetScene, options);