TSStatic

TSStatic is the main object class for individually placed static meshes in BeamNG levels. It is commonly used for buildings, signs, bridges, and other manually placed environment meshes.

For very large amounts of repeated static objects, such as trees, rocks, bushes, or small vegetation and props, clutter, barriers, the Forest system is usually more efficient. Forest items are the preferred lightweight solution for repeated assets, while TSStatic is best suited for individually placed objects.


Basic example

A simple TSStatic entry in items.level.json:

{
  "class": "TSStatic",
  "name": "garage_building",
  "shapeName": "/levels/example/art/shapes/garage.dae",
  "position": [100, 200, 0],
  "rotationMatrix": [1, 0, 0, 0, 1, 0, 0, 0, 1],
  "scale": [1, 1, 1],
  "collisionType": "Collision Mesh",
  "decalType": "Collision Mesh",
  "isRenderEnabled": true
}

Important fields

Field Type Description
class string Must be "TSStatic".
name string Scene object name. Should be unique within the level.
shapeName string Path to the model file used by this object.
position array[3] World position.
rotationMatrix array[9] Object rotation as a 3x3 matrix.
scale array[3] Object scale.
collisionType enum/string Mesh data used for physics/collision queries.
decalType enum/string Mesh data used for decal projection/clipping.
dynamic bool Disables some static-object render optimizations.
playAmbient bool Plays an animation sequence named ambient, if present.
meshCulling bool Enables per-submesh culling for large complex shapes.
originSort bool Sorts translucent rendering by object origin instead of bounds.
useInstanceRenderData bool Enables per-instance render data such as instance colors.
instanceColor color Per-instance base color multiplier.
instanceColor1 color First palette color for instance color palette materials.
instanceColor2 color Second palette color for instance color palette materials.
skin string Optional material target remapping / skin name.
prebuildCollisionData bool Prebuilds collision data at load time.
renderNormals number Debug normal rendering scale.
forceDetail integer Forces a specific shape detail/LOD level. -1 means automatic.

Shape file

The most important field is:

"shapeName": "/levels/example/art/shapes/garage.dae"

This points to the model used by the object.

Supported shape paths usually point to:

.dae
.cdae
.dts

The engine may use cached shape data internally, such as .cdae, but level files usually reference the source/imported shape path.

If the shape cannot be loaded, the engine may replace it with an error/no-mesh shape and log an error.

Transform fields

Most TSStatic objects use standard transform fields.

position

"position": [100, 200, 0]

World position in meters.

rotationMatrix

"rotationMatrix": [
  1, 0, 0,
  0, 1, 0,
  0, 0, 1
]

3x3 rotation matrix.

scale

"scale": [1, 1, 1]

Object scale.

Scaling affects rendering and collision. Large non-uniform scaling can make collision or visual results harder to validate.

Collision type

collisionType controls what mesh data is used for physics collision and raycasts.

Supported values:

Value Description
None No collision.
Bounds Uses the object bounding box. Limited support.
Collision Mesh Uses dedicated collision meshes from the shape. Recommended.
Visible Mesh Uses rendered mesh polygons for collision. Expensive.
Visible Mesh Final Uses rendered mesh polygons intentionally. Use only when required.

Example:

"collisionType": "Collision Mesh"

Recommended default:

"collisionType": "Collision Mesh"

Use None for purely decorative objects that should not collide:

"collisionType": "None"

Collision meshes

For best performance, models should include dedicated collision geometry.

A dedicated collision mesh should be much simpler than the visible mesh.

Good collision meshes are:

  • Low-poly
  • Simple enough for physics
  • Accurate enough for gameplay
  • Not overly detailed
  • Split logically if the object is complex

Avoid using visible mesh collision unless necessary.

Visible mesh collision can be expensive. If it is intentional, use Visible Mesh Final; otherwise prefer proper collision meshes.

Decal type

decalType controls which mesh data decals are clipped/projected against.

Example:

"decalType": "Collision Mesh"

Common values are the same as collisionType.

For objects that should receive projected decals, use a mesh type that matches the surface reasonably well.

For objects that should not receive decals:

"decalType": "None"

Static rendering optimization

By default, TSStatic objects without animation can be rendered through the static shape manager. This allows the engine to batch and optimize repeated static shapes.

This is important for performance, especially when a level contains many placed meshes.

The object can use static manager rendering when:

  • dynamic is false
  • No ambient animation is active
  • The shape can be treated as static

