Overview

Note

Prefer learning by example? Jump straight to the examples! Looking for a specific class or method? Check the class index

Object types and their relationships

The Logic Engine consists of a network of Lua scripts with a set of inputs and outputs with links between them. A special type of object we call binding serves as a bridge to a Ramses scene.

The following graph shows an example of such script network:

_images/overview.svg

The scripts have inputs and outputs which together define the script’s interface (more info on scripts). Scripts can be linked together directionally, so that the output of a script can provide its data to the input of another script (more info on links). Scripts can’t interact with Ramses objects directly. Instead, they can link to Bindings which are designed to “bind” Ramses objects and modify their properties’ values (node visibility, transformation values, material properties etc.) (more info on bindings).

The greyed-out slots in the image above represent input properties which are neither linked nor have a statically configured value. In bindings, this denotes that the corresponding Ramses property is not being updated by the Logic Engine (see also next section). In scripts, these properties will receive a default value at runtime (0, 0.0f, “”, true etc.) unless explicitly set by the application logic. Bindings’ input values are initialized with the values of the bound Ramses object, for all bindings except rlogic::RamsesAppearanceBinding. Usually, script inputs without a configured value or a link to other output are considered the interface of the logic network towards a runtime application, and their values are supposed to be explicitly set at runtime.

Note

One of the planned features of the Logic Engine is to formalize interface inputs in a special interface class in a future release.

Data flow

The cornerstone of the Logic Engine is the rlogic::LogicEngine::update() method which “executes” the network of logic nodes and updates the values of the Ramses scene bound to some of them. The nodes are executed based on a topological graph sort, where the traversal direction is given by the link pairs (A, B) where A is an output and B is an input property in the logic graph (as shown here).

The update logic of each node depends on its type. Lua scripts execute their run() function and modify some or all of their outputs based on the logic defined in run(). Ramses bindings pass the values of their input properties to the bound Ramses object.

Logic nodes are not executed on every rlogic::LogicEngine::update() iteration in order to save performance. However, it’s guaranteed that:

  • scripts which were just created will be executed on next update

  • scripts which inputs received a new value (either from calling rlogic::Property::set() or from a link) will be executed on next update

  • binding properties which received a value (regardless of their current value or from the value stored in Ramses) will overwrite the value in Ramses on next update. This works both for direct rlogic::Property::set() calls and for values received over links

Additionally, bindings’ properties are applied selectively - e.g. setting the scaling property of a rlogic::RamsesNodeBinding will result in a call to ramses::Node::setScaling(), but will not cause setting any other ramses::Node properties. This can be useful if you want to have your own logic e.g. to control the visibility of all Ramses nodes, and only use a Logic Engine to control transformation properties. In that case you should never set the visibility property of a Binding object, instead set the visibility directly on the bound ramses::Node.

Warning

We strongly discourage setting values to Ramses objects and to Ramses Logic bindings in the same update cycle for the same property to avoid unexpected behavior. At any given time, use one or the other, not both mechanisms to set values!

The Logic Engine can be also serialized and deserialized into binary files for fast loading. The above data flow rules still apply as if all the scripts and binding objects were just created. The first call to rlogic::LogicEngine::update() after loading from file will execute all scripts. Binding values will only be passed further to Ramses if their values were modified, e.g. by a link which produced a different value than before saving, or if the application called rlogic::Property::set() explicitly on any of the bindings’ input properties. For more details on saving and loading, see the section further down.

Script creation

The entry point to RAMSES logic is a factory-style class rlogic::LogicEngine which can create instances of all other types of objects supported by RAMSES Logic:

You can create multiple instances of rlogic::LogicEngine, but each copy owns the objects it created, and must be used to destroy them, as befits a factory class.

You can create scripts using the rlogic::LogicEngine class like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "ramses-logic/LogicEngine.h"

using namespace ramses::logic;

std::string source = R"(
    function interface()
        IN.gear = INT
        OUT.speed = FLOAT
    end

    function run()
        OUT.speed = IN.gear * 15
    end
)"

LogicEngine engine;
LuaScript* script = engine.createLuaScriptFromSource(source, "simple script");
script->getInputs()->getChild("gear")->set<int32_t>(4);

script->execute();
float speed = script->getOutputs()->getChild("speed")->get<float>();
std::cout << "OUT.speed == " << speed;

For details regarding the Lua syntax and its specifics, check the dedicated section on Lua.

rlogic::LogicNode (the base class of rlogic::LuaScript) provides an interface to access the inputs and outputs declared by the interface() function - see rlogic::LogicNode::getInputs() and rlogic::LogicNode::getOutputs().

You can link scripts to form a more sophisticated logic execution graph.

You can bind to Ramses objects to control a 3D Ramses scene.

Finally, the rlogic::LogicEngine class and all its content can be also saved/loaded from a file. Refer to the section on saving/loading from files for more details.

Object lifecycle

All objects besides the rlogic::LogicEngine instance follow a strict factory pattern. An object X is created by a method of the shape X* LogicEngine::createX(...). The pointer returned shall not be freed or deleted, instead objects must be destroyed by calling rlogic::LogicEngine::destroy().

Note

This may seem strange for a library which is based on C++17, but there are good reasons for this design choice. Smart pointers don’t work well together with Windows DLL’s, specifically when combining different CRTs. In order to provide a stable API on Windows we chose to use raw pointers and hide object creation/deletion behind a pimpl/factory pattern.

The rlogic::LogicEngine doesn’t create or destroy objects on its own - all data is explicitly created by calling create and destroy methods. There are two special cases worth mentioning:

Note

Loading data from files will invalidate all previous pointers to objects in the rlogic::LogicEngine. To avoid that, we recommend generally avoiding using a logicengine instance which already has content to load from files, and instead always create a fresh instance.

Linking scripts to Ramses scenes

Lua scripts would not make much sense on their own if they can’t interact with Ramses scene objects. The way to link script output properties to Ramses scene objects is by creating rlogic::RamsesBinding instances and linking their inputs to scripts’ outputs. There are different binding types depending on the type of Ramses object - refer to rlogic::RamsesBinding for the full list of derived classes. Bindings can be linked in the exact same way as scripts can. In fact, they derive from the same base class - rlogic::LogicNode. The only difference is that the bindings have only input properties (the outputs are implicitly defined and statically linked to the Ramses objects attached to them), whereas scripts have inputs and outputs explicitly defined in the script interface.

One might wonder, why not allow to directly link script outputs to Ramses objects? The reason for that is two-fold:

The section on data flow describes how data is passed throughout the network of logic nodes and when bound Ramses objects are updated and when not.

Note

Binding input values are initialized with the same values as the Ramses objects they “bind”. The only exception to this are Appearance bindings - extracting all data from Ramses Appearances would incur performance costs not worth the convenience.

Error handling

Some of the RAMSES Logic classes’ methods can issue errors when used incorrectly or when a Lua script encounters a compile-time or run-time error. Those errors are globally collected by the rlogic::LogicEngine class and can be obtained by calling rlogic::LogicEngine::getErrors(). The error information stored in rlogic::ErrorData contains additional stack trace information for Lua runtime errors, and a pointer to the originating rlogic::LogicNode which caused the error for errors which occured during rlogic::LogicEngine::update() and can’t be directly attributed to a specific API call. Beware that any of the mutable methods of rlogic::LogicEngine clear the previously generated errors in the list, so that the list only ever contains the errors since the last method call!

For code samples which demonstrate how compile-time and runtime errors can be gracefully handled, have a look at the examples.

Iterating over object collections

Iterating over objects can be useful, for example when loading content from files or when applying search or filter algorithms over all objects from a specific type. The rlogic::LogicEngine class provides iterator-style access to all of its objects:

1
2
3
4
5
6
7
LogicEngine logicEngine;
Collection<LuaScript> allScripts = logicEngine.scripts();

for(const auto script : allScripts)
{
    std::cout << "Script name: " << script->getName() << std::endl;
}

The rlogic::Collection class and the iterators it returns are STL-compatible, meaning that you can use them with any other STL algorithms or libraries which adhere to STL principles. The iterators implement forward iterator semantics (have a look at C++ docs).

Note

The rlogic::Iterator and rlogic::Collection classes are not following the pimpl pattern as the rest of the Ramses Logic to performance ends. Be careful not to depend on any internals of the classes (mostly the Internally wrapped STL containers) to avoid compatibility problems when updating the Ramses Logic version!

Saving/Loading from file

The rlogic::LogicEngine class and its content can be stored in a file and loaded from file again using the functions rlogic::LogicEngine::saveToFile() and rlogic::LogicEngine::loadFromFile(). The latter has an optional argument to provide a Ramses scene which should be used to resolve references to Ramses objects in the Logic Engine file. Read further for more details.

