Posted on

Computer Vision support coming soon

Currently adding in computer vision support. You’ll be able to add a frame grabber to your scene, publish the grabbed frame as a texture that can then be fed to a DNN for processing and instant preview on Linux, Windows and Android. The scene along with all resources (your dnn model and custom script for processing) are all written to a file that are then attached to a player. So in effect, your file is your app and so making changes to your model or pre / post process script in the editor can be instantly previewed on device with no code compilation. When you’re ready to deploy, simply File -> Publish to publish your computer vision app-let.

Posted on

Linux snapcraft support added (Linux Version Only)

In version 0.42.7+ when publishing your game from the editor (File -> Publish menu in the editor) a code project is generated for Linux (in addition to Windows and Android) that contains a snapcraft.yaml file that can generate an app for upload to the appstore as follows (all explained in your generated project’s displayed on publishing your game):

-- (Ubuntu 20.04) generate a game for upload to as follows:
> cd [your_published_game_dir)
> snapcraft

That’s it! Now you can upload your published game to as described in your project’s generated

The editor / engine / sdk is available here:

Here’s a little example app published by the editor and available for download on the snapcraft store:

Posted on

Linux version updated (with play on Android + fully scriptable GUIs – no code compilation or tooling needed)

Linux version now supports playing scenes on Android and features fully scriptable GUI creation straight to your Android device in < 1 second (no code compilation or tooling required) for fast iteration and prototyping. v0.42.0+ includes a new GUI plugin that contains a GUISystem and GUIComponent that supports immediate mode GUIs (defined in script). You can do this in the editor by adding a GUIComponent to an object in your scene and setting its script property to a GUI script (see below) and then view immediately on your Android device:

-- an example GUI script (lua)
function update(this, time)
	windowPos = ext_GUIScript.ImVec2(0,0)
	window = nil
	ext_GUIScript.Begin('A Window', window)
	if ext_GUIScript.Button('Click Me!') then
	    -- do stuff in response to button click

You can also do this programmatically in C++ as follows:

// Add a GUIComponent to an empty GhostSceneItem and define your GUI
auto object = static_cast<GhostSceneItem*>(firefly::SDK::GetInstance().CreateSceneItem(SceneItemType_GhostSceneItem));
auto gui    = object->AddComponent<GUIComponent>();
auto script = R"(function update(this, time)
                    windowPos = ext_GUIScript.ImVec2(0,0)
                    window = nil
                    ext_GUIScript.Begin('A Window', window, ext_GUIScript.ImGuiWindowFlags_AlwaysAutoResize)
                    if ext_GUIScript.Button('Click Me!') then
                        // do stuff in response to button click
Posted on

Generated games now package up as small as just 3MB

The “Publish to Visual Studio 2017”, Linux (as snapcraft snaps) and “Publish to Android Studio” feature (available in the editor) includes a packaging sub-project that will bundle up the generated game to a shareable zip file package as small as 4MB on Windows or 3MB per-architecture on Android (3MB per architecture, includes armeabi-v7a, x86, arm64-v8a and x86_64 versions that bundle into a 12MB package).

Posted on

Poisson disk sampling

Poisson disk sampling can be used to procedurally sample a region of space. It’s not as messy as random placement or as uniform as grid-based methods. I’ve created a little demonstration project that extends the sampling to cover objects of varying size with no overlap in this repository. An example of the generated distributions (of a 3 layer sampler) is below. Some applications:

  • Sampling an image / regions of an image
  • Procedurally placing objects of varying sizes with no overlap in a simulation / game
Posted on

Automated performance monitoring (C++/Google Benchmark)

I’ve created a small utility that looks for step changes in accumulated google C++ benchmark run history so wanted to share in case it’s of use to other google benchmark users. It’s available on github here. It produces a report (index.html) containing a chart for each benchmark with a slowdown indicator estimating where a step-change in performance occurred. Simply accumulate your google benchmark run history and then run the script:


Posted on

Mesh Instancing Support Added

