Coding

NRG has a built-in event-driven system; when coding using a Script or a C/C++ plugin with NRG; all the code that you are going to write needs to be included within the scope of predefined functions that respond to a specific type of event that will be triggered automatically the application execution sequence.

The event system is built with re-usability in mind; most of the events can be applied to multiple types of resources which allows you to reuse the same or similar code on multiple instances.

For example, both Scene and Camera respond to the kOnDraw event. Let's assume that you are building a car game that has a single rendering pass and a replay system that allows you to change the current camera on the racing track. Instead of assigning to each camera the script that contains the screen clear code to start drawing a new frame you could simply assign it to the scene handler which will respond to the function OnDraw and clear the graphics states regardless of which camera your application is currently using. This is a simple example but the same concept can be applied to much more complex scenarios.

Events scripting are independent as each script run inside a specific thread assigned to a dedicated handler (where the Script is actually attached). In example Scene, Object and Video all respond to the event OnUpdate (identified in the script by the function OnUpdate). Even if that function from the same script is called from multiple locations the local variables will remain independent as they each run in a separate environment. In case you want to share a variable globally all you have to do is to define it using the _G (global table in Lua).

For more information and to learn about each EventType refer the corresponding dedicated "Events" help section for each resource you are interested in.

This


The previous section leads us to more explanation about the keyword this. The special scripting keyword this refer to the active struct/class that trigger the event; and it works the same way as it does in C++. See the following example in Lua:

Lua
function OnUpdate() -- Print the name of the current data class used in this context. print( this ) end

Enumeration


Enumerations come in two different flavors: constants and flags. Constant enumerations always start with the letter k and flags always start with the letter f. All flags represent a specific bit; which means that they can be used in conjunction with the Lua bit module or the equivalent in C/C++.

For Lua convenience have been implemented allowing you to use to + or - operators to either add or remove a specific flag to a variable using a simple affectation as demonstrate below:

Lua
-- Enable camera debug visualization App:GetScene("Scene").debug_mode=Scene.DebugMode.fDrawCameras -- Disable camera debug visualization App:GetScene("Scene").debug_mode=-Scene.DebugMode.fDrawCameras

Userdata


While scripting all the struct/class implemented in NRG are converted into the Lua userdata type. It is, however, possible to retrieve the original name of the struct/class at runtime using the special function userdata_type. Internal source data pointers can also be evaluated using the userdata_equals function which will return a boolean value if both source pointers are the same. The example below demonstrate common usage of these functions:

Lua
local scn = App:GetScene("Scene") local mat = Lib:GetMaterial("Materials/Material") print( userdata_type( scn ) ) print( userdata_type( mat ) ) --Uncomment to create a user data equality. --if userdata_equals( scn, scn ) then if userdata_equals( scn, mat ) then print("The two user data point to the same memory address.") else print("The two user data point to different location.") end

If you run the script above (assuming that the resources exists), the output would be:

(0x000002676a34db70) >> Scene *
(0x000002676a34db70) >> Material *
(0x000002676a34db70) >> The two user data point to different location.

Garbage Collection


Lua is equipped with a powerful garbage collection mechanism that allows the virtual machine to release resources that are no longer needed. The built-in mechanism for GC is great for regular execution but for when embedded into time-critical applications the GC might be a CPU bottleneck. As a result, the internal GC mechanism has been slightly altered to clear up resources on multiple frames. The built-in Lua function lua_gc using the parameter LUA_GCSETSTEPMUL now takes a value in milliseconds. This time value now specifies that the garbage collection can only spend a specific amount of time clearing up resources per frame (set to 1ms by default). If the amount of time exceeds that limit the rest of the collection will be executed on a subsequent frame(s). As a result, this approach gives a more predictable garbage collection time and prevent extensive CPU stall.

Take note that passing a value or 0 will skip the garbage collection; by knowing this a more elaborate mechanism can now be implemented by a programmer to change the amount of time allowed to GC based on the frame rate of your application as implemented in the basic example below:

  • Lua
  • C/C++
local desired_fps = ( 1.0 / 60.0 );

if App.stats.delta_time < desired_fps then
    
    local time_remaining = ( desired_fps - App.stats.delta_time ) * 1000.0;

    -- Set how much time can be dedicated to GC
    -- for the current frame.
    collectgarbage( "setstepmul", time_remaining );

else -- Not enough time left, disable GC temporary.
    
    collectgarbage( "setstepmul", 0 )
end
float desired_fps = ( 1.0f / 60.0f );

if( App->stats.delta_time < desired_fps )
{
    float time_remaining = ( desired_fps - App->stats.delta_time ) * 1000.0f;

    // Set how much time can be dedicated to GC
    // for the current frame.
    lua_gc( Lvm->G, LUA_GCSETSTEPMUL, ( int )time_remaining );
}
else // Not enough time left, disable GC temporary.
{
    lua_gc( Lvm->G, LUA_GCSETSTEPMUL, 0 );
}

