Examples & Features

Example – Zero code

Build your scene in the editor and then launch from the terminal under the standalone executable (shipped with the editor – all game assets are serialised to the scene file in a versionable format):

./standalone MyGame.msf

Example – Minimal code

You can build simple scenes in the editor with no code and then generate your application skeleton using the in-editor code wizard leaving you to simply load your interactive scene in a single line of code. You can then obtain references to the resources / logic in your loaded app adding new resources and logic through the SDK’s apis:

#include "firefly.h"
using namespace firefly;
SceneItem* scene;

int LoadScene(int argc, char* argv[])
{
    // load MyScene.msf scene created in the editor
    scene = SDK::GetInstance().Load("MyScene.msf");
    return 0;
}

int DrawScene()
{
    SDK::GetInstance().Update(*scene);
    return 0;
}

int DestroyScene()
{
    scene->Release();    
    return 0;
}

Example – Scripting

You can also build (or load) scenes programmatically using the accessible LUA scripting language from within a scene using either a global script or script component attached to a scene item (NB. If you’re interested in Python or other language bindings get in touch):

scene = sdk:GetScene()
xForm = fireflyscript.Transform()
for count = 1,50 do
item = CastSceneItemToBoxSceneItem(sdk:CreateSceneItem(fireflyscript.SceneItemType_BoxSceneItem, 'TestBox')) xForm:Position(fireflyscript.vec3(math.random(0, 100) -50,math.random(0, 100) -50,math.random(0, 100) -50)) item:SetTransform(xForm) scene:InsertChildAtIndex(item) end

Example – Scripting mathematical operations

Scripting math operations is intuitive (NB. The SDK makes use of the open source glm math library so all glm operations are supported directly in script):

a = fireflyscript.vec3(1,0,0)
b = fireflyscript.vec3(0,1,0)
c = a + b

Example – Scripting components

SimulationStarterKit is an plugin-based Entity Component System (ECS) engine. You can add new Systems and Component plugins using the SDK provided code generators. 

To add or update a scene item’s components you use the following syntax:

 [plugin_name].GetComponent_[component_group_name]_[component_name](object) 

i.e. For a Plugin called “ext_PhysicsScript” (generated using the in-editor code wizard) containing a component group called “Physics” and a component in that group named “RigidBodyObjectComponent” you would do the following:

// load your ECS plugin's scripting module (auto-generated for you by the SDK)
require('ext_PhysicsScript')

// locate your scene object
scene = sdk:GetScene()
if(object == nil) then
object = scene:Find('YourObjectNameHere')
end

// add / obtain a reference to your object's RigidBodyObjectComponent (imported from ext_PhysicsScript plugin)
rb = ext_PhysicsScript.GetComponent_Physics_RigidBodyObjectComponent(object)

// apply a force to the object through the component
rb:Activate()
rb:ApplyImpulse(fireflyscript.vec3(0,5,0))

Example – Attaching scripts to objects

Scripts can be attached as components to objects like so (the attached scripts are executed each frame with the built-in this reference referring to the object the component is attached to):

auto mesh = SDK::GetInstance().CreateSceneItem(SceneItemType_BoxSceneItem);
ComponentHandle<ScriptComponent> c = mesh->AddComponent<ScriptComponent>();
const char* script = "this  = fireflyscript.CastSceneItemToPickableSceneItem(this)"
                     "scale = (time / 1000) % 5"
                     "this:SetTransformScale(fireflyscript.vec3(scale))";
c->SetScript(script);

Example – Script debugging

All print statements in a script are redirected to the editor’s log window to aid in debugging:

print(string.format('d.x = %f, d.y = %f, d.z = %f', myVec3.x, myVec3.y, myVec3.z))

Example – C++

You can build scenes programmatically using C++:

#include "firefly.h"
using namespace firefly;
SceneItem* scene;

int LoadScene(int argc, char* argv[])
{
    SDK::SceneSettings settings;
    settings.NormalPerVertex = true;
    SDK::GetInstance().SetSceneSettings(settings);

    // Scene root
    scene = static_cast<BranchSceneItem*>(SDK::GetInstance().CreateSceneItem(SceneItemType_Group));

    // Camera
    auto scamera = static_cast<EditorCamera*>(SDK::GetInstance().CreateSceneItem(SceneItemType_EditorCamera, "Editor Camera"));
    scamera->SetPosition(Vector3(0.f, 5.f, 20.f));
    scamera->SetLookAt(Vector3(0.f, 5.f, 0.f));
    scamera->SetPickingEnabled(true);
    scamera->SetSpeed(10.f);
    scene->InsertChildAtIndex(*scamera);
    scamera->SetActive(true);

    // Main scene
    auto mainScene = static_cast<BranchSceneItem*>(SDK::GetInstance().CreateSceneItem(SceneItemType_Group, "MainScene"));
    scamera->InsertChildAtIndex(*mainScene);

    // Light
    auto light = static_cast<Light*>(SDK::GetInstance().CreateSceneItem(SceneItemType_Light));
    light->SetPosition(Vector3(0.f, 10.f, 10.f));
    light->SetLightType(Light::Positional);
    mainScene->InsertChildAtIndex(*light);

    // Model
    auto modelScene = SDK::GetInstance().Load("model.obj");
    mainScene->InsertChildAtIndex(*modelScene);
    return 0;
}

