void UmbraSceneAssetBuilder::ProcessJob()

in Gems/Umbra/Code/Source/UmbraSceneAsset/UmbraSceneAssetBuilder.cpp [80:245]


    void UmbraSceneAssetBuilder::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
    {
        AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);

        if (jobCancelListener.IsCancelled())
        {
            response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
            return;
        }

        if (m_isShuttingDown)
        {
            response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
            return;
        }

        // Attempt to load the umbra scene from the specified source file path.
        AZStd::string scenePath = request.m_fullPath;

        Umbra::Scene* scene = Umbra::Scene::create(scenePath.c_str());
        if (!scene)
        {
            AZ_Error("UmbraSceneAssetBuilder", false, "Failed to load umbra scene: %s", scenePath.c_str());
            response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
            return;
        }

        // Replace the extension on the source file path to attempt to load the scene descriptor.
        AZStd::string sceneDescPath = request.m_fullPath;
        AZ::StringFunc::Path::ReplaceExtension(sceneDescPath, UmbraSceneDescriptor::Extension);

        // The scene descriptor is an optional sidecar file with additional parameters to configure how the scene is processed. It's treated
        // as a sidecar file instead of bundled with the umbra scene data so the umbra scene can still be loaded by the umbra debugger. If
        // there is no scene descriptor file then its default configuration will be used.
        UmbraSceneDescriptor sceneDescriptor;
        if (!AZ::JsonSerializationUtils::LoadObjectFromFile(sceneDescriptor, sceneDescPath))
        {
            AZ_Warning(
                "UmbraSceneAssetBuilder",
                false,
                "Failed to locate umbra scene descriptor. Using default descriptor values for computing tome data.: %s.",
                sceneDescPath.c_str());
        }

        AZ_Trace("AssetBuilder", "Load umbra scene data succeeded\n");

        // Set up tome computation parameters from scene descriptor.
        Umbra::ComputationParams params;
        params.setParam(Umbra::ComputationParams::COLLISION_RADIUS, sceneDescriptor.m_sceneSettings.m_collisionRadius);
        params.setParam(Umbra::ComputationParams::SMALLEST_HOLE, sceneDescriptor.m_sceneSettings.m_smallestHole);
        params.setParam(Umbra::ComputationParams::SMALLEST_OCCLUDER, sceneDescriptor.m_sceneSettings.m_smallestOccluder);

        // Update computation parameters with settings overridden by each view
        for (const auto& sceneSettingsByViewPair : sceneDescriptor.m_sceneSettingsByView)
        {
            params.setVolumeParam(
                sceneSettingsByViewPair.first,
                Umbra::ComputationParams::COLLISION_RADIUS,
                sceneSettingsByViewPair.second.m_collisionRadius);

            params.setVolumeParam(
                sceneSettingsByViewPair.first,
                Umbra::ComputationParams::SMALLEST_HOLE,
                sceneSettingsByViewPair.second.m_smallestHole);

            params.setVolumeParam(
                sceneSettingsByViewPair.first,
                Umbra::ComputationParams::SMALLEST_OCCLUDER,
                sceneSettingsByViewPair.second.m_smallestOccluder);
        }

        // Initiate processing the umbra scene.
        Umbra::LocalComputation::Params localParams;
        localParams.computationParams = &params;
        localParams.scene = scene;
        localParams.cacheSizeMegs = 100;
        localParams.numThreads = -1;

        const AZStd::string tempPath = (AZ::IO::FixedMaxPath(AZ::Utils::GetProjectUserPath()) / "umbracache").String();
        localParams.tempPath = tempPath.c_str();

        // Attempt to read the license key from an environment variable instead of relying on license files in the working directory or bin
        // folder.
        char licenseEnvBuffer[256]{};
        if (auto licenseEnvOutcome = AZ::Utils::GetEnv(licenseEnvBuffer, "UMBRA_LICENSE_KEY"); licenseEnvOutcome)
        {
            localParams.licenseKey = licenseEnvBuffer;
        }

        Umbra::Computation* computation = LocalComputation::create(localParams);
        Umbra::Computation::Result result = computation->waitForResult();

        if (result.error != Umbra::Computation::ERROR_OK)
        {
            AZ_Error("UmbraSceneAssetBuilder", false, "Failed to compile scene data: %s", result.errorStr);
            computation->release();
            response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
            return;
        }

        if (!result.tomeSize || result.tome == NULL)
        {
            AZ_Error("UmbraSceneAssetBuilder", false, "Failed to generate tome data from scene data");
            computation->release();
            response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
            return;
        }

        AZ_Trace("AssetBuilder", "Process umbra scene data succeeded\n");

        // Embed tome buffer directly into scene asset 
        UmbraSceneAsset sceneAsset;
        sceneAsset.m_objectDescriptors = sceneDescriptor.m_objectDescriptors;
        sceneAsset.m_tomeBuffer.assign(reinterpret_cast<uint8_t*>(result.tome), reinterpret_cast<uint8_t*>(result.tome) + result.tomeSize);
        computation->release();

        // Get file name from source file path, then replace the extension to generate product file name
        AZStd::string sceneAssetFileName;
        AzFramework::StringFunc::Path::GetFullFileName(request.m_fullPath.c_str(), sceneAssetFileName);
        AzFramework::StringFunc::Path::ReplaceExtension(sceneAssetFileName, UmbraSceneAsset::Extension);

        // Construct product full path
        AZStd::string sceneAssetPath;
        AzFramework::StringFunc::Path::ConstructFull(request.m_tempDirPath.c_str(), sceneAssetFileName.c_str(), sceneAssetPath, true);

        // Save the asset to binary format for production
        if (AZ::Utils::SaveObjectToFile(sceneAssetPath, AZ::DataStream::ST_BINARY, &sceneAsset, sceneAsset.GetType(), nullptr))
        {
            AssetBuilderSDK::JobProduct jobProduct;
            jobProduct.m_dependenciesHandled = true; // This builder has no dependencies to output.
            jobProduct.m_productFileName = sceneAssetPath;
            jobProduct.m_productAssetType = sceneAsset.GetType();
            jobProduct.m_productSubID = 0;
            response.m_outputProducts.emplace_back(AZStd::move(jobProduct));
            AZ_Trace("AssetBuilder", "Saved data to file %s \n", sceneAssetPath.c_str());
        }
        else
        {
            AZ_Error("UmbraSceneAssetBuilder", false, "Failed to save asset to cache");
            response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
            return;
        }

        // Save raw dumb file for other systems and debugging
        AZStd::string tomePath = sceneAssetPath;
        AzFramework::StringFunc::Path::ReplaceExtension(tomePath, "tome");
        if (AZ::Utils::SaveStreamToFile(tomePath, sceneAsset.m_tomeBuffer))
        {
            AssetBuilderSDK::JobProduct jobProduct;
            jobProduct.m_dependenciesHandled = true; // This builder has no dependencies to output.
            jobProduct.m_productFileName = tomePath;
            // We need a unique asset type identifier for the tone file since it is not directly registered or used by any other system
            jobProduct.m_productAssetType = AZ::Uuid::CreateString("{956C25FF-AEF1-4E3D-A1A5-85675731A782}");
            jobProduct.m_productSubID = 1;
            response.m_outputProducts.emplace_back(AZStd::move(jobProduct));
            AZ_Trace("AssetBuilder", "Saved raw tome to file %s \n", tomePath.c_str());
        }
        else
        {
            AZ_Error("UmbraSceneAssetBuilder", false, "Failed to save raw tome to cache");
            response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
            return;
        }

        response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
    }