Note

Even though it would be technically possible to combine the storing and loading of Ramses scenes together with the Logic Engine and its scripts in a single file, we decided to not do this but instead keep the content in separate files and load/save it independently. This allows to have the same Ramses scene stored multiple times or with different settings, but using the same logic content, as well as the other way around - having different logic implementations based on the same Ramses scene. It also leaves more freedom to choose how to store the Ramses scene. This implies that at most a single Ramses scene can be referenced at the time of saving, having more than one scene will result in error.

Object lifecycle when saving and loading to files

After loading, the current state of the logic engine objects will be completely overwritten by the contents from the file. If you don’t want this behavior, use two different instances of the class - one dedicated for loading from files and nothing else.

Here is a simple example which demonstrates how saving/loading from file works in the simplest case (i.e. no references to Ramses objects):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Creates an empty LogicEngine instance, saves it to file and destroys the object
{
    rlogic::LogicEngine engine;
    engine.saveToFile("logicEngine.bin");
}
// Loads the file we saved above into a freshly created LogicEngine instance
{
    rlogic::LogicEngine engine;
    engine.loadFromFile("logicEngine.bin");
}

After the call to rlogic::LogicEngine::loadFromFile() successfully returns (refer to the Error handling section for info on handling errors), the state of the rlogic::LogicEngine class will be overwritten with the contents loaded from the file. This implies that all objects created prior loading will be deleted and pointers to them will be pointing to invalid memory locations. We advise designing your object lifecycles around this and immediately dispose such pointers after loading from file.

Warning

In case of error during loading the rlogic::LogicEngine may be left in an inconsistent state. In the future we may implement graceful handling of deserialization errors, but for now we suggest discarding a rlogic::LogicEngine object which failed to load.

Saving and loading together with a Ramses scene

In a slightly less simple, but more realistic setup, the Logic Engine will contain objects of type Ramses<Object>Binding which contain references to Ramses objects. In that case, use the optional ramses::Scene* argument to rlogic::LogicEngine::loadFromFile() to specify the scene from which the references to Ramses objects should be resolved. Ramses Logic uses the getSceneObjectId() method of the ramses::SceneObject class to track references to scene objects. This implies that those IDs must be the same after loading, otherwise rlogic::LogicEngine::loadFromFile() will report error and fail. Ramses Logic makes no assumptions on the origin of the scene, its name or ID.

For a full-fledged example, have a look at the serialization example.

Warning

The LogicEngine expects that immediately after loading, the state of the Ramses scene is the same as it was right before saving, and will not modify Ramses objects which are attached to bindings in the LogicEngine in its first update, unless they are linked to scripts or explicitly overwritten by rlogic::Property::set() calls after loading from the file. We strongly advice to always save and load both the Ramses scene and the LogicEngine scene together to avoid data inconsistencies!

Using memory buffer instead of file

You can use rlogic::LogicEngine::loadFromBuffer() to load the contents of the logic engine from your own memory. This can be useful if you have your own file management logic, or the data comes from a different source than a file on disk. Be mindful that passing data buffers over the boundaries of libraries can be unsafe with C++, and some errors/abuse can’t be reliably prevented. Make sure you check the size of the buffer and don’t load from memory of untrusted origins.

Logging

Internally there are four log levels available.

  • Info

  • Debug

  • Warn

  • Error

By default all internal logging messages are sent to std::cout. You can toggle this with rlogic::Logger::SetDefaultLogging(). In addition, it is possible to have a custom log handler function which is called each time a log message is issued.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <iostream>

Logger::SetLogHandler([](ElogMessageType msgType, std::string_view message){
    switch(type)
    {
        case ELogMessageType::ERROR:
            std::cout << "Error: " << message << std::endl;
            break;
        default:
            std::cout << message << std::endl;
            break;
    }
});

Inside the log handler function, you get the type of the message and the message itself as a std::string_view. Keep in mind, that you can’t store the std::string_view. It will be invalid after the call to the log handler function. If you need the message for later usage, store it in a std::string.

The amount of logging can be configured with rlogic::Logger::SetLogVerbosity(). This affects both the default logging and the custom logger.

Security and memory safety

One of the biggest challenges of modern C++ is finding a balance between compatibility with older compilers and platforms, while not sacrificing memory safety and code readibility. In the RAMSES ecosystem we try to find a good balance by testing with different compilers, employing automation techniques and making use of modern compiler-based tools to perform static code analysis and introspection. The methods and tools we use are:

  • compiling on different compilers (MSVC, gcc, clang) with strict compiler settings

  • clang-tidy with fairly strict configuration

  • valgrind

  • treat warnings as errors

  • use various clang-based sanitizers (undefined behavior, thread sanitizer, address sanitizer)

Those tools cover a lot of the standard sources of problems with C++ revolving around memory. We also uphold a strict code review, ensuring that each line of code is looked at by at least two pairs of eyes, for critical parts of the code usually more than that. Still, no project is safe from bugs, thus we recommend following some or all of the additional conventions and best practices from below subchapters to minimize the risk of memory-related bugs and malicious attacks when using Ramses Logic.

Additional memory safety measures

One of the biggest sources of bugs and security problems in C++ arise from memory management, both in terms of allocation/deallocation and memory access and boundary checks. Ramses Logic takes care of memory lifecycle for all objects created by it, and provides raw pointer access to their memory. We suggest creating your own wrapper objects for anything created or loaded by the rlogic::LogicEngine class and ensure it is destroyed exactly once and only after not used any more.

Furthermore, pay special attention when passing strings as std::string_view to and from the Logic Engine as those may not be terminated by a 0 and may lead to out of bounds accesses when used by functions expecting 0-termination.

Additional security considerations

Lua is a script language, and as such provides great flexibility and expresiveness at the cost of more error potential and security risks compared to other techniques for describing logic. The Logic engine and the underlying sol library do a lot of error checking and prevents undefined behavior by executing faulty script code, but there are cases which can’t be checked.

To give one example, a script may overwrite the global variables IN or OUT from within script code because of the nature of Lua scripts. This can’t be automatically checked by the runtime without overloading the global Lua metatable and injecting every single assignment operation, which is too high a cost to avoid faulty scripts.

To avoid malicious or broken script, we suggest implementing an additional security mechanism on top of Ramses Logic which doesn’t allow execution of scripts of unknown origin. Also, build your code with errors in mind and force scripts into an automated testing process. We also advise to use hashsums and whitelisting techniques to only execute scripts which are tested and verified to be benign.

Sanitizing of files and buffers

Since the Logic Engine can deserialize itself from files and memory buffers, it opens possibilities for data corruption and truncation. To mitigate those risks, we use Flatbuffer’s “Verify” feature which checks the integrity of data, detects possible index-out-of-range issues and prevents binary data abuse. What it doesn’t check is whether the actual memory buffer size (passed in rlogic::LogicEngine::loadFromBuffer()) is consistent with the size provided by the user. The application must ensure that this size does not exceed the size of the actual memory!

Performance

The Logic Engine is designed to be fast and efficient, as long as the performance improvements are not made at cost of unreadable code. In order to be able to track and improve the runtime of the Logic Engine, we maintain a set of benchmarks based on the google-benchmark library. These benchmarks can be used to measure the time it takes for specific operations under different loads. We kindly ask our users and developers to report performance problems by creating a benchmark which describes the specific use-case which needs optimizing. Refer to the google-benchmark docs for hints how to design good benchmarks, to set the time measurement units, derive O-complexity, etc.

Class Index

Top-level API classes:

Base classes:

Iterators:

Free functions:

Type traits:

Error information:

template<typename T>
class rlogic::Collection
#include <Collection.h>

A Collection which allows STL-style algorithm with forward semantics to be executed (e.g. find_if, for_each etc.). A Collection should not be instantiated directly, instead use one of the rlogic::LogicEngine methods:

See also rlogic::Iterator for more info on the iterator objects returned by begin and end.

Template parameters:

Public Types

using iterator = Iterator<T, internal_container_type, false>

The iterator type returned by begin() and end()

using const_iterator = Iterator<T, internal_container_type, true>

The iterator type returned by cbegin() and cend()

using value_type = typename iterator::value_type

The iterator type after dereferencing

using pointer = typename iterator::pointer

Iterator value pointer type

using reference = typename iterator::reference

Iterator value reference type

Public Functions

iterator begin()

Return an iterator to the start of the collection

Return

iterator to the start of the collection

const_iterator begin() const

Return an const iterator to the start of the collection

Return

const iterator to the start of the collection

iterator end()

