Posted on

Navigation meshes, crowds, agents and obstacles

Added componentised (as in Entity Component System based) navigation mesh support via a new Navigation plugin (available for download here) that uses Mikko Mononen’s Recast library. 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).

require('fireflyscript')
require('ext_NavigationScript')

-- 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)))
Posted on

Foray into optimisation with SIMD – Part 1

I’ve been playing around with SSE recently to explore the performance benefits of using it in some performance sensitive sections of code. I’ve initially applied it to updating an AABB (Axis Aligned Bounding Box) from a rotated previous bounding box. I‘ve created a small repo that performs micro-benchmarking of non-SIMD and SIMD versions benchmarked using picobench in case it’s of use to others. The results when running a release build under gcc 7.4 (64 bit build) with optimisation disabled -O0:

===============================================================================
   Name (baseline is *)   |   Dim   |  Total ms |  ns/op  |Baseline| Ops/second
===============================================================================
       benchmark_normal * |       8 |     0.002 |     208 |      - |  4799040.2
           benchmark_simd |       8 |     0.001 |      62 |  0.301 | 15968063.9
       benchmark_normal * |      64 |     0.013 |     205 |      - |  4876933.6
           benchmark_simd |      64 |     0.004 |      58 |  0.285 | 17112299.5
       benchmark_normal * |     512 |     0.100 |     194 |      - |  5128667.4
           benchmark_simd |     512 |     0.031 |      61 |  0.315 | 16269980.0
       benchmark_normal * |    4096 |     0.838 |     204 |      - |  4885630.2
           benchmark_simd |    4096 |     0.251 |      61 |  0.299 | 16328548.3
       benchmark_normal * |    8192 |     1.669 |     203 |      - |  4907605.0
           benchmark_simd |    8192 |     0.474 |      57 |  0.284 | 17265216.7
===============================================================================

Not quite 4x as we’d expect but pretty close 🙂 However, with -O3 it’s 1:1. To be continued…

Posted on

Extending the side-project engine with plugins – Part 1

I have a side-project that I use to explore technical interests. It’s a plugin-based Entity-Component-System 3D game engine / editor and SDK for Linux, Windows and macOS. The engine can be extended by adding new components and systems in the form of plugins (generated by the in-editor code wizard):

Posted on

Post-process shader chaining

A (single) post process effect is achieved by firstly rendering the scene to a framebuffer with appropriate attachments outputting the result to a texture, applying the texture to a full screen quad, specifying an output framebuffer (usually the default framebuffer) and then executing a shader on the texture to render to the output framebuffer (the screen). To chain you take the output framebuffer and feed it as input to another post process effect using the same sequence as before. The final shader (the final link in the chain) is then output to the screen (i.e. the main framebuffer). For performance reasons of course it’s better to have an uber-shader but the support’s there. I’ve made it so adding post processing shaders to the engine / editor is painless (you simply drop them into a special folder that the editor scans on start-up – which then generates a factory / menu item entry for each effect – the same approach I take for regular non-post-process shaders in the engine / editor). Here’s a video of a vignette -> pixelate -> chromatic aberration post processing chain in action in the editor:

Posted on

Unit Testing across Linux, macOS and Windows

As SimulationStarterKit is C++ / CMake based I use CTest as the unit testing framework that’s especially helpful when you’re trying to cover a few platforms (i.e. Linux, macOS and Windows). Every time I implement a feature in the engine I try to create a corresponding test first to help determine what constitutes correct operation and also sometimes as a programming aid to help discover what a usable API might look like for a new feature. The video below demonstrates how this looks in Visual Studio.

Posted on

Python and C++ (Part 1)

You can accelerate the performance of Python code by re-implementing performance sensitive sections in C/C++ (i.e. by utilizing SSE for instance) and then making it callable from Python by using Python’s ctypes library or alternatively exposing your C/C++ code as a Python extension module. I’ve put together a minimal github repo that demonstrates the ctypes approach here and another small project that demonstrates the python extension module approach here in case it’s of use to anyone.