in Gems/Umbra/Code/Source/UmbraSceneComponent/EditorUmbraSceneComponent.cpp [163:290]
bool EditorUmbraSceneComponent::ExportUmbraScene(const AZStd::string& scenePath) const
{
if (scenePath.empty() || AZ::StringFunc::Path::IsRelative(scenePath.c_str()) ||
!AZ::StringFunc::Path::IsExtension(scenePath.c_str(), "umbrascene"))
{
AZ_Error("Umbra", false, "Scene export path is not valid: %s", scenePath.c_str());
return false;
}
// Create an umbra scene
Umbra::Scene* scene = Umbra::Scene::create();
// Copy the global scene settings from the scene component into the scene descriptor.
UmbraSceneDescriptor sceneDescriptor;
sceneDescriptor.m_sceneSettings = m_controller.GetConfiguration().m_sceneSettings;
// Enumerate every component connected to the visible geometry request bus and add all of the static geometry to the scene.
AzFramework::VisibleGeometryRequestBus::EnumerateHandlers(
[&sceneDescriptor, scene, this](AzFramework::VisibleGeometryRequestBus::Events* handler) -> bool
{
const AZ::EntityId entityId = *(AzFramework::VisibleGeometryRequestBus::GetCurrentBusId());
if (!ShouldExportEntity(entityId))
{
return true;
}
// Override the default scene object settings and flags with values from the umbra object component if one is present.
uint32_t flags = Umbra::SceneObject::OCCLUDER | Umbra::SceneObject::TARGET;
UmbraObjectComponentRequestBus::Event(entityId, [&flags](UmbraObjectComponentRequestBus::Events* handler) {
flags = 0;
flags |= (handler->GetCanOcclude() ? Umbra::SceneObject::OCCLUDER : 0);
flags |= (handler->GetCanBeOccluded() ? Umbra::SceneObject::TARGET : 0);
});
// Enumerate all visible geometry parts from components attached to this entity.
AzFramework::VisibleGeometryContainer geometryContainer;
handler->BuildVisibleGeometry(AZ::Aabb::CreateNull(), geometryContainer);
// Add each visible geometry part from the container to the umbra scene with a unique part ID.
uint32_t partId = 0;
for (const auto& geometry : geometryContainer)
{
const uint32_t objectIndex = aznumeric_cast<uint32_t>(sceneDescriptor.m_objectDescriptors.size());
// Create and umbra model using the triangle list from the visible geometry.
// TODO Implement a mechanism to reuse shared model data if shapes or multiple instances of the same model are included in the level.
const Umbra::SceneModel* model = scene->insertModel(
geometry.m_vertices.data(),
geometry.m_indices.data(),
aznumeric_cast<int>(geometry.m_vertices.size() / 3),
aznumeric_cast<int>(geometry.m_indices.size() / 3));
Umbra::Matrix4x4 transform = {};
geometry.m_transform.StoreToRowMajorFloat16(reinterpret_cast<float*>(transform.m));
// Transparent geometry will automatically be excluded as occluders.
if (geometry.m_transparent)
{
flags &= ~Umbra::SceneObject::OCCLUDER;
}
scene->insertObject(model, transform, objectIndex, flags, Umbra::MF_ROW_MAJOR);
UmbraObjectDescriptor objectDescriptor;
objectDescriptor.m_entityId = entityId;
objectDescriptor.m_partId = partId++;
sceneDescriptor.m_objectDescriptors.emplace_back(AZStd::move(objectDescriptor));
}
return true;
});
// Enumerate all view volumes and add them to the umbra seems to define the area where the camera is expected to move.
UmbraViewVolumeComponentRequestBus::EnumerateHandlers(
[&sceneDescriptor, scene, this]([[maybe_unused]] UmbraViewVolumeComponentRequestBus::Events* handler) -> bool
{
const AZ::EntityId entityId = *(UmbraViewVolumeComponentRequestBus::GetCurrentBusId());
if (!ShouldExportEntity(entityId))
{
return true;
}
// Use the entity world bounds to determine the bounding box for the view volume.
AZ::Aabb bounds = AZ::Aabb::CreateNull();
AzFramework::BoundsRequestBus::EventResult(bounds, entityId, &AzFramework::BoundsRequestBus::Events::GetWorldBounds);
if (bounds.IsValid() && bounds.IsFinite())
{
const uint32_t objectIndex = aznumeric_cast<uint32_t>(sceneDescriptor.m_objectDescriptors.size());
Umbra::Vector3 min = {};
bounds.GetMin().StoreToFloat3(min.v);
Umbra::Vector3 max = {};
bounds.GetMax().StoreToFloat3(max.v);
scene->insertViewVolume(min, max, objectIndex);
// If the view volume overrode scene settings then store them in the scene settings map for the builder.
if (handler->GetOverrideSceneSettings())
{
sceneDescriptor.m_sceneSettingsByView[objectIndex] = handler->GetSceneSettings();
}
UmbraObjectDescriptor objectDescriptor;
objectDescriptor.m_entityId = entityId;
objectDescriptor.m_partId = 0;
sceneDescriptor.m_objectDescriptors.emplace_back(AZStd::move(objectDescriptor));
}
return true;
});
// The scene will be written to a file that can be picked up by the asset processor and converted into an umbra tome.
if (!scene->writeToFile(scenePath.c_str()))
{
AZ_Error("Umbra", false, "Failed to save umbra scene: %s", scenePath.c_str());
scene->release();
return false;
}
scene->release();
AZStd::string sceneDescPath = scenePath;
AZ::StringFunc::Path::ReplaceExtension(sceneDescPath, UmbraSceneDescriptor::Extension);
if (!AZ::JsonSerializationUtils::SaveObjectToFile(&sceneDescriptor, sceneDescPath))
{
AZ_Error("Umbra", false, "Failed to save umbra scene descriptor: %s", sceneDescPath.c_str());
return false;
}
return true;
}