Return an iterator to the end of the collection

Return

iterator to the end of the collection

const_iterator end() const

Return a const iterator to the end of the collection

Return

const iterator to the end of the collection

const_iterator cbegin() const

Return a const iterator to the start of the collection

Return

const iterator to the of the collection

const_iterator cend() const

Return a const iterator to the end of the collection

Return

const iterator to the end of the collection

Collection() = delete

Default constructor is deleted because a collection is supposed to provide read-only access to internal data of the rlogic::LogicEngine class, therefore it has to be obtained by calling methods of the rlogic::LogicEngine which return such collections, e.g. rlogic::LogicEngine::scripts()

~Collection() noexcept = default

Default destructor

Collection(const Collection &other) noexcept = default

Default copy constructor

Parameters
  • other: collection to copy

Collection &operator=(const Collection &other) noexcept = default

Default assignment operator

Parameters
  • other: collection to assign from

Collection(internal_container_type &container)

Internal constructor. Not supposed to be called from user code!

Parameters
  • container: internal container

Private Types

using internal_container_type = std::vector<std::unique_ptr<T>>

Internal container type. Not supposed to be used by user code in any way!

Private Members

std::reference_wrapper<internal_container_type> m_container

Internal reference to the container holding the actual data held by the collection.

struct rlogic::ErrorData
#include <ErrorData.h>

Holds information about an error which occured during rlogic::LogicEngine API calls

Public Members

std::string message

Error description as human-readable text. For Lua errors, an extra stack trace is contained in the error string with new-line separators.

const LogicNode *node

The rlogic::LogicNode which caused the error. Can be nullptr if the error was not originating from a specific node.

template<typename T>
struct rlogic::IsPrimitiveProperty
#include <EPropertyType.h>

Type trait which can be used to check if a type is primitive or not. “primitive” in this context means the type can be used as a template for rlogic::Property::set() and rlogic::Property::get(), i.e. it has a value which can be directly set/obtained, as opposed to non-primitive types like structs or arrays which don’t have a singular settable value.

Public Static Attributes

const bool value = false
template<>
struct rlogic::IsPrimitiveProperty<bool>
#include <EPropertyType.h>

Public Static Attributes

const bool value = true
template<>
struct rlogic::IsPrimitiveProperty<float>
#include <EPropertyType.h>

Public Static Attributes

const bool value = true
template<>
struct rlogic::IsPrimitiveProperty<int>
#include <EPropertyType.h>

Public Static Attributes

const bool value = true
template<>
struct rlogic::IsPrimitiveProperty<std::string>
#include <EPropertyType.h>

Public Static Attributes

const bool value = true
template<>
struct rlogic::IsPrimitiveProperty<vec2f>
#include <EPropertyType.h>

Public Static Attributes

const bool value = true
template<>
struct rlogic::IsPrimitiveProperty<vec2i>
#include <EPropertyType.h>

Public Static Attributes

const bool value = true
template<>
struct rlogic::IsPrimitiveProperty<vec3f>
#include <EPropertyType.h>

Public Static Attributes

const bool value = true
template<>
struct rlogic::IsPrimitiveProperty<vec3i>
#include <EPropertyType.h>

Public Static Attributes

const bool value = true
template<>
struct rlogic::IsPrimitiveProperty<vec4f>
#include <EPropertyType.h>

Public Static Attributes

const bool value = true
template<>
struct rlogic::IsPrimitiveProperty<vec4i>
#include <EPropertyType.h>

Public Static Attributes

const bool value = true
template<typename T, typename internal_container, bool isConst>
class rlogic::Iterator
#include <Iterator.h>

An STL-style iterator for various object types (T) with forward-semantics. See also:

Template parameters:

  • T: the object type to iterate over, e.g. rlogic::LuaScript

  • internal_container: the internally wrapped container type. User code should NOT depend on this type to avoid API incompatibilities!

  • isConst: true for const-iterators, false for non-const iterators

Dereferencing the iterator yields a pointer of type T* or const T*, depending if the iterator is a const iterator. The class is STL-compatible, meaning that the public type declarations may safely be used.

Additional note: the Iterator class does not support swap() functionality as well as mutable assignments, because the internal data structures used to iterate over objects are not supposed to be changed by user code. The iterators are supposed to be used only in read-only scenarios. It is still possible to exercise write access on the pointers returned by the iterator, but it’s not possible to assign a new value to the pointer itself or to swap the pointers.

Public Types

using difference_type = typename internal_iterator::difference_type

Type traits as mandated by STL for custom iterators.

using value_type = typename internal_iterator::value_type

The iterator type after dereferencing

using pointer = typename internal_iterator::pointer

Iterator value pointer type

using reference = typename internal_iterator::reference

Iterator value reference type

using iterator_category = std::forward_iterator_tag

Iterator type (refer to STL documentation for more information)

Public Functions

maybe_const_T operator*() noexcept

Operator dereferencing. Returns const T* if template argument isConst == true, otherwise returns T*.

Return

T* or const T* pointer to iterable object, depending on iterator constness

maybe_const_T operator->() noexcept

Member forwarding operator. Translates to ‘(const T*)->method’ if template argument isConst == true, otherwise to ‘(T*)->method’.

Return

T* or const T* pointer to iterable object, depending on iterator constness

Iterator &operator++() noexcept

Pre-increment operator.

Return

reference to self

Iterator operator++(int) noexcept

Post-increment operator.

Return

a copy of self before the increment was performed

template<bool otherIsConst>
bool operator==(const Iterator<T, internal_container, otherIsConst> &other) const noexcept

Equality operator.

Return

true if the iterators point to the same object internally

Parameters
  • other: the other iterator to compare to

template<bool otherIsConst>
bool operator!=(const Iterator<T, internal_container, otherIsConst> &other) const noexcept

Inequality operator.

Return

true if the iterators point to different objects internally

Parameters
  • other: the other iterator to compare to

Iterator() noexcept = default

Default constructor.

template<bool otherIsConst, typename = std::enable_if_t<isConst && !otherIsConst>>
Iterator(const Iterator<T, internal_container, otherIsConst> &other) noexcept

Constructor which allows const-iterator to be constructed from a non-const iterator, but not the other way around

Parameters
  • other: iterator to construct from

template<bool otherIsConst, class = std::enable_if_t<isConst && !otherIsConst>>
Iterator &operator=(const Iterator<T, internal_container, otherIsConst> &other) noexcept

Assignment operator which allows const-iterator to be assigned from a non-const iterator, but not the other way around

Parameters
  • other: iterator to be assign from

Iterator(const Iterator&) noexcept = default

Default copy constructor. This is redundant to the template version above, but is considered good style.

Iterator &operator=(const Iterator&) noexcept = default

Default assignment operator. This is redundant to the template version above, but is considered good style.

~Iterator() noexcept = default

Default destructor.

Iterator(internal_iterator iter) noexcept

Internal constructor which should not be called by user code to avoid API dependencies. Use rlogic::Collection::begin() and rlogic::Collection::end() or their const-counterparts to obtain iterators to desired rlogic::LogicEngine objects.

Parameters
  • iter: internal iterator to be constructed from

Private Types

using const_iter = typename internal_container::const_iterator

Internal type trait (const iterator type)

using non_const_iter = typename internal_container::iterator

Internal type trait (non-const iterator type)

using internal_iterator = typename std::conditional<isConst, const_iter, non_const_iter>::type

Internal type trait (internally wrapped iterator type)

using maybe_const_T = typename std::conditional<isConst, const T*, T*>::type

Type returned when dereferencing the iterator (conditionally metaprogrammed for const-correctness when used inside const iterator)

Private Members

internal_iterator m_iterator = {}

Internal iterator. This implementation is not following the pimpl pattern for efficiency reasons.

Friends

friend class Iterator< T, internal_container, true >
friend class Iterator< T, internal_container, false >
class rlogic::LogicEngine
#include <LogicEngine.h>

Central object which creates and manages the lifecycle and execution of scripts, bindings, and all other objects supported by the Ramses Logic library. All objects created by this class’ methods must be destroyed with destroy!

  • Use the create[Type] methods to create various objects, use destroy() to delete them.

  • Use link and unlink to connect data properties between these objects

  • use update() to trigger the execution of all objects

Public Functions

LogicEngine() noexcept

Constructor of LogicEngine.

~LogicEngine() noexcept

Destructor of LogicEngine

Collection<LuaScript> scripts() const

Returns an iterable rlogic::Collection of all rlogic::LuaScript instances created by this LogicEngine.

Return

an iterable rlogic::Collection with all rlogic::LuaScript instances created by this LogicEngine

Collection<RamsesNodeBinding> ramsesNodeBindings() const