With mesh instancing you can draw thousands of copies of a mesh in a single draw call (you can specify an arbitrary number of properties for your instances by attaching per instance data to your mesh to be instanced) without adversely affecting the frame rate. v0.41.8 of the SDK comes with a small demo app (example_Instancing) that demonstrates this feature in the engine and is accessible (along with code) from the editor’s samples browser shown on start up. Here’s a code snippet that illustrates the mesh instancing support:

    // ============================================
    // MESH INSTANCING - grid of boxes
    // ============================================

    // the base mesh we will instance
    auto box = static_cast<PrefabricatedMesh*>(firefly::SDK::GetInstance().CreateSceneItem(SceneItemType_BoxSceneItem));

    // per instance transforms (i.e. position, rotation and scale)
    WIDTH = HEIGHT = DEPTH = 10;
    transforms = new glm::mat4[INSTANCE_COUNT];
    float meshWidth, meshHeight, meshDepth;
    meshWidth = meshHeight = meshDepth = 4.f;
    float xorig = -((WIDTH  * meshWidth)  / 2.f) + (meshWidth  / 2.f);
    float yorig = -((HEIGHT * meshHeight) / 2.f) + (meshHeight / 2.f);
    float zorig = -((DEPTH  * meshDepth)  / 2.f) + (meshDepth  / 2.f);
    int i = 0;
    for(int x = 0; x < WIDTH; x++)
        float xpos = xorig + (x * meshWidth);
        for(int y = 0; y < HEIGHT; y++)
            float ypos = yorig + (y * meshHeight);
            for(int z = 0; z < DEPTH; z++)
                float zpos = zorig + (z * meshDepth);
                transforms[i++] = glm::translate(glm::mat4(1.f), glm::vec3(xpos, ypos, zpos));

    // tell the mesh the instance count

    // create an instance data buffer
    auto instanceData = (VertexBuffer*)SDK::GetInstance().CreateVertexBuffer();

    // pack the per-instance data into the data buffer
    instanceData->InitInstanceDataBuffer(INSTANCE_COUNT * sizeof(glm::mat4), &transforms[0]);

    // specify the data layout (instance data -> shader attribute location mappings)
    auto mesh = box->GetMesh();
        instanceData->SetDataArrayMapping({4, 4, VertexBuffer::DataType::Float, 0, sizeof(glm::mat4), (void*)0});
        instanceData->SetDataArrayMapping({5, 4, VertexBuffer::DataType::Float, 0, sizeof(glm::mat4), (void*)(1 * sizeof(glm::vec4))});
        instanceData->SetDataArrayMapping({6, 4, VertexBuffer::DataType::Float, 0, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4))});
        instanceData->SetDataArrayMapping({7, 4, VertexBuffer::DataType::Float, 0, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4))});
        instanceData->SetDataArrayDivisor({4, 1});
        instanceData->SetDataArrayDivisor({5, 1});
        instanceData->SetDataArrayDivisor({6, 1});
        instanceData->SetDataArrayDivisor({7, 1});

    // attach the per-instance data buffer to the mesh (it will take ownership of the buffer)
    // nb. only needed if you intend to save the instance data along with the mesh otherwise you can destroy it
    // as it's now managed by the VAO

    // Shader (that supports a per-instance mat4 shader input (attribute) at location 4)
    // nb. vertex_position and vertex_texcoord0 are engine built-in attributes (see shader reference)
    // nb. ProjectionMatrix and ViewMatrix are engine built-in uniforms
    SimpleShader* shader = static_cast<SimpleShader*>(SDK::GetInstance().CreateSceneItem(SceneItemType_SimpleShader));
    shader->SetVertexShader("#version 330 core\n"
                            "layout (location = 0) in vec3 vertex_position;\n"
                            "layout (location = 2) in vec2 vertex_texcoord0;\n"
                            "layout (location = 4) in mat4 instance_matrix;\n"
                            "out vec2 TexCoords;\n"
                            "uniform mat4 ProjectionMatrix;\n"
                            "uniform mat4 ViewMatrix;\n"
                            "void main()\n"
                            "    TexCoords = vertex_texcoord0;\n"
                            "    gl_Position = ProjectionMatrix * ViewMatrix * instance_matrix * vec4(vertex_position, 1.0f);\n"
    shader->SetFragmentShader("#version 330 core\n"
                              "out vec4 FragColor;\n"
                              "in vec2 TexCoords;\n"
                              "uniform sampler2D sampler;\n"
                              "void main()\n"
                              "    FragColor = texture(sampler, TexCoords);\n"
    shader->SetPropertyValueByName("sampler", TextureManager::GetInstance().TextureWithName(GetFireflyDir() + "default_texture.png"));
Posted on

Navigation meshes, crowds, agents and obstacles

The key components are; TiledNavMeshComponent, NavMeshCrowdComponent, NavMeshObstacle and NavMeshAgent. As can be seen from the video in the editor you can now import models then generate navigable areas for agents to move around. Within the editor you can then wire up input devices to scripts that then perform ray -> mesh intersection to move your agents. The entire scene (models, nav mesh, agents, scripts and input handling) can then be saved to file or published as a code project from within the editor. A typical script (that is saved into the scene file) is included below. You would typically trigger this script in response to an event (i.e. an input device event – by adding a Mouse input device to the scene and wiring up its onMouseButtonDown event to the script’s execute method or in response to an object entering a collision volume. (see the collision volume support in the video log).


-- find the agent, nav mesh and crowd
scene            = sdk:GetScene()
agent            = scene:Find('Agent')
navMesh          = scene:Find('NavMesh')
agentComponent   = ext_NavigationScript.GetComponent_Navigation_NavMeshAgentComponent(agent)
crowdComponent   = ext_NavigationScript.GetComponent_Navigation_NavMeshCrowdComponent(navMesh)
navMeshComponent = ext_NavigationScript.GetComponent_Navigation_TiledNavMeshComponent(navMesh)

-- unproject the current mouse pos at both near / far clip planes
mousePos     = sdk:GetMousePosition()
pickRayNear  = fireflyscript.vec3()
pickRayFar   = fireflyscript.vec3()
sdk:UnprojectNearFar(mousePos:GetX(), mousePos:GetY(), pickRayNear, pickRayFar)

-- obtain navigation mesh transform
navMesh      = fireflyscript.CastSceneItemToPickableSceneItem(navMesh)
worldToLocal = navMesh:GetWorldMatrixInverse()
localToWorld = navMesh:GetWorldMatrix()

-- test pick ray against nav mesh geometry
geom                   = navMeshComponent:GetMesh()
localPickRayNear       = worldToLocal * fireflyscript.vec4(pickRayNear, 1)
localPickRayFar        = worldToLocal * fireflyscript.vec4(pickRayFar, 1)
localIntersectionPoint = fireflyscript.vec3()
geom:LineTest(fireflyscript.vec3(localPickRayNear), fireflyscript.vec3(localPickRayFar), localIntersectionPoint)

-- move the agent(s)
crowdComponent:SetTargetPoint(fireflyscript.vec3(localToWorld * fireflyscript.vec4(localIntersectionPoint,1)))