int DrawScene()
{
    SDK::GetInstance().Update(*scene);
    return 0;
}

int DestroyScene()
{
    scene->Release();   
    return 0;
}

Example – Prefabricated scene support

You can design scenes in the provided editor and then instance them into your game either in the editor using the PrefabricatedScene node, in script or programmatically:

auto prefab = static_cast<PrefabricatedScene*>(firefly::SDK::GetInstance().CreateSceneItem(SceneItemType_PrefabricatedScene));
prefab->SetScene("PrefabricatedScene.msf");
prefab->SetSceneRoot("MySubScene"); // <-- Only load items from the MySubScene scene graph branch and below

In script:

prefab = CastSceneItemToPrefabricatedScene(sdk:CreateSceneItem(fireflyscript.SceneItemType_PrefabricatedScene))
prefab:SetScene('PrefabricatedScene.msf')
prefab:SetRoot('MySubScene')

Example – Animating properties

You can animate any property of any object / shader as follows:

// Mesh
PickableSceneItem* mesh = static_cast<PickableSceneItem*>(firefly::SDK::GetInstance().CreateSceneItem("{9DE338EE-AEA6-4869-B9BA-E2C4951CEF19}"));
mainScene->InsertChildAtIndex(*mesh);
mesh->SetShowBoundingBox(true); // <- Show the updated (i.e. rotated) AABB
const Transform& meshTransform = mesh->GetTransform();

// Animation / AnimationTarget / AnimationKeyFrame
Animation* animation = static_cast<Animation*>(firefly::SDK::GetInstance().CreateSceneItem(SceneItemType_Animation));
animation->SetDuration(3.f);
animation->SetAnimationType(AbstractProperty::TRANSFORM);
animation->SetInterpolationMethod(Animation::InterpolationMethod_Spherical);
animation->AddTarget(new AnimationTarget(mesh, mesh->GetPropertyByName("Transform")));
animation->SetBehaviour(Animation::Behaviour::Loop);

TransformAnimationKeyFrame* k1 = new TransformAnimationKeyFrame();
k1->SetTime(0.f);
k1->SetTransform(Transform(glm::angleAxis(DegreesToRadians(0.f), Vector3(0.f, 1.f, 0.f)), meshTransform.Position()));
animation->AddKeyFrame(k1);

TransformAnimationKeyFrame* k2 = new TransformAnimationKeyFrame();
k2->SetTime(0.5f);
k2->SetTransform(Transform(glm::angleAxis(DegreesToRadians(180.f), Vector3(0.f, 1.f, 0.f)), meshTransform.Position()));
animation->AddKeyFrame(k2);

TransformAnimationKeyFrame* k3 = new TransformAnimationKeyFrame();
k3->SetTime(1.f);
k3->SetTransform(Transform(glm::angleAxis(DegreesToRadians(360.f), Vector3(0.f, 1.f, 0.f)), meshTransform.Position()));
animation->AddKeyFrame(k3);
mainScene->InsertChildAtIndex(*animation);
animation->Start();

Example – Input event handling

This complete example demonstrates how to apply an impulse force to an object in response to a keyboard event i.e. how to make an object bounce. The same can be achieved directly in the editor with no code and is demonstrated in the SDK samples.

#include "firefly.h"
#include "RigidBodyObjectComponent.h"
using namespace firefly;
RCPtr<SceneItem> scene;

// Applies a force to a rigid body in response to keyboard SPACE key down event
struct KeyBoardListener : public IKeyboardListener
{
    KeyBoardListener() : object(nullptr)
    {
    }

    void SetObject(SceneItem* obj)
    {
        object = obj;
    }

    virtual void OnKeyDown(const Keyboard::Key key)
    {
        if(key == Keyboard::Key::SPACE && object)
        {
            ComponentHandle<RigidBodyObjectComponent> rigidBody = object->Component<RigidBodyObjectComponent>();
            rigidBody->Activate();
            rigidBody->ApplyImpulse(Vector3(0.f, 10.f, 0.f), Vector3(0.f, 0.f, 0.f));
        }
    }

    virtual void OnKeyUp  (const Keyboard::Key key)
    {}

    SceneItem* object;
};

KeyBoardListener gKeyboardListener;

int Init()
{
    SDK::GetInstance().AddKeyboardListener(&gKeyboardListener);
return 0;
}