Returns an iterable rlogic::Collection of all rlogic::RamsesNodeBinding instances created by this LogicEngine.

Return

an iterable rlogic::Collection with all rlogic::RamsesNodeBinding instances created by this LogicEngine

Collection<RamsesAppearanceBinding> ramsesAppearanceBindings() const

Returns an iterable rlogic::Collection of all rlogic::RamsesAppearanceBinding instances created by this LogicEngine.

Return

an iterable rlogic::Collection with all rlogic::RamsesAppearanceBinding created by this LogicEngine

Collection<RamsesCameraBinding> ramsesCameraBindings() const

Returns an iterable rlogic::Collection of all rlogic::RamsesCameraBinding instances created by this LogicEngine.

Return

an iterable rlogic::Collection with all rlogic::RamsesCameraBinding created by this LogicEngine

LuaScript *findScript(std::string_view name) const

Returns a pointer to the first occurrence of a script with a given name if such exists, and nullptr otherwise.

Return

a pointer to the script, or nullptr if none was found

Parameters
  • name: the name of the script to search for

RamsesNodeBinding *findNodeBinding(std::string_view name) const

Returns a pointer to the first occurrence of a node binding with a given name if such exists, and nullptr otherwise.

Return

a pointer to the node binding, or nullptr if none was found

Parameters
  • name: the name of the node binding to search for

RamsesAppearanceBinding *findAppearanceBinding(std::string_view name) const

Returns a pointer to the first occurrence of an appearance binding with a given name if such exists, and nullptr otherwise.

Return

a pointer to the appearance binding, or nullptr if none was found

Parameters
  • name: the name of the appearance binding to search for

RamsesCameraBinding *findCameraBinding(std::string_view name) const

Returns a pointer to the first occurrence of a camera binding with a given name if such exists, and nullptr otherwise.

Return

a pointer to the camera binding, or nullptr if none was found

Parameters
  • name: the name of the camera binding to search for

LuaScript *createLuaScriptFromFile(std::string_view filename, std::string_view scriptName = "")

Creates a new rlogic::LuaScript from an existing Lua source file. Refer to the rlogic::LuaScript class documentation for requirements that Lua scripts must fulfill in order to be added to the LogicEngine.

Attention! This method clears all previous errors! See also docs of getErrors()

Return

a pointer to the created object or nullptr if something went wrong during creation. In that case, use getErrors() to obtain errors. The script can be destroyed by calling the destroy method

Parameters
  • filename: path to file from which to load the script source code

  • scriptName: name to assign to the script once it’s created

LuaScript *createLuaScriptFromSource(std::string_view source, std::string_view scriptName = "")

Creates a new Lua script from a source string. Refer to the rlogic::LuaScript class for requirements which Lua scripts must fulfill in order to be added to the LogicEngine.

Attention! This method clears all previous errors! See also docs of getErrors()

Return

a pointer to the created object or nullptr if something went wrong during creation. In that case, use getErrors() to obtain errors. The script can be destroyed by calling the destroy method

Parameters
  • source: the Lua source code

  • scriptName: name to assign to the script once it’s created

RamsesNodeBinding *createRamsesNodeBinding(ramses::Node &ramsesNode, std::string_view name = "")

Creates a new rlogic::RamsesNodeBinding which can be used to set the properties of a Ramses Node object.

Attention! This method clears all previous errors! See also docs of getErrors()

Return

a pointer to the created object or nullptr if something went wrong during creation. In that case, use getErrors() to obtain errors. The binding can be destroyed by calling the destroy method

Parameters

RamsesAppearanceBinding *createRamsesAppearanceBinding(ramses::Appearance &ramsesAppearance, std::string_view name = "")

Creates a new rlogic::RamsesAppearanceBinding which can be used to set the properties of a Ramses Appearance object.

Attention! This method clears all previous errors! See also docs of getErrors()

Return

a pointer to the created object or nullptr if something went wrong during creation. In that case, use getErrors() to obtain errors. The binding can be destroyed by calling the destroy method

Parameters

RamsesCameraBinding *createRamsesCameraBinding(ramses::Camera &ramsesCamera, std::string_view name = "")

Creates a new rlogic::RamsesCameraBinding which can be used to set the properties of a Ramses Camera object.

Attention! This method clears all previous errors! See also docs of getErrors()

Return

a pointer to the created object or nullptr if something went wrong during creation. In that case, use getErrors() to obtain errors. The binding can be destroyed by calling the destroy method

Parameters

bool update()

Updates all rlogic::LogicNode’s which were created by this LogicEngine instance. The order in which rlogic::LogicNode’s are executed is determined by the links created between them (see link and unlink). rlogic::LogicNode’s which don’t have any links between then are executed in arbitrary order, but the order is always the same between two invocations of update without any calls to link or unlink between them. As an optimization rlogic::LogicNode’s are only updated, if at least one input of a rlogic::LogicNode has changed since the last call to update. If the links between logic nodes create a loop, this method will fail with an error and will not execute any of the logic nodes.

Attention! This method clears all previous errors! See also docs of getErrors()

Return

true if the update was successful, false otherwise In case of an error, use getErrors() to obtain errors.

bool link(const Property &sourceProperty, const Property &targetProperty)

Links a property of a rlogic::LogicNode to another rlogic::Property of another rlogic::LogicNode. After linking, calls to update will propagate the value of sourceProperty to the targetProperty. Creating links influences the order in which scripts are executed - if node A provides data to node B, then node A will be executed before node B. A single output property (sourceProperty) can be linked to any number of input properties (targetProperty), but any input property can have at most one link to an output property (links are directional and support a 1-to-N relationships).

The link() method will fail when:

Creating link loops will cause the next call to update() to fail with an error. Loops are directional, it is OK to have A->B, A->C and B->C, but is not OK to have A->B->C->A.

After calling link, the value of the targetProperty will not change until the next call to update. Creating and destroying links generally has no effect until update is called.

Attention! This method clears all previous errors! See also docs of getErrors()

Return

true if linking was successful, false otherwise. To get more detailed error information use getErrors()

Parameters
  • sourceProperty: the output property which will provide data for targetProperty

  • targetProperty: the target property which will receive its value from sourceProperty

bool unlink(const Property &sourceProperty, const Property &targetProperty)

Unlinks two properties which were linked with link. After a link is destroyed, calls to update will no longer propagate the output value from the sourceProperty to the input value of the targetProperty. The value of the targetProperty will remain as it was after the last call to update - it will not be restored to a default value or to any value which was set manually with calls to rlogic::Property::set().

Attention! This method clears all previous errors! See also docs of getErrors()

Return

true if unlinking was successful, false otherwise. To get more detailed error information use getErrors().

Parameters
  • sourceProperty: the output property which is currently linked to targetProperty

  • targetProperty: the property which will no longer receive the value from sourceProperty

bool isLinked(const LogicNode &logicNode) const

Checks if an input or output of a given LogicNode is linked to another LogicNode

Return

true if the given LogicNode is linked to any other LogicNode, false otherwise.

Parameters
  • logicNode: the node to check for linkage.

const std::vector<ErrorData> &getErrors() const

Returns the list of all errors which occurred during the last API call to a LogicEngine method or any other method of its subclasses (scripts, bindings etc). Note that errors get wiped by all mutable methods of the LogicEngine.

This method can be used in two different ways:

  • To debug the correct usage of the Logic Engine API (e.g. by wrapping all API calls with a check for their return value and using this method to find out the cause of the error)

  • To check for runtime errors of scripts which come from a dynamic source, e.g. by calling the method after an unsuccessful call to update() with a faulty script

Return

a list of errors

bool destroy(LogicNode &logicNode)

Destroys a rlogic::LogicNode instance. If any links are connected to this rlogic::LogicNode, they will be destroyed too. Note that after this call, the execution order of rlogic::LogicNode may change! See the docs of link and unlink for more information.

Attention! This method clears all previous errors! See also docs of getErrors()

Return

true if logicNode destroyed, false otherwise. Call getErrors() for error details upon failure.

Parameters
  • logicNode: the logic node instance to destroy

bool saveToFile(std::string_view filename)

Writes the whole LogicEngine and all of its objects to a binary file with the given filename. The RAMSES scene potentially referenced by rlogic::RamsesBinding objects is not saved - that is left to the application. LogicEngine saves the references to those object, and restores them after loading. Thus, deleting Ramses objects which are being referenced from within the LogicEngine will result in errors if the Logic Engine is loaded from the file again. Note that it is not sufficient to have objects with the same name, they have to be the exact same objects as during saving! For more in-depth information regarding saving and loading, refer to the online documentation at https://ramses-logic.readthedocs.io/en/latest/api.html#save-load-to-file

