Using the default 3D engine
OGL was seleceted as the default 3D engine for sparcl, because it is very small and still manages to hide away the specifics of WebGL.
Viewers depend on the RenderingEngine interface (src/core/engines/RenderingEngine.ts), not on OGL types directly.
- OGL (default):
src/core/engines/ogl/ogl.ts - Three.js (experimental):
src/core/engines/three/threeEngine.ts
Instantiate via createRenderingEngine() (src/core/engines/createRenderingEngine.ts). Select the backend with URL ?engine=ogl or ?engine=three, or persist with localStorage key sparcl.renderingEngine (helpers setPersistedRenderingEngineId, getPersistedRenderingEngineId, constant RENDERING_ENGINE_STORAGE_KEY in that module). In the app dashboard, Debug settings includes a Rendering engine control that writes the same key (restart AR to apply; URL still overrides for a single load).
Neutral helpers used by viewers and future engines:
PRIMITIVES/PrimitiveShape—src/core/contents/primitives.ts(SCR placeholder shapes)- OGL procedural meshes —
src/core/engines/ogl/oglPrimitives.ts(createModel, programs, placeholders);createRandomObjectDescription—src/core/contents/objectDescription.ts model3DFormatFromRef/pointCloudFormatFromRef—src/core/contents/contentFormats.tsgetExternalCameraParameters—src/core/engines/externalCameraPose.ts(returnsExternalCameraParameters: projection intrinsics +cameraposeextrinsic column-majormat4)RigidPoseplacement —addModelWithRigidPose,addDynamicObjectWithRigidPose,addTextObjectWithRigidPose, etc.- Scene graph handles — opaque
SceneNodeId(string, exported fromRenderingEngine.ts).add*methods return this id; usegetNodePose/setNodePose/remove(nodeId)— never OGLMesh/Transformor THREEObject3Din viewers.addModel/addModelWithRigidPosereturnSceneNodeId(GLTF root). Engine-local registries:OglSceneNodeRegistry(src/core/engines/ogl/oglSceneNodeRegistry.ts),ThreeSceneNodeRegistry(src/core/engines/three/threeSceneNodeRegistry.ts). - OGL GPU teardown —
src/core/engines/ogl/oglDisposeHelper.ts:disposeOglGpuResourcesUnder(scene)atogl.cleanup()(full scene).disposeOglGpuResourcesForDetachedSubtree(node, scene)fromogl.remove()before detaching the node: geometry/programs are disposed only when their total use undersceneequals use under the subtree being removed, so shared GLTF assets stay valid. After full dispose,ogl.cleanup()also clearsgltfCacheandTextureLoader.clearCache()(OGL’s module-level image texture cache used by e.g. PBR helpers), so the next load does not reuse deleted WebGL programs or textures. Each PLY mesh has its ownProgram(createPlyMeshProgram/createPlyPointsPrograminoglPlyHelper.ts); there is no shared PLY program cache. - OGL long sessions /
remove(nodeId)— Sampler textures attached only to programs that are disposed aredeleteTexture’d.addLogoObjectandaddVideoObjectreturnSceneNodeId | null(registered meshes); logos use per-instanceTextureobjects (notTextureLoader’s global cache), so logos can be removed without leaking or breaking other billboards.gltfCacheis not cleared on a singleremove; it keeps unpacked.glbbytes in memory for the same URL (bounded if URLs repeat; can grow if you stream endlessly many unique GLB URLs). - Three.js
remove/cleanup—disposeThreeSubtree(node, scene)inthreeEngine.tsdisposesBufferGeometryandMaterialonly when their reference counts under the fullsceneequal those under the subtree being removed (GLTF often shares one geometry across several meshes; disposing it once per mesh left siblings withattribute.arrayundefined and WebGLbyteLengtherrors). - Poses at the engine boundary — gl-matrix
ReadonlyVec3,ReadonlyQuat, andmat4. Build placement poses withvec3.fromValues/quat.fromValues(or fromRigidPosefields). Read or update nodes withgetNodePose,setNodePose,translateNode,setNodeUniformScale,setNodeVisible,getNodeWorldMatrix.