#if FIREFLY_PLATFORM == PLATFORM_WIN32
int LoadScene(int argc, wchar_t* argv[])
#else
int LoadScene(int argc, char* argv[])
#endif
{
    SDK::SceneSettings settings;
    settings.NormalPerVertex = true;
    SDK::GetInstance().SetSceneSettings(settings);
    scene = static_cast<BranchSceneItem*>(SDK::GetInstance().CreateSceneItem(SceneItemType_Group));

    // Viewport
    scene->InsertChildAtIndex(*SDK::GetInstance().CreateSceneItem(SceneItemType_Viewport), 0);

    // Depth testing enabled
    auto depthAttribute = static_cast<StateAttribute*>(SDK::GetInstance().CreateSceneItem(SceneItemType_StateAttribute));
    depthAttribute->SetStateType(StateAttribute::StateType_DEPTH_TEST);
    depthAttribute->SetStateValue(StateAttribute::StateValue_ENABLED);
    scene->InsertChildAtIndex(*depthAttribute);

    // lighting enabled
    auto lightingAttribute = static_cast<StateAttribute*>(SDK::GetInstance().CreateSceneItem(SceneItemType_StateAttribute));
    lightingAttribute->SetStateType(StateAttribute::StateType_LIGHTING);
    lightingAttribute->SetStateValue(StateAttribute::StateValue_ENABLED);
    scene->InsertChildAtIndex(*lightingAttribute);

    // Clear color
    scene->InsertChildAtIndex(*SDK::GetInstance().CreateSceneItem(SceneItemType_ClearColor), 3);

    // Camera
    auto camera = static_cast<EditorCamera*>(SDK::GetInstance().CreateSceneItem(SceneItemType_EditorCamera));
    scene->InsertChildAtIndex(*camera);

    // Main scene
    auto mainScene = static_cast<BranchSceneItem*>(SDK::GetInstance().CreateSceneItem(SceneItemType_Group, "MainScene"));
    camera->InsertChildAtIndex(*mainScene);
    camera->SetPosition(Vector3(-10.f, 20.f, 60.f));
    camera->SetLookAt(Vector3(0.f, 0.f, 0.f));
    camera->SetNearClipPlane(0.5f);
    camera->SetFarClipPlane(500.f);
    camera->SetFieldOfView(60.f);
    camera->SetSpeed(5.f);
    camera->SetPickingEnabled(false);
    camera->SetActive(true);

    // Light
    auto light = static_cast<Light*>(SDK::GetInstance().CreateSceneItem(SceneItemType_Light));
    light->SetPosition(camera->GetPosition());
    light->SetLightType(Light::Positional);
    light->SetRenderVisual(true);
    light->SetIsPickable(false);
    camera->InsertChildAtIndex(*light);
    camera->GetSignalByName("OnPositionChanged(const Vector3&)")->Connect(light->GetSlotByName("Position(const Vector3&)"));

    // Ground plane
    auto ground = static_cast<PlaneSceneItem*>(SDK::GetInstance().CreateSceneItem(SceneItemType_Plane));
    mainScene->InsertChildAtIndex(*ground);
    ground->SetWidth(50);
    ground->SetPlaneDepth(50);
    ground->AddComponent<RigidBodyObjectComponent>();
    ground->Component<RigidBodyObjectComponent>()->SetMass(0.f);

    // Bouncing object (the object we apply an impulse force to)
    auto box = static_cast<BoxSceneItem*>(SDK::GetInstance().CreateSceneItem(SceneItemType_BoxSceneItem));
    box->AddComponent<RigidBodyObjectComponent>();
    ComponentHandle<RigidBodyObjectComponent> rb = box->Component<RigidBodyObjectComponent>();
    rb->SetMass(1.f);                                                   // mass
    rb->SetLinearFactor(Vector3(1.f, 1.f, 0.f));                        // only allow movement along X and Y axis
    rb->SetAngularFactor(Vector3(0.f, 0.f, 0.f));                       // disable changes to orientation
    rb->SetRestitution(0.f);                                            // no bounce (to have no bounce zero restitution on bouncing object and surface)
    ground->Component<RigidBodyObjectComponent>()->SetRestitution(0.f); // no bounce (surface)
    box->SetTransformPosition(Vector3(0.f, 1.f, 0.f));                  // position
    mainScene->InsertChildAtIndex(*box);

    // Pass Bouncer object to the keyboard handler so it can have force applied to it when key pressed
    gKeyboardListener.SetObject(box);

    // Scripting (give the object a name so it can be obtained in script if required)
    box->SetInstanceName("Box");
    SDK::GetInstance().SetScene(scene.Get());
    return 0;
}

int DrawScene()
{
    SDK::GetInstance().Update(*scene);
    return 0;
}

int DestroyScene()
{
    scene->Release();
    return 0;
}