Note: The method reports error and aborts if the rlogic::RamsesBinding objects reference more than one Ramses scene (this is acceptable during runtime, but not for saving to file).

Attention! This method clears all previous errors! See also docs of getErrors()

Return

true if saving was successful, false otherwise. To get more detailed error information use getErrors()

Parameters
  • filename: path to file to save the data (relative or absolute). The file will be created or overwritten if it exists!

bool loadFromFile(std::string_view filename, ramses::Scene *ramsesScene = nullptr, bool enableMemoryVerification = true)

Loads the whole LogicEngine data from the given file. See also saveToFile(). After loading, the previous state of the LogicEngine will be overwritten with the contents loaded from the file, i.e. all previously created objects (scripts, bindings, etc.) will be deleted and pointers to them will be invalid. The (optionally) provided ramsesScene will be used to resolve potential rlogic::RamsesBinding objects which point to Ramses objects. You can provide a nullptr if you know for sure that the LogicEngine loaded from the file has no rlogic::RamsesBinding objects which point to a Ramses scene object. Otherwise, the call to loadFromFile will fail with an error. In case of errors, the LogicEngine may be left in an inconsistent state. For more in-depth information regarding saving and loading, refer to the online documentation at https://ramses-logic.readthedocs.io/en/latest/api.html#saving-loading-from-file

Attention! This method clears all previous errors! See also docs of getErrors()

Return

true if deserialization was successful, false otherwise. To get more detailed error information use getErrors()

Parameters
  • filename: path to file from which to load content (relative or absolute)

  • ramsesScene: pointer to the Ramses Scene which holds the objects referenced in the Ramses Logic file

  • enableMemoryVerification: flag to enable memory verifier (a flatbuffers feature which checks bounds and ranges). Disable this only if the file comes from a trusted source and performance is paramount.

bool loadFromBuffer(const void *rawBuffer, size_t bufferSize, ramses::Scene *ramsesScene = nullptr, bool enableMemoryVerification = true)

Loads the whole LogicEngine data from the given memory buffer. This method is equivalent to loadFromFile but allows to have the file-opening logic done by the user and only pass the data as a buffer. The logic engine only reads the data, does not take ownership of it and does not modify it. The memory can be freed or modified after the call returns, the LogicEngine keeps no references to it.

Return

true if deserialization was successful, false otherwise. To get more detailed error information use getErrors()

Parameters
  • rawBuffer: pointer to the raw data in memory

  • bufferSize: size of the data (bytes)

  • ramsesScene: pointer to the Ramses Scene which holds the objects referenced in the Ramses Logic file

  • enableMemoryVerification: flag to enable memory verifier (a flatbuffers feature which checks bounds and ranges). Disable this only if the file comes from a trusted source and performance is paramount.

LogicEngine(const LogicEngine &other) = delete

Copy Constructor of LogicEngine is deleted because logic engines hold named resources and are not supposed to be copied

Parameters
  • other: logic engine to copy from

LogicEngine(LogicEngine &&other) = delete

Move Constructor of LogicEngine is deleted because because logic engine is not supposed to be moved

Parameters
  • other: logic engine to move from

LogicEngine &operator=(const LogicEngine &other) = delete

Assignment operator of LogicEngine is deleted because logic engines hold named resources and are not supposed to be copied

Parameters
  • other: logic engine to assign from

LogicEngine &operator=(LogicEngine &&other) = delete

Move assignment operator of LogicEngine is deleted because logic engine is not supposed to be moved

Parameters
  • other: logic engine to move from

Public Members

std::unique_ptr<internal::LogicEngineImpl> m_impl

Implementation detail of LogicEngine

class rlogic::LogicNode
#include <LogicNode.h>

A base class for multiple logic classes which provides a unified interface to their inputs and outputs. Some subclasses don’t have inputs or outputs - in that case the getInputs or getOutputs methods respectively will return nullptr. Some subclasses, like the rlogic::RamsesAppearanceBinding, will have their inputs depending on their current state (in this example the GLSL uniforms of the shader to which the bound ramses Appearance belongs). In those cases, getInputs()/getOutputs() will return a rlogic::Property which represents an empty struct (type Struct, but no child properties).

Subclassed by rlogic::LuaScript, rlogic::RamsesBinding

Public Functions

Property *getInputs()

Returns a property of type Struct which holds the inputs of the LogicNode.

Returns the root Property of the LogicNode which contains potentially nested list of properties. The properties are different for the classes which derive from LogicNode. Look at the documentation of each derived class for more information on the properties. *

Return

a tree like structure with the inputs of the LogicNode

const Property *getInputs() const

Returns a property of type Struct which holds the inputs of the LogicNode.

Returns the root Property of the LogicNode which contains potentially nested list of properties. The properties are different for the classes which derive from LogicNode. Look at the documentation of each derived class for more information on the properties. *

Return

a tree like structure with the inputs of the LogicNode

const Property *getOutputs() const

Returns a property of type Struct which holds the outputs of the LogicNode

Returns the root Property of the LogicNode which contains potentially nested list of properties. The properties are different for the classes which derive from LogicNode. Look at the documentation of each derived class for more information on the properties. *

Return

a tree like structure with the outputs of the LogicNode

std::string_view getName() const

Returns the name of this LogicNode.

Return

the name of the LogicNode

void setName(std::string_view name)

Sets the name of this LogicNode.

Parameters

~LogicNode() noexcept

Destructor of LogicNode

LogicNode(const LogicNode &other) = delete

Copy Constructor of LogicNode is deleted because LogicNodes are not supposed to be copied

Parameters

LogicNode(LogicNode &&other) = delete

Move Constructor of LogicNode is deleted because LogicNodes are not supposed to be moved

Parameters

LogicNode &operator=(const LogicNode &other) = delete

Assignment operator of LogicNode is deleted because LogicNodes are not supposed to be copied

Parameters

LogicNode &operator=(LogicNode &&other) = delete

Move assignment operator of LogicNode is deleted because LogicNodes are not supposed to be moved

Parameters

Public Members

std::reference_wrapper<internal::LogicNodeImpl> m_impl

Implementation detail of LuaScript

Protected Functions

LogicNode(std::reference_wrapper<internal::LogicNodeImpl> impl) noexcept

Constructor of LogicNode. User is not supposed to call this - LogcNodes are created by subclasses

Parameters
  • impl: implementation details of the LogicNode

class rlogic::LuaScript : public rlogic::LogicNode
#include <LuaScript.h>

The LuaScript class is the cornerstone of RAMSES Logic as it encapsulates a Lua script and the associated with it Lua environment. LuaScript instances are created by the rlogic::LogicEngine class.

A LuaScript can be created from Lua source code which must fulfill following requirements:

  • valid Lua 5.1 syntax

  • contains two global functions - interface() and run() with no parameters and no return values

  • declares its inputs and outputs in the interface() function, and its logic in the run() function

  • the interface() function declares zero or more inputs and outputs to the IN and OUT global symbols

  • inputs and outputs are declared like this:

    function interface()
        IN.input_name = TYPE
        OUT.output_name = TYPE
    end
    

  • TYPE is one of [INT|FLOAT|BOOL|STRING|VEC2F|VEC3F|VEC4F|VEC2I|VEC3I|VEC4I], or…

  • TYPE can be also a Lua table with nested properties, obeying the same rules as above, or…

  • TYPE can be an array declaration of the form ARRAY(n, T) where:

    • n is a positive integer

    • T obeys the same rules as TYPE, except T can not be an ARRAY itself

    • T can be a struct, i.e. arrays of structs are supported

  • Each property must have a name (string) - other types like number, bool etc. are not supported as keys

  • the run() function only accesses the IN and OUT global symbols and the properties defined by it

Violating any of these requirements will result in errors, which can be obtained by calling rlogic::LogicEngine::getErrors(). The LuaScript object encapsulates a Lua environment (see official Lua docs) which strips all global table entries after the script is loaded to the Lua state, and leaves only the run() function.

See also the full documentation at https://ramses-logic.readthedocs.io/en/latest/api.html for more details on Lua and its interaction with C++.

Public Functions

std::string_view getFilename() const

Returns the filename provided when the script was created

Return

the filename of the script

void overrideLuaPrint(LuaPrintFunction luaPrintFunction)

Overrides the lua print function with a custom function. Each time “print” is used inside a lua script, the function will be called. Because the lua “print” function allows an arbitrary amount of parameters, the function is called for each provided parameter.

Parameters
  • luaPrintFunction: to use for printing

LuaScript(std::unique_ptr<internal::LuaScriptImpl> impl) noexcept

