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 renderer = new THREE.WebGLRenderer({ canvas });
|
||||
const spark = new SparkRenderer({ renderer });
|
||||
scene.add(spark);
|
||||
|
||||
function handleResize() {
|
||||
const width = canvas.clientWidth;
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
|
||||
// Explicitly create a SparkRenderer to render environment maps
|
||||
const spark = new SparkRenderer({ renderer });
|
||||
scene.add(spark);
|
||||
|
||||
const splatURL = await getAssetFileURL("fireplace.spz");
|
||||
const packedSplats = new PackedSplats({ url: splatURL });
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
|
||||
// Explicitly create a SparkRenderer in the scene to spawn new viewpoints
|
||||
const spark = new SparkRenderer({ renderer });
|
||||
scene.add(spark);
|
||||
|
||||
const splatURL = await getAssetFileURL("butterfly.spz");
|
||||
const butterfly = new SplatMesh({ url: splatURL });
|
||||
|
||||
@@ -40,48 +40,6 @@ import {
|
||||
// of 5 to avoid excessive memory usage.
|
||||
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 = {
|
||||
/**
|
||||
* 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.clock = options.clock ? cloneClock(options.clock) : new THREE.Clock();
|
||||
|
||||
sparkRendererInstance = this;
|
||||
}
|
||||
|
||||
static makeUniforms() {
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
type SplatEncoding,
|
||||
} from "./PackedSplats";
|
||||
import { type RgbaArray, readRgbaArray } from "./RgbaArray";
|
||||
import { SparkRenderer } from "./SparkRenderer";
|
||||
import { SplatEdit, SplatEditSdf, SplatEdits } from "./SplatEdit";
|
||||
import {
|
||||
type GsplatModifier,
|
||||
@@ -235,6 +236,8 @@ export class SplatMesh extends SplatGenerator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.add(createRendererDetectionMesh());
|
||||
}
|
||||
|
||||
async asyncInitialize(options: SplatMeshOptions) {
|
||||
@@ -933,3 +936,39 @@ export function evaluateSH3(
|
||||
`),
|
||||
}).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