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)
- Dependencies:
asset.require(...)loads another asset first so itsIdentifierexists before your node attaches to it. - Unique Identifier: Must be globally unique across the entire loaded scene graph (no spaces or special characters).
- Parent Attachment: Sets the coordinate origin (
0, 0, 0). Common parents include"Root","SolarSystemBarycenter","Earth", or another spacecraft node. - Transform Block: Controls position (
Translation), orientation (Rotation), and uniform/non-uniformScalerelative toParent. - Renderable Block: Defines what actually draws on screen (3D model, image plane, trail, globe, or point cloud). Omit
Renderableto create an invisible anchor/pivot node. - GUI Placement: Determines where the node appears inside the in-app Scene tree.
- Clean Unloading: Always pair
addSceneGraphNodewithremoveSceneGraphNodeinonDeinitializeso 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/Landmarksfolder expanded with a child node's Transform and Renderable sub-menus visible. - Callouts: (1) Custom
GUI.Pathfolder, (2)Identifierdisplay name, (3) ExpandedRenderableandTransformproperty groups.