Constructor of LuaScript. User is not supposed to call this - script are created by other factory classes

Parameters
  • impl: implementation details of the script

~LuaScript() noexcept override

Destructor of LuaScript

LuaScript(const LuaScript &other) = delete

Copy Constructor of LuaScript is deleted because scripts are not supposed to be copied

Parameters
  • other: script to copy from

LuaScript(LuaScript &&other) = delete

Move Constructor of LuaScript is deleted because scripts are not supposed to be moved

Parameters
  • other: script to move from

LuaScript &operator=(const LuaScript &other) = delete

Assignment operator of LuaScript is deleted because scripts are not supposed to be copied

Parameters
  • other: script to assign from

LuaScript &operator=(LuaScript &&other) = delete

Move assignment operator of LuaScript is deleted because scripts are not supposed to be moved

Parameters
  • other: script to move from

Public Members

std::unique_ptr<internal::LuaScriptImpl> m_script

Implementation detail of LuaScript

class rlogic::Property
#include <Property.h>

Represents a generic property slot of the rlogic::LogicNode and its derived classes. Properties can have primitive types (string, integer, etc.) or complex types (structs, arrays). Complex types can have “children”, i.e. nested properties: named fields (structs), or indexed fields (arrays).

Public Functions

EPropertyType getType() const

Returns the type of this Property

Return

the type of this Property

std::string_view getName() const

Returns the name of this Property. Note that not all properties have a name - for example an array element does not have a name. In that case the result will be an empty string view. Struct fields always have a non-empty name.

Return

the name of this Property

size_t getChildCount() const

Returns the amount of available child (nested) properties. If the Property is of type rlogic::EPropertyType::Struct, the returned number will correspond to the number of named properties of the struct. If the Property is of type rlogic::EPropertyType::Array, the method will return the array size. For all other property types getChildCount returns zero.

Return

the number of nested properties.

const Property *getChild(size_t index) const

Returns the child property with the given index. index must be < getChildCount().

This method can be used to get nested properties of structs and arrays. For primitive types this will always return nullptr.

Note that array indexing in getChild follows C++ conventions and not Lua conventions! Inside the Lua scripts, you can and must use Lua conventions when indexing arrays (start at 1, end at N) while in C++ you must use [0, N-1].

Struct properties can also be retrieved by index. The ordering is not guaranteed to match the order of declaration inside Lua scripts (Lua explicitly warns to not rely on ordering of named table entries!). However, once a script is created, the index will not change, i.e. it is permitted that user code caches the property index for faster future access.

Return

the child with the given index, or nullptr if the property is primitive or the index is out of range

Property *getChild(size_t index)

Returns the child property with the given index. index must be < getChildCount().

This method can be used to get nested properties of structs and arrays. For primitive types this will always return nullptr.

Note that array indexing in getChild follows C++ conventions and not Lua conventions! Inside the Lua scripts, you can and must use Lua conventions when indexing arrays (start at 1, end at N) while in C++ you must use [0, N-1].

Struct properties can also be retrieved by index. The ordering is not guaranteed to match the order of declaration inside Lua scripts (Lua explicitly warns to not rely on ordering of named table entries!). However, once a script is created, the index will not change, i.e. it is permitted that user code caches the property index for faster future access.

Return

the child with the given index, or nullptr if the property is primitive or the index is out of range

Property *getChild(std::string_view name)

Searches for a child with the given name. Only properties of type rlogic::EPropertyType::Struct can return a child by name. In case of a primitive property or array this method will return nullptr.

Note that this method may be slower than getChild(size_t index) as it must do a string-based search.

Return

the child with the given name, or nullptr if property is not of type rlogic::EPropertyType::Struct

const Property *getChild(std::string_view name) const

Searches for a child with the given name. Only properties of type rlogic::EPropertyType::Struct can return a child by name. In case of a primitive property or array this method will return nullptr.

Note that this method may be slower than getChild(size_t index) as it must do a string-based search.

Return

the child with the given name, or nullptr if property is not of type rlogic::EPropertyType::Struct

template<typename T>
std::optional<T> get() const

Returns the value of this property. The supported template types are defined by rlogic::IsPrimitiveProperty where IsPrimitiveProperty<T>::value == true for a type T.

Attention! We recommend always specifying the template argument T explicitly, and don’t rely on the compiler’s type deduction! If T is not one of the supported types, a static_assert will be triggered!

Returns nullopt if the template type T does not match the internal type of the property.

Return

the value of this Property as std::optional or std::nullopt if T does not match.

template<typename T>
bool set(T value)

Sets the value of this Property. Same rules apply to template parameter T as in get()

Attention! We recommend always specifying the template argument T explicitly, and don’t rely on the compiler’s type deduction! If T is not one of the supported types, a static_assert will be triggered!

Return

true if setting the value was successful, false otherwise.

Parameters
  • value: the value to set for this Property

Property(std::unique_ptr<internal::PropertyImpl> impl) noexcept

Constructor of Property. User is not supposed to call this - properties are created by other factory classes

Parameters
  • impl: implementation details of the property

~Property() noexcept

Destructor of Property. User is not supposed to call this - properties are destroyed by other factory classes

Property(const Property &other) = delete

Copy Constructor of Property is deleted because properties are not supposed to be copied

Parameters
  • other: property to copy from

Property(Property &&other) = delete

Move Constructor of Property is deleted because properties are not supposed to be moved

Parameters
  • other: property to move from

Property &operator=(const Property &other) = delete

Assignment operator of Property is deleted because properties are not supposed to be copied

Parameters
  • other: property to assign from

Property &operator=(Property &&other) = delete

Move assignment operator of Property is deleted because properties are not supposed to be moved

Parameters
  • other: property to move from

Public Members

std::unique_ptr<internal::PropertyImpl> m_impl

Implementation details of the Property class

Private Functions

template<typename T>
std::optional<T> getInternal() const

Internal implementation of get

template<typename T>
bool setInternal(T value)

Internal implementation of set

template<typename T>
struct PropertyTypeToEnum
#include <EPropertyType.h>

Type trait which converts C++ types to rlogic::EPropertyType enum for primitive types.

template<>
struct rlogic::PropertyTypeToEnum<bool>
#include <EPropertyType.h>

Public Static Attributes

const EPropertyType TYPE = EPropertyType::Bool
template<>
struct rlogic::PropertyTypeToEnum<float>
#include <EPropertyType.h>

Public Static Attributes

const EPropertyType TYPE = EPropertyType::Float
template<>
struct rlogic::PropertyTypeToEnum<int>
#include <EPropertyType.h>

Public Static Attributes

const EPropertyType TYPE = EPropertyType::Int32
template<>
struct rlogic::PropertyTypeToEnum<std::string>
#include <EPropertyType.h>

Public Static Attributes

const EPropertyType TYPE = EPropertyType::String
template<>
struct rlogic::PropertyTypeToEnum<vec2f>
#include <EPropertyType.h>

Public Static Attributes

const EPropertyType TYPE = EPropertyType::Vec2f
template<>
struct rlogic::PropertyTypeToEnum<vec2i>
#include <EPropertyType.h>

Public Static Attributes

const EPropertyType TYPE = EPropertyType::Vec2i
template<>
struct rlogic::PropertyTypeToEnum<vec3f>
#include <EPropertyType.h>

Public Static Attributes

const EPropertyType TYPE = EPropertyType::Vec3f
template<>
struct rlogic::PropertyTypeToEnum<vec3i>
#include <EPropertyType.h>

Public Static Attributes

const EPropertyType TYPE = EPropertyType::Vec3i
template<>
struct rlogic::PropertyTypeToEnum<vec4f>
#include <EPropertyType.h>

Public Static Attributes

const EPropertyType TYPE = EPropertyType::Vec4f
template<>
struct rlogic::PropertyTypeToEnum<vec4i>
#include <EPropertyType.h>

Public Static Attributes

const EPropertyType TYPE = EPropertyType::Vec4i
class rlogic::RamsesAppearanceBinding : public rlogic::RamsesBinding
#include <RamsesAppearanceBinding.h>

The RamsesAppearanceBinding is a type of rlogic::RamsesBinding which allows the rlogic::LogicEngine to control instances of ramses::Appearance. RamsesAppearanceBinding’s can be created with rlogic::LogicEngine::createRamsesAppearanceBinding.

The RamsesAppearanceBinding has a static link to a ramses::Appearance. After creation, rlogic::LogicNode::getInputs will return a struct property with children equivalent to the uniform inputs of the provided ramses Appearance.

