/**
* This example demonstrates how to link LogicEngine scripts and
* Ramses scene content, so that whenever scripts are executed
* their output is propagated to the linked Ramses scene(s).
* This example only demonstrates code, for general explanations
* and more context, check the doc pages at:
* https://ramses-logic.readthedocs.io/en/latest/api.html#creating-links-between-scripts
*/
struct SceneAndNode
{
ramses::Scene* scene;
ramses::Node* node;
};
/**
* Helper method which creates a simple ramses scene. For more ramses
* examples, check the ramses docs at https://covesa.github.io/ramses
*/
SceneAndNode CreateSceneWithTriangle(ramses::RamsesClient& client);
int main(int argc, char* argv[])
{
/**
* Create Ramses framework and client objects. Ramses Logic does not manage
* or encapsulate Ramses objects - it only interacts with existing Ramses objects.
* The application must take special care to not destroy Ramses objects while a
* LogicEngine instance is still referencing them!
*/
ramses::RamsesFramework ramsesFramework(argc, argv);
ramses::RamsesClient* ramsesClient = ramsesFramework.createClient("example client");
/**
* To keep this example simple, we don't include a Renderer, but only provide the scene
* over network. Start a ramses daemon and a renderer additionally to see the visual result!
* The connect() ensures the scene published in this example will be distributed over network
*/
ramsesFramework.connect();
/**
* Create a test Ramses scene with just a single triangle
*/
auto [scene, triangleNode] = CreateSceneWithTriangle(*ramsesClient);
rlogic::LogicEngine logicEngine;
/**
* Create a binding object which serves as a bridge between logic scripts and animations on one end
* and a Ramses scene on the other end
*/
rlogic::RamsesNodeBinding* nodeBinding = logicEngine.createRamsesNodeBinding(*triangleNode, rlogic::ERotationType::Euler_XYZ, "binding to triangle mesh node");
/**
* Create a simple script which takes the current time in milliseconds
* and rotates around the Z axis slowly based on how much time is passed
*/
rlogic::LuaScript* script = logicEngine.createLuaScript(R"(
function interface()
IN.time_msec = INT32
OUT.rotationZ = VEC3F
end
function run()
-- Rotate around Z axis with 100 degrees per second
OUT.rotationZ = {0, 0, IN.time_msec / 10}
end
)");
/**
* Connect the script output 'rotationZ' with the rotation property of the RamsesNodeBinding object.
* After this, the value computed in the script will be propagated to the ramses node.
*/
logicEngine.link(
*script->getOutputs()->getChild("rotationZ"),
*nodeBinding->getInputs()->getChild("rotation"));
/**
* Simulate an infinite animation and provide the time to the control script
* so that it can use it to advance the rotation of the animated triangle.
* Usually this would be replaced by an application loop and data.
*/
for(int32_t simulatedTime = 0; simulatedTime < 100000; ++simulatedTime)
{
script->getInputs()->getChild("time_msec")->set<int32_t>(simulatedTime);
/**
* Update the LogicEngine. This will execute the script above, and will set the rotation of the
* Ramses node bound to the output of the script.
*/
logicEngine.update();
/**
* Tell ramses to "flush" the scene state and finish the frame.
*/
scene->flush();
/**
* Throttle the "animation" by sleeping for a bit.
*/
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
/**
* Destruction order is important! First destroy the logic engine content (created last) then the Ramses scene
* (after nothing is referencing the scene any more)
*/
logicEngine.destroy(*script);
logicEngine.destroy(*nodeBinding);
ramsesClient->destroy(*scene);
return 0;
}
SceneAndNode CreateSceneWithTriangle(ramses::RamsesClient& client)
{
ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "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::EClearFlags_None);
renderPass->setCamera(*camera);
ramses::RenderGroup* renderGroup = scene->createRenderGroup();
renderPass->addRenderGroup(*renderGroup);
std::array<float, 9u> vertexPositionsArray = { -1.f, 0.f, -1.f, 1.f, 0.f, -1.f, 0.f, 1.f, -1.f };
ramses::ArrayResource* vertexPositions = scene->createArrayResource(ramses::EDataType::Vector3F, 3, 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::ResourceCacheFlag_DoNotCache);
ramses::Appearance* appearance = scene->createAppearance(*effect);
ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect);
ramses::AttributeInput positionsInput;
effect->findAttributeInput("a_position", positionsInput);
geometry->setInputBuffer(positionsInput, *vertexPositions);
ramses::MeshNode* meshNode = scene->createMeshNode("triangle mesh node");
meshNode->setAppearance(*appearance);
meshNode->setIndexCount(3);
meshNode->setGeometryBinding(*geometry);
renderGroup->addMeshNode(*meshNode);
scene->flush();
scene->publish();
return SceneAndNode{ scene, meshNode };
}