Editor Variable


When scripting using Lua another built-in global variable have been added for convenience named NRG_EDITOR. This variable enables you to take action at execution time based if the script is currently running within the editor or not; else this variable will be equal to nil.

Lua
-- Print either true or nil if the script is -- running within the editor environment or not. print( NRG_EDITOR ) -- Alternatively, you can access the variable directly -- from the global table. print( _G.NRG_EDITOR )

Mobile Variable


Similar as the editor flag; the NRG_MOBILE variable is available globally when NRG is running on a mobile platform. If initialized on a desktop; this variable will be equal to nil.

Lua
-- Print either true or nil if the script is -- running within the editor environment or not. print( NRG_MOBILE ) -- Alternatively, you can access the variable directly -- from the global table. print( _G.NRG_MOBILE )

Name Convention

Multiple types of resources have the variable name to help you to identify a specific element of an array. All conventional names are capped at 32 characters at the exception of assets and node blocks.

For assets, their name is mapped on 128 bytes. The first 96 bytes are dedicated to the path and the last 32 bytes are the the actual unique name of the Asset. Duplicating assets work the same way as for resources; digits will be appended at the end of the name to remove duplicates and to ensure consistency.

For NodeBlock; since the block name can be a function the maximum characters allowed are 64 bytes. (take note that no digits are added to node blocks since they represent actual code, see below).

Additionally, if two name resides at the same location (whether in a list internally maintained or an asset on disk) the system will automatically add a digit or replace the last character with a digit that is incremented as the resource gets duplicated.

For example, if you duplicate a resource called Object the duplicate will be named Object0, if it gets duplicated again (either Object or Object0) the new resource will be named Object1 and so on.

This rule can be applied to every type of resources and internal identifiers (name of a group, a layer, a variable etc...).

The only exception comes when dealing with Asset names. An asset name is essentially a file path (as they are directly saved and stored on disk, or compressed within an NPK) starting at the root of your project. And can be retrieved by your program using that name the same way (by specifying its relative file path).

Get & Remove(At) Functions


All structures that maintain internal arrays will be equipped with the same type of functionalities to manage the different arrays within the structure. The traditional functions such as Add, Get and Remove is no exception. However, they can be used in multiple ways as described below.

Get Function


When using a Get function both the name or the uid (unique id) of the resource can be passed down to the name parameter to retrieve an element:

Lua
-- Retrieve the object named "Camera". local obj0 = App:GetScene("Scene"):GetObject("Camera") -- Retrieve the object "Camera" using its uid local obj1 = App:GetScene("Scene"):GetObject(obj0.uid) -- Check if they are truly the same if( userdata_equals(obj0,obj1) ) then print("Yes!") end

Remove(At) Function


Based on the flavor of the Remove(At) function you are using passing either nil or a negative value (-1) value will remove all elements contained in the array:

Lua
-- Retrieve the object named "Camera". local obj = App:GetScene("Scene"):GetObject("Camera") -- Remove all attachments by passing nil to the function. obj:RemoveAttachment(nil) -- Remove all variables by passing -1 to the function. obj:RemoveAttachmentAt(-1)

C/C++ Plugins


Although Lua execution speed is good enough for common logic tasks sometimes more performance is required. NRG provides you a plugin hot reload system that allows you to code using low-level API. Since Lua is written in C it is easy to build plugins using C/C++. The plugins system is using the same dynamic linking the mechanism provided by Lua and will automatically reload the library in case it is updated (under the editor environment). When publishing your application all dynamic libraries that you have specified will be bundled within your final NRG pack(s).

Dynamic libraries written in C/C++ will provide you more speed and more low level flexibility; however it is up to the user to compile for each platform and CPU architecture your application will be published on. In addition each library have to be manually linked for each platform via the External Files section of the application properties.

To start creating C/C++ plugins you must first login and access the Download > Source section from your account page then download the Plugins SDK.

Note
When developing plugins for a different type of CPU simply add the architecture suffix at the end of the library and the system will automatically select the appropriate one ie. plugin_x64.dll, plugin_arm.so, plugin_arm64.so, plugin_x64.dylib etc...
Warning
Always ensure that your plugins are running the same version of the SDK as the one used by the NRGeditor / NRGviewer. Every time you update the NRGeditor or NRGviewer to a newer version you should also update the libraries and headers found in the Plugins SDK (which will also be updated) and recompile all your dynamic libraries. Mixing different versions of the base NRG system and associated static libraries found in the SDK might cause crashes and/or undefined behaviors.




NRG - API 2022.4.384543 - Fri Nov 4 2022
Copyright © 2022 nrgcore.com. All Rights Reserved. Terms of Service - Privacy Policy - EULA