Since the RamsesAppearanceBinding derives from rlogic::RamsesBinding, it also provides the rlogic::LogicNode::getInputs and rlogic::LogicNode::getOutputs method. For this particular implementation, the methods behave as follows:

  • rlogic::LogicNode::getInputs: returns the inputs corresponding to the available shader uniforms of the bound ramses::Appearance

  • rlogic::LogicNode::getOutputs: returns always nullptr, because a RamsesAppearanceBinding does not have outputs, it implicitly controls the ramses Appearance

  • The values of this binding’s inputs are initialized to default values (0, 0.0f, etc) and not loaded from the values in Ramses

All shader uniforms are supported, except the following:

  • texture samplers of any kind

  • matrix types (e.g. mat4, mat23 etc.)

  • any uniform with attached semantics (e.g. display resolution) - see ramses::EEffectUniformSemantic docs

Uniform types which are not supported are not available when queried over rlogic::LogicNode::getInputs.

Public Functions

ramses::Appearance &getRamsesAppearance() const

Returns the bound Ramses Appearance.

Return

the bound ramses appearance

RamsesAppearanceBinding(std::unique_ptr<internal::RamsesAppearanceBindingImpl> impl) noexcept

Constructor of RamsesAppearanceBinding. User is not supposed to call this - RamsesAppearanceBinding are created by other factory classes

Parameters

~RamsesAppearanceBinding() noexcept override

Destructor of RamsesAppearanceBinding.

RamsesAppearanceBinding(const RamsesAppearanceBinding &other) = delete

Copy Constructor of RamsesAppearanceBinding is deleted because RamsesAppearanceBinding are not supposed to be copied

Parameters
  • other: RamsesNodeBindings to copy from

RamsesAppearanceBinding(RamsesAppearanceBinding &&other) = delete

Move Constructor of RamsesAppearanceBinding is deleted because RamsesAppearanceBinding are not supposed to be moved

Parameters

RamsesAppearanceBinding &operator=(const RamsesAppearanceBinding &other) = delete

Assignment operator of RamsesAppearanceBinding is deleted because RamsesAppearanceBinding are not supposed to be copied

Parameters

RamsesAppearanceBinding &operator=(RamsesAppearanceBinding &&other) = delete

Move assignment operator of RamsesAppearanceBinding is deleted because RamsesAppearanceBinding are not supposed to be moved

Parameters

Public Members

std::unique_ptr<internal::RamsesAppearanceBindingImpl> m_appearanceBinding

Implementation detail of RamsesAppearanceBinding

class rlogic::RamsesBinding : public rlogic::LogicNode
#include <RamsesBinding.h>

The RamsesBinding is a shared base class for bindings to Ramses objects. For details on each type of binding, look at the derived classes.

Subclassed by rlogic::RamsesAppearanceBinding, rlogic::RamsesCameraBinding, rlogic::RamsesNodeBinding

Public Functions

RamsesBinding(std::reference_wrapper<internal::RamsesBindingImpl> impl) noexcept

Constructor of RamsesBinding. User is not supposed to call this - RamsesBinding are created by other factory classes

Parameters

~RamsesBinding() noexcept override = default

Destructor of RamsesBinding.

RamsesBinding(const RamsesBinding &other) = delete

Copy Constructor of RamsesBinding is deleted because RamsesBindings are not supposed to be copied

Parameters
  • other: RamsesBindings to copy from

RamsesBinding(RamsesBinding &&other) = delete

Move Constructor of RamsesBinding is deleted because RamsesBindings are not supposed to be moved

Parameters
  • other: RamsesBindings to move from

RamsesBinding &operator=(const RamsesBinding &other) = delete

Assignment operator of RamsesBinding is deleted because RamsesBindings are not supposed to be copied

Parameters
  • other: RamsesBindings to assign from

RamsesBinding &operator=(RamsesBinding &&other) = delete

Move assignment operator of RamsesBinding is deleted because RamsesBindings are not supposed to be moved

Parameters
  • other: RamsesBindings to assign from

class rlogic::RamsesCameraBinding : public rlogic::RamsesBinding
#include <RamsesCameraBinding.h>

The RamsesCameraBinding is a type of rlogic::RamsesBinding which allows the rlogic::LogicEngine to control instances of ramses::Camera. RamsesCameraBinding’s can be created with rlogic::LogicEngine::createRamsesCameraBinding.

The RamsesCameraBinding has a static link to a ramses::Camera. After creation, rlogic::LogicNode::getInputs will return a struct property with children equivalent to the camera settings of the provided ramses::Camera.

There are two types of ramses::Camera:

  • ramses::PerspectiveCamera

  • ramses::OrthographicCamera. Both camera types are defined through their viewport and their frustum properties. These are represented as two separate property structs in the RamsesCameraBinding. Be aware if you set one or more values to one of the structs on the binding and update the LogicEngine it will lead to all properties of this struct being set on the actual ramses::Camera. For example if you only set the Viewport/OffsetX of the Camera per linked script or directly on the binding it will set Viewport/OffsetX, Viewport/OffsetY, Viewport/Width and Viewport/Height to whatever the state of the properties is at that moment. The values of the RamsesCameraBinding inputs are initialized with the values of the provided ramses::Camera during creation. The frustum values of the ramses::Camera are not affected when setting viewport values, and vice-versa. Check the ramses::Camera API to see which values belong together. To avoid unexpected behavior, we highly recommend setting all viewport values together, and also setting all frustum planes together (either by link or by setting them directly). This way unwanted behavior can be avoided.

Since the RamsesCameraBinding derives from rlogic::RamsesBinding, it also provides the rlogic::LogicNode::getInputs and rlogic::LogicNode::getOutputs method. For this particular implementation, the methods behave as follows:

  • rlogic::LogicNode::getInputs: returns an empty struct with no child properties if no ramses::Camera is currently assigned

  • rlogic::LogicNode::getInputs: returns inputs struct with two child properties: viewport and frustum. Their child properties in turn vary for the two available camera types (ramses::PerspectiveCamera and ramses::OrthographicCamera)

    • Perspective Camera: viewport > offsetX, offsetY, width, height frustum > fieldOfView, aspectRatio, nearPlane, farPlane

    • Orthographic Camera: viewport > same as for Perspective Camera frustum > leftPlane, rightPlane, bottomPlane, topPlane, nearPlane, farPlane

  • rlogic::LogicNode::getOutputs: returns always nullptr, because a RamsesCameraBinding does not have outputs, it implicitly controls the ramses Camera

Public Functions

ramses::Camera &getRamsesCamera() const

Returns the bound ramses camera.

Return

the bound ramses camera

RamsesCameraBinding(std::unique_ptr<internal::RamsesCameraBindingImpl> impl) noexcept

Constructor of RamsesCameraBinding. User is not supposed to call this - RamsesCameraBindings are created by other factory classes

Parameters

~RamsesCameraBinding() noexcept override

Destructor of RamsesCameraBinding.

RamsesCameraBinding(const RamsesCameraBinding &other) = delete

Copy Constructor of RamsesCameraBinding is deleted because RamsesCameraBindings are not supposed to be copied

Parameters
  • other: RamsesNodeBindings to copy from

RamsesCameraBinding(RamsesCameraBinding &&other) = delete

Move Constructor of RamsesCameraBinding is deleted because RamsesCameraBindings are not supposed to be moved

Parameters

RamsesCameraBinding &operator=(const RamsesCameraBinding &other) = delete

Assignment operator of RamsesCameraBinding is deleted because RamsesCameraBindings are not supposed to be copied

Parameters

RamsesCameraBinding &operator=(RamsesCameraBinding &&other) = delete

Move assignment operator of RamsesCameraBinding is deleted because RamsesCameraBindings are not supposed to be moved

Parameters

Public Members

std::unique_ptr<internal::RamsesCameraBindingImpl> m_cameraBinding

Implementation detail of RamsesCameraBinding

struct rlogic::RamsesLogicVersion
#include <RamsesLogicVersion.h>

Ramses Logic version information.

Public Members

const std::string_view string

Version information as string in format major.minor.patch with an optional arbitrary suffix.

uint32_t major

Major version.

uint32_t minor

Minor version.

uint32_t patch

Patch version.

class rlogic::RamsesNodeBinding : public rlogic::RamsesBinding
#include <RamsesNodeBinding.h>

The RamsesNodeBinding is a type of rlogic::RamsesBinding which allows manipulation of Ramses nodes. RamsesNodeBinding’s can be created with rlogic::LogicEngine::createRamsesNodeBinding.

The RamsesNodeBinding has a fixed set of inputs which correspond to properties of ramses::Node. They have a fixed type and name: ‘visibility’ (type bool) ‘rotation’ (type vec3f) ‘translation’ (type vec3f) ‘scaling’ (type vec3f)

