Drop-in CesiumJS integration for Nuclideon's udSDK — render UDS point clouds
inside a Cesium Viewer with depth correctly interleaving against
Cesium-rendered terrain, 3D Tiles, and primitives.
Language: Javascript / HTML
Type: Integration
Contributor: Nuclideon Development Team <info@nuclideon.com>
Organization: Nuclideon, https://nuclideon.com
Date: 2026-05-22
Cesium Versions: 1.100+ (UMD build); tested against 1.141
<script src="https://cesium.com/downloads/cesiumjs/releases/1.141/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.141/Build/Cesium/Widgets/widgets.css" rel="stylesheet" />
<script src="https://nuclideon.com/cdn/integrations/cesium/1.0.0/udsdk-cesium.js"></script>
<div id="cesiumContainer" style="width:100%;height:100vh"></div>
<script>
const viewer = new Cesium.Viewer("cesiumContainer", { /* ... */ });
udSDKCesium.attach(viewer, {
clientName: "Acme Portal",
models: ["https://my-bucket.s3.amazonaws.com/site.uds"],
});
</script>A worked example lives in demo.html.
- CesiumJS 1.100+ on the page as the
Cesiumglobal (UMD build, not the ES module build). Tested against 1.141. - Host page must be cross-origin isolated — udSDK is a threaded
emscripten build and needs
SharedArrayBuffer. Set these on the page response:Verify at runtime:Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: credentialless (or require-corp)window.crossOriginIsolated === true. - A Nuclideon udCloud account to sign in.
udSDKCesium.attach(viewer, opts) returns an EventTarget controller.
| Option | Default | Notes |
|---|---|---|
clientName |
"cesium-<hostname>" |
OAuth consent app name |
models |
[] |
array of url strings or {url, zOffset, zone, classification, flyTo} |
signInUI |
"auto" |
"auto" shows the built-in box; false to BYO |
signInLabel |
"Sign in to udCloud" |
button text |
autoEnable |
true |
make the udSDK primitive visible immediately |
autoFlyTo |
true |
fly the camera to the first loaded model |
tryDomainLogin |
"auto" |
try silent SSO before falling back to OAuth popup |
serverAddress |
(default udCloud) | override for staging / self-hosted / regional |
baseUrl |
derived from the script's own URL | where to fetch udSDKjs.* siblings |
onStatus |
null |
emscripten loader status callback |
signIn()→Promise. Must be triggered from a user gesture.loadModel(spec)→Promise<{handle, slot}>.specis a url string or{url, zOffset, zone, classification, flyTo}.enabled— show/hide the udSDK primitive.signedIn,viewer,primitive,ready.destroy()— remove primitive + UI.
| Event | detail |
|---|---|
ready |
— |
signed-in |
{ method: "oauth" | "domain" } |
model-loaded |
{ url, handle, slot } |
model-error |
{ url, code, error } |
sign-in-error |
{ code, error } |
error |
{ error } |
A tiny static server is included that issues the required COOP/COEP headers so the demo runs locally without setting up nginx/Caddy/etc.:
node serve.js
# http://localhost:8080/demo.html
-
Bootstrap. udSDK is a threaded emscripten build, which means three non-obvious things have to happen when loading it cross-origin:
Module.locateFilepoints emscripten at the CDN base so it fetchesudSDKjs.wasmfrom there instead of the page's origin.- The pthread pool spawns workers via
new Worker(scriptUrl), which browsers refuse for cross-origin URLs regardless of CORS. The wrapper fetchesudSDKjs.jsas text, wraps it in aBlob, and hands the resulting same-origin Blob URL to emscripten viaModule.mainScriptUrlOrBlob. - The page has to be cross-origin isolated (see Requirements) so
SharedArrayBufferis exposed.
-
Sign-in. Tries
udSDKJS_CreateSharedFrom_udCloud(name, null)first — a silent domain auth that succeeds if the browser already has a session with the configured udCloud server (typically via corporate SSO). If that fails, the built-in sign-in box opens an OAuth popup from a user gesture and finishes viaudSDKJS_ConnectStart+udSDKJS_ConnectComplete. -
Per-frame render. A custom
Primitiveis added toviewer.scene.primitives. Itsupdate(), each frame:- Resizes udSDK's offscreen scene if the canvas changed.
- Pushes Cesium's
viewMatrix+projectionMatrixinto udSDK. - Calls
udSDKJS_RenderQueue(). - Copies udSDK's colour + depth buffers into two Cesium-managed GL textures.
- Submits a screen-space
DrawCommandthat samples them. The fragment shader unprojects each sample's depth, computes world-space distance from the camera, and writes it throughczm_writeLogDepth— so udSDK fragments depth-test correctly against everything else Cesium renders.
-
Depth-buffer packing. udSDK delivers the depth buffer as raw IEEE-754 little-endian float32 bytes. The wrapper uploads it as an RGBA8 texture (4 bytes per pixel == 1 float per pixel) and decodes back to float in the shader. This avoids requiring R32F renderable-texture support on the Cesium side.
-
Asyncify yield handling. udSDK's wasm uses emscripten Asyncify and yields by throwing the literal string
"unwind". If that escapes into Cesium's render loop, Cesium catches it as a fatal renderError and stops rendering for good. The primitive'supdate()catches it locally and drops the frame instead — the last good colour/depth buffer stays bound on the textures so it's visually unnoticeable.
- Single-instance only. udSDK's render state is process-global, so one
attach()per page. - No WebGL context-loss recovery. Colour/depth textures aren't recreated after a lost context. Reload the page.
- WebGL2 required. The shader is GLSL 3.00 ES. Modern Cesium runs WebGL2 by default, but older Cesium pre-1.95 or a forced-WebGL1 context will not work.