mirror of
https://github.com/storytold/spark.git
synced 2026-10-09 00:09:53 +00:00
Use child mesh in SplatMesh to auto-inject SparkRenderer instead of monkey-patching (#158)
This commit is contained in:
@@ -79,6 +79,7 @@
|
|||||||
const canvas = document.getElementById("canvas");
|
const canvas = document.getElementById("canvas");
|
||||||
const renderer = new THREE.WebGLRenderer({ canvas });
|
const renderer = new THREE.WebGLRenderer({ canvas });
|
||||||
const spark = new SparkRenderer({ renderer });
|
const spark = new SparkRenderer({ renderer });
|
||||||
|
scene.add(spark);
|
||||||
|
|
||||||
function handleResize() {
|
function handleResize() {
|
||||||
const width = canvas.clientWidth;
|
const width = canvas.clientWidth;
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
|
|
||||||
// Explicitly create a SparkRenderer to render environment maps
|
// Explicitly create a SparkRenderer to render environment maps
|
||||||
const spark = new SparkRenderer({ renderer });
|
const spark = new SparkRenderer({ renderer });
|
||||||
|
scene.add(spark);
|
||||||
|
|
||||||
const splatURL = await getAssetFileURL("fireplace.spz");
|
const splatURL = await getAssetFileURL("fireplace.spz");
|
||||||
const packedSplats = new PackedSplats({ url: splatURL });
|
const packedSplats = new PackedSplats({ url: splatURL });
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
|
|
||||||
// Explicitly create a SparkRenderer in the scene to spawn new viewpoints
|
// Explicitly create a SparkRenderer in the scene to spawn new viewpoints
|
||||||
const spark = new SparkRenderer({ renderer });
|
const spark = new SparkRenderer({ renderer });
|
||||||
|
scene.add(spark);
|
||||||
|
|
||||||
const splatURL = await getAssetFileURL("butterfly.spz");
|
const splatURL = await getAssetFileURL("butterfly.spz");
|
||||||
const butterfly = new SplatMesh({ url: splatURL });
|
const butterfly = new SplatMesh({ url: splatURL });
|
||||||
|
|||||||
@@ -40,48 +40,6 @@ import {
|
|||||||
// of 5 to avoid excessive memory usage.
|
// of 5 to avoid excessive memory usage.
|
||||||
const MAX_ACCUMULATORS = 5;
|
const MAX_ACCUMULATORS = 5;
|
||||||
|
|
||||||
// Scene.onBeforeRender monkey-patch to
|
|
||||||
// inject a SparkRenderer into a scene with SplatMeshes if there isn't
|
|
||||||
// one already. Restore original Scene.onBeforeRenderer and Scene.add when done.
|
|
||||||
let hasSplatMesh = false;
|
|
||||||
let hasSparkRenderer = false;
|
|
||||||
|
|
||||||
let sparkRendererInstance: SparkRenderer;
|
|
||||||
|
|
||||||
function containsSplatMesh(object3D: THREE.Object3D) {
|
|
||||||
let hasSplatMesh = false;
|
|
||||||
if (object3D instanceof SplatMesh) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
object3D.traverse((child: THREE.Object3D) => {
|
|
||||||
hasSplatMesh = hasSplatMesh || child instanceof SplatMesh;
|
|
||||||
});
|
|
||||||
return hasSplatMesh;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sceneAdd = THREE.Scene.prototype.add;
|
|
||||||
THREE.Scene.prototype.add = function (object: THREE.Object3D) {
|
|
||||||
hasSplatMesh = hasSplatMesh || containsSplatMesh(object);
|
|
||||||
hasSparkRenderer = hasSparkRenderer || object instanceof SparkRenderer;
|
|
||||||
sceneAdd.call(this, object);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
const sceneOnBeforeRender = THREE.Scene.prototype.onBeforeRender;
|
|
||||||
THREE.Scene.prototype.onBeforeRender = function (
|
|
||||||
renderer: THREE.WebGLRenderer,
|
|
||||||
) {
|
|
||||||
if (!hasSplatMesh) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!hasSparkRenderer) {
|
|
||||||
const spark = sparkRendererInstance || new SparkRenderer({ renderer });
|
|
||||||
this.add(spark);
|
|
||||||
}
|
|
||||||
THREE.Scene.prototype.onBeforeRender = sceneOnBeforeRender;
|
|
||||||
THREE.Scene.prototype.add = sceneAdd;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SparkRendererOptions = {
|
export type SparkRendererOptions = {
|
||||||
/**
|
/**
|
||||||
* Pass in your THREE.WebGLRenderer instance so Spark can perform work
|
* Pass in your THREE.WebGLRenderer instance so Spark can perform work
|
||||||
@@ -368,8 +326,6 @@ export class SparkRenderer extends THREE.Mesh {
|
|||||||
this.prepareViewpoint(this.viewpoint);
|
this.prepareViewpoint(this.viewpoint);
|
||||||
|
|
||||||
this.clock = options.clock ? cloneClock(options.clock) : new THREE.Clock();
|
this.clock = options.clock ? cloneClock(options.clock) : new THREE.Clock();
|
||||||
|
|
||||||
sparkRendererInstance = this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static makeUniforms() {
|
static makeUniforms() {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
type SplatEncoding,
|
type SplatEncoding,
|
||||||
} from "./PackedSplats";
|
} from "./PackedSplats";
|
||||||
import { type RgbaArray, readRgbaArray } from "./RgbaArray";
|
import { type RgbaArray, readRgbaArray } from "./RgbaArray";
|
||||||
|
import { SparkRenderer } from "./SparkRenderer";
|
||||||
import { SplatEdit, SplatEditSdf, SplatEdits } from "./SplatEdit";
|
import { SplatEdit, SplatEditSdf, SplatEdits } from "./SplatEdit";
|
||||||
import {
|
import {
|
||||||
type GsplatModifier,
|
type GsplatModifier,
|
||||||
@@ -235,6 +236,8 @@ export class SplatMesh extends SplatGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.add(createRendererDetectionMesh());
|
||||||
}
|
}
|
||||||
|
|
||||||
async asyncInitialize(options: SplatMeshOptions) {
|
async asyncInitialize(options: SplatMeshOptions) {
|
||||||
@@ -933,3 +936,39 @@ export function evaluateSH3(
|
|||||||
`),
|
`),
|
||||||
}).outputs.rgb;
|
}).outputs.rgb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const EMPTY_GEOMETRY = new THREE.BufferGeometry();
|
||||||
|
const EMPTY_MATERIAL = new THREE.ShaderMaterial();
|
||||||
|
|
||||||
|
// Creates an empty mesh to hook into Three.js rendering.
|
||||||
|
// This is used to detect if a SparkRenderer is present in the scene.
|
||||||
|
// If not, one will be injected automatically.
|
||||||
|
function createRendererDetectionMesh(): THREE.Mesh {
|
||||||
|
const mesh = new THREE.Mesh(EMPTY_GEOMETRY, EMPTY_MATERIAL);
|
||||||
|
mesh.frustumCulled = false;
|
||||||
|
mesh.onBeforeRender = function (renderer, scene) {
|
||||||
|
if (!scene.isScene) {
|
||||||
|
// The SplatMesh is part of render call that doesn't have a Scene at its root
|
||||||
|
// Don't auto-inject a renderer.
|
||||||
|
this.removeFromParent();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the scene has a SparkRenderer instance
|
||||||
|
let hasSparkRenderer = false;
|
||||||
|
scene.traverse((c) => {
|
||||||
|
if (c instanceof SparkRenderer) {
|
||||||
|
hasSparkRenderer = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasSparkRenderer) {
|
||||||
|
// No spark renderer present in the scene, inject one.
|
||||||
|
scene.add(new SparkRenderer({ renderer }));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove mesh to stop checking
|
||||||
|
this.removeFromParent();
|
||||||
|
};
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user