The default values of the input properties are taken from the bound ramses::Node provided during construction. The rotation convention is also taken from Ramses (see rlogic::RamsesNodeBinding::setRotationConvention)..

The RamsesNodeBinding class has no output properties (thus getOutputs() will return nullptr) because the outputs are implicitly the properties of the bound Ramses node.

Note

In case no values were set (because the user neither set a value explicitly nor linked the input of rlogic::RamsesNodeBinding to another LogicNode output) the Ramses values are not touched. It is possible to set values directly to ramses::Node which will not be overwritten by rlogic::RamsesNodeBinding if you never explicitly assigned a value to the rlogic::RamsesNodeBinding inputs. You can also mix-and-match this behavior - assign some properties and others not.

The LogicEngine does not restrict which Scene the bound nodes belong to - it is possible to have nodes from different scenes bound to the same LogicEngine, and vice-versa. The effects on the ramses::Node property values which are bound to a rlogic::RamsesNodeBinding are immediately visible after rlogic::LogicEngine::update() returns, however the user has to call ramses::Scene::flush() explicitly based on their scene update logic and frame lifecycle.

Public Functions

ramses::Node &getRamsesNode() const

Returns the bound ramses node.

Return

the bound ramses node

bool setRotationConvention(ramses::ERotationConvention rotationConvention)

Sets the rotation convention used to set the rotation values to a potentially bound ramses::Node. Default is the same as the ramses default. Use this to change the setting.

Return

true if successful, false otherwise

Parameters
  • rotationConvention: the rotation convention to use

ramses::ERotationConvention getRotationConvention() const

Returns the currently used rotation convention for the node rotation property.

Return

the currently used rotation convention

RamsesNodeBinding(std::unique_ptr<internal::RamsesNodeBindingImpl> impl) noexcept

Constructor of RamsesNodeBinding. User is not supposed to call this - RamsesNodeBindings are created by other factory classes

Parameters

~RamsesNodeBinding() noexcept override

Destructor of RamsesNodeBinding.

RamsesNodeBinding(const RamsesNodeBinding &other) = delete

Copy Constructor of RamsesNodeBinding is deleted because RamsesNodeBindings are not supposed to be copied

Parameters
  • other: RamsesNodeBindings to copy from

RamsesNodeBinding(RamsesNodeBinding &&other) = delete

Move Constructor of RamsesNodeBinding is deleted because RamsesNodeBindings are not supposed to be moved

Parameters
  • other: RamsesNodeBindings to move from

RamsesNodeBinding &operator=(const RamsesNodeBinding &other) = delete

Assignment operator of RamsesNodeBinding is deleted because RamsesNodeBindings are not supposed to be copied

Parameters
  • other: RamsesNodeBindings to assign from

RamsesNodeBinding &operator=(RamsesNodeBinding &&other) = delete

Move assignment operator of RamsesNodeBinding is deleted because RamsesNodeBindings are not supposed to be moved

Parameters
  • other: RamsesNodeBindings to assign from

Public Members

std::unique_ptr<internal::RamsesNodeBindingImpl> m_nodeBinding

Implementation detail of RamsesNodeBinding

namespace ramses
namespace rlogic

Typedefs

using vec2f = std::array<float, 2>
using vec3f = std::array<float, 3>
using vec4f = std::array<float, 4>
using vec2i = std::array<int, 2>
using vec3i = std::array<int, 3>
using vec4i = std::array<int, 4>
using LuaPrintFunction = std::function<void(std::string_view scriptName, std::string_view message)>

Enums

enum ELogMessageType

ELogMessageType lists the types of available log messages. The integer represents the priority of the log messages (lower means more important, thus higher priority).

Values:

enumerator Off = 0
enumerator Fatal = 1
enumerator Error = 2
enumerator Warn = 3
enumerator Info = 4
enumerator Debug = 5
enumerator Trace = 6
enum EPropertyType

EPropertyType lists the types of properties created and managed by the rlogic::LogicNode class and its derivates.

Values:

enumerator Float
enumerator Vec2f
enumerator Vec3f
enumerator Vec4f
enumerator Int32
enumerator Vec2i
enumerator Vec3i
enumerator Vec4i
enumerator Struct
enumerator String
enumerator Bool
enumerator Array

Functions

constexpr const char *GetLuaPrimitiveTypeName(EPropertyType type)

Returns the string representation of a property type. This string corresponds to the syntax that has to be used in the Lua source code used to create scripts with properties with the corresponding type.

RamsesLogicVersion GetRamsesLogicVersion()

Retrieve currently used Ramses Logic version information.

Return

the Ramses Logic version of the currently used build

namespace internal
namespace rlogic::Logger

Interface to interact with the internal logger. If you want to handle log messages by yourself, you can register your own log handler function with rlogic::Logger::SetLogHandler, which is called each time a log message is logged. In addition you can silence the standard output of the log messages

Typedefs

using LogHandlerFunc = std::function<void(ELogMessageType, std::string_view)>

The LogHandlerFunc can be used to implement a custom log handler. The function is called for each log message separately. After the call to the function the string data behind std::string_view is deleted. If you want to keep it, you must copy it e.g. to a std::string. E.g.

rlogic::Logger::SetLogHandler([](ElogMessageType msgType, std::string_view message){
    std::cout << message std::endl;
});

Functions

void SetLogVerbosityLimit(ELogMessageType verbosityLimit)

Controls how verbose the logging is. verbosityLimit has the following semantics:

  • if log message has message type with higher or equal priority as verbosityLimit, then it is logged

  • log priority is as documented by rlogic::ELogMessageType (Errors are more important than Warnings, etc)

  • the default value is rlogic::ELogMessageType::Info, meaning that log messages are processed if they have INFO priority or higher.

Parameters
  • verbosityLimit: least priority a log message must have in order to be processed

ELogMessageType GetLogVerbosityLimit()

Returns the current log verbosity limit of the logger. See rlogic::Logger::SetLogVerbosityLimit for more info on semantics.

Return

current log verbosity limit

void SetLogHandler(const LogHandlerFunc &logHandlerFunc)

Sets a custom log handler function, which is called each time a log message occurs. Note: setting a custom logger incurs a slight performance cost because log messages will be assembled and reported, even if default logging is disabled (SetDefaultLogging).

@ param logHandlerFunc function which is called for each log message

void SetDefaultLogging(bool loggingEnabled)

Sets the default logging to std::out to enabled or disabled. Enabled by default.

Parameters
  • loggingEnabled: true if you want to enable logging to std::out, false otherwise

file APIExport.h

Defines

RLOGIC_API
file Collection.h
#include “ramses-logic/APIExport.h”#include “ramses-logic/Iterator.h”#include <functional>#include <memory>
file ELogMessageType.h
#include <cassert>
file EPropertyType.h
#include “ramses-logic/APIExport.h”#include <array>#include <string>
file ErrorData.h
#include <string>
file Iterator.h
#include “ramses-logic/APIExport.h”#include <type_traits>#include <iterator>
file Logger.h
#include “ramses-logic/APIExport.h”#include “ramses-logic/ELogMessageType.h”#include <functional>#include <string_view>
file LogicEngine.h
#include “ramses-logic/APIExport.h”#include “ramses-logic/LuaScript.h”#include “ramses-logic/Collection.h”#include “ramses-logic/ErrorData.h”#include <vector>#include <string_view>
file LogicNode.h
#include “ramses-logic/APIExport.h”#include <vector>#include <string>#include <functional>
file LuaScript.h
#include “ramses-logic/LogicNode.h”#include <string>#include <memory>
file Property.h
#include “ramses-logic/APIExport.h”#include “ramses-logic/EPropertyType.h”#include <cstddef>#include <optional>#include <memory>#include <string>
file RamsesAppearanceBinding.h
#include “ramses-logic/APIExport.h”#include “ramses-logic/RamsesBinding.h”#include <memory>
file RamsesBinding.h
#include “ramses-logic/LogicNode.h”
file RamsesCameraBinding.h
#include “ramses-logic/APIExport.h”#include “ramses-logic/RamsesBinding.h”#include <memory>
file RamsesLogicVersion.h
#include “ramses-logic/APIExport.h”#include <string_view>#include <cstdint>
file RamsesNodeBinding.h
#include “ramses-logic/APIExport.h”#include “ramses-logic/RamsesBinding.h”#include <memory>
dir /home/docs/checkouts/readthedocs.org/user_builds/ramses-logic/checkouts/v0.7.0/include
dir /home/docs/checkouts/readthedocs.org/user_builds/ramses-logic/checkouts/v0.7.0/include/ramses-logic