Save/load from file example

/**
 * This example demonstrates how to save the contents of the LogicEngine class
 * to a file and then load it again, including references to a Ramses scene.
 */


void CreateAndSaveContent(const std::string& ramsesSceneFile);

int main()
{
    /**
     * Define file names to use where content will be saved
     */
    const std::string ramsesSceneFile = "scene.ramses";

    /**
     * Create a simple triangle scene and a script which controls it. Save both to its own file.
     */
    CreateAndSaveContent(ramsesSceneFile);

    /**
     * Load the Ramses scene from the file. It has to be loaded first, so that we can
     * resolve the Ramses objects when we load the logic content later.
     */
    ramses::RamsesFrameworkConfig config{ramses::EFeatureLevel_Latest};
    ramses::RamsesFramework ramsesFramework(config);
    ramses::RamsesClient& client = *ramsesFramework.createClient("example client");
    ramses::Scene* scene = client.loadSceneFromFile(ramsesSceneFile.c_str());

    /**
     * The loaded scene contains the Logic instance
     */
    ramses::LogicEngine& logicEngine = *scene->findObject<ramses::LogicEngine>("example logic");

    /**
     * ramses::LogicEngine provides iterable collections to its objects. We can use them to resolve objects after loading
     */
    ramses::Collection<ramses::LuaScript> loadedScripts = logicEngine.getCollection<ramses::LuaScript>();
    ramses::Collection<ramses::NodeBinding> loadedNodeBindings = logicEngine.getCollection<ramses::NodeBinding>();

    /**
     * We can use any STL algorithm on the collections.
     * In this simple example, we use find_if() to search for a specific script by its name.
     * We get an iterator which can be dereferenced to ramses::LuaScript*
     */
    ramses::Collection<ramses::LuaScript>::iterator triangleRotationScript = std::find_if(loadedScripts.begin(), loadedScripts.end(),
        [](ramses::LuaScript* script) {return script->getName() == "simple rotation script"; });

    /**
     * We can do the same to find a ramses node binding. We can use the binding to obtain a pointer to the ramses::Node further down.
     * This is an alternative to ramses::Scene::findObject() methods.
     * Note that this example uses fully qualified names and type traits for documentation sake. You can also just use 'auto'.
     */
    ramses::Collection<ramses::NodeBinding>::const_iterator triangleNodeBinding = std::find_if(loadedNodeBindings.cbegin(), loadedNodeBindings.cend(),
        [](const ramses::NodeBinding* binding) {return binding->getName() == "link to triangle node"; });

    /**
     * The LogicEngine iterators work just like any other STL forward iterator - can be compared, dereferenced, incremented etc.
     */
    assert(triangleRotationScript != loadedScripts.end());
    assert(triangleNodeBinding != loadedNodeBindings.cend());

    /**
     * Changing properties on the freshly loaded LogicEngine and calling update() works as expected - executes all scripts,
     * passes data over the linked logic nodes, and updates the values in the ramses scene.
     */
    triangleRotationScript->getInputs()->getChild("time_msec")->set<int32_t>(300);
    logicEngine.update();

    ramses::vec3f nodeRotation;
    triangleNodeBinding->getRamsesNode().getRotation(nodeRotation);

    std::cout << "\n\nRamses node rotation after loading from file and updating: {" << nodeRotation[0] << ", " << nodeRotation[1] << ", " << nodeRotation[2] << "}\n\n";

    return 0;
}


/**
 * Helper method which creates a simple ramses scene, a simple script, and
 * saves the content in two separate files
 */
void CreateAndSaveContent(const std::string &ramsesSceneFile)
{
    /**
     * Boilerplate Ramses code which saves a red triangle scene in a file. For more ramses
     * examples and details, check the ramses docs at https://bmwcarit.github.io/ramses
     */
    ramses::RamsesFrameworkConfig config{ramses::EFeatureLevel_Latest};
    ramses::RamsesFramework ramsesFramework(config);
    ramses::RamsesClient& client = *ramsesFramework.createClient("example client");

    ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), "red triangle scene");

    ramses::PerspectiveCamera* camera = scene->createPerspectiveCamera();
    camera->setFrustum(19.0f, 1.0f, 0.1f, 100.0f);
    camera->setViewport(0, 0, 800, 800);
    camera->setTranslation({0.0f, 0.0f, 5.0f});
    ramses::RenderPass* renderPass = scene->createRenderPass();
    renderPass->setClearFlags(ramses::EClearFlag::None);
    renderPass->setCamera(*camera);
    ramses::RenderGroup* renderGroup = scene->createRenderGroup();
    renderPass->addRenderGroup(*renderGroup);

    std::array<ramses::vec3f, 3u> vertexPositionsArray{ ramses::vec3f{-1.f, 0.f, -1.f}, ramses::vec3f{1.f, 0.f, -1.f}, ramses::vec3f{0.f, 1.f, -1.f} };
    ramses::ArrayResource* vertexPositions = scene->createArrayResource(3u, vertexPositionsArray.data());

    ramses::EffectDescription effectDesc;
    effectDesc.setVertexShader(R"(
        #version 100

        uniform highp mat4 mvpMatrix;

        attribute vec3 a_position;

        void main()
        {
            gl_Position = mvpMatrix * vec4(a_position, 1.0);
        }
        )");
    effectDesc.setFragmentShader(R"(
        #version 100

        void main(void)
        {
            gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
        }
        )");

    effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix);

    const ramses::Effect* effect = scene->createEffect(effectDesc);
    ramses::Appearance* appearance = scene->createAppearance(*effect);

    ramses::Geometry* geometry = scene->createGeometry(*effect);
    std::optional<ramses::AttributeInput> optPositionsInput = effect->findAttributeInput("a_position");
    assert(optPositionsInput.has_value());
    geometry->setInputBuffer(*optPositionsInput, *vertexPositions);

    ramses::MeshNode* meshNode = scene->createMeshNode("triangle mesh node");
    meshNode->setAppearance(*appearance);
    meshNode->setIndexCount(3);
    meshNode->setGeometry(*geometry);

    renderGroup->addMeshNode(*meshNode);

    scene->flush();

    /**
     * Create a LogicEngine instance for creating and saving a simple script which references a ramses Node
     */
    ramses::LogicEngine& logicEngine{ *scene->createLogicEngine("example logic") };
    ramses::NodeBinding* nodeBinding = logicEngine.createNodeBinding(*meshNode, ramses::ERotationType::Euler_XYZ, "link to triangle node");

    /**
     * Create a simple script which sets the rotation values of a node based on simulated time
     */
    ramses::LuaScript* simpleScript = logicEngine.createLuaScript(R"(
            function interface(IN,OUT)
                IN.time_msec = Type:Int32()
                OUT.rotationZ = Type:Vec3f()
            end

            function run(IN,OUT)
                -- Rotate around Z axis with 100 degrees per second
                OUT.rotationZ = {0, 0, IN.time_msec / 10}
            end
        )", {}, "simple rotation script");

    /**
     * Link the script output to the node binding input so that the value produced by the script is passed to the ramses node on update
     */
    logicEngine.link(*simpleScript->getOutputs()->getChild("rotationZ"), *nodeBinding->getInputs()->getChild("rotation"));

    /**
     * Call update() before saving to ensure the ramses scene is in a state where all settings (in this case, the node's rotation)
     * have been set once before saving
     */
    logicEngine.update();

    /**
     * Save the ramses scene (includes the script, the node binding and the link)
     */
    [[maybe_unused]] auto status = scene->saveToFile(ramsesSceneFile.c_str(), {});
}