If an object is dynamic or animated, it uses its own TSShapeInstance.


dynamic

"dynamic": true

dynamic disables some render optimizations for static objects.

Use it only when needed, such as for objects that need per-object dynamic behavior or cannot be managed as a static instance.

For normal level props and buildings, leave it false or omit it.


Ambient animation

TSStatic can automatically play an animation sequence named:

ambient

if the shape contains it and playAmbient is enabled.

"playAmbient": true

This is useful for simple repeating animations such as:

  • Fans
  • Rotating signs
  • Small mechanical props
  • Animated background elements

If an ambient animation exists, the object becomes process-enabled and will not use the most static optimized path.


meshCulling

"meshCulling": true

meshCulling enables more detailed per-submesh culling inside the object.

This is a legacy feature that only works if dynamic is enabled, howver it’s not recommend to use as it creates performance issues.


originSort

"originSort": true

originSort changes translucent sorting behavior by using the object origin instead of bounds.

This can help with some transparent objects, but it can also create sorting issues if the origin is poorly placed.

Use only when needed for translucent materials.


Instance render data

useInstanceRenderData enables per-instance render properties.

"useInstanceRenderData": true

This is used by materials that support instance color or palette data.

Important fields:

"instanceColor": [1, 1, 1, 1],
"instanceColor1": [1, 0, 0, 1],
"instanceColor2": [0, 0, 1, 1]

These can drive material features such as:

  • Instance base color
  • Instance opacity
  • Instance emissive
  • Color palettes

The material must be authored to use these features.


Example: instance-colored prop

{
  "class": "TSStatic",
  "name": "colored_barrier",
  "shapeName": "/levels/example/art/shapes/barrier.dae",
  "position": [0, 0, 0],
  "rotationMatrix": [1, 0, 0, 0, 1, 0, 0, 0, 1],
  "scale": [1, 1, 1],
  "useInstanceRenderData": true,
  "instanceColor": [1, 0.2, 0.1, 1],
  "collisionType": "Collision Mesh"
}

Skin

The skin field remaps material targets on the shape.

"skin": "blue"

Basic behavior:

  • Material targets beginning with the old skin name are renamed.
  • The default old skin name is usually base.
  • The new target must resolve to an existing material.

Example:

base_body → blue_body

Advanced syntax:

"skin": "base=blue;glass=darkglass"

This replaces multiple material target prefixes.

Skinning changes material target names. It does not modify mesh geometry.

Materials

TSStatic material targets come from the shape’s material list. These targets are matched to BeamNG Material definitions through mapTo.

For example, if the model has a material target:

garage_wall

then the material definition should use:

"mapTo": "garage_wall"

Use the Material Editor or tooling to inspect material targets.

Useful runtime methods for tools include:

getTargetCount()
getTargetName(index)

LOD and detail levels

TSStatic uses the shape’s detail levels for LOD.

The engine selects a detail level based on the object’s estimated screen-space size. In simple terms, it checks how large the object appears on screen and chooses an appropriate LOD.

Several factors affect this:

  • Camera distance - farther objects appear smaller, so lower detail levels are used.
  • Camera FOV - camera FOV changes how big/small object appear on screen.
  • Shape size - larger objects remain visually large for longer, so they keep higher detail levels at greater distances.
  • Object scale - scaled-up objects behave like larger objects and switch to lower LODs later.
  • Viewport size / projection - screen resolution and camera projection affect how many pixels the object occupies.
  • Global detail adjustment - $pref::TS::detailAdjust scales the calculated pixel size, allowing higher or lower LOD bias.

The result is a calculated pixelSize value. This is compared against the detail levels stored in the shape. Higher pixelSize means the object appears larger on screen and should use a higher-detail mesh; lower pixelSize means the object appears smaller and can use a lower-detail mesh.

For example, a small prop and a large building at the same distance will not necessarily use the same LOD. The building has a larger bounding radius, so it occupies more screen space and may keep a higher detail level farther away.

Relevant settings include:

$pref::TS::detailAdjust
$pref::TS::skipRenderDLs

forceDetail

"forceDetail": -1

-1 means automatic detail selection.

A non-negative value forces a specific detail level:

"forceDetail": 0

Use this only for debugging or special cases. For normal content, let the engine select LOD automatically.


Billboard details

