deck.gl layers for rendering 3D meshes and scene graphs in WebGL-based data visualizations
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
ScenegraphLayer renders complete glTF scene graphs with support for hierarchical node structures, animations, and physically-based rendering (PBR) materials. It's designed for complex 3D models that require advanced lighting and animation capabilities.
import { ScenegraphLayer } from "@deck.gl/mesh-layers";
import type { ScenegraphLayerProps } from "@deck.gl/mesh-layers";class ScenegraphLayer<DataT = any, ExtraPropsT extends {} = {}> extends Layer<
ExtraPropsT & Required<ScenegraphLayerProps<DataT>>
> {
constructor(props: ScenegraphLayerProps<DataT> & ExtraPropsT);
static readonly layerName: "ScenegraphLayer";
static readonly defaultProps: DefaultProps<ScenegraphLayerProps>;
}interface ScenegraphLayerProps<DataT = unknown> extends LayerProps {
// Data and scene
data: LayerDataSource<DataT>;
scenegraph: any;
// Scene management
getScene?: (
scenegraph: any,
context: {device?: Device; layer: ScenegraphLayer<DataT>}
) => GroupNode;
getAnimator?: (
scenegraph: any,
context: {device?: Device; layer: ScenegraphLayer<DataT>}
) => GLTFAnimator;
loaders?: any[];
// Styling
sizeScale?: number;
sizeMinPixels?: number;
sizeMaxPixels?: number;
// Lighting
_lighting?: 'flat' | 'pbr';
_imageBasedLightingEnvironment?:
| PBREnvironment
| ((context: {gl: WebGL2RenderingContext; layer: ScenegraphLayer<DataT>}) => PBREnvironment);
// Animation
_animations?: {
[name: number | string | '*']: {
playing?: boolean;
startTime?: number;
speed?: number;
};
} | null;
// Accessors
getPosition?: Accessor<DataT, Position>;
getColor?: Accessor<DataT, Color>;
getOrientation?: Accessor<DataT, Orientation>;
getScale?: Accessor<DataT, Scale>;
getTranslation?: Accessor<DataT, Translation>;
getTransformMatrix?: Accessor<DataT, number[]>;
}LayerDataSource<DataT>any(scenegraph, context) => GroupNode (optional)(scenegraph, context) => GLTFAnimator (optional)any[] (optional)[GLTFLoader]number (optional, default: 1)number (optional, default: 0)number (optional, default: Number.MAX_SAFE_INTEGER)'flat' | 'pbr' (optional, default: 'flat')PBREnvironment | Function (optional)Object | null (optional, default: null)playing: boolean - Whether the animation is playingstartTime: number - Start time of the animation (default: 0)speed: number - Speed multiplier of the animation (default: 1)Accessor<DataT, Position> (optional, default: x => x.position)[x, y, z]Accessor<DataT, Color> (optional, default: [255, 255, 255, 255])[r, g, b, a]Accessor<DataT, Orientation> (optional, default: [0, 0, 0])[pitch, yaw, roll] in degreesAccessor<DataT, Scale> (optional, default: [1, 1, 1])[x, y, z]Accessor<DataT, Translation> (optional, default: [0, 0, 0])[x, y, z] in metersAccessor<DataT, number[]> (optional, default: [])class ScenegraphLayer<DataT, ExtraPropsT> {
// Layer state
get isLoaded(): boolean;
// Rendering
getShaders(): ShaderModule;
initializeState(): void;
updateState(params: UpdateParameters<this>): void;
finalizeState(context: LayerContext): void;
draw(opts: {context: any}): void;
}import { ScenegraphLayer } from "@deck.gl/mesh-layers";
const data = [
{coordinates: [-122.4, 37.8, 0], name: "San Francisco"},
{coordinates: [-74.0, 40.7, 0], name: "New York"},
];
const layer = new ScenegraphLayer({
id: 'gltf-models',
data,
scenegraph: 'https://example.com/models/building.gltf',
getPosition: d => d.coordinates,
sizeScale: 500
});const layer = new ScenegraphLayer({
id: 'animated-scene',
data: myData,
scenegraph: '/path/to/animated-model.glb',
getPosition: d => d.position,
getOrientation: d => [0, d.heading, 0],
// Enable PBR lighting
_lighting: 'pbr',
_imageBasedLightingEnvironment: myPBREnvironment,
// Configure animations
_animations: {
'*': {playing: true, speed: 1.5}, // All animations at 1.5x speed
'walk': {playing: true, speed: 2.0}, // Walk animation at 2x speed
'idle': {playing: false} // Disable idle animation
}
});import { createScenegraphsFromGLTF } from "@luma.gl/gltf";
const layer = new ScenegraphLayer({
id: 'custom-scene',
data: myData,
scenegraph: myGLTFData,
// Custom scene extraction
getScene: (scenegraph, {device, layer}) => {
const gltfObjects = createScenegraphsFromGLTF(device, scenegraph, {
modelOptions: {
id: layer.props.id + '-model',
bufferLayout: layer.getAttributeManager()!.getBufferLayouts(),
...layer.getShaders()
}
});
return gltfObjects.scenes[0];
},
// Custom animator
getAnimator: (scenegraph) => {
return scenegraph.animator || null;
}
});// Start with stopped animations
const layer = new ScenegraphLayer({
id: 'controlled-animation',
data: myData,
scenegraph: myModel,
_animations: {
'*': {playing: false}
}
});
// Later, start specific animations
layer.setProps({
_animations: {
'run': {playing: true, startTime: Date.now() / 1000, speed: 1.2},
'jump': {playing: true, startTime: Date.now() / 1000 + 2.0, speed: 0.8}
}
});const layer = new ScenegraphLayer({
id: 'size-constrained',
data: myData,
scenegraph: myModel,
sizeScale: 1000,
sizeMinPixels: 10, // Never smaller than 10 pixels
sizeMaxPixels: 200, // Never larger than 200 pixels
getPosition: d => d.coordinates
});ScenegraphLayer supports various input formats:
Uses @loaders.gl/gltf for loading and processing glTF assets with automatic:
0, 1, 2 - Animation by index"walk", "idle" - Animation by name"*" - Affects all animationsAnimations use the deck.gl timeline system and automatically:
_animations is configured'flat')'pbr')_imageBasedLightingEnvironment for realistic resultssizeMinPixels/sizeMaxPixels to control level-of-detailInstall with Tessl CLI
npx tessl i tessl/npm-deck-gl--mesh-layers