std::shared_ptr EffectFactory::Impl::CreateEffect()

in Kits/DirectXTK12/Src/EffectFactory.cpp [111:466]


std::shared_ptr<IEffect> EffectFactory::Impl::CreateEffect(
    const EffectInfo& info,
    const EffectPipelineStateDescription& opaquePipelineState,
    const EffectPipelineStateDescription& alphaPipelineState,
    const D3D12_INPUT_LAYOUT_DESC& inputLayoutDesc,
    int textureDescriptorOffset,
    int samplerDescriptorOffset)
{
    // If textures are required, make sure we have a descriptor heap
    if (!mTextureDescriptors && (info.diffuseTextureIndex != -1 || info.specularTextureIndex != -1 || info.normalTextureIndex != -1 || info.emissiveTextureIndex != -1))
    {
        DebugTrace("ERROR: EffectFactory created without texture descriptor heap with texture index set (diffuse %d, specular %d, normal %d, emissive %d)!\n",
            info.diffuseTextureIndex, info.specularTextureIndex, info.normalTextureIndex, info.emissiveTextureIndex);
        throw std::runtime_error("EffectFactory");
    }
    if (!mSamplerDescriptors && (info.samplerIndex != -1 || info.samplerIndex2 != -1))
    {
        DebugTrace("ERROR: EffectFactory created without sampler descriptor heap with sampler index set (samplerIndex %d, samplerIndex2 %d)!\n",
            info.samplerIndex, info.samplerIndex2);
        throw std::runtime_error("EffectFactory");
    }

    // If we have descriptors, make sure we have both texture and sampler descriptors
    if ((mTextureDescriptors == nullptr) != (mSamplerDescriptors == nullptr))
    {
        DebugTrace("ERROR: A texture or sampler descriptor heap was provided, but both are required.\n");
        throw std::runtime_error("EffectFactory");
    }

    // Validate the we have either both texture and sampler descriptors, or neither
    if ((info.diffuseTextureIndex == -1) != (info.samplerIndex == -1))
    {
        DebugTrace("ERROR: Material provides either a texture or sampler, but both are required.\n");
        throw std::runtime_error("EffectFactory");
    }

    int diffuseTextureIndex = (info.diffuseTextureIndex != -1 && mTextureDescriptors != nullptr) ? info.diffuseTextureIndex + textureDescriptorOffset : -1;
    int specularTextureIndex = (info.specularTextureIndex != -1 && mTextureDescriptors != nullptr) ? info.specularTextureIndex + textureDescriptorOffset : -1;
    int emissiveTextureIndex = (info.emissiveTextureIndex != -1 && mTextureDescriptors != nullptr) ? info.emissiveTextureIndex + textureDescriptorOffset : -1;
    int normalTextureIndex = (info.normalTextureIndex != -1 && mTextureDescriptors != nullptr) ? info.normalTextureIndex + textureDescriptorOffset : -1;
    int samplerIndex = (info.samplerIndex != -1 && mSamplerDescriptors != nullptr) ? info.samplerIndex + samplerDescriptorOffset : -1;
    int samplerIndex2 = (info.samplerIndex2 != -1 && mSamplerDescriptors != nullptr) ? info.samplerIndex2 + samplerDescriptorOffset : -1;

    // Modify base pipeline state
    EffectPipelineStateDescription derivedPSD = (info.alphaValue < 1.0f) ? alphaPipelineState : opaquePipelineState;
    derivedPSD.inputLayout = inputLayoutDesc;

    std::wstring cacheName;
    if (info.enableSkinning)
    {
        int effectflags = (mEnablePerPixelLighting) ? EffectFlags::PerPixelLighting : EffectFlags::Lighting;

        if (mEnableFog)
        {
            effectflags |= EffectFlags::Fog;
        }

        if (info.biasedVertexNormals)
        {
            effectflags |= EffectFlags::BiasedVertexNormals;
        }

        if (info.enableNormalMaps && mUseNormalMapEffect)
        {
            // SkinnedNormalMapEffect
            if (specularTextureIndex != -1)
            {
                effectflags |= EffectFlags::Specular;
            }

            if (mSharing && !info.name.empty())
            {
                uint32_t hash = derivedPSD.ComputeHash();
                cacheName = std::to_wstring(effectflags) + info.name + std::to_wstring(hash);

                auto it = mEffectCacheNormalMapSkinned.find(cacheName);
                if (mSharing && it != mEffectCacheNormalMapSkinned.end())
                {
                    return it->second;
                }
            }

            auto effect = std::make_shared<SkinnedNormalMapEffect>(mDevice.Get(), effectflags, derivedPSD);

            SetMaterialProperties(effect.get(), info);

            if (diffuseTextureIndex != -1)
            {
                effect->SetTexture(
                    mTextureDescriptors->GetGpuHandle(static_cast<size_t>(diffuseTextureIndex)),
                    mSamplerDescriptors->GetGpuHandle(static_cast<size_t>(samplerIndex)));
            }

            if (specularTextureIndex != -1)
            {
                effect->SetSpecularTexture(mTextureDescriptors->GetGpuHandle(static_cast<size_t>(specularTextureIndex)));
            }

            if (normalTextureIndex != -1)
            {
                effect->SetNormalTexture(mTextureDescriptors->GetGpuHandle(static_cast<size_t>(normalTextureIndex)));
            }

            if (mSharing && !info.name.empty())
            {
                std::lock_guard<std::mutex> lock(mutex);
                EffectCache::value_type v(cacheName, effect);
                mEffectCacheNormalMapSkinned.insert(v);
            }

            return std::move(effect);
        }
        else
        {
            // SkinnedEffect
            if (mSharing && !info.name.empty())
            {
                uint32_t hash = derivedPSD.ComputeHash();
                cacheName = std::to_wstring(effectflags) + info.name + std::to_wstring(hash);

                auto it = mEffectCacheSkinning.find(cacheName);
                if (mSharing && it != mEffectCacheSkinning.end())
                {
                    return it->second;
                }
            }

            auto effect = std::make_shared<SkinnedEffect>(mDevice.Get(), effectflags, derivedPSD);

            SetMaterialProperties(effect.get(), info);

            if (diffuseTextureIndex != -1)
            {
                effect->SetTexture(
                    mTextureDescriptors->GetGpuHandle(static_cast<size_t>(diffuseTextureIndex)),
                    mSamplerDescriptors->GetGpuHandle(static_cast<size_t>(samplerIndex)));
            }

            if (mSharing && !info.name.empty())
            {
                std::lock_guard<std::mutex> lock(mutex);
                EffectCache::value_type v(cacheName, effect);
                mEffectCacheSkinning.insert(v);
            }

            return std::move(effect);
        }
    }
    else if (info.enableDualTexture)
    {
        // DualTextureEffect
        int effectflags = EffectFlags::None;

        if (mEnableFog)
        {
            effectflags |= EffectFlags::Fog;
        }

        if (mSharing && !info.name.empty())
        {
            uint32_t hash = derivedPSD.ComputeHash();
            cacheName = std::to_wstring(effectflags) + info.name + std::to_wstring(hash);

            auto it = mEffectCacheDualTexture.find(cacheName);
            if (mSharing && it != mEffectCacheDualTexture.end())
            {
                return it->second;
            }
        }

        if (info.perVertexColor)
        {
            effectflags |= EffectFlags::VertexColor;
        }

        auto effect = std::make_shared<DualTextureEffect>(mDevice.Get(), effectflags, derivedPSD);

        // Dual texture effect doesn't support lighting (usually it's lightmaps)
        effect->SetAlpha(info.alphaValue);

        XMVECTOR color = XMLoadFloat3(&info.diffuseColor);
        effect->SetDiffuseColor(color);

        if (diffuseTextureIndex != -1)
        {
            effect->SetTexture(
                mTextureDescriptors->GetGpuHandle(static_cast<size_t>(diffuseTextureIndex)),
                mSamplerDescriptors->GetGpuHandle(static_cast<size_t>(samplerIndex)));
        }

        if (emissiveTextureIndex != -1)
        {
            if (samplerIndex2 == -1)
            {
                DebugTrace("ERROR: Dual-texture requires a second sampler (emissive %d)\n", emissiveTextureIndex);
                throw std::runtime_error("EffectFactory");
            }

            effect->SetTexture2(
                mTextureDescriptors->GetGpuHandle(static_cast<size_t>(emissiveTextureIndex)),
                mSamplerDescriptors->GetGpuHandle(static_cast<size_t>(samplerIndex2)));
        }
        else if (specularTextureIndex != -1)
        {
            // If there's no emissive texture specified, use the specular texture as the second texture
            if (samplerIndex2 == -1)
            {
                DebugTrace("ERROR: Dual-texture requires a second sampler (specular %d)\n", specularTextureIndex);
                throw std::runtime_error("EffectFactory");
            }

            effect->SetTexture2(
                mTextureDescriptors->GetGpuHandle(static_cast<size_t>(specularTextureIndex)),
                mSamplerDescriptors->GetGpuHandle(static_cast<size_t>(samplerIndex2)));
        }

        if (mSharing && !info.name.empty())
        {
            std::lock_guard<std::mutex> lock(mutex);
            EffectCache::value_type v(cacheName, effect);
            mEffectCacheDualTexture.insert(v);
        }

        return std::move(effect);
    }
    else if (info.enableNormalMaps && mUseNormalMapEffect)
    {
        // NormalMapEffect
        int effectflags = EffectFlags::None;

        if (mEnableFog)
        {
            effectflags |= EffectFlags::Fog;
        }

        if (mEnableInstancing)
        {
            effectflags |= EffectFlags::Instancing;
        }

        if (info.perVertexColor)
        {
            effectflags |= EffectFlags::VertexColor;
        }

        if (info.biasedVertexNormals)
        {
            effectflags |= EffectFlags::BiasedVertexNormals;
        }

        if (specularTextureIndex != -1)
        {
            effectflags |= EffectFlags::Specular;
        }

        if (mSharing && !info.name.empty())
        {
            uint32_t hash = derivedPSD.ComputeHash();
            cacheName = std::to_wstring(effectflags) + info.name + std::to_wstring(hash);

            auto it = mEffectCacheNormalMap.find(cacheName);
            if (mSharing && it != mEffectCacheNormalMap.end())
            {
                return it->second;
            }
        }

        auto effect = std::make_shared<NormalMapEffect>(mDevice.Get(), effectflags, derivedPSD);

        SetMaterialProperties(effect.get(), info);

        if (diffuseTextureIndex != -1)
        {
            effect->SetTexture(
                mTextureDescriptors->GetGpuHandle(static_cast<size_t>(diffuseTextureIndex)),
                mSamplerDescriptors->GetGpuHandle(static_cast<size_t>(samplerIndex)));
        }

        if (specularTextureIndex != -1)
        {
            effect->SetSpecularTexture(mTextureDescriptors->GetGpuHandle(static_cast<size_t>(specularTextureIndex)));
        }

        if (normalTextureIndex != -1)
        {
            effect->SetNormalTexture(mTextureDescriptors->GetGpuHandle(static_cast<size_t>(normalTextureIndex)));
        }

        if (mSharing && !info.name.empty())
        {
            std::lock_guard<std::mutex> lock(mutex);
            EffectCache::value_type v(cacheName, effect);
            mEffectCacheNormalMap.insert(v);
        }

        return std::move(effect);
    }
    else
    {
        // set effect flags for creation
        int effectflags = (mEnablePerPixelLighting) ? EffectFlags::PerPixelLighting : EffectFlags::Lighting;

        if (mEnableFog)
        {
            effectflags |= EffectFlags::Fog;
        }

        if (info.perVertexColor)
        {
            effectflags |= EffectFlags::VertexColor;
        }

        if (diffuseTextureIndex != -1)
        {
            effectflags |= EffectFlags::Texture;
        }

        if (info.biasedVertexNormals)
        {
            effectflags |= EffectFlags::BiasedVertexNormals;
        }

        // BasicEffect
        if (mSharing && !info.name.empty())
        {
            uint32_t hash = derivedPSD.ComputeHash();
            cacheName = std::to_wstring(effectflags) + info.name + std::to_wstring(hash);

            auto it = mEffectCache.find(cacheName);
            if (mSharing && it != mEffectCache.end())
            {
                return it->second;
            }
        }

        auto effect = std::make_shared<BasicEffect>(mDevice.Get(), effectflags, derivedPSD);

        SetMaterialProperties(effect.get(), info);

        if (diffuseTextureIndex != -1)
        {
            effect->SetTexture(
                mTextureDescriptors->GetGpuHandle(static_cast<size_t>(diffuseTextureIndex)),
                mSamplerDescriptors->GetGpuHandle(static_cast<size_t>(samplerIndex)));
        }

        if (mSharing && !info.name.empty())
        {
            std::lock_guard<std::mutex> lock(mutex);
            EffectCache::value_type v(cacheName, effect);
            mEffectCache.insert(v);
        }

        return std::move(effect);
    }
}