446static void setMaterialProperties(QSSGSceneDesc::Material &target,
const aiMaterial &source,
const SceneInfo &sceneInfo, QSSGSceneDesc::Material::RuntimeType type)
448 if (target.name.isNull()) {
449 aiString materialName = source.GetName();
450 target.name = fromAiString(materialName);
453 const auto createTextureNode = [&sceneInfo, &target](
const aiMaterial &material, aiTextureType textureType,
unsigned int index) {
454 const auto &srcScene = sceneInfo
.scene;
455 QSSGSceneDesc::Texture *tex =
nullptr;
456 aiString texturePath;
459 if (material.GetTexture(textureType, index, &texturePath, &texInfo
.mapping, &texInfo
.uvIndex,
nullptr,
nullptr, texInfo
.modes) == aiReturn_SUCCESS) {
460 if (texturePath.length > 0) {
461 aiUVTransform transform;
462 if (material.Get(AI_MATKEY_UVTRANSFORM(textureType, index), transform) == aiReturn_SUCCESS)
463 texInfo.transform = transform;
465 material.Get(AI_MATKEY_UVWSRC(textureType, index), texInfo
.uvIndex);
466 material.Get(AI_MATKEY_GLTF_MAPPINGFILTER_MIN(textureType, index), texInfo
.minFilter);
467 material.Get(AI_MATKEY_GLTF_MAPPINGFILTER_MAG(textureType, index), texInfo
.magFilter);
469 auto &textureMap = sceneInfo.textureMap;
471 QByteArray texName = QByteArray(texturePath.C_Str(), texturePath.length);
473 const auto it = textureMap.constFind(
TextureEntry{texName, texInfo});
474 if (it != textureMap.cend()) {
475 Q_ASSERT(it->texture);
480 tex =
new QSSGSceneDesc::Texture(QSSGSceneDesc::Texture::RuntimeType::Image2D, texName);
481 textureMap.insert(TextureEntry{fromAiString(texturePath), texInfo, tex});
482 QSSGSceneDesc::addNode(target, *tex);
483 setTextureProperties(*tex, texInfo, sceneInfo);
485 auto aEmbeddedTex = srcScene.GetEmbeddedTextureAndIndex(texturePath.C_Str());
486 const auto &embeddedTexId = aEmbeddedTex.second;
487 if (embeddedTexId > -1) {
488 QSSGSceneDesc::TextureData *textureData =
nullptr;
489 auto &embeddedTextures = sceneInfo.embeddedTextureMap;
490 textureData = embeddedTextures[embeddedTexId];
492 const auto *sourceTexture = aEmbeddedTex.first;
493 Q_ASSERT(sourceTexture->pcData);
495 const bool isCompressed = (sourceTexture->mHeight == 0);
498 const qsizetype asize = (isCompressed) ? sourceTexture->mWidth : (sourceTexture->mHeight * sourceTexture->mWidth) *
sizeof(aiTexel);
499 const QSize size = (!isCompressed) ? QSize(
int(sourceTexture->mWidth),
int(sourceTexture->mHeight)) : QSize();
500 QByteArray imageData {
reinterpret_cast<
const char *>(sourceTexture->pcData), asize };
501 const auto format = (isCompressed) ? QByteArray(sourceTexture->achFormatHint) : QByteArrayLiteral(
"rgba8888");
502 const quint8 flags = isCompressed ? quint8(QSSGSceneDesc::TextureData::Flags::Compressed) : 0;
503 textureData =
new QSSGSceneDesc::TextureData(imageData, size, format, flags);
504 QSSGSceneDesc::addNode(*tex, *textureData);
505 embeddedTextures[embeddedTexId] = textureData;
509 QSSGSceneDesc::setProperty(*tex,
"textureData", &QQuick3DTexture::setTextureData, textureData);
511 auto relativePath = QString::fromUtf8(texturePath.C_Str());
514 relativePath.replace(
"\\",
"/");
515 const auto path = sceneInfo.workingDir.absoluteFilePath(relativePath);
516 QSSGSceneDesc::setProperty(*tex,
"source", &QQuick3DTexture::setSource, QUrl{ path });
527 if (type == QSSGSceneDesc::Material::RuntimeType::PrincipledMaterial) {
529 aiColor4D baseColorFactor;
530 result = source.Get(AI_MATKEY_BASE_COLOR, baseColorFactor);
531 if (result == aiReturn_SUCCESS) {
533 const QColor sRGBBaseColorFactor = QSSGUtils::color::linearTosRGB(QVector4D(baseColorFactor.r, baseColorFactor.g, baseColorFactor.b, baseColorFactor.a));
534 QSSGSceneDesc::setProperty(target,
"baseColor", &QQuick3DPrincipledMaterial::setBaseColor, sRGBBaseColorFactor);
537 aiColor3D diffuseColor;
538 result = source.Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor);
539 if (result == aiReturn_SUCCESS)
540 QSSGSceneDesc::setProperty(target,
"baseColor", &QQuick3DPrincipledMaterial::setBaseColor, aiColorToQColor(diffuseColor));
544 if (
auto baseColorTexture = createTextureNode(source, AI_MATKEY_BASE_COLOR_TEXTURE)) {
545 QSSGSceneDesc::setProperty(target,
"baseColorMap", &QQuick3DPrincipledMaterial::setBaseColorMap, baseColorTexture);
546 QSSGSceneDesc::setProperty(target,
"opacityChannel", &QQuick3DPrincipledMaterial::setOpacityChannel, QQuick3DPrincipledMaterial::TextureChannelMapping::A);
547 }
else if (
auto diffuseMapTexture = createTextureNode(source, aiTextureType_DIFFUSE, 0)) {
549 QSSGSceneDesc::setProperty(target,
"baseColorMap", &QQuick3DPrincipledMaterial::setBaseColorMap, diffuseMapTexture);
552 if (
auto metalicRoughnessTexture = createTextureNode(source, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE)) {
553 QSSGSceneDesc::setProperty(target,
"metalnessMap", &QQuick3DPrincipledMaterial::setMetalnessMap, metalicRoughnessTexture);
554 QSSGSceneDesc::setProperty(target,
"metalnessChannel", &QQuick3DPrincipledMaterial::setMetalnessChannel, QQuick3DPrincipledMaterial::TextureChannelMapping::B);
555 QSSGSceneDesc::setProperty(target,
"roughnessMap", &QQuick3DPrincipledMaterial::setRoughnessMap, metalicRoughnessTexture);
556 QSSGSceneDesc::setProperty(target,
"roughnessChannel", &QQuick3DPrincipledMaterial::setRoughnessChannel, QQuick3DPrincipledMaterial::TextureChannelMapping::G);
560 ai_real metallicFactor;
561 result = source.Get(AI_MATKEY_METALLIC_FACTOR, metallicFactor);
562 if (result == aiReturn_SUCCESS)
563 QSSGSceneDesc::setProperty(target,
"metalness", &QQuick3DPrincipledMaterial::setMetalness,
float(metallicFactor));
567 ai_real roughnessFactor;
568 result = source.Get(AI_MATKEY_ROUGHNESS_FACTOR, roughnessFactor);
569 if (result == aiReturn_SUCCESS)
570 QSSGSceneDesc::setProperty(target,
"roughness", &QQuick3DPrincipledMaterial::setRoughness,
float(roughnessFactor));
573 if (
auto normalTexture = createTextureNode(source, aiTextureType_NORMALS, 0)) {
574 QSSGSceneDesc::setProperty(target,
"normalMap", &QQuick3DPrincipledMaterial::setNormalMap, normalTexture);
577 result = source.Get(AI_MATKEY_GLTF_TEXTURE_SCALE(aiTextureType_NORMALS, 0), normalScale);
578 if (result == aiReturn_SUCCESS)
579 QSSGSceneDesc::setProperty(target,
"normalStrength", &QQuick3DPrincipledMaterial::setNormalStrength,
float(normalScale));
584 if (
auto occlusionTexture = createTextureNode(source, aiTextureType_LIGHTMAP, 0)) {
585 QSSGSceneDesc::setProperty(target,
"occlusionMap", &QQuick3DPrincipledMaterial::setOcclusionMap, occlusionTexture);
586 QSSGSceneDesc::setProperty(target,
"occlusionChannel", &QQuick3DPrincipledMaterial::setOcclusionChannel, QQuick3DPrincipledMaterial::TextureChannelMapping::R);
588 ai_real occlusionAmount;
589 result = source.Get(AI_MATKEY_GLTF_TEXTURE_STRENGTH(aiTextureType_LIGHTMAP, 0), occlusionAmount);
590 if (result == aiReturn_SUCCESS)
591 QSSGSceneDesc::setProperty(target,
"occlusionAmount", &QQuick3DPrincipledMaterial::setOcclusionAmount,
float(occlusionAmount));
595 if (
auto emissiveTexture = createTextureNode(source, aiTextureType_EMISSIVE, 0))
596 QSSGSceneDesc::setProperty(target,
"emissiveMap", &QQuick3DPrincipledMaterial::setEmissiveMap, emissiveTexture);
599 aiColor3D emissiveColorFactor;
600 result = source.Get(AI_MATKEY_COLOR_EMISSIVE, emissiveColorFactor);
601 if (result == aiReturn_SUCCESS)
602 QSSGSceneDesc::setProperty(target,
"emissiveFactor", &QQuick3DPrincipledMaterial::setEmissiveFactor, QVector3D { emissiveColorFactor.r, emissiveColorFactor.g, emissiveColorFactor.b });
607 result = source.Get(AI_MATKEY_TWOSIDED, isDoubleSided);
608 if (result == aiReturn_SUCCESS && isDoubleSided)
609 QSSGSceneDesc::setProperty(target,
"cullMode", &QQuick3DPrincipledMaterial::setCullMode, QQuick3DPrincipledMaterial::CullMode::NoCulling);
614 result = source.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode);
615 if (result == aiReturn_SUCCESS) {
616 auto mode = QQuick3DPrincipledMaterial::AlphaMode::Default;
617 if (QByteArrayView(alphaMode.C_Str()) ==
"OPAQUE")
618 mode = QQuick3DPrincipledMaterial::AlphaMode::Opaque;
619 else if (QByteArrayView(alphaMode.C_Str()) ==
"MASK")
620 mode = QQuick3DPrincipledMaterial::AlphaMode::Mask;
621 else if (QByteArrayView(alphaMode.C_Str()) ==
"BLEND")
622 mode = QQuick3DPrincipledMaterial::AlphaMode::Blend;
624 if (mode != QQuick3DPrincipledMaterial::AlphaMode::Default) {
625 QSSGSceneDesc::setProperty(target,
"alphaMode", &QQuick3DPrincipledMaterial::setAlphaMode, mode);
627 if (mode == QQuick3DPrincipledMaterial::AlphaMode::Mask)
628 QSSGSceneDesc::setProperty(target,
"depthDrawMode", &QQuick3DPrincipledMaterial::setDepthDrawMode, QQuick3DMaterial::OpaquePrePassDepthDraw);
635 result = source.Get(AI_MATKEY_GLTF_ALPHACUTOFF, alphaCutoff);
636 if (result == aiReturn_SUCCESS)
637 QSSGSceneDesc::setProperty(target,
"alphaCutoff", &QQuick3DPrincipledMaterial::setAlphaCutoff,
float(alphaCutoff));
641 int shadingModel = 0;
642 result = source.Get(AI_MATKEY_SHADING_MODEL, shadingModel);
643 if (result == aiReturn_SUCCESS && shadingModel == aiShadingMode_Unlit)
644 QSSGSceneDesc::setProperty(target,
"lighting", &QQuick3DPrincipledMaterial::setLighting, QQuick3DPrincipledMaterial::Lighting::NoLighting);
652 ai_real clearcoatFactor = 0.0f;
653 result = source.Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoatFactor);
654 if (result == aiReturn_SUCCESS)
655 QSSGSceneDesc::setProperty(target,
657 &QQuick3DPrincipledMaterial::setClearcoatAmount,
658 float(clearcoatFactor));
663 ai_real clearcoatRoughnessFactor = 0.0f;
664 result = source.Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoatRoughnessFactor);
665 if (result == aiReturn_SUCCESS)
666 QSSGSceneDesc::setProperty(target,
667 "clearcoatRoughnessAmount",
668 &QQuick3DPrincipledMaterial::setClearcoatRoughnessAmount,
669 float(clearcoatRoughnessFactor));
673 if (
auto clearcoatTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_TEXTURE))
674 QSSGSceneDesc::setProperty(target,
"clearcoatMap", &QQuick3DPrincipledMaterial::setClearcoatMap, clearcoatTexture);
677 if (
auto clearcoatRoughnessTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE))
678 QSSGSceneDesc::setProperty(target,
679 "clearcoatRoughnessMap",
680 &QQuick3DPrincipledMaterial::setClearcoatRoughnessMap,
681 clearcoatRoughnessTexture);
684 if (
auto clearcoatNormalTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE))
685 QSSGSceneDesc::setProperty(target,
"clearcoatNormalMap", &QQuick3DPrincipledMaterial::setClearcoatNormalMap, clearcoatNormalTexture);
692 ai_real transmissionFactor = 0.0f;
693 result = source.Get(AI_MATKEY_TRANSMISSION_FACTOR, transmissionFactor);
694 if (result == aiReturn_SUCCESS)
695 QSSGSceneDesc::setProperty(target,
696 "transmissionFactor",
697 &QQuick3DPrincipledMaterial::setTransmissionFactor,
698 float(transmissionFactor));
703 if (
auto transmissionImage = createTextureNode(source, AI_MATKEY_TRANSMISSION_TEXTURE))
704 QSSGSceneDesc::setProperty(target,
706 &QQuick3DPrincipledMaterial::setTransmissionMap,
716 ai_real thicknessFactor = 0.0f;
717 result = source.Get(AI_MATKEY_VOLUME_THICKNESS_FACTOR, thicknessFactor);
718 if (result == aiReturn_SUCCESS)
719 QSSGSceneDesc::setProperty(target,
"thicknessFactor", &QQuick3DPrincipledMaterial::setThicknessFactor,
float(thicknessFactor));
724 if (
auto thicknessImage = createTextureNode(source, AI_MATKEY_VOLUME_THICKNESS_TEXTURE))
725 QSSGSceneDesc::setProperty(target,
"thicknessMap", &QQuick3DPrincipledMaterial::setThicknessMap, thicknessImage);
730 ai_real attenuationDistance = 0.0f;
731 result = source.Get(AI_MATKEY_VOLUME_ATTENUATION_DISTANCE, attenuationDistance);
732 if (result == aiReturn_SUCCESS)
733 QSSGSceneDesc::setProperty(target,
734 "attenuationDistance",
735 &QQuick3DPrincipledMaterial::setAttenuationDistance,
736 float(attenuationDistance));
741 aiColor3D attenuationColor;
742 result = source.Get(AI_MATKEY_VOLUME_ATTENUATION_COLOR, attenuationColor);
743 if (result == aiReturn_SUCCESS)
744 QSSGSceneDesc::setProperty(target,
746 &QQuick3DPrincipledMaterial::setAttenuationColor,
747 aiColorToQColor(attenuationColor));
755 result = source.Get(AI_MATKEY_REFRACTI, ior);
756 if (result == aiReturn_SUCCESS)
757 QSSGSceneDesc::setProperty(target,
759 &QQuick3DPrincipledMaterial::setIndexOfRefraction,
763 }
else if (type == QSSGSceneDesc::Material::RuntimeType::DefaultMaterial) {
764 int shadingModel = 0;
765 auto material = &source;
766 result = material->Get(AI_MATKEY_SHADING_MODEL, shadingModel);
768 if (result == aiReturn_SUCCESS && (shadingModel == aiShadingMode_NoShading))
769 QSSGSceneDesc::setProperty(target,
"lighting", &QQuick3DDefaultMaterial::setLighting, QQuick3DDefaultMaterial::Lighting::NoLighting);
771 if (
auto diffuseMapTexture = createTextureNode(source, aiTextureType_DIFFUSE, 0)) {
772 QSSGSceneDesc::setProperty(target,
"diffuseMap", &QQuick3DDefaultMaterial::setDiffuseMap, diffuseMapTexture);
776 aiColor3D diffuseColor;
777 result = material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor);
778 if (result == aiReturn_SUCCESS)
779 QSSGSceneDesc::setProperty(target,
"diffuseColor", &QQuick3DDefaultMaterial::setDiffuseColor, aiColorToQColor(diffuseColor));
782 if (
auto emissiveTexture = createTextureNode(source, aiTextureType_EMISSIVE, 0))
783 QSSGSceneDesc::setProperty(target,
"emissiveMap", &QQuick3DDefaultMaterial::setEmissiveMap, emissiveTexture);
786 if (
auto specularTexture = createTextureNode(source, aiTextureType_SPECULAR, 0))
787 QSSGSceneDesc::setProperty(target,
"specularMap", &QQuick3DDefaultMaterial::setSpecularMap, specularTexture);
791 result = material->Get(AI_MATKEY_OPACITY, opacity);
792 if (result == aiReturn_SUCCESS)
793 QSSGSceneDesc::setProperty(target,
"opacity", &QQuick3DDefaultMaterial::setOpacity,
float(opacity));
796 if (
auto opacityTexture = createTextureNode(source, aiTextureType_OPACITY, 0))
797 QSSGSceneDesc::setProperty(target,
"opacityMap", &QQuick3DDefaultMaterial::setOpacityMap, opacityTexture);
800 if (
auto bumpTexture = createTextureNode(source, aiTextureType_HEIGHT, 0)) {
801 QSSGSceneDesc::setProperty(target,
"bumpMap", &QQuick3DDefaultMaterial::setBumpMap, bumpTexture);
804 result = material->Get(AI_MATKEY_BUMPSCALING, bumpAmount);
805 if (result == aiReturn_SUCCESS)
806 QSSGSceneDesc::setProperty(target,
"bumpAmount", &QQuick3DDefaultMaterial::setBumpAmount,
float(bumpAmount));
810 if (
auto normalTexture = createTextureNode(source, aiTextureType_NORMALS, 0))
811 QSSGSceneDesc::setProperty(target,
"normalMap", &QQuick3DDefaultMaterial::setNormalMap, normalTexture);
812 }
else if (type == QSSGSceneDesc::Material::RuntimeType::SpecularGlossyMaterial) {
814 aiColor4D albedoFactor;
815 result = source.Get(AI_MATKEY_COLOR_DIFFUSE, albedoFactor);
816 if (result == aiReturn_SUCCESS)
817 QSSGSceneDesc::setProperty(target,
"albedoColor", &QQuick3DSpecularGlossyMaterial::setAlbedoColor, aiColorToQColor(albedoFactor));
820 if (
auto albedoTexture = createTextureNode(source, aiTextureType_DIFFUSE, 0)) {
821 QSSGSceneDesc::setProperty(target,
"albedoMap", &QQuick3DSpecularGlossyMaterial::setAlbedoMap, albedoTexture);
822 QSSGSceneDesc::setProperty(target,
"opacityChannel", &QQuick3DSpecularGlossyMaterial::setOpacityChannel, QQuick3DSpecularGlossyMaterial::TextureChannelMapping::A);
825 if (
auto specularGlossinessTexture = createTextureNode(source, aiTextureType_SPECULAR, 0)) {
826 QSSGSceneDesc::setProperty(target,
"specularMap", &QQuick3DSpecularGlossyMaterial::setSpecularMap, specularGlossinessTexture);
827 QSSGSceneDesc::setProperty(target,
"glossinessMap", &QQuick3DSpecularGlossyMaterial::setGlossinessMap, specularGlossinessTexture);
828 QSSGSceneDesc::setProperty(target,
"glossinessChannel", &QQuick3DSpecularGlossyMaterial::setGlossinessChannel, QQuick3DSpecularGlossyMaterial::TextureChannelMapping::A);
832 aiColor4D specularColorFactor;
833 result = source.Get(AI_MATKEY_COLOR_SPECULAR, specularColorFactor);
834 if (result == aiReturn_SUCCESS)
835 QSSGSceneDesc::setProperty(target,
"specularColor", &QQuick3DSpecularGlossyMaterial::setSpecularColor, aiColorToQColor(specularColorFactor));
839 ai_real glossinessFactor;
840 result = source.Get(AI_MATKEY_GLOSSINESS_FACTOR, glossinessFactor);
841 if (result == aiReturn_SUCCESS)
842 QSSGSceneDesc::setProperty(target,
"glossiness", &QQuick3DSpecularGlossyMaterial::setGlossiness,
float(glossinessFactor));
845 if (
auto normalTexture = createTextureNode(source, aiTextureType_NORMALS, 0)) {
846 QSSGSceneDesc::setProperty(target,
"normalMap", &QQuick3DSpecularGlossyMaterial::setNormalMap, normalTexture);
849 result = source.Get(AI_MATKEY_GLTF_TEXTURE_SCALE(aiTextureType_NORMALS, 0), normalScale);
850 if (result == aiReturn_SUCCESS)
851 QSSGSceneDesc::setProperty(target,
"normalStrength", &QQuick3DSpecularGlossyMaterial::setNormalStrength,
float(normalScale));
856 if (
auto occlusionTexture = createTextureNode(source, aiTextureType_LIGHTMAP, 0)) {
857 QSSGSceneDesc::setProperty(target,
"occlusionMap", &QQuick3DSpecularGlossyMaterial::setOcclusionMap, occlusionTexture);
858 QSSGSceneDesc::setProperty(target,
"occlusionChannel", &QQuick3DSpecularGlossyMaterial::setOcclusionChannel, QQuick3DSpecularGlossyMaterial::TextureChannelMapping::R);
860 ai_real occlusionAmount;
861 result = source.Get(AI_MATKEY_GLTF_TEXTURE_STRENGTH(aiTextureType_LIGHTMAP, 0), occlusionAmount);
862 if (result == aiReturn_SUCCESS)
863 QSSGSceneDesc::setProperty(target,
"occlusionAmount", &QQuick3DSpecularGlossyMaterial::setOcclusionAmount,
float(occlusionAmount));
867 if (
auto emissiveTexture = createTextureNode(source, aiTextureType_EMISSIVE, 0))
868 QSSGSceneDesc::setProperty(target,
"emissiveMap", &QQuick3DSpecularGlossyMaterial::setEmissiveMap, emissiveTexture);
871 aiColor3D emissiveColorFactor;
872 result = source.Get(AI_MATKEY_COLOR_EMISSIVE, emissiveColorFactor);
873 if (result == aiReturn_SUCCESS)
874 QSSGSceneDesc::setProperty(target,
"emissiveFactor", &QQuick3DSpecularGlossyMaterial::setEmissiveFactor, QVector3D { emissiveColorFactor.r, emissiveColorFactor.g, emissiveColorFactor.b });
879 result = source.Get(AI_MATKEY_TWOSIDED, isDoubleSided);
880 if (result == aiReturn_SUCCESS && isDoubleSided)
881 QSSGSceneDesc::setProperty(target,
"cullMode", &QQuick3DSpecularGlossyMaterial::setCullMode, QQuick3DSpecularGlossyMaterial::CullMode::NoCulling);
886 result = source.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode);
887 if (result == aiReturn_SUCCESS) {
888 auto mode = QQuick3DSpecularGlossyMaterial::AlphaMode::Default;
889 if (QByteArrayView(alphaMode.C_Str()) ==
"OPAQUE")
890 mode = QQuick3DSpecularGlossyMaterial::AlphaMode::Opaque;
891 else if (QByteArrayView(alphaMode.C_Str()) ==
"MASK")
892 mode = QQuick3DSpecularGlossyMaterial::AlphaMode::Mask;
893 else if (QByteArrayView(alphaMode.C_Str()) ==
"BLEND")
894 mode = QQuick3DSpecularGlossyMaterial::AlphaMode::Blend;
896 if (mode != QQuick3DSpecularGlossyMaterial::AlphaMode::Default) {
897 QSSGSceneDesc::setProperty(target,
"alphaMode", &QQuick3DSpecularGlossyMaterial::setAlphaMode, mode);
899 if (mode == QQuick3DSpecularGlossyMaterial::AlphaMode::Mask)
900 QSSGSceneDesc::setProperty(target,
"depthDrawMode", &QQuick3DSpecularGlossyMaterial::setDepthDrawMode, QQuick3DMaterial::OpaquePrePassDepthDraw);
907 result = source.Get(AI_MATKEY_GLTF_ALPHACUTOFF, alphaCutoff);
908 if (result == aiReturn_SUCCESS)
909 QSSGSceneDesc::setProperty(target,
"alphaCutoff", &QQuick3DSpecularGlossyMaterial::setAlphaCutoff,
float(alphaCutoff));
913 int shadingModel = 0;
914 result = source.Get(AI_MATKEY_SHADING_MODEL, shadingModel);
915 if (result == aiReturn_SUCCESS && shadingModel == aiShadingMode_Unlit)
916 QSSGSceneDesc::setProperty(target,
"lighting", &QQuick3DSpecularGlossyMaterial::setLighting, QQuick3DSpecularGlossyMaterial::Lighting::NoLighting);
924 ai_real clearcoatFactor = 0.0f;
925 result = source.Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoatFactor);
926 if (result == aiReturn_SUCCESS)
927 QSSGSceneDesc::setProperty(target,
929 &QQuick3DSpecularGlossyMaterial::setClearcoatAmount,
930 float(clearcoatFactor));
935 ai_real clearcoatRoughnessFactor = 0.0f;
936 result = source.Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoatRoughnessFactor);
937 if (result == aiReturn_SUCCESS)
938 QSSGSceneDesc::setProperty(target,
939 "clearcoatRoughnessAmount",
940 &QQuick3DSpecularGlossyMaterial::setClearcoatRoughnessAmount,
941 float(clearcoatRoughnessFactor));
945 if (
auto clearcoatTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_TEXTURE))
946 QSSGSceneDesc::setProperty(target,
"clearcoatMap", &QQuick3DSpecularGlossyMaterial::setClearcoatMap, clearcoatTexture);
949 if (
auto clearcoatRoughnessTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE))
950 QSSGSceneDesc::setProperty(target,
951 "clearcoatRoughnessMap",
952 &QQuick3DSpecularGlossyMaterial::setClearcoatRoughnessMap,
953 clearcoatRoughnessTexture);
956 if (
auto clearcoatNormalTexture = createTextureNode(source, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE))
957 QSSGSceneDesc::setProperty(target,
"clearcoatNormalMap", &QQuick3DSpecularGlossyMaterial::setClearcoatNormalMap, clearcoatNormalTexture);
964 ai_real transmissionFactor = 0.0f;
965 result = source.Get(AI_MATKEY_TRANSMISSION_FACTOR, transmissionFactor);
966 if (result == aiReturn_SUCCESS)
967 QSSGSceneDesc::setProperty(target,
968 "transmissionFactor",
969 &QQuick3DSpecularGlossyMaterial::setTransmissionFactor,
970 float(transmissionFactor));
975 if (
auto transmissionImage = createTextureNode(source, AI_MATKEY_TRANSMISSION_TEXTURE))
976 QSSGSceneDesc::setProperty(target,
978 &QQuick3DSpecularGlossyMaterial::setTransmissionMap,
988 ai_real thicknessFactor = 0.0f;
989 result = source.Get(AI_MATKEY_VOLUME_THICKNESS_FACTOR, thicknessFactor);
990 if (result == aiReturn_SUCCESS)
991 QSSGSceneDesc::setProperty(target,
"thicknessFactor", &QQuick3DSpecularGlossyMaterial::setThicknessFactor,
float(thicknessFactor));
996 if (
auto thicknessImage = createTextureNode(source, AI_MATKEY_VOLUME_THICKNESS_TEXTURE))
997 QSSGSceneDesc::setProperty(target,
"thicknessMap", &QQuick3DSpecularGlossyMaterial::setThicknessMap, thicknessImage);
1002 ai_real attenuationDistance = 0.0f;
1003 result = source.Get(AI_MATKEY_VOLUME_ATTENUATION_DISTANCE, attenuationDistance);
1004 if (result == aiReturn_SUCCESS)
1005 QSSGSceneDesc::setProperty(target,
1006 "attenuationDistance",
1007 &QQuick3DSpecularGlossyMaterial::setAttenuationDistance,
1008 float(attenuationDistance));
1013 aiColor3D attenuationColor;
1014 result = source.Get(AI_MATKEY_VOLUME_ATTENUATION_COLOR, attenuationColor);
1015 if (result == aiReturn_SUCCESS)
1016 QSSGSceneDesc::setProperty(target,
1018 &QQuick3DSpecularGlossyMaterial::setAttenuationColor,
1019 aiColorToQColor(attenuationColor));
1672static QString
importImp(
const QUrl &url,
const QJsonObject &options, QSSGSceneDesc::Scene &targetScene)
1674 auto filePath = url.path();
1676 const bool maybeLocalFile = QQmlFile::isLocalFile(url);
1677 if (maybeLocalFile && !QFileInfo::exists(filePath))
1678 filePath = QQmlFile::urlToLocalFileOrQrc(url);
1680 auto sourceFile = QFileInfo(filePath);
1681 if (!sourceFile.exists())
1682 return QLatin1String(
"File not found");
1683 targetScene.sourceDir = sourceFile.path();
1685 std::unique_ptr<Assimp::Importer> importer(
new Assimp::Importer());
1688 aiPostProcessSteps postProcessSteps;
1689 if (options.isEmpty())
1692 postProcessSteps = processOptions(options, importer);
1695 importer->SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE);
1696 importer->SetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES, 1);
1698 if (filePath.startsWith(
":"))
1699 importer->SetIOHandler(
new ResourceIOSystem);
1701 auto sourceScene = importer->ReadFile(filePath.toStdString(), postProcessSteps);
1704 return QString::fromLocal8Bit(importer->GetErrorString());
1710 targetScene.id = sourceFile.canonicalFilePath();
1713 using It =
decltype(sourceScene->mNumMeshes);
1717 const auto &srcRootNode = *sourceScene->mRootNode;
1721 AnimationNodeMap animatingNodes;
1723 if (sourceScene->HasLights()) {
1724 for (It i = 0, end = sourceScene->mNumLights; i != end; ++i) {
1725 const auto &type = *sourceScene->mLights[i];
1726 if (
auto node = srcRootNode.FindNode(type.mName))
1727 nodeMap[node] = { i, NodeInfo::Type::Light };
1731 if (sourceScene->HasCameras()) {
1732 for (It i = 0, end = sourceScene->mNumCameras; i != end; ++i) {
1733 const auto &srcCam = *sourceScene->mCameras[i];
1734 if (
auto node = srcRootNode.FindNode(srcCam.mName))
1735 nodeMap[node] = { i, NodeInfo::Type::Camera };
1739 if (sourceScene->HasAnimations()) {
1740 for (It i = 0, end = sourceScene->mNumAnimations; i != end; ++i) {
1741 const auto &srcAnim = *sourceScene->mAnimations[i];
1742 const auto channelCount = srcAnim.mNumChannels;
1743 for (It cIdx = 0; cIdx != channelCount; ++cIdx) {
1744 const auto &srcChannel = srcAnim.mChannels[cIdx];
1745 const auto &nodeName = srcChannel->mNodeName;
1746 if (nodeName.length > 0) {
1748 QByteArray name(nodeName.C_Str(), qsizetype(nodeName.length));
1749 if (!animatingNodes.contains(name))
1750 animatingNodes.insert(name,
nullptr);
1753 const auto morphChannelCount = srcAnim.mNumMorphMeshChannels;
1754 for (It cIdx = 0; cIdx != morphChannelCount; ++cIdx) {
1755 const auto &srcChannel = srcAnim.mMorphMeshChannels[cIdx];
1756 const auto &nodeName = srcChannel->mName;
1757 if (nodeName.length > 0) {
1758 const auto morphKeys = srcChannel->mKeys;
1759 const auto numMorphTargets = qMin(morphKeys[0].mNumValuesAndWeights, 8U);
1761 for (It j = 0; j < numMorphTargets; ++j) {
1762 QString morphTargetName(nodeName.C_Str());
1763 morphTargetName += QStringLiteral(
"_morph") + QString::number(j);
1764 animatingNodes.insert(morphTargetName.toUtf8(),
nullptr);
1773 const auto materialCount = sourceScene->mNumMaterials;
1774 SceneInfo::MaterialMap materials;
1775 materials.reserve(materialCount);
1777 const auto meshCount = sourceScene->mNumMeshes;
1778 SceneInfo::MeshMap meshes;
1779 meshes.reserve(meshCount);
1780 SceneInfo::Mesh2SkinMap mesh2skin;
1781 mesh2skin.reserve(meshCount);
1783 const auto embeddedTextureCount = sourceScene->mNumTextures;
1784 SceneInfo::EmbeddedTextureMap embeddedTextures;
1786 SceneInfo::SkinMap skins;
1788 for (It i = 0; i != materialCount; ++i)
1789 materials.push_back({sourceScene->mMaterials[i],
nullptr});
1791 for (It i = 0; i != meshCount; ++i) {
1792 meshes.push_back({sourceScene->mMeshes[i],
nullptr});
1793 if (sourceScene->mMeshes[i]->HasBones()) {
1794 mesh2skin.push_back(skins.size());
1795 const auto boneCount = sourceScene->mMeshes[i]->mNumBones;
1796 auto bones = sourceScene->mMeshes[i]->mBones;
1802 for (It j = 0; j != boneCount; ++j) {
1803 const auto &nodeName = bones[j]->mName;
1804 if (nodeName.length > 0) {
1805 animatingNodes.insert(QByteArray{ nodeName.C_Str(),
1806 qsizetype(nodeName.length) },
1811 mesh2skin.push_back(-1);
1815 for (It i = 0; i != embeddedTextureCount; ++i)
1816 embeddedTextures.push_back(
nullptr);
1818 SceneInfo::TextureMap textureMap;
1820 if (!targetScene.root) {
1821 auto root =
new QSSGSceneDesc::Node(QSSGSceneDesc::Node::Type::Transform, QSSGSceneDesc::Node::RuntimeType::Node);
1822 QSSGSceneDesc::addNode(targetScene, *root);
1826 auto opt = processSceneOptions(options);
1828 const auto extension = sourceFile.suffix().toLower();
1829 if (extension == QStringLiteral(
"gltf") || extension == QStringLiteral(
"glb"))
1830 opt.gltfMode =
true;
1831 else if (extension == QStringLiteral(
"fbx"))
1834 SceneInfo sceneInfo { *sourceScene, materials, meshes, embeddedTextures,
1835 textureMap, skins, mesh2skin, sourceFile.dir(), opt };
1837 if (!qFuzzyCompare(opt.globalScaleValue, 1.0f) && !qFuzzyCompare(opt.globalScaleValue, 0.0f)) {
1838 const auto gscale = opt.globalScaleValue;
1839 QSSGSceneDesc::setProperty(*targetScene.root,
"scale", &QQuick3DNode::setScale, QVector3D { gscale, gscale, gscale });
1843 if (sourceScene->mRootNode)
1844 processNode(sceneInfo, *sourceScene->mRootNode, *targetScene.root, nodeMap, animatingNodes);
1846 for (It i = 0, endI = skins.size(); i != endI; ++i) {
1847 const auto &skin = skins[i];
1853 QList<QMatrix4x4> inverseBindPoses;
1854 QVarLengthArray<QSSGSceneDesc::Node *> joints;
1855 joints.reserve(skin.mNumBones);
1856 for (It j = 0, endJ = skin.mNumBones; j != endJ; ++j) {
1857 const auto &bone = *skin.mBones[j];
1858 const auto &nodeName = bone.mName;
1859 if (nodeName.length > 0) {
1860 auto targetNode = animatingNodes.value(QByteArray{ nodeName.C_Str(), qsizetype(nodeName.length) });
1861 joints.push_back(targetNode);
1862 const auto &osMat = bone.mOffsetMatrix;
1863 auto pose = QMatrix4x4(osMat[0][0], osMat[0][1], osMat[0][2], osMat[0][3],
1864 osMat[1][0], osMat[1][1], osMat[1][2], osMat[1][3],
1865 osMat[2][0], osMat[2][1], osMat[2][2], osMat[2][3],
1866 osMat[3][0], osMat[3][1], osMat[3][2], osMat[3][3]);
1867 inverseBindPoses.push_back(pose);
1870 QSSGSceneDesc::setProperty(*skin.node,
"joints", &QQuick3DSkin::joints, joints);
1871 QSSGSceneDesc::setProperty(*skin.node,
"inverseBindPoses", &QQuick3DSkin::setInverseBindPoses, inverseBindPoses);
1874 static const auto fuzzyComparePos = [](
const aiVectorKey *pos,
const aiVectorKey *prev){
1877 return qFuzzyCompare(pos->mValue.x, prev->mValue.x)
1878 && qFuzzyCompare(pos->mValue.y, prev->mValue.y)
1879 && qFuzzyCompare(pos->mValue.z, prev->mValue.z);
1882 static const auto fuzzyCompareRot = [](
const aiQuatKey *rot,
const aiQuatKey *prev){
1885 return qFuzzyCompare(rot->mValue.x, prev->mValue.x)
1886 && qFuzzyCompare(rot->mValue.y, prev->mValue.y)
1887 && qFuzzyCompare(rot->mValue.z, prev->mValue.z)
1888 && qFuzzyCompare(rot->mValue.w, prev->mValue.w);
1891 static const auto createAnimation = [](QSSGSceneDesc::Scene &targetScene,
const aiAnimation &srcAnim,
const AnimationNodeMap &animatingNodes) {
1892 using namespace QSSGSceneDesc;
1893 Animation targetAnimation;
1894 auto &channels = targetAnimation.channels;
1895 qreal freq = qFuzzyIsNull(srcAnim.mTicksPerSecond) ? 1.0
1896 : 1000.0 / srcAnim.mTicksPerSecond;
1897 targetAnimation.framesPerSecond = srcAnim.mTicksPerSecond;
1898 targetAnimation.name = fromAiString(srcAnim.mName);
1900 for (It i = 0, end = srcAnim.mNumChannels; i != end; ++i) {
1901 const auto &srcChannel = *srcAnim.mChannels[i];
1903 const auto &nodeName = srcChannel.mNodeName;
1904 if (nodeName.length > 0) {
1905 const auto aNodeEnd = animatingNodes.cend();
1906 const auto aNodeIt = animatingNodes.constFind(QByteArray{ nodeName.C_Str(), qsizetype(nodeName.length) });
1907 if (aNodeIt != aNodeEnd && aNodeIt.value() !=
nullptr) {
1908 auto targetNode = aNodeIt.value();
1911 const auto currentPropertyValue = [targetNode](
const char *propertyName) -> QVariant {
1912 for (
auto *p : targetNode->properties) {
1913 if (!qstrcmp(propertyName, p->name))
1920 const auto posKeyEnd = srcChannel.mNumPositionKeys;
1921 Animation::Channel targetChannel;
1922 targetChannel.targetProperty = Animation::Channel::TargetProperty::Position;
1923 targetChannel.target = targetNode;
1924 const aiVectorKey *prevPos =
nullptr;
1925 for (It posKeyIdx = 0; posKeyIdx != posKeyEnd; ++posKeyIdx) {
1926 const auto &posKey = srcChannel.mPositionKeys[posKeyIdx];
1927 if (fuzzyComparePos(&posKey, prevPos))
1929 targetChannel.keys.push_back(
new Animation::KeyPosition(toAnimationKey(posKey, freq)));
1933 const auto isUnchanged = [&targetChannel, currentPropertyValue]() {
1934 if (targetChannel.keys.count() != 1)
1936 auto currentPos = currentPropertyValue(
"position").value<QVector3D>();
1937 return qFuzzyCompare(targetChannel.keys[0]->value.toVector3D(), currentPos);
1939 if (!targetChannel.keys.isEmpty()) {
1940 if (!isUnchanged()) {
1941 channels.push_back(
new Animation::Channel(targetChannel));
1942 float endTime =
float(srcChannel.mPositionKeys[posKeyEnd - 1].mTime) * freq;
1943 if (targetAnimation.length < endTime)
1944 targetAnimation.length = endTime;
1947 qDeleteAll(targetChannel.keys);
1953 const auto rotKeyEnd = srcChannel.mNumRotationKeys;
1954 Animation::Channel targetChannel;
1955 targetChannel.targetProperty = Animation::Channel::TargetProperty::Rotation;
1956 targetChannel.target = targetNode;
1957 const aiQuatKey *prevRot =
nullptr;
1958 for (It rotKeyIdx = 0; rotKeyIdx != rotKeyEnd; ++rotKeyIdx) {
1959 const auto &rotKey = srcChannel.mRotationKeys[rotKeyIdx];
1960 if (fuzzyCompareRot(&rotKey, prevRot))
1962 targetChannel.keys.push_back(
new Animation::KeyPosition(toAnimationKey(rotKey, freq)));
1966 const auto isUnchanged = [&targetChannel, currentPropertyValue]() {
1967 if (targetChannel.keys.count() != 1)
1969 auto currentVal = currentPropertyValue(
"rotation");
1970 QQuaternion rot = currentVal.isValid() ? currentVal.value<QQuaternion>() : QQuaternion{};
1971 return qFuzzyCompare(QQuaternion(targetChannel.keys[0]->value), rot);
1973 if (!targetChannel.keys.isEmpty()) {
1974 if (!isUnchanged()) {
1975 channels.push_back(
new Animation::Channel(targetChannel));
1976 float endTime =
float(srcChannel.mRotationKeys[rotKeyEnd - 1].mTime) * freq;
1977 if (targetAnimation.length < endTime)
1978 targetAnimation.length = endTime;
1981 qDeleteAll(targetChannel.keys);
1987 const auto scaleKeyEnd = srcChannel.mNumScalingKeys;
1988 Animation::Channel targetChannel;
1989 targetChannel.targetProperty = Animation::Channel::TargetProperty::Scale;
1990 targetChannel.target = targetNode;
1991 const aiVectorKey *prevScale =
nullptr;
1992 for (It scaleKeyIdx = 0; scaleKeyIdx != scaleKeyEnd; ++scaleKeyIdx) {
1993 const auto &scaleKey = srcChannel.mScalingKeys[scaleKeyIdx];
1994 if (fuzzyComparePos(&scaleKey, prevScale))
1996 targetChannel.keys.push_back(
new Animation::KeyPosition(toAnimationKey(scaleKey, freq)));
1997 prevScale = &scaleKey;
2000 const auto isUnchanged = [&targetChannel, currentPropertyValue]() {
2001 if (targetChannel.keys.count() != 1)
2003 auto currentVal = currentPropertyValue(
"scale");
2004 QVector3D scale = currentVal.isValid() ? currentVal.value<QVector3D>() : QVector3D{ 1, 1, 1 };
2005 return qFuzzyCompare(targetChannel.keys[0]->value.toVector3D(), scale);
2008 if (!targetChannel.keys.isEmpty()) {
2009 if (!isUnchanged()) {
2010 channels.push_back(
new Animation::Channel(targetChannel));
2011 float endTime =
float(srcChannel.mScalingKeys[scaleKeyEnd - 1].mTime) * freq;
2012 if (targetAnimation.length < endTime)
2013 targetAnimation.length = endTime;
2016 qDeleteAll(targetChannel.keys);
2024 for (It i = 0, end = srcAnim.mNumMorphMeshChannels; i != end; ++i) {
2025 const auto &srcMorphChannel = *srcAnim.mMorphMeshChannels[i];
2026 const QString nodeName(srcMorphChannel.mName.C_Str());
2027 const auto *morphKeys = srcMorphChannel.mKeys;
2028 const auto numMorphTargets = qMin(morphKeys[0].mNumValuesAndWeights, 8U);
2029 for (It targetId = 0; targetId != numMorphTargets; ++targetId) {
2030 QString morphTargetName = nodeName + QStringLiteral(
"_morph") + QString::number(targetId);
2031 const auto aNodeEnd = animatingNodes.cend();
2032 const auto aNodeIt = animatingNodes.constFind(morphTargetName.toUtf8());
2033 if (aNodeIt != aNodeEnd && aNodeIt.value() !=
nullptr) {
2034 auto targetNode = aNodeIt.value();
2035 const auto weightKeyEnd = srcMorphChannel.mNumKeys;
2036 Animation::Channel targetChannel;
2037 targetChannel.targetProperty = Animation::Channel::TargetProperty::Weight;
2038 targetChannel.target = targetNode;
2039 for (It wId = 0; wId != weightKeyEnd; ++wId) {
2040 const auto &weightKey = srcMorphChannel.mKeys[wId];
2041 const auto animationKey =
new Animation::KeyPosition(toAnimationKey(weightKey, freq, targetId));
2042 targetChannel.keys.push_back(animationKey);
2044 if (!targetChannel.keys.isEmpty()) {
2045 channels.push_back(
new Animation::Channel(targetChannel));
2046 float endTime =
float(srcMorphChannel.mKeys[weightKeyEnd - 1].mTime) * freq;
2047 if (targetAnimation.length < endTime)
2048 targetAnimation.length = endTime;
2055 if (!targetAnimation.channels.isEmpty())
2056 targetScene.animations.push_back(
new Animation(targetAnimation));
2060 if (sourceScene->HasAnimations()) {
2061 const auto animationCount = sourceScene->mNumAnimations;
2062 targetScene.animations.reserve(animationCount);
2063 for (It i = 0, end = animationCount; i != end; ++i) {
2064 const auto &srcAnim = *sourceScene->mAnimations[i];
2065 createAnimation(targetScene, srcAnim, animatingNodes);
2074 QSSGQmlUtilities::applyEdit(&targetScene, options);