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 = ¶ms;
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;
}