### Start Spark.js development server Source: https://github.com/sparkjsdev/spark/blob/main/README.md Starts a local development server to run Spark.js examples. By default, it runs at http://localhost:8080/. ```shell npm start ``` -------------------------------- ### Test the site locally Source: https://github.com/sparkjsdev/spark/wiki/Update-Spark-Web Starts a local web server to preview changes before deployment. ```bash npm site:serve ``` -------------------------------- ### Install Rustup Source: https://github.com/sparkjsdev/spark/blob/main/rust/spark-rs/README.md Installs Rustup and Rust using the recommended script from the official Rust homepage. ```shell curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` -------------------------------- ### Serve Spark Website Locally Source: https://github.com/sparkjsdev/spark/blob/main/README.md Starts a local static server to view the built Spark website. This command is run using npm. ```bash npm run site:serve ``` -------------------------------- ### Basic Three.js and Spark Renderer Setup Source: https://github.com/sparkjsdev/spark/blob/main/examples/basic-xr/index.html Initializes a Three.js scene, camera, and Spark Renderer. Sets up basic event listeners for window resizing and appends the renderer's canvas to the DOM. This is the foundational setup for any Spark WebXR application. ```javascript import * as THREE from "three"; import { SparkRenderer, PackedSplats, ExtSplats, SplatMesh, SplatEdit, SplatEditRgbaBlendMode, SplatEditSdf, SplatEditSdfType, SparkControls, SparkXr, textSplats, SplatSkinning } from "@sparkjsdev/spark"; import GUI from "lil-gui"; const RENDER_TIMEOUT_MS = 30 * 1000; const stats = new Stats(); document.body.appendChild(stats.dom); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 1000); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); window.THREE = THREE; window.scene = scene; window.renderer = renderer; window.addEventListener("resize", onWindowResize, false); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } ``` -------------------------------- ### Basic Spark LoD Setup Source: https://github.com/sparkjsdev/spark/blob/main/examples/lod/index.html Initializes the SparkRenderer, scene, camera, and basic event listeners for resizing. This forms the foundation for rendering with LoD. ```javascript import * as THREE from "three"; import { SparkRenderer, SplatMesh, PackedSplats, SplatLoader, SparkControls, isMobile, SplatEdit, SplatEditSdf, SplatEditSdfType } from "@sparkjsdev/spark"; import GUI from "lil-gui"; import { getAssetFileURL } from "../js/get-asset-url.js"; const RENDER_TIMEOUT_MS = 20000; async function main() { // await testSpark(); // return; const scene = new THREE.Scene(); // scene.background = new THREE.Color("#caf0fe"); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 1000); camera.position.set(0, 0, 1); // camera.position.set(0, 0.2, 0.5); // camera.position.set(5.92, 13.68, 3.17); // camera.quaternion.set(-0.315, 0.178, 0.060, 0.930).normalize(); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); window.addEventListener('resize', onWindowResize, false); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } const spark = new SparkRenderer({ renderer, // sort32: false, // sort360: true, // sortRadial: true, // preUpdate: false, // minSortIntervalMs: 1000, // minLodIntervalMs: 1000, // lodSplatCount: 500000, // lodSplatScale: 0.5, outsideFoveate: 1.0, behindFoveate: 1.0, }); scene.add(spark); console.log("SPARK: ", spark); const stats = new Stats(); document.body.appendChild(stats.dom); const controls = new SparkControls({ canvas: renderer.domElement }); controls.pointerControls.pointerRollScale = 0.0; controls.pointerControls.reverseRotate = isMobile(); controls.pointerControls.rotateSpeed *= 2.0; // controls.pointerControls.slideSpeed *= 1.5; // controls.fpsMovement.moveSpeed *= 1.5; document.addEventListener("keydown", (event) => { if (event.key === "\\") { console.log("Camera position: ", camera.position, "quaternion: ", camera.quaternion); } }); const gui = new GUI({ title: "Settings" }); const renderEnable = { value: true }; let lastMoved = Number.POSITIVE_INFINITY; gui.add(renderEnable, "value").name("Enable render").listen().onChange((enabled) => { if (enabled) { lastMoved = performance.now(); } }); gui.add(controls.pointerControls, "reverseRotate").name("Reverse controls").listen(); // ... rest of the code for loading splats ... } main(); ``` -------------------------------- ### Setup GUI and Controls Source: https://github.com/sparkjsdev/spark/blob/main/examples/editor/index.html Initializes the main and secondary GUI panels using lil-gui and sets up SparkControls for user interaction. OrbitControls are configured for camera manipulation but initially disabled. ```javascript const frame = new THREE.Group(); frame.quaternion.set(1, 0, 0, 0); scene.add(frame); // Keep track of original file bytes for each loaded splat mesh const inputs = []; const grid = new SplatMesh({ constructSplats: (splats) => constructGrid({ splats, extents: new THREE.Box3(new THREE.Vector3(-10, -10, -10), new THREE.Vector3(10, 10, 10)), }), }); grid.opacity = 0; grid.visible = false; scene.add(grid); const stats = new Stats(); document.body.appendChild(stats.dom); stats.dom.style.display = "none"; const gui = new GUI({ title: "Settings", container: document.getElementById("main-gui") }); const secondGui = new GUI({ title: "Splats", container: document.getElementById("second-gui") }).close(); const controls = new SparkControls({ canvas }); // Setup mouse controls to orbit the camera around const orbitControls = new OrbitControls(camera, renderer.domElement); orbitControls.enabled = false; orbitControls.target.set(0, 0, 0); orbitControls.minDistance = 0.1; orbitControls.maxDistance = 10; ``` -------------------------------- ### Three.js Scene Setup with SparkRenderer Source: https://github.com/sparkjsdev/spark/blob/main/examples/raycasting/index.html Initializes a Three.js scene, camera, and WebGLRenderer, then integrates SparkRenderer for rendering. ```javascript import * as THREE from "three"; import { SparkRenderer, SplatMesh, PackedSplats } from "@sparkjsdev/spark"; import { getAssetFileURL } from "../js/get-asset-url.js"; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10); camera.position.set(0, -0.25, -1.5); camera.lookAt(0, -0.15, 0); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); const spark = new SparkRenderer({ renderer }); scene.add(spark); window.addEventListener('resize', onWindowResize, false); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } ``` -------------------------------- ### Install Spark.js with NPM Source: https://github.com/sparkjsdev/spark/blob/main/docs/docs/index.md Use this command to install the Spark.js library into your project via NPM. This is the recommended approach for development. ```shell npm install @sparkjsdev/spark ``` -------------------------------- ### Build and Develop Spark.js Source: https://github.com/sparkjsdev/spark/blob/main/docs/docs/index.md These commands are used to build the WebAssembly component of Spark.js and start a development server. Rust must be installed on your machine. The development server will be available at http://localhost:8080/. ```shell npm install npm run build:wasm npm run dev ``` -------------------------------- ### Make Instructions Source: https://github.com/sparkjsdev/spark/blob/main/examples/editor/index.html Creates a text-based instruction object for the scene. This is useful for guiding users on how to interact with the viewer. ```javascript function makeInstructions() { const instructions = textSplats({ text: "Drag and Drop\na Gsplat file\nhere to view", textAlign: "center", fontSize: 64, objectScale: 0.1 / 64, }); instructions.quaternion.copy(frame.quaternion).invert(); instructions.enableWorldToView = true; instructions.worldModifier = makeWorldModifier(instructions); instructions.updateGenerator(); return instructions; } instructions = makeInstructions(); addBoundingBoxHelper(instructions); frame.add(instructions); ``` -------------------------------- ### Get build-lod command help Source: https://github.com/sparkjsdev/spark/blob/main/docs/docs/lod-getting-started.md Run the `build-lod` tool without any parameters to display its usage instructions and available options. ```shell npm run build-lod Usage: build-lod [--unlod] // Remove LoD nodes with children from file [--csplat] [--gsplat] // Use compact (csplat) or higher-precision (default gsplat) splat encoding [--quick] [--quality] // Use quick (tiny-lod) or quality (bhatt-lod) LoD method (default quick) ... ``` -------------------------------- ### Scene and Renderer Setup Source: https://github.com/sparkjsdev/spark/blob/main/examples/particle-animation/index.html Initializes the Three.js scene, camera, renderer, and SparkRenderer. Handles window resizing to maintain aspect ratio. ```javascript const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); scene.background = new THREE.Color(0x08a2d3); const renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); const spark = new SparkRenderer({ renderer }); scene.add(spark); function handleResize() { const width = window.innerWidth; const height = window.innerHeight; renderer.setSize(width, height); camera.aspect = width / height; camera.updateProjectionMatrix(); } handleResize(); window.addEventListener("resize", handleResize); ``` -------------------------------- ### Setup Spark Scene and Renderer Source: https://github.com/sparkjsdev/spark/blob/main/examples/procedural-splats/index.html Initializes the Three.js scene, camera, and WebGL renderer, then integrates SparkRenderer for splat rendering. Includes a resize handler for responsiveness. ```javascript import * as THREE from "three"; import { SparkRenderer, SparkControls, SplatMesh, constructGrid, constructAxes, textSplats, imageSplats } from "@sparkjsdev/spark"; import { getAssetFileURL } from "../js/get-asset-url.js"; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); const spark = new SparkRenderer({ renderer }); scene.add(spark); window.addEventListener('resize', onWindowResize, false); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } ``` -------------------------------- ### Install Mkdocs Material Source: https://github.com/sparkjsdev/spark/blob/main/README.md Installs the Mkdocs Material theme, which is used for building documentation. This command is typically run using pip. ```bash pip install mkdocs-material ``` -------------------------------- ### Scene Setup and Initialization Source: https://github.com/sparkjsdev/spark/blob/main/examples/interactive-holes/index.html Initializes the Three.js scene, camera, renderer, and Spark renderer. Sets up basic scene properties and event listeners for window resizing. ```javascript import * as THREE from "three"; import { SplatMesh, SparkRenderer, dyno, SparkControls, SplatEdit, SplatEditSdf, SplatEditSdfType, SplatEditRgbaBlendMode } from "@sparkjsdev/spark"; import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js"; import { GUI } from "lil-gui"; import { getAssetFileURL } from "../js/get-asset-url.js"; // Scene const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 50); camera.position.set(0, -0.3, -3); camera.lookAt(0, 0, 1); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); const spark = new SparkRenderer({ renderer }); scene.add(spark); window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); ``` -------------------------------- ### Spark Scene Setup and SplatMesh Loading Source: https://github.com/sparkjsdev/spark/blob/main/examples/splat-shader-effects/index.html Initializes the Spark scene, camera, and renderer. Loads a SplatMesh asset and adds it to the scene. ```javascript import * as THREE from "three"; import { SparkRenderer, SparkControls, SplatMesh, dyno } from "@sparkjsdev/spark"; import { getAssetFileURL } from "../js/get-asset-url.js"; import GUI from "lil-gui"; // Scene setup const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); const spark = new SparkRenderer({ renderer }); scene.add(spark); document.body.appendChild(renderer.domElement); window.addEventListener('resize', onWindowResize, false); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } // Cat model setup const splatURL = await getAssetFileURL("cat.spz"); const cat = new SplatMesh({ url: splatURL }); cat.quaternion.set(1, 0, 0, 0); cat.position.set(0, 0, -1.5); cat.scale.set(.5, .5, .5); scene.add(cat); ``` -------------------------------- ### Spark Renderer Initialization and Scene Setup Source: https://github.com/sparkjsdev/spark/blob/main/examples/nonlod/index.html Initializes the Three.js scene, camera, and SparkRenderer with LoD capabilities. Sets up basic window resizing. ```javascript import * as THREE from "three"; import { SparkRenderer, SplatMesh, SparkControls, dyno } from "@sparkjsdev/spark"; import GUI from "lil-gui"; import { getAssetFileURL } from "../js/get-asset-url.js"; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 1000); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } window.addEventListener('resize', onWindowResize, false); const spark = new SparkRenderer({ renderer, // Limit to 100K LoD splats so we can clearly see individual splats lodSplatCount: 100000, }); scene.add(spark); ``` -------------------------------- ### Three.js Scene and Renderer Setup Source: https://github.com/sparkjsdev/spark/blob/main/examples/lofi/index.html Initializes a Three.js scene, camera, and WebGL renderer. Sets up basic lighting and handles window resizing to maintain aspect ratio and renderer size. ```javascript const scene = new THREE.Scene(); scene.background = new THREE.Color(0, 0, 0); const camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.01, 1000); const renderer = new THREE.WebGLRenderer({ canvas: document.getElementById("canvas") }); renderer.setSize(window.innerWidth, window.innerHeight); scene.add(new THREE.AmbientLight(0x4444ff, 0.1)); scene.add(new THREE.DirectionalLight(0xffffcc, 1.0)); window.addEventListener("resize", onWindowResize, false); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } ``` -------------------------------- ### Splat Flow Imports and Setup Source: https://github.com/sparkjsdev/spark/blob/main/examples/splat-flow/index.html Imports necessary modules for SparkJS, Three.js, and GLTF loading. Sets up the canvas and Three.js renderer. ```javascript import { dyno, SparkRenderer, SplatMesh } from "@sparkjsdev/spark"; import * as THREE from "three"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"; import { getAssetFileURL } from "../js/get-asset-url.js"; import { GUI } from "lil-gui"; const splatFiles = ["woobles.spz", "dessert.spz", "robot-head.spz"]; const skyFile = "dali-env.glb"; const PARAMETERS = { speedMultiplier: 1.0, objectRotation: true, pause: false, fixedMinScale: false, waves: 0.5, cameraRotation: true }; const PAUSE_SECONDS = 2.0; const canvas = document.getElementById("canvas"); const renderer = new THREE.WebGLRenderer({ canvas, antialias: false }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(canvas.clientWidth, canvas.clientHeight, false); renderer.setClearColor(0x000000, 1); const scene = new THREE.Scene(); const spark = new SparkRenderer({ renderer }); scene.add(spark); const camera ``` -------------------------------- ### Initialize GUI and Animation Loop Source: https://github.com/sparkjsdev/spark/blob/main/examples/lod-on-demand/index.html Sets up the Lil-GUI for controlling splat properties and starts the Three.js animation loop. Includes controls for changing colors, creating LoD splats, and toggling LoD visibility. ```javascript const gui = new GUI({ title: "Settings" }); const buttons = { newColors, createLod }; gui.add(buttons, "newColors").name("Change non-LoD splat colors"); gui.add(buttons, "createLod").name("Create LoD splats"); gui.add(splats, "enableLod").name("Show LoD splats").listen().onChange((enable) => { if (enable && !splats.splats.lodSplats) { splats.enableLod = false; alert("Create LoD splats first!"); } }); gui.add(spark, "lodSplatCount", 10000, 600000, 10000).name("LoD splat count"); const controls = new SparkControls({ canvas: renderer.domElement }); renderer.setAnimationLoop(function animate(time) { controls.update(camera); renderer.render(scene, camera); }); ``` -------------------------------- ### Initialize Spark and Three.js Scene Source: https://github.com/sparkjsdev/spark/blob/main/examples/envmap/index.html Sets up the basic Three.js scene, camera, and renderer, then initializes the SparkRenderer. This is the foundational setup for using Spark JS. ```javascript import * as THREE from "three"; import { SplatMesh, SparkRenderer, PackedSplats } from "@sparkjsdev/spark"; import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js"; import { getAssetFileURL } from "../js/get-asset-url.js"; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.y = 0.5; const renderer = new THREE.WebGLRenderer(); renderer.setClearColor(new THREE.Color(0x1b2037), 1.0); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); window.addEventListener('resize', onWindowResize, false); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } const spark = new SparkRenderer({ renderer }); scene.add(spark); ``` -------------------------------- ### Three.js Scene Setup with SparkRenderer and SplatMesh Source: https://github.com/sparkjsdev/spark/blob/main/examples/sogs/index.html This snippet initializes a Three.js scene, camera, and WebGLRenderer. It then sets up SparkRenderer and adds a SplatMesh loaded from a URL. OrbitControls are configured for camera manipulation. ```javascript body { margin: 0; } { "imports": { "three": "../js/vendor/three/build/three.module.js", "three/addons/": "../js/vendor/three/examples/jsm/", "@sparkjsdev/spark": "../../dist/spark.module.js" } } import * as THREE from "three"; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import { Sky } from 'three/addons/objects/Sky.js'; import { SparkRenderer, SplatMesh } from "@sparkjsdev/spark"; import { getAssetFileURL } from "../js/get-asset-url.js"; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 1000); camera.position.set(0, 1.5, -1.2); const renderer = new THREE.WebGLRenderer(); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); const spark = new SparkRenderer({ renderer }); scene.add(spark); document.body.appendChild(renderer.domElement); window.addEventListener('resize', onWindowResize, false); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } const splatURL = await getAssetFileURL("sutro.zip"); const sutroTower = new SplatMesh({ url: splatURL }); sutroTower.quaternion.set(1, 0, 0, 0); scene.add(sutroTower); const sky = new Sky(); sky.scale.setScalar(450000); const phi = THREE.MathUtils.degToRad(20); const theta = THREE.MathUtils.degToRad(90); const sunPosition = new THREE.Vector3().setFromSphericalCoords(1, phi, theta); sky.material.uniforms.sunPosition.value = sunPosition; scene.add( sky ); const controls = new OrbitControls(camera, renderer.domElement); controls.target.set(0, 1.5, 0); controls.minDistance = 0.2; controls.maxDistance = 8.0; controls.enablePan = false; renderer.setAnimationLoop(function animate(time) { controls.update(camera); renderer.render(scene, camera); }); ``` -------------------------------- ### Load and Initialize Splat Mesh Source: https://github.com/sparkjsdev/spark/blob/main/examples/splat-painter/index.html Loads a splat file from a URL, sets up the scene, and initializes the splat mesh with painting capabilities. Handles scene resizing and camera setup. ```javascript 0, 0); camera.lookAt(0, 0, -1); scene.add(camera); function handleResize() { const width = canvas.clientWidth; const height = canvas.clientHeight; renderer.setSize(width, height, false); camera.aspect = width / height; camera.updateProjectionMatrix(); } handleResize(); window.addEventListener("resize", handleResize); async function loadSplatFromFile(url) { if (currentSplatMesh) { scene.remove(currentSplatMesh); } // Extract filename for export currentFileName = url.split("/").pop().split("?")[0].split(".")[0] || "painted-splat"; // Create an empty RgbaArray that will be populated after the mesh loads currentSplatMeshOriginalRGBA = new RgbaArray(); currentSplatMesh = await paintableSplatMesh( url, PARAMETERS.brushEnabled, PARAMETERS.eraseEnabled, PARAMETERS.undoEnabled, PARAMETERS.brushRadius, PARAMETERS.brushDepth, PARAMETERS.brushOrigin, PARAMETERS.brushDirection, PARAMETERS.brushColor, currentSplatMeshOriginalRGBA.dyno ); currentSplatMesh.quaternion.set(1, 0, 0, 0); scene.add(currentSplatMesh); // Wait for the mesh to fully load before accessing packed data await currentSplatMesh.initialized; // Extract original RGBA directly from the packed splats currentSplatMeshOriginalRGBA = new RgbaArray(); currentSplatMeshOriginalRGBA.fromPackedSplats({ packedSplats: currentSplatMesh.packedSplats, base: 0, count: currentSplatMesh.packedSplats.numSplats, renderer: renderer }); // Update the world modifier with the populated original RGBA currentSplatMesh.worldModifier = brushDyno( PARAMETERS.brushEnabled, PARAMETERS.eraseEnabled, PARAMETERS.undoEnabled, PARAMETERS.brushRadius, PARAMETERS.brushDepth, PARAMETERS.brushOrigin, PARAMETERS.brushDirection, PARAMETERS.brushColor, currentSplatMeshOriginalRGBA.dyno ); currentSplatMesh.updateGenerator(); } await loadSplatFromFile(await getAssetFileURL(assetID)); ``` -------------------------------- ### Initialize Three.js Scene and Spark Renderer Source: https://github.com/sparkjsdev/spark/blob/main/examples/editor/index.html Sets up the basic Three.js scene, camera, and SparkRenderer. Handles window resizing to maintain aspect ratio. This is the foundational setup for any Spark editor application. ```javascript import * as THREE from "three"; import { OrbitControls } from "three/addons/controls/OrbitControls.js"; import { GUI } from "lil-gui"; import { constructGrid, SparkControls, SparkRenderer, SplatMesh, textSplats, dyno, transcodeSpz, isMobile, isPcSogs, LN_SCALE_MIN, LN_SCALE_MAX } from "@sparkjsdev/spark"; import { getAssetFileURL } from "../js/get-asset-url.js"; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 1000); camera.position.set(0, 0, 1); const canvas = document.getElementById("canvas"); const renderer = new THREE.WebGLRenderer({ canvas }); const spark = new SparkRenderer({ renderer }); scene.add(spark); function handleResize() { const width = canvas.clientWidth; const height = canvas.clientHeight; renderer.setSize(width, height, false); camera.aspect = width / height; camera.updateProjectionMatrix(); } handleResize(); window.addEventListener("resize", handleResize); ``` -------------------------------- ### Spark Streaming LoD Initialization and Scene Setup Source: https://github.com/sparkjsdev/spark/blob/main/examples/streaming-lod/index.html Initializes the SparkRenderer, Three.js scene, camera, and renderer. Sets up event listeners for window resizing and configures the SparkRenderer for paged splats and foveated rendering. ```javascript import * as THREE from "three"; import { SparkRenderer, SplatMesh, SparkControls, isMobile } from "@sparkjsdev/spark"; import GUI from "lil-gui"; const initialWorld = "Coit Tower, SF"; const worlds = { "Hobbiton": { url: "https://storage.googleapis.com/forge-dev-public/asundqui/rad/260219/tijerin_w6_hobbiton-lod.rad", quaternion: [1, 0, 0, 0], background: "#cafefe", description: "24M splats created by Tijerin with World Labs Marble", }, "Cozy Spaceship": { url: "https://storage.googleapis.com/forge-dev-public/asundqui/rad/260217/cozy-spaceship_2-lod.rad", position: [0, -6.5, 0], background: "#000000", description: "6M splats created by Britt Casado with World Labs Marble", }, "Coit Tower, SF": { url: "https://storage.googleapis.com/forge-dev-public/asundqui/rad/260217/coit-40m-sh1-lod.rad", quaternion: [1, 0, 0, 0], scale: 10.0, cameraPosition: [-0.858, 2.203, -1.128], cameraQuaternion: [-0.043, -0.909, -0.097, 0.402], background: "#cafefe", lodSplatScale: 1.5, highDpi: !isMobile(), description: "40M splats scanned by Vincent Woo", }, "Jastrzębia Góra, Poland": { url: "https://storage.googleapis.com/forge-dev-public/asundqui/rad/260217/poland-lod.rad", quaternion: [1, 0, 0, 0], scale: 0.05, cameraPosition: [43.7, -3.5, -1.7], cameraQuaternion: [-0.230, 0.241, 0.006, 0.943], background: "#cafefe", highDpi: !isMobile(), description: "100M splats scanned by Andrii Shramko", }, }; const title = document.getElementById("title"); const subtitle = document.getElementById("subtitle"); const scene = new THREE.Scene(); scene.background = new THREE.Color("#000000"); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 1000); const renderer = new THREE.WebGLRenderer({ canvas: document.getElementById("canvas") }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } window.addEventListener('resize', onWindowResize, false); const spark = new SparkRenderer({ renderer, pagedExtSplats: true, coneFov0: 70.0, coneFov: 120.0, behindFoveate: 0.2, coneFoveate: 0.4, }); scene.add(spark); const settings = { worldKey: initialWorld, }; let world = null; function selectWorld(worldKey) { if (world) { scene.remove(world); world.dispose(); } const { url, quaternion, position, scale, background, description, cameraPosition, cameraQuaternion, lodSplatScale, highDpi } = worlds[worldKey]; world = new SplatMesh({ url, paged: true }); world.quaternion.set(...quaternion ?? [0, 0, 0, 1]).normalize(); world.position.set(...(position ?? [0, 0, 0])); world.scale.setScalar(scale ?? 1.0); scene.add(world); scene.background = new THREE.Color(background); camera.position.set(...(cameraPosition ?? [0, 0, 0])); camera.quaternion.set(...(cameraQuaternion ?? [0, 0, 0, 1])).normalize(); title.textContent = worldKey; subtitle.textContent = description; spark.lodSplatScale = lodSplatScale ?? 1.0; renderer.setPixelRatio(highDpi ? window.devicePixelRatio : 1); onWindowResize(); console.log("Render size", renderer.domElement.width, renderer.domElement.height); renderer.domElement.focus(); } selectWorld(settings.worldKey); const gui = new GUI({ title: "Settings" }); gui.add(settings, "worldKey", Object.keys(worlds)).name("World").onChange(selectWorld); gui.add(spark, "lodSplatScale", 0.01, 2.5, 0.001).name("Level of Detail").listen(); const controls = new SparkControls({ canvas: renderer.domElement }); renderer.setAnimationLoop(function animate(time) { controls.update(camera); renderer.render(scene, camera); }); ``` -------------------------------- ### Initialize Spark Scene and Load SplatMesh Source: https://github.com/sparkjsdev/spark/blob/main/examples/dynamic-lighting/index.html Sets up the Three.js scene, renderer, and camera. It then initializes SparkRenderer and loads a SplatMesh, positioning it within the scene. This is the foundational setup for rendering splats and applying lighting. ```javascript const canvas = document.getElementById("canvas"); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); camera.position.set(0, 0.3, -2.5); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); window.addEventListener('resize', onWindowResize, false); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } // Create a SparkRenderer and add it to the scene to render all the Gsplats. const spark = new SparkRenderer({ renderer }); scene.add(spark); const splatURL = await getAssetFileURL("fireplace.spz"); const fireplace = new SplatMesh({ url: splatURL }); fireplace.quaternion.set(1, 0, 0, 0); fireplace.position.set(0, -1, -10); scene.add(fireplace); ``` -------------------------------- ### Install Mkdocs Material via Brew (macOS) Source: https://github.com/sparkjsdev/spark/blob/main/README.md If you encounter an 'externally managed environment' error on macOS after installing Python via Homebrew, use this command to install Mkdocs Material. ```bash brew install mkdocs-material ``` -------------------------------- ### Initialize Spark and Load Splats Source: https://github.com/sparkjsdev/spark/blob/main/examples/multiple-splats/index.html Sets up the scene, camera, and SparkRenderer. Loads multiple splat files and adds them to the scene. Includes animation loop for dynamic rendering. ```javascript import * as THREE from "three"; import { SparkRenderer, SplatMesh, PackedSplats } from "@sparkjsdev/spark"; import { EXRLoader } from "three/addons/loaders/EXRLoader.js"; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import { getAssetFileURL } from "../js/get-asset-url.js"; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 0, 6.5); camera.lookAt(0, 0, 0); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setClearColor(new THREE.Color(0xFFFFFF), 1); const spark = new SparkRenderer({ renderer }); scene.add(spark); document.body.appendChild(renderer.domElement); window.addEventListener('resize', onWindowResize, false); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } let splatURL = await getAssetFileURL("butterfly-ai.spz"); const butterflySplats = new PackedSplats({ url: splatURL }); const butterflies = []; for (let i = 0; i < 6; i++) { const splat = new SplatMesh({ packedSplats: butterflySplats }); splat.quaternion.set(1, 0, 0, 0); scene.add(splat); butterflies.push(splat); } splatURL = await getAssetFileURL("cat.spz"); const cat = new SplatMesh({ url: splatURL }); cat.quaternion.set(1, 0, 0, 0); cat.scale.setScalar(0.5); scene.add(cat); // Setup mouse controls to orbit the camera around const controls = new OrbitControls(camera, renderer.domElement); controls.target.set(0, 0, 0); controls.minDistance = 0.3; controls.maxDistance = 20; controls.update(); const RADIUS = 2; renderer.setAnimationLoop(function animate(time) { controls.update(); // Rotate food for (let i = 0; i < butterflies.length; i++) { const ang = (-time / 10000) + i / butterflies.length * Math.PI * 2; butterflies[i].position.set(Math.cos(ang) * RADIUS, Math.sin(ang) - 0.2, 0); butterflies[i].rotation.y = i + time / 4000; } // Animate food master cat.position.y = -0.8 + Math.sin(time / 1000) * 0.1; renderer.render(scene, camera); }); ``` -------------------------------- ### Define Example Mapping Object Source: https://github.com/sparkjsdev/spark/blob/main/examples.html A constant object mapping unique example keys to their corresponding file paths for navigation. ```javascript const exampleMap = { 'hello-world': './hello-world/index.html', 'envmap': './envmap/index.html', 'interactivity': './interactivity/index.html', 'multiple-splats': './multiple-splats/index.html', 'multiple-viewpoints': './multiple-viewpoints/index.html', 'procedural-splats': './procedural-splats/index.html', 'raycasting': './raycasting/index.html', 'dynamic-lighting': './dynamic-lighting/index.html', 'particle-animation': './particle-animation/index.html', 'particle-simulation': './particle-simulation/index.html', 'splat-painter': './splat-painter/index.html', 'splat-reveal-effects': './splat-reveal-effects/index.html', 'splat-shader-effects': './splat-shader-effects/index.html', 'splat-dissolve-effects': './splat-dissolve-effects/index.html', 'splat-transitions': './splat-transitions/index.html', 'stochastic': './stochastic/index.html', 'sogs': './sogs/index.html', 'webxr': './webxr/index.html', 'glsl': './glsl/index.html', 'debug-color': './debug-color/index.html', 'depth-of-field': './depth-of-field/index.html', 'splat-texture': './splat-texture/index.html', 'editor': './editor/index.html', 'viewer': './viewer/index.html', 'basic-xr': './basic-xr/index.html', 'interactive-holes': './interactive-holes/index.html', 'lofi': './lofi/index.html', 'mobile-joystick': './mobile-joystick/index.html', 'render-cube-depth': './render-cube-depth/index.html', 'lod-on-demand': './lod-on-demand/index.html', 'nonlod': './nonlod/index.html', 'extsplats': './extsplats/index.html', 'streaming-lod': './streaming-lod/index.html', } ``` -------------------------------- ### Setting up GUI for LOD Control and Animation Loop Source: https://github.com/sparkjsdev/spark/blob/main/examples/multi-lod/index.html This snippet demonstrates setting up a GUI to control the 'lodSplatScale' parameter, which affects the level of detail. It also includes the animation loop for rendering the scene with camera controls. ```javascript const gui = new GUI({ title: "Settings" }); gui.add(spark, "lodSplatScale", 0.001, 2.0, 0.001).name("Level of Detail").listen(); const controls = new SparkControls({ canvas: renderer.domElement }); renderer.setAnimationLoop(function animate(time) { controls.update(camera); renderer.render(scene, camera); }); ``` -------------------------------- ### Install Rust Wasm Build Dependencies Source: https://github.com/sparkjsdev/spark/blob/main/rust/spark-rs/README.md Adds the target for WebAssembly compilation and installs wasm-pack, a tool for building Rust-generated WebAssembly. ```shell rustup target add wasm32-unknown-unknown cargo install wasm-pack ``` -------------------------------- ### Initialize GUI for Effect Selection Source: https://github.com/sparkjsdev/spark/blob/main/examples/splat-reveal-effects/index.html Sets up a GUI using lil-gui to allow users to select and switch between different splat reveal effects. Includes a button to reset the animation time. ```javascript // Initialize user interface const gui = new GUI(); const effectFolder = gui.addFolder('Effects'); // Effect selector dropdown effectFolder.add(effectParams, 'effect', ['Magic', 'Spread', 'Unroll', 'Twister', 'Rain']) .name('Effect Type') .onChange(async () => { await loadSplatForEffect(effectParams.effect); }); // Animation controls const guiControls = { resetTime: () => { baseTime = 0; animateT.value = 0; } }; effectFolder.add(guiControls, 'resetTime').name('Reset Time'); effectFolder.open(); ``` -------------------------------- ### Import Statements for Spark JS Example Source: https://github.com/sparkjsdev/spark/blob/main/examples/interactive-deform/index.html These imports set up the necessary libraries for the Spark JS interactive deformation example, including Three.js and lil-gui for UI controls. ```javascript { "imports": { "three": "../js/vendor/three/build/three.module.js", "three/addons/": "../js/vendor/three/examples/jsm/", "@sparkjsdev/spark": "../../dist/spark.module.js", "lil-gui": "../js/vendor/lil-gui/dist/lil-gui.esm.js" } } ``` -------------------------------- ### Initialize SparkControls and GUI Source: https://github.com/sparkjsdev/spark/blob/main/examples/newportal/index.html Sets up interactive controls for camera movement and a GUI for adjusting portal properties in real-time. The GUI allows modification of LOD scale, portal radii, and portal positions. ```javascript // ========== Controls ========== const controls = new SparkControls({ renderer, canvas: renderer.domElement, }); // ========== GUI ========== const gui = new GUI({ title: "Portal Settings" }); gui.add(portals.portalRenderer, "lodSplatScale", 0.001, 2.0, 0.001).name("LOD Scale").onChange((v) => { portals.behindRenderer.lodSplatScale = v; }); // Portal Pair 1 const pair1Folder = gui.addFolder("Portal Pair 1"); pair1Folder.add(pair1, "radius", 0.1, 3.0, 0.1).name("Radius"); const pair1EntryFolder = pair1Folder.addFolder("Entry Portal"); pair1EntryFolder.add(pair1.entryPortal.position, "x", -10, 10, 0.1).name("X"); pair1EntryFolder.add(pair1.entryPortal.position, "y", -10, 10, 0.1).name("Y"); pair1EntryFolder.add(pair1.entryPortal.position, "z", -10, 10, 0.1).name("Z"); const pair1ExitFolder = pair1Folder.addFolder("Exit Portal"); pair1ExitFolder.add(pair1.exitPortal.position, "x", -10, 10, 0.1).name("X"); pair1ExitFolder.add(pair1.exitPortal.position, "y", -10, 10, 0.1).name("Y"); pair1ExitFolder.add(pair1.exitPortal.position, "z", -10, 10, 0.1).name("Z"); // Portal Pair 2 const pair2Folder = gui.addFolder("Portal Pair 2"); pair2Folder.add(pair2, "radius", 0.1, 3.0, 0.1).name("Radius"); const pair2EntryFolder = pair2Folder.addFolder("Entry Portal"); pair2EntryFolder.add(pair2.entryPortal.position, "x", -10, 10, 0.1).name("X"); pair2EntryFolder.add(pair2.entryPortal.position, "y", -10, 10, 0.1).name("Y"); pair2EntryFolder.add(pair2.entryPortal.position, "z", -10, 10, 0.1).name("Z"); const pair2ExitFolder = pair2Folder.addFolder("Exit Portal"); pair2ExitFolder.add(pair2.exitPortal.position, "x", -10, 10, 0.1).name("X"); pair2ExitFolder.add(pair2.exitPortal.position, "y", -10, 10, 0.1).name("Y"); pair2ExitFolder.add(pair2.exitPortal.position, "z", -10, 10, 0.1).name("Z"); // Collapse sub-folders by default pair1EntryFolder.close(); pair1ExitFolder.close(); pair2Folder.close(); ``` -------------------------------- ### Run Documentation Build Source: https://github.com/sparkjsdev/spark/blob/main/README.md Executes the script to build the documentation. This command is run using npm. ```bash npm run docs ``` -------------------------------- ### Initialize Perspective Camera Source: https://github.com/sparkjsdev/spark/blob/main/examples/splat-flow/index.html Sets up a perspective camera for the 3D scene. Handles window resizing to maintain aspect ratio. ```javascript const camera = new THREE.PerspectiveCamera(50, innerWidth / innerHeight, 0.01, 1000); camera.position.set(0, 5, 8); camera.lookAt(0, 0, 0); scene.add(camera); window.addEventListener("resize", () => { const w = canvas.clientWidth; const h = canvas.clientHeight; renderer.setSize(w, h, false); camera.aspect = w / h; camera.updateProjectionMatrix(); }); ``` -------------------------------- ### Initialize Clouds and Controls Source: https://github.com/sparkjsdev/spark/blob/main/examples/particle-animation/index.html Creates the initial cloud mesh and sets up pointer controls for camera navigation. Includes a function to recreate clouds when parameters change. ```javascript const controls = new PointerControls({ canvas: renderer.domElement }); let clouds = createCloudMesh(params, BOUNDS); clouds.position.set(0, -0.5, -2); scene.add(clouds); function recreateClouds() { if (clouds) { scene.remove(clouds); clouds.dispose(); } clouds = createCloudMesh(params, BOUNDS); clouds.position.set(0, -0.5, -2); scene.add(clouds); } ``` -------------------------------- ### Get Empty Texture Source: https://github.com/sparkjsdev/spark/blob/main/docs/docs/packed-splats.md Provides an uninitialized THREE.DataArrayTexture. This can be used as a placeholder or uniform that will be updated later with the result of getTexture(). ```typescript getEmpty() ``` -------------------------------- ### Build Spark Website Source: https://github.com/sparkjsdev/spark/blob/main/README.md Builds the static site and documentation into a 'site' directory. This command is executed using npm. ```bash npm run site:build ``` -------------------------------- ### Cloud Parameter Controls with GUI Source: https://github.com/sparkjsdev/spark/blob/main/examples/particle-animation/index.html Sets up a GUI to control cloud parameters like wind speed, opacity, fluffiness, turbulence, and density. Changes trigger cloud recreation. ```javascript const gui = new GUI({ title: "Clouds" }); gui.add(params, "windSpeed", -1, 1, 0.1).name("Wind Speed"); gui .add(params, "opacity", 0.05, 1, 0.01) .name("Thickness") .onChange(recreateClouds); gui .add(params, "fluffiness", 0, 2, 0.01) .name("Fluffiness") .onChange(recreateClouds); gui .add(params, "turbulence", 0, 1, 0.01) .name("Turbulence") .onChange(recreateClouds); gui .add(params, "cloudDensity", 0.1, 1, 0.01) .name("Cloud Density") .onChange(recreateClouds); const advancedFolder = gui.addFolder("Advanced"); advancedFolder .add(params, "octaves", 1, 8, 1) .name("Noise Octaves") .onChange(recreateClouds); advancedFolder .add(params, "frequency", 0.1, 1, 0.01) .name("Noise Frequency") .onChange(recreateClouds); ```