bool EditorUmbraSceneComponent::ExportUmbraScene()

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;
    }