Skip to content

Asset Anatomy (.asset & Lua)

Every object in OpenSpace—from planets and spacecraft to labels, trails, and volumetric grids—is defined by a Lua script with an .asset extension. Understanding how OpenSpace loads, links, and tears down these files keeps custom scenes modular and crash-free.


Anatomy of a SceneGraphNode

At minimum, an .asset file declares a Lua table representing a SceneGraphNode and registers it inside asset.onInitialize:

local assetHelper = asset.require("util/asset_helper") -- (1)!

local CustomNode = {
    Identifier = "DemoLandmark",                       -- (2)!
    Parent = "Earth",                                  -- (3)!
    Transform = {                                      -- (4)!
        Translation = {
            Type = "GlobeTranslation",
            Globe = "Earth",
            Latitude = 41.8663,
            Longitude = -87.6068,
            Altitude = 500
        }
    },
    Renderable = {                                     -- (5)!
        Type = "RenderablePlaneImageLocal",
        Size = 15000,
        Texture = asset.localResource("marker.png")
    },
    GUI = {                                            -- (6)!
        Name = "Adler Planetarium Marker",
        Path = "/Custom/Landmarks"
    }
}

asset.onInitialize(function()
    openspace.addSceneGraphNode(CustomNode)
end)

asset.onDeinitialize(function()
    openspace.removeSceneGraphNode(CustomNode)         -- (7)!
end)
  1. Dependencies: asset.require(...) loads another asset first so its Identifier exists before your node attaches to it.
  2. Unique Identifier: Must be globally unique across the entire loaded scene graph (no spaces or special characters).
  3. Parent Attachment: Sets the coordinate origin (0, 0, 0). Common parents include "Root", "SolarSystemBarycenter", "Earth", or another spacecraft node.
  4. Transform Block: Controls position (Translation), orientation (Rotation), and uniform/non-uniform Scale relative to Parent.
  5. Renderable Block: Defines what actually draws on screen (3D model, image plane, trail, globe, or point cloud). Omit Renderable to create an invisible anchor/pivot node.
  6. GUI Placement: Determines where the node appears inside the in-app Scene tree.
  7. Clean Unloading: Always pair addSceneGraphNode with removeSceneGraphNode in onDeinitialize so assets unload cleanly when switching profiles.

Loading Resources: Local vs. HTTP Sync

OpenSpace provides two resource helpers inside .asset files so paths resolve reliably across machines:

Use for textures, .obj/.gltf models, and .bsp kernels stored in the same folder (or subfolder) as your .asset file inside ${USER}/data/assets/:

local modelFile = asset.localResource("models/probe.gltf")
local textureFile = asset.localResource("textures/albedo.png")

Use when pulling datasets automatically from OpenSpace's data servers into ${USER}/cache/ on startup:

local syncedData = asset.resource({
    Name = "Mission Dataset",
    Type = "HttpSynchronization",
    Identifier = "mission_dataset_v1",
    Version = 1
})

Require Parent Nodes Explicitly

If your node sets Parent = "Mars", add asset.require("scene/solarsystem/planets/mars/mars") at the top of your .asset file. Without asset.require, loading your asset in a minimal profile where Mars isn't already loaded will throw an unknown-parent error on startup.


Exporting & Reusing Asset Groups

You can bundle multiple related nodes (such as a spacecraft model, its trajectory trail, and a label) into a single .asset file by passing a list to assetHelper.registerSceneGraphNodesAndReturnIdentifiers({ NodeA, NodeB, NodeC }), or by creating a master .asset that calls asset.require("./sub_asset_1") and asset.require("./sub_asset_2").

Screenshot Placeholder: scene-graph-hierarchy.png

  • Crop: Capture the Scene tree panel showing a custom /Custom/Landmarks folder expanded with a child node's Transform and Renderable sub-menus visible.
  • Callouts: (1) Custom GUI.Path folder, (2) Identifier display name, (3) Expanded Renderable and Transform property groups.