Shapes can contain billboard/imposter detail levels. The engine can use these for far-away rendering if the shape is set up correctly.

This is handled by the shape data and is not configured directly in TSStatic.


Debug fields

renderNormals

"renderNormals": 1

If greater than zero, renders debug normals for the current detail level.

Useful for checking:

  • Broken normals
  • Incorrect shading
  • Mirrored/incorrect geometry
  • Export issues

forceDetail

Forces a detail level, useful for checking LODs.


Raycasts and selection

TSStatic supports raycasts and poly list generation.

Depending on context, it may use:

  • Bounds
  • Collision meshes
  • Visible meshes
  • Current rendered detail
  • LOS details

This affects:

  • Editor selection
  • Physics queries
  • Decal projection
  • Gameplay raycasts
  • Collision

Resource reloading

TSStatic listens for shape resource changes.

If the referenced shape file changes, the object can recreate its shape and refresh collision/material data.

This is useful when iterating on level art.


Full example

{
  "class": "TSStatic",
  "name": "warehouse_01",
  "shapeName": "/levels/example/art/shapes/buildings/warehouse_01.dae",
  "position": [250, 120, 0],
  "rotationMatrix": [0, -1, 0, 1, 0, 0, 0, 0, 1],
  "scale": [1, 1, 1],
  "collisionType": "Collision Mesh",
  "decalType": "Collision Mesh",
  "dynamic": false,
  "playAmbient": true,
  "meshCulling": true,
  "originSort": false,
  "useInstanceRenderData": false,
  "isRenderEnabled": true,
  "canSaveDynamicFields": true
}

When to use TSStatic vs Forest

Use TSStatic for individually placed objects:

  • Buildings
  • Bridges
  • Barriers
  • Large props
  • Unique objects
  • Objects requiring specific placement
  • Objects needing individual collision or material setup

Use the Forest system for large quantities of repeated objects:

  • Trees
  • Rocks
  • Bushes
  • Grass clumps
  • Scatter props
  • Repeated assets

Forest items are generally more lightweight for high object counts.


Best practices

  • Use TSStatic for manually placed static level meshes.
  • Use Forest items for large repeated natural/scatter assets.
  • Use dedicated collision meshes whenever possible.
  • Avoid visible mesh collision unless absolutely needed.
  • Use collisionType: None for decorative non-collidable objects.
  • Keep object origins sensible, especially for transparent objects and sorting.
  • Leave dynamic disabled unless required.
  • Let LOD selection happen automatically; avoid forceDetail except for debugging.
  • Use instance render data only when the material needs it.
  • Keep material target names stable and match them with mapTo.
  • Use .dae/.cdae assets with proper LODs and collision detail.

Common issues

Object does not appear

Possible causes:

  • Invalid shapeName
  • Missing shape file
  • Shape failed to load
  • Object is hidden
  • LOD/detail setup hides it
  • Materials failed badly enough to make it appear invisible

Object has warning material

Possible causes:

  • Missing material definition
  • mapTo mismatch
  • Material file not loaded
  • Texture missing

Object has no collision

Check:

"collisionType": "Collision Mesh"

Also verify the shape actually contains collision mesh/detail data.

Object collision is too expensive

Possible causes:

  • Using visible mesh collision
  • Collision mesh too detailed
  • Object is very large or complex
  • Too many collidable TSStatic objects

Decals do not project correctly

Check:

"decalType": "Collision Mesh"

or use a mesh type better suited for decal clipping.

Ambient animation does not play

Check that:

  • playAmbient is true
  • The shape contains an animation sequence named ambient
  • The object is not hidden

Instance color does nothing

Check that:

  • useInstanceRenderData is true
  • The material uses instance color features
  • The material is authored to multiply base color/emissive/opacity by instance color

Transparent sorting looks wrong

Try originSort, but also check:

  • Object origin
  • Material blend mode
  • Geometry split
  • Draw order

Summary

TSStatic is the main object class for individually placed static 3D meshes in levels.

For large numbers of repeated natural or scatter assets, use the Forest system instead. BeamNG level geometry should normally be placed either as TSStatic objects or as Forest items.

TSStatic directly references a shape file and supports rendering, collision, decals, LODs, material remapping, optional ambient animation, and per-instance render data.

Last modified: June 2, 2026

Any further questions?

Join our discord
Our documentation is currently incomplete and undergoing active development. If you have any questions or feedback, please visit this forum thread.