@hology/core 0.0.252 → 0.0.254
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/effects/sequence/sequencer.js +1 -1
- package/dist/gameplay/actors/actor.d.ts +9 -0
- package/dist/gameplay/actors/actor.js +1 -1
- package/dist/gameplay/actors/builtin/components/character/character-movement.d.ts +2 -0
- package/dist/gameplay/actors/builtin/components/character/character-movement.js +1 -1
- package/dist/gameplay/actors/controller/actor-controller.d.ts +21 -0
- package/dist/gameplay/actors/controller/actor-controller.js +1 -1
- package/dist/gameplay/actors/controller/game-state.d.ts +8 -0
- package/dist/gameplay/actors/controller/game-state.js +4 -0
- package/dist/gameplay/actors/index.d.ts +1 -0
- package/dist/gameplay/actors/index.js +1 -1
- package/dist/gameplay/game-mode.d.ts +34 -0
- package/dist/gameplay/game-mode.js +4 -0
- package/dist/gameplay/index.d.ts +8 -1
- package/dist/gameplay/index.js +1 -1
- package/dist/gameplay/initiate.d.ts +180 -85
- package/dist/gameplay/initiate.js +1 -1
- package/dist/gameplay/net/browser/index.d.ts +4 -1
- package/dist/gameplay/net/browser/index.js +1 -1
- package/dist/gameplay/net/index.d.ts +3 -0
- package/dist/gameplay/net/index.js +1 -1
- package/dist/gameplay/net/net-connection.d.ts +4 -0
- package/dist/gameplay/net/net-session.d.ts +3 -0
- package/dist/gameplay/net/player-session.d.ts +40 -0
- package/dist/gameplay/net/player-session.js +4 -0
- package/dist/gameplay/net/service/net-decorator.js +1 -1
- package/dist/gameplay/net/service/net-serializer.js +1 -1
- package/dist/gameplay/net/service/net-service.d.ts +41 -0
- package/dist/gameplay/net/service/net-service.js +1 -1
- package/dist/gameplay/network-travel-coordinator.d.ts +27 -0
- package/dist/gameplay/network-travel-coordinator.js +4 -0
- package/dist/gameplay/player-lifecycle-coordinator.d.ts +29 -0
- package/dist/gameplay/player-lifecycle-coordinator.js +4 -0
- package/dist/gameplay/player-runtime.d.ts +24 -0
- package/dist/gameplay/player-runtime.js +4 -0
- package/dist/gameplay/rendering-host.d.ts +40 -0
- package/dist/gameplay/rendering-host.js +4 -0
- package/dist/gameplay/services/physics/physics-system.js +1 -1
- package/dist/gameplay/services/render.d.ts +1 -1
- package/dist/gameplay/services/render.js +1 -1
- package/dist/gameplay/services/scene-transition.d.ts +19 -0
- package/dist/gameplay/services/scene-transition.js +4 -0
- package/dist/gameplay/services/world.d.ts +16 -6
- package/dist/gameplay/services/world.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/rendering/graphics-runtime.d.ts +8 -0
- package/dist/rendering/graphics-runtime.js +4 -0
- package/dist/rendering/intersection-observer.d.ts +5 -0
- package/dist/rendering/intersection-observer.js +4 -0
- package/dist/rendering/webgpu-experimental-backend.d.ts +12 -1
- package/dist/rendering/webgpu-experimental-backend.js +1 -1
- package/dist/rendering.d.ts +18 -3
- package/dist/rendering.js +1 -1
- package/dist/scene/bootstrap.d.ts +2 -0
- package/dist/scene/bootstrap.js +1 -1
- package/dist/scene/custom-param-runtime-types.js +1 -1
- package/dist/scene/materializer.d.ts +3 -1
- package/dist/scene/materializer.js +1 -1
- package/dist/scene/model.d.ts +2 -1
- package/dist/scene/model.js +1 -1
- package/dist/scene/objects/prefab.d.ts +3 -0
- package/dist/scene/objects/prefab.js +1 -1
- package/dist/scene/scene-ref.d.ts +18 -0
- package/dist/scene/scene-ref.js +4 -0
- package/dist/shader/parameter.d.ts +2 -1
- package/dist/test/graphics-runtime.test.d.ts +2 -0
- package/dist/test/graphics-runtime.test.js +4 -0
- package/dist/test/intersection-observer.test.d.ts +2 -0
- package/dist/test/intersection-observer.test.js +4 -0
- package/dist/test/net-player-registry.test.d.ts +2 -0
- package/dist/test/net-player-registry.test.js +4 -0
- package/dist/test/net-serializer.test.d.ts +2 -0
- package/dist/test/net-serializer.test.js +4 -0
- package/dist/test/player-controller.test.d.ts +2 -0
- package/dist/test/player-controller.test.js +4 -0
- package/dist/test/rendering-host.test.d.ts +2 -0
- package/dist/test/rendering-host.test.js +4 -0
- package/dist/test/world-lifecycle.test.js +1 -1
- package/dist/test/world-prefab-spawn.test.js +1 -1
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Constructable } from "typedi";
|
|
2
|
-
import { BaseActor } from '../actors/actor.js';
|
|
2
|
+
import { ActorLifetime, BaseActor } from '../actors/actor.js';
|
|
3
3
|
import { Euler, Scene, Vector3 } from 'three';
|
|
4
4
|
import { Observable } from 'rxjs';
|
|
5
5
|
import { RenderingView } from "../../rendering.js";
|
|
6
|
-
import { Prefab } from "../../scene/objects/prefab.js";
|
|
6
|
+
import { Prefab, PrefabOf } from "../../scene/objects/prefab.js";
|
|
7
7
|
import type { SceneMaterializer } from "../../scene/materializer.js";
|
|
8
8
|
import { PrefabInstance } from '../../scene/objects/prefab.js';
|
|
9
9
|
import { BasePlayerController } from "../actors/controller/actor-controller.js";
|
|
@@ -26,7 +26,13 @@ declare class DirectionalLightController {
|
|
|
26
26
|
get position(): Vector3;
|
|
27
27
|
}
|
|
28
28
|
export type ActorSpawnOptions = {
|
|
29
|
-
owner
|
|
29
|
+
owner?: BaseActor;
|
|
30
|
+
/** Overrides actor metadata. Runtime spawns default to scene lifetime. */
|
|
31
|
+
lifetime?: ActorLifetime;
|
|
32
|
+
};
|
|
33
|
+
export type PrefabSpawnOptions = {
|
|
34
|
+
/** Applies to the entire prefab, including its main actor and every child actor. */
|
|
35
|
+
lifetime?: ActorLifetime;
|
|
30
36
|
};
|
|
31
37
|
export declare class World {
|
|
32
38
|
private actorFactory;
|
|
@@ -36,13 +42,16 @@ export declare class World {
|
|
|
36
42
|
private assetsProvider;
|
|
37
43
|
private eventGraphAssets;
|
|
38
44
|
readonly actors: BaseActor[];
|
|
45
|
+
private readonly actorLifetimes;
|
|
46
|
+
private readonly actorPrefabInstances;
|
|
47
|
+
private readonly prefabInstances;
|
|
39
48
|
readonly actorAdded: Observable<BaseActor>;
|
|
40
49
|
readonly actorRemoved: Observable<BaseActor>;
|
|
41
50
|
scene: Scene;
|
|
42
51
|
readonly directionalLight: DirectionalLightController;
|
|
43
52
|
private readonly sceneEventGraphs;
|
|
44
53
|
private readonly sceneEventGraphSubscriptions;
|
|
45
|
-
spawnActor<T extends BaseActor>(type: Constructable<T>, position?: Vector3, rotation?: Euler, options?: ActorSpawnOptions): Promise<T>;
|
|
54
|
+
spawnActor<T extends BaseActor>(type: Constructable<T> | PrefabOf<T>, position?: Vector3, rotation?: Euler, options?: ActorSpawnOptions): Promise<T>;
|
|
46
55
|
/**
|
|
47
56
|
* Spawn the player controller to run locally.
|
|
48
57
|
* This is only called by the server and if it is not a dedicated server in
|
|
@@ -50,7 +59,7 @@ export declare class World {
|
|
|
50
59
|
* It sets isLocallyControlled to true
|
|
51
60
|
*/
|
|
52
61
|
spawnLocalPlayerController<T extends BasePlayerController>(type: Constructable<T>, position?: Vector3, rotation?: Euler): Promise<T>;
|
|
53
|
-
addActor(actor: BaseActor, position?: Vector3, rotation?: Euler): void;
|
|
62
|
+
addActor(actor: BaseActor, position?: Vector3, rotation?: Euler, lifetime?: ActorLifetime): void;
|
|
54
63
|
/**
|
|
55
64
|
* Materialize and compile visual effects before gameplay exposes the action
|
|
56
65
|
* that uses them. Call this at a loading boundary rather than on first use.
|
|
@@ -59,13 +68,14 @@ export declare class World {
|
|
|
59
68
|
removeActor(actor: BaseActor): void;
|
|
60
69
|
findActorByType<T extends BaseActor>(type: Constructable<T>, name?: string): T;
|
|
61
70
|
findActorsByType<T extends BaseActor>(type: Constructable<T>, name?: string): T[];
|
|
71
|
+
getActorLifetime(actor: BaseActor): ActorLifetime;
|
|
62
72
|
/** Starts event graphs owned by prefabs that were materialized with the scene. */
|
|
63
73
|
startSceneEventGraphs(materializer: SceneMaterializer): Promise<void>;
|
|
64
74
|
/** Stops scene-owned graphs before their actors or materializer are disposed. */
|
|
65
75
|
stopSceneEventGraphs(): void;
|
|
66
76
|
private activateSceneEventGraph;
|
|
67
77
|
private deactivateSceneEventGraph;
|
|
68
|
-
spawnPrefab(prefab: Prefab, position?: Vector3, rotation?: Euler): Promise<PrefabInstance>;
|
|
78
|
+
spawnPrefab(prefab: Prefab, position?: Vector3, rotation?: Euler, options?: PrefabSpawnOptions): Promise<PrefabInstance>;
|
|
69
79
|
removePrefab(instance: PrefabInstance): void;
|
|
70
80
|
}
|
|
71
81
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as t}from"tslib";import{Service as e}from"typedi";import{BaseActor as
|
|
1
|
+
import{__decorate as t}from"tslib";import{Service as e}from"typedi";import{ActorLifetime as r,BaseActor as s,getActorOptions as i}from"../actors/actor.js";import{ActorFactory as n}from"../actors/factory.js";import{Group as o,Vector3 as a}from"three";import{ActorComponent as c}from"../../gameplay/actors/component.js";import{Subject as h}from"rxjs";import{RenderingView as l}from"../../rendering.js";import{randomUUID as p}from"../../utils/uuid.js";import{PrefabInstance as m}from"../../scene/objects/prefab.js";import{PhysicsSystem as f}from"./physics/physics-system.js";import{inject as d}from"../../gameplay/inject.js";import{isComponentEnabledInEnvironment as v}from"../actors/internal/component-init.js";import{NetRole as w}from"../net/service/net-actor-role.js";import{BasePlayerController as u}from"../actors/controller/actor-controller.js";import{EventGraphInstance as y}from"../event-graph/runtime.js";import{ViewController as b}from"./render.js";import{AssetsProvider as E}from"../../scene/assets-provider.js";import{WorldEventGraphAssetRuntime as A}from"../event-graph/runtime-assets.js";import{hasExecutableEventGraphDocument as g}from"../event-graph/model.js";const j=new a;class G{constructor(t){this.view=t}get direction(){return this.view.csm.lightDirection}set intensity(t){this.view.csm.lightIntensity=t,this.view.csm.lights.forEach(t=>t.intensity=this.view.csm.lightIntensity)}get intensity(){return this.view.csm.lightIntensity}get position(){return 0==this.view.csm.lights.length?j:this.view.csm.lights[0].position}}let I=class{constructor(){this.actorFactory=d(n),this.view=d(l),this.viewController=d(b),this.physics=d(f),this.assetsProvider=d(E),this.eventGraphAssets=new A(this,this.assetsProvider),this.actors=[],this.actorLifetimes=new Map,this.actorPrefabInstances=new Map,this.prefabInstances=new Set,this.actorAdded=new h,this.actorRemoved=new h,this.directionalLight=new G(this.view),this.sceneEventGraphs=new Map,this.sceneEventGraphSubscriptions=[]}async spawnActor(t,e,r,s){if(null==t)throw new Error("Cannot spawn actor with null type");if("prefab"in t){const i=t.prefab,n=await this.spawnPrefab(i,e,r,{lifetime:s?.lifetime}),o=n.mainActor;if(null==o)throw new Error(`Prefab has no main actor or there was an error when spwaning it. Prefab: ${JSON.stringify(i)}`);return o.disposed.subscribe(()=>{const t=n.actors.findIndex(t=>t===o);t>=0&&n.actors.splice(t,1),this.removePrefab(n)}),null!=s?.owner&&(o.owner=s.owner),o}const i=await this.actorFactory.create(t,e,r);return null!=s?.owner&&(i.owner=s.owner),this.addActor(i,e,r,s?.lifetime),i}async spawnActorWithNetInfo(t,e,r,s,i,n){const o=await this.actorFactory.create(t,i,n,!0),a=null!=s&&0!==s?this.actors.find(t=>t.__netid===s):null;return o.__netid=e,o.netRole=r,o.owner=a,o instanceof u&&o.setLocallyControlled(r===w.autonomousProxy),await this.actorFactory.initActor(o),this.addActor(o,i,n),o}async spawnLocalPlayerController(t,e,r){const s=await this.actorFactory.create(t,e,r,!0);return s.setLocallyControlled(!0),await this.actorFactory.initActor(s),this.addActor(s,e,r),s}spawnActorSync(t,e,r,s){if(null==t)throw new Error("Cannot spawn actor with null type");const i=this.actorFactory.createSync(t,e,r);return null!=s?.owner&&(i.owner=s.owner),this.addActor(i,e,r,s?.lifetime),i}addActor(t,e,s,n){e&&t.object.position.copy(e),s&&t.object.rotation.copy(s);(i(t.constructor).spatial??!0)&&null==t.object.parent&&this.scene.add(t.object),this.actors.push(t),this.actorLifetimes.set(t,n??i(t.constructor).lifetime??r.scene),P(t,t=>t.onBeginPlay(),this.actorFactory.inEditor),this.actorAdded.next(t)}async preloadVisualEffects(t){await(this.materializer?.preloadVisualEffects(t))}removeActor(t){const e=this.actorPrefabInstances.get(t);if(null!=e&&!e.destroyed)return void this.removePrefab(e);for(const[e,r]of[...this.sceneEventGraphs])r.binding.mainActor===t&&this.deactivateSceneEventGraph(e);P(t,t=>t.onEndPlay(),this.actorFactory.inEditor);const r=this.actors.indexOf(t);r>=0&&this.actors.splice(r,1),this.actorLifetimes.delete(t),this.actorPrefabInstances.delete(t),t.object.removeFromParent(),t.disposed.next(!0),this.actorRemoved.next(t),this.physics.removeActor(t)}findActorByType(t,e){return this.actors.find(r=>r instanceof t&&(null==e||r.object.name==e))}findActorsByType(t,e){return this.actors.filter(r=>r instanceof t&&(null==e||r.object.name==e))}getActorLifetime(t){return this.actorLifetimes.get(t)??i(t.constructor).lifetime??r.scene}removeSceneLifetimeActors(t){const e=new Set(t),s=new Set(this.actors.filter(t=>this.getActorLifetime(t)!==r.scene)),i=new Set([...s].map(t=>t.object));for(const t of s){let r=t.object.parent,s=!1,n=!1;for(;null!=r;)i.has(r)&&(s=!0),e.has(r)&&(n=!0),r=r.parent;n&&!s&&this.scene.attach(t.object)}for(const t of[...this.prefabInstances])t.lifetime===r.scene&&this.removePrefab(t);for(const t of[...this.actors])this.getActorLifetime(t)===r.scene&&this.removeActor(t);return s}resolveSceneActor(t){return this.materializer?.resolveSceneActor(t)}async startSceneEventGraphs(t){this.stopSceneEventGraphs(),this.sceneEventGraphSubscriptions.push(t.prefabEventGraphAdded$.subscribe(t=>{this.activateSceneEventGraph(t).catch(e=>{console.error(`Failed to start scene prefab event graph ${JSON.stringify(t.id)}`,e)})}),t.prefabEventGraphRemoved$.subscribe(t=>{this.deactivateSceneEventGraph(t.id);for(const e of t.actors)this.actors.includes(e)&&this.removeActor(e)}));for(const e of t.prefabEventGraphInstances)await this.activateSceneEventGraph(e)}stopSceneEventGraphs(){for(const t of this.sceneEventGraphSubscriptions)t.unsubscribe();this.sceneEventGraphSubscriptions.length=0;for(const t of[...this.sceneEventGraphs.keys()])this.deactivateSceneEventGraph(t)}async activateSceneEventGraph(t){if(this.sceneEventGraphs.has(t.id))return;for(const e of t.actors)this.actors.includes(e)||this.addActor(e);const e=new y(t.graph,{world:this,actorsById:t.actorsById,mainActor:t.mainActor,updates:this.viewController.onUpdate(),timer:this.viewController,physics:this.physics,assets:this.eventGraphAssets});this.sceneEventGraphs.set(t.id,{binding:t,runtime:e});try{await e.beginPlay()}catch(r){throw this.sceneEventGraphs.delete(t.id),e.endPlay(),r}}deactivateSceneEventGraph(t){const e=this.sceneEventGraphs.get(t);null!=e&&(this.sceneEventGraphs.delete(t),e.runtime.endPlay())}async spawnPrefab(t,e,s,n){if(null==this.materializer)return console.error("Internal error: Materializer is missing on World"),null;const a=new m,c=new o;null!=e&&c.position.copy(e),null!=s&&c.rotation.copy(s),this.materializer.setScene(this.scene),this.materializer.inEditor=this.actorFactory.inEditor;const h=null!=t.asset.id?await(this.assetsProvider?.getAsset(t.asset.id))??t.asset:t.asset,{object:l,actors:f,actorsById:d,mainActor:v,instanceId:w}=await this.materializer.createFromPrefabAsset(h,{sceneObjectChain:["r-"+p()],runtimeSpawn:!0},c,void 0,!1);a.object=l,a.actors=f,a.mainActor=v,a.lifetime=n?.lifetime??(null!=v?i(v.constructor).lifetime:void 0)??r.scene,a.__materializerInstanceId=w,this.prefabInstances.add(a);for(const t of f)this.actorPrefabInstances.set(t,a);this.scene.attach(l),f.forEach(t=>{this.addActor(t,void 0,void 0,a.lifetime)}),this.physics.addFromScene(l);const u=h.prefab?.eventGraph;if(!this.actorFactory.inEditor&&g(u)){a.eventGraph=new y(u,{world:this,actorsById:d,mainActor:v,updates:this.viewController.onUpdate(),timer:this.viewController,physics:this.physics,assets:this.eventGraphAssets});try{await a.eventGraph.beginPlay()}catch(t){throw this.removePrefab(a),t}}return a}removePrefab(t){if(t.destroyed)return;t.destroyed=!0,this.prefabInstances.delete(t),t.eventGraph?.endPlay(),t.eventGraph=void 0,null!=t.__materializerInstanceId&&(this.materializer.unregisterLivePrefabInstance?.(t.__materializerInstanceId),t.__materializerInstanceId=void 0);t.actors.splice(0).forEach(t=>this.removeActor(t)),t.object?.removeFromParent(),this.physics.removeRemoved(t.object)}};I=t([e()],I);export{I as World};function P(t,e,r=!1,i=new WeakSet){if(!i.has(t))return i.add(t),e(t),t instanceof s&&t.attachedComponents.forEach(t=>{v(t,r)&&P(t,e,r,i)}),Object.entries(t).filter(([t,e])=>e instanceof c&&v(e,r)).forEach(([t,s])=>{P(s,e,r,i)})}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from './gameplay/actors/builtin/water-spline-volume-actor.js';
|
|
|
10
10
|
export * from './gameplay/actors/builtin/components/water-post-process-settings.js';
|
|
11
11
|
export * from './scene/objects/prefab.js';
|
|
12
12
|
export * from './scene/objects/data-asset.js';
|
|
13
|
+
export * from './scene/scene-ref.js';
|
|
13
14
|
export * from './effects/sequence/index.js';
|
|
14
15
|
export * from './data/surface-definition.js';
|
|
15
16
|
export * from './scene/surface-query.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export{loadScene}from"./scene/bootstrap.js";export{BaseGameController}from"./controllers/base-game-controller.js";export*from"./scene/collision/collision-shape.js";export{AssetMeshInstance}from"./scene/asset-resource-loader.js";export{registerWorker,initWorker}from"./worker/index.js";export*from"./shader/index.js";export*from"./gameplay/actors/builtin/post-process-volume-actor.js";export*from"./gameplay/actors/builtin/water-volume-actor.js";export*from"./gameplay/actors/builtin/water-spline-volume-actor.js";export*from"./gameplay/actors/builtin/components/water-post-process-settings.js";export*from"./scene/objects/prefab.js";export*from"./scene/objects/data-asset.js";export*from"./effects/sequence/index.js";export*from"./data/surface-definition.js";export*from"./scene/surface-query.js";/*
|
|
1
|
+
export{loadScene}from"./scene/bootstrap.js";export{BaseGameController}from"./controllers/base-game-controller.js";export*from"./scene/collision/collision-shape.js";export{AssetMeshInstance}from"./scene/asset-resource-loader.js";export{registerWorker,initWorker}from"./worker/index.js";export*from"./shader/index.js";export*from"./gameplay/actors/builtin/post-process-volume-actor.js";export*from"./gameplay/actors/builtin/water-volume-actor.js";export*from"./gameplay/actors/builtin/water-spline-volume-actor.js";export*from"./gameplay/actors/builtin/components/water-post-process-settings.js";export*from"./scene/objects/prefab.js";export*from"./scene/objects/data-asset.js";export*from"./scene/scene-ref.js";export*from"./effects/sequence/index.js";export*from"./data/surface-definition.js";export*from"./scene/surface-query.js";/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Application/process-lifetime owner for reusable graphics backends and caches. */
|
|
2
|
+
export declare class GraphicsRuntime {
|
|
3
|
+
private readonly pools;
|
|
4
|
+
acquire<T>(poolName: string, key: string, create: () => Promise<T>): Promise<T>;
|
|
5
|
+
release<T>(poolName: string, key: string, destroy: (value: T) => void): void;
|
|
6
|
+
}
|
|
7
|
+
export declare const defaultGraphicsRuntime: GraphicsRuntime;
|
|
8
|
+
//# sourceMappingURL=graphics-runtime.d.ts.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export class GraphicsRuntime{constructor(){this.pools=new Map}async acquire(e,s,t){let l=this.pools.get(e);null==l&&(l=new Map,this.pools.set(e,l));let r=l.get(s);null==r&&(r={promise:t(),value:null,references:0},l.set(s,r),r.promise.then(e=>{r.value=e},()=>{l.get(s)===r&&l.delete(s)}));const n=await r.promise;return r.references++,n}release(e,s,t){const l=this.pools.get(e),r=l?.get(s);null!=r&&(r.references=Math.max(0,r.references-1),0===r.references&&(l.delete(s),0===l.size&&this.pools.delete(e),null!=r.value&&t(r.value)))}}export const defaultGraphicsRuntime=new GraphicsRuntime;/*
|
|
2
|
+
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
|
+
* See the LICENSE.md file for details.
|
|
4
|
+
*/
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
type IntersectionCallback = (isIntersecting: boolean) => void;
|
|
2
|
+
/** Shares container visibility without letting one view replace or remove another. */
|
|
3
|
+
export declare function observeElementIntersection(element: Element, callback: IntersectionCallback): () => void;
|
|
4
|
+
export {};
|
|
5
|
+
//# sourceMappingURL=intersection-observer.d.ts.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
const e=new Map;let t=null;export function observeElementIntersection(n,s){if(null==window.IntersectionObserver)return s(!0),()=>{};t??(t=new IntersectionObserver(t=>{for(const n of t){const t=e.get(n.target);if(null!=t){t.isIntersecting=n.isIntersecting;for(const e of t.callbacks)e(n.isIntersecting)}}},{threshold:0}));let l=e.get(n);return null==l&&(l={callbacks:new Set,isIntersecting:void 0},e.set(n,l),t.observe(n)),l.callbacks.add(s),null!=l.isIntersecting&&s(l.isIntersecting),()=>{l.callbacks.delete(s),0===l.callbacks.size&&e.get(n)===l&&(t.unobserve(n),e.delete(n))}}/*
|
|
2
|
+
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
|
+
* See the LICENSE.md file for details.
|
|
4
|
+
*/
|
|
@@ -11,8 +11,17 @@ export type AntiAliasingMode = 'off' | 'fxaa' | 'smaa' | 'taa';
|
|
|
11
11
|
export type ExperimentalWebGpuRenderGraphExtension = ((graph: FrameGraphBuilder, color: string) => void) & {
|
|
12
12
|
getSceneTextureReads?: () => readonly string[];
|
|
13
13
|
/** Pre-scene extensions run before shadow, depth and color geometry passes. */
|
|
14
|
-
stage?: 'beforeScene' | 'afterScene';
|
|
14
|
+
stage?: 'beforeScene' | 'afterScene' | 'beforePresent';
|
|
15
15
|
};
|
|
16
|
+
export type WebGpuPresentationExtensionContext = {
|
|
17
|
+
readonly device: GPUDevice;
|
|
18
|
+
readonly encoder: GPUCommandEncoder;
|
|
19
|
+
readonly targetView: GPUTextureView;
|
|
20
|
+
readonly targetFormat: GPUTextureFormat;
|
|
21
|
+
readonly presentationWidth: number;
|
|
22
|
+
readonly presentationHeight: number;
|
|
23
|
+
};
|
|
24
|
+
export type WebGpuPresentationExtension = (context: WebGpuPresentationExtensionContext) => void;
|
|
16
25
|
export interface ExperimentalWebGpuPostProcessingOptions {
|
|
17
26
|
/** Request native DLSS-SR when supported by the host. Falls back to TAA. */
|
|
18
27
|
readonly nativeUpscaling?: false | {
|
|
@@ -98,6 +107,7 @@ export declare class ExperimentalWebGpuBackend implements RenderingBackendDriver
|
|
|
98
107
|
private readonly outline;
|
|
99
108
|
private readonly postProcessEffects;
|
|
100
109
|
private readonly renderGraphExtensions;
|
|
110
|
+
private readonly presentationExtensions;
|
|
101
111
|
private readonly antiAliasingState;
|
|
102
112
|
private readonly directionalShadowState;
|
|
103
113
|
private readonly nativeDlss;
|
|
@@ -123,6 +133,7 @@ export declare class ExperimentalWebGpuBackend implements RenderingBackendDriver
|
|
|
123
133
|
setPostProcessEffects(effects: readonly ExperimentalWebGpuPostProcessEffect[]): void;
|
|
124
134
|
getDevice(): GPUDevice | null;
|
|
125
135
|
setRenderGraphExtensions(extensions: Iterable<ExperimentalWebGpuRenderGraphExtension>): void;
|
|
136
|
+
setPresentationExtensions(extensions: Iterable<WebGpuPresentationExtension>): void;
|
|
126
137
|
setEnableOutlines(enabled: boolean): void;
|
|
127
138
|
setDynamicBatchingEnabled(enabled: boolean): void;
|
|
128
139
|
setSelectedObjects(objects: Iterable<THREE.Object3D>): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as e from"three";import{NodeMaterialWebGpuResolver as t}from"./node-material-webgpu-resolver.js";import{configureWebGpuRuntimeBindings as i}from"./webgpu-runtime-bridge.js";import{float as n,varyingTransformed as r,vec4 as o}from"three-shader-graph";import{NodeShaderMaterial as a}from"../shader-nodes/index.js";import{sceneMapUniformName as s}from"../shader-nodes/scene-sample.js";import{depthUniformName as l}from"../shader-nodes/depth.js";import{MATERIAL_FLAG_ALPHA_TEST as d,MATERIAL_FLAG_RECEIVE_DECALS as c}from"../shader-nodes/material-flags.js";import{createMotionVectorOutput as h}from"./motion-vector-node.js";export class ExperimentalWebGpuBackend{constructor(e,t,i,n,r,o,a,s,l,d,c,h,u,p,m,g,f,v,S,b,x){this.renderer=e,this.fallbackMaterial=t,this.nodeMaterialResolver=i,this.gBufferFallbackSource=n,this.bloom=r,this.fxaa=o,this.smaa=a,this.gtao=s,this.ssr=l,this.taa=d,this.motionBlur=c,this.volumetricFog=h,this.colorGrading=u,this.depthOfField=p,this.outline=m,this.postProcessEffects=g,this.renderGraphExtensions=f,this.antiAliasingState=v,this.directionalShadowState=S,this.nativeDlss=b,this.kind="webgpu",this.renderScale=1,this.pmremGenerator=null,this.pmremResults=new WeakMap,this.ownedPmremResults=new Set,this.warnedUnsupportedPostProcessStages=new Set,this.canvas=x}static async probeAvailability(){if("undefined"==typeof navigator||!("gpu"in navigator))return{available:!1,reason:"The runtime does not expose navigator.gpu."};try{const e=await navigator.gpu.requestAdapter({powerPreference:"high-performance"});return null==e?{available:!1,reason:"No WebGPU adapter is available."}:{available:!0,adapter:e}}catch(e){return{available:!1,reason:`Requesting a WebGPU adapter failed: ${e instanceof Error?e.message:String(e)}`}}}static async create(m={},g){const f=await import("@hology/webgpu-renderer");i({createComputeKernel:(e,t)=>new f.ComputeKernel(e,t),buildParticleLightingShaderHeader:f.buildParticleLightingShaderHeader});const v=document.createElement("canvas");v.dataset.hologyRenderer="webgpu-experimental";const S=new f.WGSLShaderMaterial({vertexShader:u,fragmentShader:p}),b=!1!==m.nativeUpscaling&&"dlss"===m.nativeUpscaling?.method,x=b?(await import("./native-dlss-upscaler.js")).createNativeDlssUpscaler(m.nativeUpscaling?.mode??"quality"):null,P=null!=x;S.side=f.DoubleSide;const w=f,y=null!=m.motionBlur&&!1!==m.motionBlur,F=!1!==m.gtao&&(null==m.gtao||!1!==m.gtao.temporal),M=!1!==m.volumetricFog&&(null==m.volumetricFog||!1!==m.volumetricFog.temporal),C=P?"off":b?"taa":null!=(D=m).antiAliasing?D.antiAliasing:null!=D.taa&&!1!==D.taa?"taa":null!=D.smaa&&!1!==D.smaa?"smaa":null!=D.fxaa&&!1!==D.fxaa?"fxaa":!1===D.taa?"off":"taa";var D;const E=null!=m.antiAliasing,L=y||F||M||(E||"taa"===C)||P;let O=null;const G=new t(w,(e,t)=>"opaque"===t?[e.outputColor.xyz.rgba(n(e.alphaTest>0?d:0).add(!1===e.userData.receiveDecals?0:c)),o((e.outputNormal??r.normal).multiplyScalar(.5).addScalar(.5),e.outputRoughness??n(1)),...L?[h(e)]:[]]:void 0,e=>O?.getEnvironmentTexture(e)??e),R=new e.MeshStandardMaterial({color:8688803,roughness:1});if(null==G.resolve(R,"opaque"))throw S.dispose(),R.dispose(),new Error("Failed to compile the WebGPU G-buffer fallback material.");const T=!1===m.gtao?null:new f.GTAOEffect({normalSpace:"view",...f.GTAO_PRESETS.high,...m.gtao??{}}),A=!1===m.ssr||null==m.ssr?null:new f.SSREffect({normalSpace:"view",resolutionScale:.5,...m.ssr??{}}),B=!1===m.bloom?null:new f.BloomEffect({threshold:1,strength:.9,radius:.2,...m.bloom??{}}),W=E||!1!==m.fxaa&&"fxaa"===C?new f.FXAAEffect(!1===m.fxaa?{}:m.fxaa):null,V=E||!1!==m.smaa&&"smaa"===C?new f.SMAAEffect(!1===m.smaa?{}:m.smaa):null,k=P||E||!1!==m.taa&&"taa"===C?new f.TemporalAAEffect({renderScale:1,...!1===m.taa?{}:m.taa}):null,U={mode:C},I=!1===m.motionBlur||null==m.motionBlur?null:new f.MotionBlurEffect(m.motionBlur),j=m.volumetricFog||void 0,N=!1===m.volumetricFog?null:new f.VolumetricFogEffect(void 0,{...f.VOLUMETRIC_FOG_PRESETS.high,maxDistance:64,spatialJitter:!0,scatteringIntensity:Math.PI,...j,temporal:!1!==j?.temporal&&{...f.VOLUMETRIC_FOG_PRESETS.high.temporal,...j?.temporal},blur:!1!==j?.blur&&{...!1===f.VOLUMETRIC_FOG_PRESETS.high.blur?{}:f.VOLUMETRIC_FOG_PRESETS.high.blur,depthAwareUpsample:!0,...j?.blur}}),H=!1===m.colorGrading?null:new f.ColorGradingEffect({enabled:!1,...m.colorGrading??{}}),$=!1===m.depthOfField?null:new f.DepthOfFieldEffect({enabled:!1,...m.depthOfField??{}}),_=!1===m.outline?null:new f.OutlineEffect({enabled:!1,...m.outline??{}}),q=[],z=new WeakMap,J=new Set,K=[],Z=(e,t,i)=>{let n=i;const r=q.filter(e=>e.enabled&&e.stage===t).sort((e,t)=>e.priority-t.priority||e.id-t.id);for(const t of r){if(!(t.material instanceof a)){const e=`${t.id}:material`;J.has(e)||(J.add(e),console.warn(`[Hology WebGPU experiment] Skipping custom post-process effect ${t.id}: only NodeShaderMaterial effects are supported.`));continue}try{let i=z.get(t.material);null!=i&&i.version===t.material.version||(i={version:t.material.version,shader:G.compileFullscreen(t.material)},z.set(t.material,i));const r=e.createColorTarget(`hologyCustomPostProcess${t.id}`);e.addNodeFullscreenPass({name:`HologyCustomPostProcess${t.id}`,shader:i.shader,inputOverrides:{[s]:n},outputs:r}),n=r}catch(e){const i=`${t.id}:compile:${t.material.version}`;J.has(i)||(J.add(i),console.warn(`[Hology WebGPU experiment] Skipping custom post-process effect ${t.id}: ${e instanceof Error?e.message:String(e)}`,e))}}return n},Q={enabled:!0},X=new f.WebGpuRenderer({canvas:v,fallbackMaterial:S,materialResolver:(e,t)=>G.resolve(e,t),invokeObjectRenderCallbacks:!1,hiZOcclusionCulling:!0,renderGraphProfiling:!1,sampleCount:1,multiDrawIndirect:!1,nativePostSubmit:x?.postSubmit});X.setRenderScale("taa"===C?k?.renderScale??1:1),X.setRenderGraphBuilder((e,t)=>{const i=new f.FrameGraphBuilder(t),n=i.createColorTarget("hologyNormalRoughness",{format:"rgba16float"}),r=L?i.createColorTarget("hologyMotionVectors",{format:"rgba16float"}):null;for(const e of K)"beforeScene"===e.stage&&e(i,i.sceneColor);Q.enabled&&i.addDirectionalShadowPass(),i.addPointShadowPass(),i.addSpotShadowPass(),i.addDepthPrepass(),i.addHiZPasses(),i.addVisibilityPass(),i.addMaterialPass({name:"OpaqueGBufferPass",outputs:null==r?[i.sceneColor,n]:[i.sceneColor,n,r],drawStage:"opaque",loadDepth:!0}),i.addHiZHistoryPass("OpaqueGBufferPass");let o=i.sceneColor;if(T?.enabled){const e=T.addPasses(i,{depth:i.mainDepth,normal:n,velocity:r??void 0});o=T.addCompositePass(i,o,e)}null!=A&&(o=A.addPasses(i,{sceneColor:o,depth:i.mainDepth,normalRoughness:n}));const a=new Set(K.flatMap(e=>e.getSceneTextureReads?.()??[]));i.addSceneTextureCopyPass(o,{sceneColor:a.has("sceneColorSnapshot"),sceneDepth:a.has("sceneDepthSnapshot")||q.some(e=>e.enabled&&null!=e.material.uniforms[l])}),o=Z(i,"beforeFog",o),i.addLatePass(o);for(const e of K)"beforeScene"!==e.stage&&e(i,o);null!=N&&(o=N.addPasses(i,{sceneColor:o,depth:i.mainDepth,velocity:r??void 0,volumeObjects:t.fogVolumeObjects,lights:t.extractedLights})),null!=I&&null!=r&&(o=I.addPass(i,o,r,i.mainDepth)),null!=B&&(o=B.addPass(i,o)),o=Z(i,"beforeOutline",o),null!=_&&(o=_.addPass(i,o)),o=Z(i,"beforeDepthOfField",o),null!=$&&(o=$.addPass(i,o,i.mainDepth)),o=Z(i,"beforeColorAdjustment",o),null!=H&&(o=H.addPass(i,o)),o=Z(i,"beforeAntiAliasing",o);let s="taa"===U.mode&&null!=k?k.addPass(i,o,i.mainDepth,r??void 0):o;"smaa"===U.mode&&null!=V?s=V.addPass(i,s):"fxaa"===U.mode&&null!=W&&(s=W.addPass(i,s)),s=Z(i,"beforeOutput",s),!0===x?.enabled&&null!=r?x.addInputPass(i,s,r):i.present(s)});try{return await X.initialize(g),O=new ExperimentalWebGpuBackend(X,S,G,R,B,W,V,T,A,k,I,N,H,$,_,q,K,U,Q,x,v),O}catch(e){throw X.destroy(),S.dispose(),G.dispose(),R.dispose(),T?.dispose(),V?.dispose(),I?.dispose(),N?.dispose(),_?.dispose(),e}}static supportsCamera(t){return t instanceof e.PerspectiveCamera||t instanceof e.OrthographicCamera}setSize(e,t,i,n=1){const r=this.canvas.width,o=this.canvas.height;let a=Math.min(1,Math.max(.25,n));if(!0===this.nativeDlss?.enabled){const n=Math.max(1,Math.floor(e*i)),r=Math.max(1,Math.floor(t*i));a=this.nativeDlss.getRenderScale(n,r)}const s=a!==this.renderScale;this.renderScale=a,this.renderer.setRenderScale(a),this.taa?.setRenderScale(a),this.renderer.setSize(e,t,i),(s||this.canvas.width!==r||this.canvas.height!==o)&&this.resetViewHistory()}initTexture(e){this.renderer.initTexture(e)}setPostProcessEffects(e){this.postProcessEffects.splice(0,this.postProcessEffects.length,...e);for(const t of e){if("beforeLut"!==t.stage)continue;const e=`${t.id}:${t.stage}`;this.warnedUnsupportedPostProcessStages.has(e)||(this.warnedUnsupportedPostProcessStages.add(e),console.warn(`[Hology WebGPU experiment] Skipping custom post-process effect ${t.id}: stage "${t.stage}" has no native WebGPU anchor.`))}}getDevice(){return this.renderer.getDevice()}setRenderGraphExtensions(e){this.renderGraphExtensions.length=0;for(const t of e)this.renderGraphExtensions.push(t)}setEnableOutlines(e){null!=this.outline&&(this.outline.enabled=e)}setDynamicBatchingEnabled(e){this.renderer.setDynamicBatchingEnabled(e)}setSelectedObjects(e){this.outline?.setSelectedObjects(e)}setSelectedObjectInstances(e){this.outline?.setSelectedInstances(e)}setCascadedShadowRanges(e,t=[]){this.renderer.setCascadedShadowRanges(e,t),this.volumetricFog?.setCascadedDirectionalLights(t)}setDirectionalShadowOptions(e,t){this.directionalShadowState.enabled=e,this.renderer.setDirectionalShadowsEnabled(e),this.renderer.setDirectionalShadowMapSize(t)}setWorldEnvironment(e){if(null==e)return void this.nodeMaterialResolver.setWorldEnvironment(null);const t=this.getEnvironmentTexture(e.source);this.nodeMaterialResolver.setWorldEnvironment({texture:t,intensity:e.intensity})}getEnvironmentTexture(t){if(t.mapping===e.CubeUVReflectionMapping)return t;let i=this.pmremResults.get(t);return null==i&&(this.pmremGenerator??(this.pmremGenerator=this.renderer.createPMREMGenerator()),i=this.pmremGenerator.fromEquirectangular(t),this.pmremResults.set(t,i),this.ownedPmremResults.add(i)),i.texture}resetViewHistory(){this.renderer.resetViewHistory(),this.taa?.reset(),this.motionBlur?.reset(),this.gtao?.reset(),this.volumetricFog?.reset(),this.nativeDlss?.reset()}async compile(e,t){await this.renderer.compileAsync(e,t)}createStaticDrawSet(e,t,i=!1){this.renderer.setHiZOcclusionCullingEnabled(!0);const n=this.renderer.createStaticDrawSet(e,{label:t,onUnsupported:"skip",allowUnsortedLateDraws:i});return 0===n.drawCount?(n.dispose(),null):n}render(e,t,i){const n="taa"===this.antiAliasingState.mode?this.taa:null;this.volumetricFog?.setScene(e),this.renderer.setMotionVectorProjection(t.projectionMatrix),this.motionBlur?.prepareCamera(t,this.canvas.width,this.canvas.height,i),this.motionBlur?.requiresRendererHistoryReset()&&(this.renderer.resetViewHistory(),this.gtao?.reset(),this.volumetricFog?.reset()),n?.prepareCamera(t,this.canvas.width,this.canvas.height),this.nativeDlss?.prepareFrame(t);const r=!0===this.nativeDlss?.enabled?this.nativeDlss.jitter:null;this.motionBlur?.setJitterUv(n?.currentJitterUv.x??(null==r?0:r.x/Math.max(1,this.canvas.width*this.renderScale)),n?.currentJitterUv.y??(null==r?0:-r.y/Math.max(1,this.canvas.height*this.renderScale))),null!=r&&this.renderer.setProjectionJitter(r.x,r.y);let o=!1;try{this.renderer.render(e,t,i),o=!0}finally{null!=r&&this.renderer.clearProjectionJitter(),n?.finishCamera(t),o&&this.motionBlur?.commitCamera(),o&&this.nativeDlss?.finishFrame(t)}}setMotionBlurOptions(e){null!=this.motionBlur&&Object.assign(this.motionBlur.options,e)}setGTAOOptions(e){this.gtao?.setOptions(e)}setAntiAliasingMode(e){e!==this.antiAliasingState.mode&&("taa"===e&&null==this.taa||"smaa"===e&&null==this.smaa||"fxaa"===e&&null==this.fxaa?console.warn(`[Hology WebGPU experiment] Anti-aliasing mode "${e}" was not initialized. Set webgpuPostProcessing.antiAliasing in the initial rendering configuration to enable runtime switching.`):(this.antiAliasingState.mode=e,this.resetTemporalHistory()))}setGTAOQuality(e){this.gtao?.applyPreset(e)}setVolumetricFogOptions(e){this.volumetricFog?.setOptions(e)}setVolumetricFogQuality(e){this.volumetricFog?.applyPreset(e)}pushMotionBlurOverride(e){return this.motionBlur?.pushOverride(e)??null}invalidateObjectMotion(e){this.renderer.invalidateObjectMotion(e)}getLastFrameProfile(){return this.renderer.getLastFrameProfile()}setPostProcessState(e){null!=this.colorGrading&&(Object.assign(this.colorGrading.options,e??{enabled:!1}),this.colorGrading.options.enabled=!0===e?.enabled),null!=this.depthOfField&&(this.depthOfField.options.enabled=null!=e?.depthFocus||null!=e?.depthAperture||null!=e?.depthMaxBlur,this.depthOfField.options.focus=e?.depthFocus,this.depthOfField.options.aperture=e?.depthAperture,this.depthOfField.options.maxBlur=e?.depthMaxBlur)}setProfilingEnabled(e){this.renderer.setRenderGraphProfilingEnabled(e)}resetTemporalHistory(){this.taa?.reset(),this.motionBlur?.reset(),this.gtao?.reset(),this.volumetricFog?.reset(),this.renderer.resetViewHistory(),this.nativeDlss?.reset()}inspect(e,t){return inspectWebGpuScene(e,t,this.nodeMaterialResolver)}destroy(){this.nativeDlss?.dispose(),this.nodeMaterialResolver.dispose(),this.gBufferFallbackSource.dispose(),this.gtao?.dispose(),this.smaa?.dispose(),this.motionBlur?.dispose(),this.volumetricFog?.dispose();for(const e of this.ownedPmremResults)e.dispose();this.ownedPmremResults.clear(),this.renderer.destroy(),this.fallbackMaterial.dispose()}}export function inspectWebGpuScene(e,t,i){const n={meshes:0,skinnedMeshes:0,instancedMeshes:0,batchedMeshes:0,sprites:0,points:0,lines:0,missingPositionGeometry:0,transparentMaterials:0,shaderMaterialsReplaced:0,materialsReplaced:0,unsupportedCamera:ExperimentalWebGpuBackend.supportsCamera(t)?0:1};return e.traverse(e=>{if(e.isSprite)return void n.sprites++;if(e.isPoints)return void n.points++;if(e.isLine)return void n.lines++;if(!e.isMesh)return;const t=e;t.isBatchedMesh?n.batchedMeshes++:t.isInstancedMesh?n.instancedMeshes++:t.isSkinnedMesh?n.skinnedMeshes++:n.meshes++,null==t.geometry?.getAttribute("position")&&n.missingPositionGeometry++;const r=Array.isArray(t.material)?t.material:[t.material];for(const e of r){if(null==e)continue;e.transparent&&n.transparentMaterials++;const t=!0===e.isWGSLShaderMaterial||!0===e.isLineMaterial,r=!t&&!0===i?.canResolve(e);t||r||(n.materialsReplaced++,e.isShaderMaterial&&n.shaderMaterialsReplaced++)}}),n}export function formatWebGpuSceneCapabilitySummary(e){return`[Hology WebGPU experiment] ${JSON.stringify(e)}`}const u="\nstruct VertexInput {\n @location(0) position: vec3<f32>,\n @location(1) normal: vec3<f32>,\n @location(2) uv: vec2<f32>,\n @location(3) color: vec4<f32>,\n#ifdef USE_SKINNING\n @location(4) skinIndex: vec4<u32>,\n @location(5) skinWeight: vec4<f32>,\n#endif\n @builtin(instance_index) instanceIndex: u32,\n};\n\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) worldPosition: vec3<f32>,\n @location(1) worldNormal: vec3<f32>,\n @location(2) color: vec4<f32>,\n};\n\n@vertex\nfn vsMain(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n#ifdef USE_SKINNING\n let localPosition = rendererSkinPosition(input.position, input.skinIndex, input.skinWeight);\n let localNormal = rendererSkinNormal(input.normal, input.skinIndex, input.skinWeight);\n#else\n let localPosition = input.position;\n let localNormal = input.normal;\n#endif\n let world = rendererWorldPosition(localPosition, input.instanceIndex);\n output.position = rendererClipPosition(localPosition, input.instanceIndex);\n output.worldPosition = world.xyz;\n output.worldNormal = rendererWorldNormal(localNormal, input.instanceIndex);\n output.color = input.color * rendererObjectColor(input.instanceIndex);\n return output;\n}\n",p="\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) worldPosition: vec3<f32>,\n @location(1) worldNormal: vec3<f32>,\n @location(2) color: vec4<f32>,\n};\n\nfn distanceAttenuation(distanceToLight: f32, cutoffDistance: f32, decayExponent: f32) -> f32 {\n var distanceFalloff = 1.0 / max(pow(distanceToLight, decayExponent), 0.01);\n if (cutoffDistance > 0.0) {\n let ratio = distanceToLight / cutoffDistance;\n let cutoff = clamp(1.0 - ratio * ratio * ratio * ratio, 0.0, 1.0);\n distanceFalloff *= cutoff * cutoff;\n }\n return distanceFalloff;\n}\n\n@fragment\nfn fsMain(input: VertexOutput) -> @location(0) vec4<f32> {\n let normal = normalize(input.worldNormal);\n let normalTint = normal * 0.5 + vec3<f32>(0.5);\n let base = mix(vec3<f32>(0.52, 0.58, 0.64), normalTint, 0.22) * input.color.rgb;\n var lighting = rendererIndirectDiffuse(normal) + vec3<f32>(0.08);\n var directionalLighting = vec3<f32>(0.0);\n let directionalCount = rendererDirectionalLightCount();\n\n for (var index: u32 = 0u; index < directionalCount; index = index + 1u) {\n let lightDirection = rendererDirectionalLightDirectionForLight(index);\n let shadow = rendererSampleDirectionalShadowForLightWithNormal(index, input.worldPosition, normal);\n directionalLighting += rendererDirectionalLightColorForLight(index) * max(dot(normal, lightDirection), 0.0) * shadow;\n }\n if (directionalCount > 0u) {\n lighting += directionalLighting / f32(directionalCount);\n }\n\n for (var index: u32 = 0u; index < rendererPointLightCount(); index = index + 1u) {\n let lightVector = rendererPointLightPositionForLight(index) - input.worldPosition;\n let distanceToLight = length(lightVector);\n let lightDirection = normalize(lightVector);\n let shadow = rendererSamplePointShadowForLightWithNormal(index, input.worldPosition, normal);\n lighting += rendererPointLightColorForLight(index) * max(dot(normal, lightDirection), 0.0) * distanceAttenuation(distanceToLight, rendererPointLightRangeForLight(index), rendererPointLightDecayForLight(index)) * shadow;\n }\n\n for (var index: u32 = 0u; index < rendererSpotLightCount(); index = index + 1u) {\n let lightVector = rendererSpotLightPositionForLight(index) - input.worldPosition;\n let distanceToLight = length(lightVector);\n let lightDirection = normalize(lightVector);\n let angleCos = dot(-lightDirection, rendererSpotLightDirectionForLight(index));\n let cone = smoothstep(rendererSpotLightConeCosForLight(index), rendererSpotLightPenumbraCosForLight(index), angleCos);\n let shadow = rendererSampleSpotShadowForLightWithNormal(index, input.worldPosition, normal);\n lighting += rendererSpotLightColorForLight(index) * max(dot(normal, lightDirection), 0.0) * cone * distanceAttenuation(distanceToLight, rendererSpotLightRangeForLight(index), rendererSpotLightDecayForLight(index)) * shadow;\n }\n\n return vec4<f32>(base * max(lighting, vec3<f32>(0.08)), input.color.a);\n}\n";/*
|
|
1
|
+
import*as e from"three";import{NodeMaterialWebGpuResolver as t}from"./node-material-webgpu-resolver.js";import{configureWebGpuRuntimeBindings as i}from"./webgpu-runtime-bridge.js";import{float as n,varyingTransformed as r,vec4 as o}from"three-shader-graph";import{NodeShaderMaterial as s}from"../shader-nodes/index.js";import{sceneMapUniformName as a}from"../shader-nodes/scene-sample.js";import{depthUniformName as l}from"../shader-nodes/depth.js";import{MATERIAL_FLAG_ALPHA_TEST as d,MATERIAL_FLAG_RECEIVE_DECALS as c}from"../shader-nodes/material-flags.js";import{createMotionVectorOutput as h}from"./motion-vector-node.js";export class ExperimentalWebGpuBackend{constructor(e,t,i,n,r,o,s,a,l,d,c,h,u,p,m,g,f,v,S,b,x,P){this.renderer=e,this.fallbackMaterial=t,this.nodeMaterialResolver=i,this.gBufferFallbackSource=n,this.bloom=r,this.fxaa=o,this.smaa=s,this.gtao=a,this.ssr=l,this.taa=d,this.motionBlur=c,this.volumetricFog=h,this.colorGrading=u,this.depthOfField=p,this.outline=m,this.postProcessEffects=g,this.renderGraphExtensions=f,this.presentationExtensions=v,this.antiAliasingState=S,this.directionalShadowState=b,this.nativeDlss=x,this.kind="webgpu",this.renderScale=1,this.pmremGenerator=null,this.pmremResults=new WeakMap,this.ownedPmremResults=new Set,this.warnedUnsupportedPostProcessStages=new Set,this.canvas=P}static async probeAvailability(){if("undefined"==typeof navigator||!("gpu"in navigator))return{available:!1,reason:"The runtime does not expose navigator.gpu."};try{const e=await navigator.gpu.requestAdapter({powerPreference:"high-performance"});return null==e?{available:!1,reason:"No WebGPU adapter is available."}:{available:!0,adapter:e}}catch(e){return{available:!1,reason:`Requesting a WebGPU adapter failed: ${e instanceof Error?e.message:String(e)}`}}}static async create(m={},g){const f=await import("@hology/webgpu-renderer");i({createComputeKernel:(e,t)=>new f.ComputeKernel(e,t),buildParticleLightingShaderHeader:f.buildParticleLightingShaderHeader});const v=document.createElement("canvas");v.dataset.hologyRenderer="webgpu-experimental";const S=new f.WGSLShaderMaterial({vertexShader:u,fragmentShader:p}),b=!1!==m.nativeUpscaling&&"dlss"===m.nativeUpscaling?.method,x=b?(await import("./native-dlss-upscaler.js")).createNativeDlssUpscaler(m.nativeUpscaling?.mode??"quality"):null,P=null!=x;S.side=f.DoubleSide;const w=f,y=null!=m.motionBlur&&!1!==m.motionBlur,F=!1!==m.gtao&&(null==m.gtao||!1!==m.gtao.temporal),M=!1!==m.volumetricFog&&(null==m.volumetricFog||!1!==m.volumetricFog.temporal),E=P?"off":b?"taa":null!=(C=m).antiAliasing?C.antiAliasing:null!=C.taa&&!1!==C.taa?"taa":null!=C.smaa&&!1!==C.smaa?"smaa":null!=C.fxaa&&!1!==C.fxaa?"fxaa":!1===C.taa?"off":"taa";var C;const D=null!=m.antiAliasing,L=y||F||M||(D||"taa"===E)||P;let O=null;const G=new t(w,(e,t)=>"opaque"===t?[e.outputColor.xyz.rgba(n(e.alphaTest>0?d:0).add(!1===e.userData.receiveDecals?0:c)),o((e.outputNormal??r.normal).multiplyScalar(.5).addScalar(.5),e.outputRoughness??n(1)),...L?[h(e)]:[]]:void 0,e=>O?.getEnvironmentTexture(e)??e),R=new e.MeshStandardMaterial({color:8688803,roughness:1});if(null==G.resolve(R,"opaque"))throw S.dispose(),R.dispose(),new Error("Failed to compile the WebGPU G-buffer fallback material.");const T=!1===m.gtao?null:new f.GTAOEffect({normalSpace:"view",...f.GTAO_PRESETS.high,...m.gtao??{}}),A=!1===m.ssr||null==m.ssr?null:new f.SSREffect({normalSpace:"view",resolutionScale:.5,...m.ssr??{}}),B=!1===m.bloom?null:new f.BloomEffect({threshold:1,strength:.9,radius:.2,...m.bloom??{}}),W=D||!1!==m.fxaa&&"fxaa"===E?new f.FXAAEffect(!1===m.fxaa?{}:m.fxaa):null,V=D||!1!==m.smaa&&"smaa"===E?new f.SMAAEffect(!1===m.smaa?{}:m.smaa):null,k=P||D||!1!==m.taa&&"taa"===E?new f.TemporalAAEffect({renderScale:1,...!1===m.taa?{}:m.taa}):null,U={mode:E},I=!1===m.motionBlur||null==m.motionBlur?null:new f.MotionBlurEffect(m.motionBlur),j=m.volumetricFog||void 0,N=!1===m.volumetricFog?null:new f.VolumetricFogEffect(void 0,{...f.VOLUMETRIC_FOG_PRESETS.high,maxDistance:64,spatialJitter:!0,scatteringIntensity:Math.PI,...j,temporal:!1!==j?.temporal&&{...f.VOLUMETRIC_FOG_PRESETS.high.temporal,...j?.temporal},blur:!1!==j?.blur&&{...!1===f.VOLUMETRIC_FOG_PRESETS.high.blur?{}:f.VOLUMETRIC_FOG_PRESETS.high.blur,depthAwareUpsample:!0,...j?.blur}}),H=!1===m.colorGrading?null:new f.ColorGradingEffect({enabled:!1,...m.colorGrading??{}}),$=!1===m.depthOfField?null:new f.DepthOfFieldEffect({enabled:!1,...m.depthOfField??{}}),_=!1===m.outline?null:new f.OutlineEffect({enabled:!1,...m.outline??{}}),q=[],z=new WeakMap,J=new Set,K=[],Z=[],Q=(e,t,i)=>{let n=i;const r=q.filter(e=>e.enabled&&e.stage===t).sort((e,t)=>e.priority-t.priority||e.id-t.id);for(const t of r){if(!(t.material instanceof s)){const e=`${t.id}:material`;J.has(e)||(J.add(e),console.warn(`[Hology WebGPU experiment] Skipping custom post-process effect ${t.id}: only NodeShaderMaterial effects are supported.`));continue}try{let i=z.get(t.material);null!=i&&i.version===t.material.version||(i={version:t.material.version,shader:G.compileFullscreen(t.material)},z.set(t.material,i));const r=e.createColorTarget(`hologyCustomPostProcess${t.id}`);e.addNodeFullscreenPass({name:`HologyCustomPostProcess${t.id}`,shader:i.shader,inputOverrides:{[a]:n},outputs:r}),n=r}catch(e){const i=`${t.id}:compile:${t.material.version}`;J.has(i)||(J.add(i),console.warn(`[Hology WebGPU experiment] Skipping custom post-process effect ${t.id}: ${e instanceof Error?e.message:String(e)}`,e))}}return n},X={enabled:!0},Y=new f.WebGpuRenderer({canvas:v,fallbackMaterial:S,materialResolver:(e,t)=>G.resolve(e,t),invokeObjectRenderCallbacks:!1,hiZOcclusionCulling:!0,renderGraphProfiling:!1,sampleCount:1,multiDrawIndirect:!1,nativePostSubmit:x?.postSubmit,presentationOverlay:e=>{for(const t of Z)t(e)}});Y.setRenderScale("taa"===E?k?.renderScale??1:1),Y.setRenderGraphBuilder((e,t)=>{const i=new f.FrameGraphBuilder(t),n=i.createColorTarget("hologyNormalRoughness",{format:"rgba16float"}),r=L?i.createColorTarget("hologyMotionVectors",{format:"rgba16float"}):null;for(const e of K)"beforeScene"===e.stage&&e(i,i.sceneColor);X.enabled&&i.addDirectionalShadowPass(),i.addPointShadowPass(),i.addSpotShadowPass(),i.addDepthPrepass(),i.addHiZPasses(),i.addVisibilityPass(),i.addMaterialPass({name:"OpaqueGBufferPass",outputs:null==r?[i.sceneColor,n]:[i.sceneColor,n,r],drawStage:"opaque",loadDepth:!0}),i.addHiZHistoryPass("OpaqueGBufferPass");let o=i.sceneColor;if(T?.enabled){const e=T.addPasses(i,{depth:i.mainDepth,normal:n,velocity:r??void 0});o=T.addCompositePass(i,o,e)}null!=A&&(o=A.addPasses(i,{sceneColor:o,depth:i.mainDepth,normalRoughness:n}));const s=new Set(K.flatMap(e=>e.getSceneTextureReads?.()??[]));i.addSceneTextureCopyPass(o,{sceneColor:s.has("sceneColorSnapshot"),sceneDepth:s.has("sceneDepthSnapshot")||q.some(e=>e.enabled&&null!=e.material.uniforms[l])}),o=Q(i,"beforeFog",o),i.addLatePass(o);for(const e of K)null!=e.stage&&"afterScene"!==e.stage||e(i,o);null!=N&&(o=N.addPasses(i,{sceneColor:o,depth:i.mainDepth,velocity:r??void 0,volumeObjects:t.fogVolumeObjects,lights:t.extractedLights})),null!=I&&null!=r&&(o=I.addPass(i,o,r,i.mainDepth)),null!=B&&(o=B.addPass(i,o)),o=Q(i,"beforeOutline",o),null!=_&&(o=_.addPass(i,o)),o=Q(i,"beforeDepthOfField",o),null!=$&&(o=$.addPass(i,o,i.mainDepth)),o=Q(i,"beforeColorAdjustment",o),null!=H&&(o=H.addPass(i,o)),o=Q(i,"beforeAntiAliasing",o);let a="taa"===U.mode&&null!=k?k.addPass(i,o,i.mainDepth,r??void 0):o;"smaa"===U.mode&&null!=V?a=V.addPass(i,a):"fxaa"===U.mode&&null!=W&&(a=W.addPass(i,a)),a=Q(i,"beforeOutput",a);for(const e of K)"beforePresent"===e.stage&&e(i,a);!0===x?.enabled&&null!=r?x.addInputPass(i,a,r):i.present(a)});try{return await Y.initialize(g),O=new ExperimentalWebGpuBackend(Y,S,G,R,B,W,V,T,A,k,I,N,H,$,_,q,K,Z,U,X,x,v),O}catch(e){throw Y.destroy(),S.dispose(),G.dispose(),R.dispose(),T?.dispose(),V?.dispose(),I?.dispose(),N?.dispose(),_?.dispose(),e}}static supportsCamera(t){return t instanceof e.PerspectiveCamera||t instanceof e.OrthographicCamera}setSize(e,t,i,n=1){const r=this.canvas.width,o=this.canvas.height;let s=Math.min(1,Math.max(.25,n));if(!0===this.nativeDlss?.enabled){const n=Math.max(1,Math.floor(e*i)),r=Math.max(1,Math.floor(t*i));s=this.nativeDlss.getRenderScale(n,r)}const a=s!==this.renderScale;this.renderScale=s,this.renderer.setRenderScale(s),this.taa?.setRenderScale(s),this.renderer.setSize(e,t,i),(a||this.canvas.width!==r||this.canvas.height!==o)&&this.resetViewHistory()}initTexture(e){this.renderer.initTexture(e)}setPostProcessEffects(e){this.postProcessEffects.splice(0,this.postProcessEffects.length,...e);for(const t of e){if("beforeLut"!==t.stage)continue;const e=`${t.id}:${t.stage}`;this.warnedUnsupportedPostProcessStages.has(e)||(this.warnedUnsupportedPostProcessStages.add(e),console.warn(`[Hology WebGPU experiment] Skipping custom post-process effect ${t.id}: stage "${t.stage}" has no native WebGPU anchor.`))}}getDevice(){return this.renderer.getDevice()}setRenderGraphExtensions(e){this.renderGraphExtensions.length=0;for(const t of e)this.renderGraphExtensions.push(t)}setPresentationExtensions(e){this.presentationExtensions.length=0;for(const t of e)this.presentationExtensions.push(t)}setEnableOutlines(e){null!=this.outline&&(this.outline.enabled=e)}setDynamicBatchingEnabled(e){this.renderer.setDynamicBatchingEnabled(e)}setSelectedObjects(e){this.outline?.setSelectedObjects(e)}setSelectedObjectInstances(e){this.outline?.setSelectedInstances(e)}setCascadedShadowRanges(e,t=[]){this.renderer.setCascadedShadowRanges(e,t),this.volumetricFog?.setCascadedDirectionalLights(t)}setDirectionalShadowOptions(e,t){this.directionalShadowState.enabled=e,this.renderer.setDirectionalShadowsEnabled(e),this.renderer.setDirectionalShadowMapSize(t)}setWorldEnvironment(e){if(null==e)return void this.nodeMaterialResolver.setWorldEnvironment(null);const t=this.getEnvironmentTexture(e.source);this.nodeMaterialResolver.setWorldEnvironment({texture:t,intensity:e.intensity})}getEnvironmentTexture(t){if(t.mapping===e.CubeUVReflectionMapping)return t;let i=this.pmremResults.get(t);return null==i&&(this.pmremGenerator??(this.pmremGenerator=this.renderer.createPMREMGenerator()),i=this.pmremGenerator.fromEquirectangular(t),this.pmremResults.set(t,i),this.ownedPmremResults.add(i)),i.texture}resetViewHistory(){this.renderer.resetViewHistory(),this.taa?.reset(),this.motionBlur?.reset(),this.gtao?.reset(),this.volumetricFog?.reset(),this.nativeDlss?.reset()}async compile(e,t){await this.renderer.compileAsync(e,t)}createStaticDrawSet(e,t,i=!1){this.renderer.setHiZOcclusionCullingEnabled(!0);const n=this.renderer.createStaticDrawSet(e,{label:t,onUnsupported:"skip",allowUnsortedLateDraws:i});return 0===n.drawCount?(n.dispose(),null):n}render(e,t,i){const n="taa"===this.antiAliasingState.mode?this.taa:null;this.volumetricFog?.setScene(e),this.renderer.setMotionVectorProjection(t.projectionMatrix),this.motionBlur?.prepareCamera(t,this.canvas.width,this.canvas.height,i),this.motionBlur?.requiresRendererHistoryReset()&&(this.renderer.resetViewHistory(),this.gtao?.reset(),this.volumetricFog?.reset()),n?.prepareCamera(t,this.canvas.width,this.canvas.height),this.nativeDlss?.prepareFrame(t);const r=!0===this.nativeDlss?.enabled?this.nativeDlss.jitter:null;this.motionBlur?.setJitterUv(n?.currentJitterUv.x??(null==r?0:r.x/Math.max(1,this.canvas.width*this.renderScale)),n?.currentJitterUv.y??(null==r?0:-r.y/Math.max(1,this.canvas.height*this.renderScale))),null!=r&&this.renderer.setProjectionJitter(r.x,r.y);let o=!1;try{this.renderer.render(e,t,i),o=!0}finally{null!=r&&this.renderer.clearProjectionJitter(),n?.finishCamera(t),o&&this.motionBlur?.commitCamera(),o&&this.nativeDlss?.finishFrame(t)}}setMotionBlurOptions(e){null!=this.motionBlur&&Object.assign(this.motionBlur.options,e)}setGTAOOptions(e){this.gtao?.setOptions(e)}setAntiAliasingMode(e){e!==this.antiAliasingState.mode&&("taa"===e&&null==this.taa||"smaa"===e&&null==this.smaa||"fxaa"===e&&null==this.fxaa?console.warn(`[Hology WebGPU experiment] Anti-aliasing mode "${e}" was not initialized. Set webgpuPostProcessing.antiAliasing in the initial rendering configuration to enable runtime switching.`):(this.antiAliasingState.mode=e,this.resetTemporalHistory()))}setGTAOQuality(e){this.gtao?.applyPreset(e)}setVolumetricFogOptions(e){this.volumetricFog?.setOptions(e)}setVolumetricFogQuality(e){this.volumetricFog?.applyPreset(e)}pushMotionBlurOverride(e){return this.motionBlur?.pushOverride(e)??null}invalidateObjectMotion(e){this.renderer.invalidateObjectMotion(e)}getLastFrameProfile(){return this.renderer.getLastFrameProfile()}setPostProcessState(e){null!=this.colorGrading&&(Object.assign(this.colorGrading.options,e??{enabled:!1}),this.colorGrading.options.enabled=!0===e?.enabled),null!=this.depthOfField&&(this.depthOfField.options.enabled=null!=e?.depthFocus||null!=e?.depthAperture||null!=e?.depthMaxBlur,this.depthOfField.options.focus=e?.depthFocus,this.depthOfField.options.aperture=e?.depthAperture,this.depthOfField.options.maxBlur=e?.depthMaxBlur)}setProfilingEnabled(e){this.renderer.setRenderGraphProfilingEnabled(e)}resetTemporalHistory(){this.taa?.reset(),this.motionBlur?.reset(),this.gtao?.reset(),this.volumetricFog?.reset(),this.renderer.resetViewHistory(),this.nativeDlss?.reset()}inspect(e,t){return inspectWebGpuScene(e,t,this.nodeMaterialResolver)}destroy(){this.nativeDlss?.dispose(),this.nodeMaterialResolver.dispose(),this.gBufferFallbackSource.dispose(),this.gtao?.dispose(),this.smaa?.dispose(),this.motionBlur?.dispose(),this.volumetricFog?.dispose();for(const e of this.ownedPmremResults)e.dispose();this.ownedPmremResults.clear(),this.renderer.destroy(),this.fallbackMaterial.dispose()}}export function inspectWebGpuScene(e,t,i){const n={meshes:0,skinnedMeshes:0,instancedMeshes:0,batchedMeshes:0,sprites:0,points:0,lines:0,missingPositionGeometry:0,transparentMaterials:0,shaderMaterialsReplaced:0,materialsReplaced:0,unsupportedCamera:ExperimentalWebGpuBackend.supportsCamera(t)?0:1};return e.traverse(e=>{if(e.isSprite)return void n.sprites++;if(e.isPoints)return void n.points++;if(e.isLine)return void n.lines++;if(!e.isMesh)return;const t=e;t.isBatchedMesh?n.batchedMeshes++:t.isInstancedMesh?n.instancedMeshes++:t.isSkinnedMesh?n.skinnedMeshes++:n.meshes++,null==t.geometry?.getAttribute("position")&&n.missingPositionGeometry++;const r=Array.isArray(t.material)?t.material:[t.material];for(const e of r){if(null==e)continue;e.transparent&&n.transparentMaterials++;const t=!0===e.isWGSLShaderMaterial||!0===e.isLineMaterial,r=!t&&!0===i?.canResolve(e);t||r||(n.materialsReplaced++,e.isShaderMaterial&&n.shaderMaterialsReplaced++)}}),n}export function formatWebGpuSceneCapabilitySummary(e){return`[Hology WebGPU experiment] ${JSON.stringify(e)}`}const u="\nstruct VertexInput {\n @location(0) position: vec3<f32>,\n @location(1) normal: vec3<f32>,\n @location(2) uv: vec2<f32>,\n @location(3) color: vec4<f32>,\n#ifdef USE_SKINNING\n @location(4) skinIndex: vec4<u32>,\n @location(5) skinWeight: vec4<f32>,\n#endif\n @builtin(instance_index) instanceIndex: u32,\n};\n\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) worldPosition: vec3<f32>,\n @location(1) worldNormal: vec3<f32>,\n @location(2) color: vec4<f32>,\n};\n\n@vertex\nfn vsMain(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n#ifdef USE_SKINNING\n let localPosition = rendererSkinPosition(input.position, input.skinIndex, input.skinWeight);\n let localNormal = rendererSkinNormal(input.normal, input.skinIndex, input.skinWeight);\n#else\n let localPosition = input.position;\n let localNormal = input.normal;\n#endif\n let world = rendererWorldPosition(localPosition, input.instanceIndex);\n output.position = rendererClipPosition(localPosition, input.instanceIndex);\n output.worldPosition = world.xyz;\n output.worldNormal = rendererWorldNormal(localNormal, input.instanceIndex);\n output.color = input.color * rendererObjectColor(input.instanceIndex);\n return output;\n}\n",p="\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) worldPosition: vec3<f32>,\n @location(1) worldNormal: vec3<f32>,\n @location(2) color: vec4<f32>,\n};\n\nfn distanceAttenuation(distanceToLight: f32, cutoffDistance: f32, decayExponent: f32) -> f32 {\n var distanceFalloff = 1.0 / max(pow(distanceToLight, decayExponent), 0.01);\n if (cutoffDistance > 0.0) {\n let ratio = distanceToLight / cutoffDistance;\n let cutoff = clamp(1.0 - ratio * ratio * ratio * ratio, 0.0, 1.0);\n distanceFalloff *= cutoff * cutoff;\n }\n return distanceFalloff;\n}\n\n@fragment\nfn fsMain(input: VertexOutput) -> @location(0) vec4<f32> {\n let normal = normalize(input.worldNormal);\n let normalTint = normal * 0.5 + vec3<f32>(0.5);\n let base = mix(vec3<f32>(0.52, 0.58, 0.64), normalTint, 0.22) * input.color.rgb;\n var lighting = rendererIndirectDiffuse(normal) + vec3<f32>(0.08);\n var directionalLighting = vec3<f32>(0.0);\n let directionalCount = rendererDirectionalLightCount();\n\n for (var index: u32 = 0u; index < directionalCount; index = index + 1u) {\n let lightDirection = rendererDirectionalLightDirectionForLight(index);\n let shadow = rendererSampleDirectionalShadowForLightWithNormal(index, input.worldPosition, normal);\n directionalLighting += rendererDirectionalLightColorForLight(index) * max(dot(normal, lightDirection), 0.0) * shadow;\n }\n if (directionalCount > 0u) {\n lighting += directionalLighting / f32(directionalCount);\n }\n\n for (var index: u32 = 0u; index < rendererPointLightCount(); index = index + 1u) {\n let lightVector = rendererPointLightPositionForLight(index) - input.worldPosition;\n let distanceToLight = length(lightVector);\n let lightDirection = normalize(lightVector);\n let shadow = rendererSamplePointShadowForLightWithNormal(index, input.worldPosition, normal);\n lighting += rendererPointLightColorForLight(index) * max(dot(normal, lightDirection), 0.0) * distanceAttenuation(distanceToLight, rendererPointLightRangeForLight(index), rendererPointLightDecayForLight(index)) * shadow;\n }\n\n for (var index: u32 = 0u; index < rendererSpotLightCount(); index = index + 1u) {\n let lightVector = rendererSpotLightPositionForLight(index) - input.worldPosition;\n let distanceToLight = length(lightVector);\n let lightDirection = normalize(lightVector);\n let angleCos = dot(-lightDirection, rendererSpotLightDirectionForLight(index));\n let cone = smoothstep(rendererSpotLightConeCosForLight(index), rendererSpotLightPenumbraCosForLight(index), angleCos);\n let shadow = rendererSampleSpotShadowForLightWithNormal(index, input.worldPosition, normal);\n lighting += rendererSpotLightColorForLight(index) * max(dot(normal, lightDirection), 0.0) * cone * distanceAttenuation(distanceToLight, rendererSpotLightRangeForLight(index), rendererSpotLightDecayForLight(index)) * shadow;\n }\n\n return vec4<f32>(base * max(lighting, vec3<f32>(0.08)), input.color.a);\n}\n";/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
package/dist/rendering.d.ts
CHANGED
|
@@ -12,11 +12,13 @@ import { Cluster } from "./rendering/cluster-generator.js";
|
|
|
12
12
|
import { LightVolume } from "./rendering/light-probes/light-volume-capture.js";
|
|
13
13
|
import { type PostProcessMaterialSource } from "./shader/post-process-shader.js";
|
|
14
14
|
import { type UpscalingOptions } from "./rendering/upscaling-pass.js";
|
|
15
|
-
import { type AntiAliasingMode, type ExperimentalWebGpuPostProcessingOptions, type GTAOOptions, type GTAOQuality, type MotionBlurOptions, type MotionBlurOverride, type MotionBlurOverrideHandle, type RendererFrameProfile, type ExperimentalWebGpuRenderGraphExtension, type VolumetricFogOptions, type VolumetricFogQuality } from './rendering/webgpu-experimental-backend.js';
|
|
15
|
+
import { type AntiAliasingMode, type ExperimentalWebGpuPostProcessingOptions, type GTAOOptions, type GTAOQuality, type MotionBlurOptions, type MotionBlurOverride, type MotionBlurOverrideHandle, type RendererFrameProfile, type ExperimentalWebGpuRenderGraphExtension, type WebGpuPresentationExtension, type VolumetricFogOptions, type VolumetricFogQuality } from './rendering/webgpu-experimental-backend.js';
|
|
16
16
|
import type { WorldEnvironmentState } from './rendering/environment.js';
|
|
17
17
|
import type { VfxBackendPreference } from './effects/vfx/gpu-sprite-compiler.js';
|
|
18
|
+
import { type GraphicsRuntime } from './rendering/graphics-runtime.js';
|
|
19
|
+
export { GraphicsRuntime } from './rendering/graphics-runtime.js';
|
|
18
20
|
export type { UpscalingOptions } from "./rendering/upscaling-pass.js";
|
|
19
|
-
export type { AntiAliasingMode, ExperimentalWebGpuPostProcessingOptions, ExperimentalWebGpuRenderGraphExtension } from './rendering/webgpu-experimental-backend.js';
|
|
21
|
+
export type { AntiAliasingMode, ExperimentalWebGpuPostProcessingOptions, ExperimentalWebGpuRenderGraphExtension, WebGpuPresentationExtension, WebGpuPresentationExtensionContext } from './rendering/webgpu-experimental-backend.js';
|
|
20
22
|
export type { GTAOOptions, GTAOQuality, MotionBlurOptions, MotionBlurOverride, MotionBlurOverrideHandle, VolumetricFogOptions, VolumetricFogQuality } from './rendering/webgpu-experimental-backend.js';
|
|
21
23
|
/** Renderer requested by a view. `webgpu-experimental` is a deprecated alias. */
|
|
22
24
|
export type RenderingBackend = 'webgl' | 'webgpu' | 'webgpu-experimental';
|
|
@@ -64,6 +66,8 @@ export type DynamicResolutionOptions = {
|
|
|
64
66
|
increaseCooldown?: number;
|
|
65
67
|
};
|
|
66
68
|
export type RenderingViewOptions = {
|
|
69
|
+
/** Shared device/backend/cache owner. Views in one application should use the same instance. */
|
|
70
|
+
graphicsRuntime?: GraphicsRuntime;
|
|
67
71
|
backend?: RenderingBackend;
|
|
68
72
|
enableOutlines?: boolean;
|
|
69
73
|
enableXR?: boolean;
|
|
@@ -118,10 +122,10 @@ export type RenderingViewOptions = {
|
|
|
118
122
|
export declare class RenderingView {
|
|
119
123
|
container: HTMLElement;
|
|
120
124
|
readonly options: RenderingViewOptions;
|
|
121
|
-
private static readonly webGpuBackendPool;
|
|
122
125
|
private static activeView;
|
|
123
126
|
private static experimentalWebGpuPresentationView;
|
|
124
127
|
private backendHandle;
|
|
128
|
+
private readonly graphicsRuntime;
|
|
125
129
|
private webGlBackend;
|
|
126
130
|
private experimentalWebGpuBackend;
|
|
127
131
|
private experimentalWebGpuBackendPoolKey;
|
|
@@ -135,6 +139,7 @@ export declare class RenderingView {
|
|
|
135
139
|
private selectedOutlineObjects;
|
|
136
140
|
private experimentalStaticDrawSetRegistrations;
|
|
137
141
|
private experimentalWebGpuRenderGraphExtensions;
|
|
142
|
+
private webGpuPresentationExtensions;
|
|
138
143
|
private worldEnvironment;
|
|
139
144
|
get activeBackend(): ActiveRenderingBackend | null;
|
|
140
145
|
get canvas(): HTMLCanvasElement | null;
|
|
@@ -158,8 +163,10 @@ export declare class RenderingView {
|
|
|
158
163
|
/** @deprecated Use registerWebGpuRenderGraphExtension. */
|
|
159
164
|
registerExperimentalWebGpuRenderGraphExtension(extension: ExperimentalWebGpuRenderGraphExtension): () => void;
|
|
160
165
|
private isIntersecting;
|
|
166
|
+
private stopIntersectionObservation;
|
|
161
167
|
private needsPausedRender;
|
|
162
168
|
private wakePausedRenderer;
|
|
169
|
+
private resumeAnimationLoop;
|
|
163
170
|
camera: THREE.Camera;
|
|
164
171
|
windowVisible: boolean;
|
|
165
172
|
/** @deprecated WebGL-specific escape hatch. Use webGlRenderer and backend-neutral RenderingView APIs. */
|
|
@@ -299,6 +306,8 @@ export declare class RenderingView {
|
|
|
299
306
|
private updateRenderedOcclusionClusters;
|
|
300
307
|
private fixStatsStyle;
|
|
301
308
|
setEnableOutlines(enabled: boolean): void;
|
|
309
|
+
registerWebGpuPresentationExtension(extension: WebGpuPresentationExtension): () => void;
|
|
310
|
+
get sharedGraphicsRuntime(): GraphicsRuntime;
|
|
302
311
|
setCamera(camera: Camera): void;
|
|
303
312
|
setSelectedObjects(obj: Object3D[]): void;
|
|
304
313
|
setExperimentalWebGpuDynamicBatchingEnabled(enabled: boolean): void;
|
|
@@ -334,6 +343,12 @@ export declare class RenderingView {
|
|
|
334
343
|
private createGRenderTarget;
|
|
335
344
|
private setupEventListeners;
|
|
336
345
|
stop(dispose?: boolean): void;
|
|
346
|
+
/** Stops frame scheduling without releasing the renderer, GPU device, or presentation surface. */
|
|
347
|
+
stopAnimationLoop(): void;
|
|
348
|
+
/** Detach this view from presentation without releasing its backend resources. */
|
|
349
|
+
suspendPresentation(): void;
|
|
350
|
+
/** Make this view authoritative for its surface and restore its backend-local state. */
|
|
351
|
+
activatePresentation(): void;
|
|
337
352
|
private onLoopCallbacks;
|
|
338
353
|
onLoop(onFrame: (deltaTime: number) => any): void;
|
|
339
354
|
removeOnLoop(onFrame: (deltaTime: number) => any): void;
|