@hology/core 0.0.246 → 0.0.248
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/gameplay/actors/actor.js +1 -1
- package/dist/gameplay/actors/builtin/components/index.d.ts +2 -0
- package/dist/gameplay/actors/builtin/components/index.js +1 -1
- package/dist/gameplay/actors/builtin/components/spline-component.d.ts +65 -0
- package/dist/gameplay/actors/builtin/components/spline-component.js +4 -0
- package/dist/gameplay/actors/builtin/components/water-post-process-settings.d.ts +24 -0
- package/dist/gameplay/actors/builtin/components/water-post-process-settings.js +4 -0
- package/dist/gameplay/actors/builtin/index.d.ts +6 -0
- package/dist/gameplay/actors/builtin/index.js +1 -1
- package/dist/gameplay/actors/builtin/navmesh-actor.js +1 -1
- package/dist/gameplay/actors/builtin/post-process-volume-actor.d.ts +1 -2
- package/dist/gameplay/actors/builtin/spline-actor.d.ts +6 -0
- package/dist/gameplay/actors/builtin/spline-actor.js +4 -0
- package/dist/gameplay/actors/builtin/water-spline-volume-actor.d.ts +23 -0
- package/dist/gameplay/actors/builtin/water-spline-volume-actor.js +4 -0
- package/dist/gameplay/actors/builtin/water-volume-actor.d.ts +19 -0
- package/dist/gameplay/actors/builtin/water-volume-actor.js +4 -0
- package/dist/gameplay/actors/camera/camera-component.js +1 -1
- package/dist/gameplay/actors/camera/third-person-camera-component.js +1 -1
- package/dist/gameplay/actors/component.d.ts +0 -1
- package/dist/gameplay/actors/component.js +1 -1
- package/dist/gameplay/actors/index.d.ts +4 -0
- package/dist/gameplay/actors/index.js +1 -1
- package/dist/gameplay/actors/internal/component-init.d.ts +1 -1
- package/dist/gameplay/actors/internal/component-init.js +1 -1
- package/dist/gameplay/ai/build-tile.d.ts +3 -1
- package/dist/gameplay/ai/build-tile.js +1 -1
- package/dist/gameplay/ai/dynamic-tiled-navmesh.d.ts +4 -21
- package/dist/gameplay/ai/dynamic-tiled-navmesh.js +1 -1
- package/dist/gameplay/ai/dynamic-tiled-navmesh.worker.d.ts +10 -1
- package/dist/gameplay/ai/dynamic-tiled-navmesh.worker.js +1 -1
- package/dist/gameplay/ai/index.d.ts +1 -0
- package/dist/gameplay/ai/index.js +1 -1
- package/dist/gameplay/ai/navigation-areas.d.ts +22 -0
- package/dist/gameplay/ai/navigation-areas.js +4 -0
- package/dist/gameplay/ai/navigation.d.ts +28 -4
- package/dist/gameplay/ai/navigation.js +1 -1
- package/dist/gameplay/inject.d.ts +3 -0
- package/dist/gameplay/inject.js +1 -1
- package/dist/gameplay/services/actor-update-registry.d.ts +13 -0
- package/dist/gameplay/services/actor-update-registry.js +4 -0
- package/dist/gameplay/services/render.d.ts +1 -0
- package/dist/gameplay/services/render.js +1 -1
- package/dist/gameplay/services/world.js +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -1
- package/dist/rendering/webgpu-experimental-backend.js +1 -1
- package/dist/rendering.d.ts +8 -0
- package/dist/rendering.js +1 -1
- package/dist/scene/materializer.js +1 -1
- package/dist/shader/graph/compiler.js +1 -1
- package/dist/shader/graph/model.d.ts +1 -1
- package/dist/shader/graph/registry.js +1 -1
- package/dist/shader/parameter.d.ts +3 -0
- package/dist/shader/post-process-shader.js +1 -1
- package/dist/shader-nodes/depth.d.ts +3 -1
- package/dist/shader-nodes/depth.js +1 -1
- package/dist/shader-nodes/index.d.ts +1 -0
- package/dist/shader-nodes/index.js +1 -1
- package/dist/shader-nodes/water-volume.d.ts +9 -0
- package/dist/shader-nodes/water-volume.js +4 -0
- package/dist/shader-nodes/water.d.ts +12 -0
- package/dist/shader-nodes/water.js +4 -0
- package/dist/test/build-tile.test.js +1 -1
- package/dist/test/materializer-dynamic-transform.test.js +1 -1
- package/dist/test/navmesh-worker-pool.test.js +1 -1
- package/dist/test/node-material-webgpu-resolver.test.js +1 -1
- package/dist/test/post-process-effect.test.js +1 -1
- package/dist/test/shader-graph.test.js +1 -1
- package/dist/test/spline-component.test.d.ts +2 -0
- package/dist/test/spline-component.test.js +4 -0
- package/dist/test/view-controller-updates.test.d.ts +2 -0
- package/dist/test/view-controller-updates.test.js +4 -0
- package/dist/test/water-spline-volume-actor.test.d.ts +2 -0
- package/dist/test/water-spline-volume-actor.test.js +4 -0
- package/dist/test/worker-task-executor.test.d.ts +2 -0
- package/dist/test/worker-task-executor.test.js +4 -0
- package/dist/worker/index.d.ts +11 -2
- package/dist/worker/index.js +1 -1
- package/dist/worker/task-executor.d.ts +22 -0
- package/dist/worker/task-executor.js +4 -0
- package/dist/worker/task-worker.d.ts +3 -0
- package/dist/worker/task-worker.js +4 -0
- package/dist/worker/task.d.ts +39 -0
- package/dist/worker/task.js +4 -0
- package/package.json +3 -3
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{NavMeshCreateParams as e,Raw as t,RecastBuildContext as i,TriangleAreasArray as
|
|
1
|
+
import{NavMeshCreateParams as e,Raw as t,RecastBuildContext as i,TriangleAreasArray as o,TrianglesArray as r,VerticesArray as l,allocCompactHeightfield as a,allocContourSet as s,allocHeightfield as n,allocPolyMesh as c,allocPolyMeshDetail as d,buildCompactHeightfield as h,buildContours as g,buildDistanceField as m,buildPolyMesh as u,buildPolyMeshDetail as f,buildRegions as b,calcGridSize as S,cloneRcConfig as w,createHeightfield as R,createNavMeshData as p,createRcConfig as C,erodeWalkableArea as M,filterLedgeSpans as _,filterLowHangingWalkableObstacles as x,filterWalkableLowHeightSpans as y,freeCompactHeightfield as A,freeContourSet as k,freeHeightfield as v,FloatArray as z,markConvexPolyArea as E,markWalkableTriangles as T,rasterizeTriangles as B,vec3 as H}from"@recast-navigation/core";import{dtIlog2 as O,dtNextPow2 as P}from"@recast-navigation/generators";import*as G from"three";import{NavigationArea as L,NavigationTraversalFlag as D}from"./navigation-areas.js";export const buildConfig=({recastConfig:e,navMeshBounds:[t,i]})=>{const o=C(e),r=S(t,i,o.cs);o.width=r.width,o.height=r.height,o.minRegionArea=o.minRegionArea*o.minRegionArea,o.mergeRegionArea=o.mergeRegionArea*o.mergeRegionArea,o.tileSize=Math.floor(o.tileSize),o.borderSize=o.walkableRadius+3,o.width=o.tileSize+2*o.borderSize,o.height=o.tileSize+2*o.borderSize,o.detailSampleDist=o.detailSampleDist<.9?0:o.cs*o.detailSampleDist,o.detailSampleMaxError=o.ch*o.detailSampleMaxError;const l=Math.floor(o.tileSize),a=Math.floor((r.width+l-1)/l),s=Math.floor((r.height+l-1)/l),n=o.tileSize*o.cs,c=H.fromArray(t);let d=Math.min(Math.floor(O(P(a*s))),14);d>14&&(d=14);const h=22-d;return{config:o,orig:c,tileBits:d,polyBits:h,maxTiles:1<<d,maxPolysPerTile:1<<h,tcs:n,tileWidth:a,tileHeight:s}};export const buildTile=({positions:S,indices:C,navMeshBounds:H,recastConfig:O,tileX:P,tileY:F,tileBoundsMin:W,tileBoundsMax:$,keepIntermediates:V,areaModifiers:I=[]})=>{const K=new i,X=S,Y=S.length/3,j=new l;j.copy(X);const N=C,U=C.length/3;(new r).copy(N);const q={tileX:P,tileY:F},J=()=>{V||(q.compactHeightfield&&A(q.compactHeightfield),q.heightfield&&v(q.heightfield),q.contourSet&&k(q.contourSet))},Q=e=>(K.log(t.Module.RC_LOG_ERROR,e),J(),{success:!1,error:e,tileIntermediates:q,buildContext:K}),{config:Z}=buildConfig({recastConfig:O,navMeshBounds:H}),ee=w(Z),te=[...W],ie=[...$];te[0]-=ee.borderSize*ee.cs,te[2]-=ee.borderSize*ee.cs,ie[0]+=ee.borderSize*ee.cs,ie[2]+=ee.borderSize*ee.cs,ee.set_bmin(0,te[0]),ee.set_bmin(1,te[1]),ee.set_bmin(2,te[2]),ee.set_bmax(0,ie[0]),ee.set_bmax(1,ie[1]),ee.set_bmax(2,ie[2]),K.resetTimers(),K.startTimer(t.Module.RC_TIMER_TOTAL),K.log(t.Module.RC_LOG_PROGRESS,`Building tile at x: ${P}, y: ${F}`),K.log(t.Module.RC_LOG_PROGRESS,` - ${Z.width} x ${Z.height} cells`),K.log(t.Module.RC_LOG_PROGRESS,` - ${Y/1e3}fK verts, ${U/1e3}K tris`);const oe=n();if(q.heightfield=oe,!R(K,oe,ee.width,ee.height,te,ie,ee.cs,ee.ch))return Q("Could not create heightfield");const re=((e,t,i)=>{const o=[],r=new G.Vector3,l=new G.Vector3,a=new G.Vector3,s=new G.Triangle;for(let n=0;n<t.length;n+=3){const c=t[n],d=t[n+1],h=t[n+2];r.fromArray(e,3*c),l.fromArray(e,3*d),a.fromArray(e,3*h),s.set(r,l,a),s.intersectsBox(i)&&o.push(c,d,h)}return o})(S,C,new G.Box3(new G.Vector3(te[0],te[1],te[2]),new G.Vector3(ie[0],ie[1],ie[2]))),le=re.length/3,ae=new r;ae.copy(re);const se=new o;se.resize(le),T(K,ee.walkableSlopeAngle,j,Y,ae,le,se);const ne=B(K,j,Y,ae,se,le,oe,ee.walkableClimb);if(se.destroy(),!ne)return Q("Could not rasterize triangles");x(K,ee.walkableClimb,oe),_(K,ee.walkableHeight,ee.walkableClimb,oe),y(K,ee.walkableHeight,oe);const ce=a();if(q.compactHeightfield=ce,!h(K,ee.walkableHeight,ee.walkableClimb,oe,ce))return Q("Could not build compact heightfield");if(V||(v(q.heightfield),q.heightfield=void 0),!M(K,ee.walkableRadius,ce))return Q("Could not erode walkable area");for(const e of I){if(e.vertices.length<3)continue;const t=new z;t.copy(e.vertices.flat()),E(K,t,e.vertices.length,e.minHeight,e.maxHeight,e.area,ce),t.destroy()}if(!m(K,ce))return Q("Failed to build distance field");if(!b(K,ce,ee.borderSize,ee.minRegionArea,ee.mergeRegionArea))return Q("Failed to build regions");const de=s();if(q.contourSet=de,!g(K,ce,ee.maxSimplificationError,ee.maxEdgeLen,de,t.Module.RC_CONTOUR_TESS_WALL_EDGES))return Q("Failed to create contours");const he=c();if(!u(K,de,ee.maxVertsPerPoly,he))return Q("Failed to triangulate contours");if(0===he.npolys())return J(),{success:!0,tileIntermediates:q,buildContext:K};const ge=d();if(!f(K,he,ce,ee.detailSampleDist,ee.detailSampleMaxError,ge))return Q("Failed to build detail mesh");V||(A(ce),q.compactHeightfield=void 0,k(de),q.contourSet=void 0);for(let e=0;e<he.npolys();e++){he.areas(e)==t.Recast.WALKABLE_AREA&&he.setAreas(e,0);const i=he.areas(e);i==L.Ground&&he.setFlags(e,D.Walk),i==L.Water&&he.setFlags(e,D.Swim)}const me=new e;me.setPolyMeshCreateParams(he),me.setPolyMeshDetailCreateParams(ge),me.setWalkableHeight(ee.walkableHeight),me.setWalkableRadius(ee.walkableRadius),me.setWalkableClimb(ee.walkableClimb),me.setCellSize(ee.cs),me.setCellHeight(ee.ch),me.setBuildBvTree(!0),me.setTileX(P),me.setTileY(F);const ue=p(me);return ue.success?(K.log(t.Module.RC_LOG_PROGRESS,`>> Polymesh: ${he.nverts()} vertices ${he.npolys()} polygons`),J(),{success:!0,data:ue.navMeshData,tileIntermediates:q,buildContext:K}):Q("Failed to create Detour navmesh data")};/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { RecastConfig, Vector3Tuple } from 'recast-navigation';
|
|
2
2
|
import { NavMesh } from 'recast-navigation';
|
|
3
|
-
import
|
|
4
|
-
import type { BuildTileMeshProps } from './build-tile.js';
|
|
3
|
+
import * as THREE from 'three';
|
|
5
4
|
import { Subject } from 'rxjs';
|
|
5
|
+
import type { NavigationAreaModifier } from './navigation-areas.js';
|
|
6
6
|
export type DynamicTiledNavMeshProps = {
|
|
7
7
|
navMeshBounds: THREE.Box3;
|
|
8
8
|
recastConfig: Partial<RecastConfig>;
|
|
@@ -11,23 +11,6 @@ export type DynamicTiledNavMeshProps = {
|
|
|
11
11
|
cacheId: string;
|
|
12
12
|
};
|
|
13
13
|
export declare const MAX_NAV_MESH_WORKERS = 4;
|
|
14
|
-
type NavMeshWorkerResult = {
|
|
15
|
-
tileX: number;
|
|
16
|
-
tileY: number;
|
|
17
|
-
navMeshData?: Uint8Array;
|
|
18
|
-
error?: string;
|
|
19
|
-
};
|
|
20
|
-
export declare class NavMeshWorkerPool {
|
|
21
|
-
private workerFactory;
|
|
22
|
-
private slots;
|
|
23
|
-
private queue;
|
|
24
|
-
private workerLimit;
|
|
25
|
-
constructor(workerFactory?: () => Worker);
|
|
26
|
-
schedule(owner: object, data: BuildTileMeshProps, transfer: Transferable[] | undefined, onResult: (event: MessageEvent<NavMeshWorkerResult>) => void, requestedWorkers: number): void;
|
|
27
|
-
cancel(owner: object): void;
|
|
28
|
-
private drain;
|
|
29
|
-
private createSlot;
|
|
30
|
-
}
|
|
31
14
|
export declare const getTileBounds: (navMeshBoundsMin: THREE.Vector3, navMeshBoundsMax: THREE.Vector3, tileX: number, tileY: number, tileWorldSize: number) => [min: Vector3Tuple, max: Vector3Tuple];
|
|
32
15
|
export declare class DynamicTiledNavMesh {
|
|
33
16
|
navMesh: NavMesh;
|
|
@@ -43,13 +26,13 @@ export declare class DynamicTiledNavMesh {
|
|
|
43
26
|
recastConfig: RecastConfig;
|
|
44
27
|
private cacheId;
|
|
45
28
|
private workerLimit;
|
|
29
|
+
private abortController;
|
|
46
30
|
constructor(props: DynamicTiledNavMeshProps);
|
|
47
31
|
private onResult;
|
|
48
|
-
buildTile(positions: Float32Array, indices: Uint32Array, [tileX, tileY]: [x: number, y: number], useTileCache?: boolean): Promise<unknown>;
|
|
32
|
+
buildTile(positions: Float32Array, indices: Uint32Array, [tileX, tileY]: [x: number, y: number], useTileCache?: boolean, areaModifiers?: NavigationAreaModifier[]): Promise<unknown>;
|
|
49
33
|
buildAllTiles(positions: Float32Array, indices: Uint32Array): void;
|
|
50
34
|
getTileForWorldPosition(worldPosition: THREE.Vector3): [x: number, y: number];
|
|
51
35
|
getTilesForBounds(bounds: THREE.Box3): [x: number, y: number][];
|
|
52
36
|
destroy(): void;
|
|
53
37
|
}
|
|
54
|
-
export {};
|
|
55
38
|
//# sourceMappingURL=dynamic-tiled-navmesh.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{NavMesh as t,NavMeshParams as e,Raw as
|
|
1
|
+
import{NavMesh as t,NavMeshParams as e,Raw as i,UnsignedCharArray as s,recastConfigDefaults as o,statusToReadableString as r}from"recast-navigation";import*as n from"three";import{buildConfig as a,buildTile as h}from"./build-tile.js";import{Subject as l}from"rxjs";import{executeTask as d,hasWorkerBuilder as u}from"../../worker/index.js";import{hasSharedArrayBufferSupport as c}from"../../utils/buffer.js";export const MAX_NAV_MESH_WORKERS=4;export const getTileBounds=(t,e,i,s,o)=>[[t.x+i*o,t.y,t.z+s*o],[t.x+(i+1)*o,e.y,t.z+(s+1)*o]];export class DynamicTiledNavMesh{constructor(i){this.navMeshVersion=0,this.onNavMeshUpdate=new l,this.abortController=new AbortController;const s=i.navMeshBounds.min,r=i.navMeshBounds.max,n=[s.toArray(),r.toArray()],h=i.navMeshBounds.min;this.navMeshBoundsMin=s,this.navMeshBoundsMax=r,this.navMeshBounds=n,this.navMeshOrigin=h,this.cacheId=i.cacheId,this.workerLimit=Math.min(4,Math.max(0,i.workers));const d={...o,...i.recastConfig};this.recastConfig=d;const u=new t,{tileWidth:c,tileHeight:M,tcs:m,maxPolysPerTile:v}=a({recastConfig:d,navMeshBounds:n});this.tileWidth=c,this.tileHeight=M,this.tcs=m;const f=e.create({orig:h,tileWidth:d.tileSize*d.cs,tileHeight:d.tileSize*d.cs,maxTiles:i.maxTiles,maxPolys:v});u.initTiled(f),this.navMesh=u}onResult(t){if(t.data.error)return void console.error(t.data.error);const{tileX:e,tileY:o,navMeshData:n}=t.data;if(this.navMesh.removeTile(this.navMesh.getTileRefAt(e,o,0)),null==n)return this.navMeshVersion++,void this.onNavMeshUpdate.next([this.navMeshVersion,[e,o]]);const a=new s;a.copy(n);const h=this.navMesh.addTile(a,i.Module.DT_TILE_FREE_DATA,0);i.Detour.statusFailed(h.status)&&(console.error(i.Module.RC_LOG_WARNING,`Failed to add tile to nav mesh\n\ttx: ${e}, ty: ${o},status: ${r(h.status)} (${h.status})`),a.destroy()),this.navMeshVersion++,this.onNavMeshUpdate.next([this.navMeshVersion,[e,o]])}buildTile(t,e,[i,s],o=!1,r=[]){const n=c&&t.buffer instanceof window.SharedArrayBuffer,a=n?t:new Float32Array(t),l=n?e:new Uint32Array(e),[v,f]=getTileBounds(this.navMeshBoundsMin,this.navMeshBoundsMax,i,s,this.tcs),g={tileX:i,tileY:s,tileBoundsMin:v,tileBoundsMax:f,recastConfig:this.recastConfig,navMeshBounds:this.navMeshBounds,keepIntermediates:!1,positions:a,indices:l,areaModifiers:r};return this.workerLimit>0&&u()?d("navmesh.buildTile",g,{transfer:n?void 0:[a.buffer,l.buffer],signal:this.abortController.signal,priority:"background"}).then(t=>this.onResult({data:t})).catch(t=>{"AbortError"!==t?.name&&console.error("Navmesh worker failed",t)}):new Promise(t=>{requestIdleCallback(()=>{const e=this.cacheId+JSON.stringify({tileX:g.tileX,tileY:g.tileY,areaModifiers:g.areaModifiers});if(o){const i=localStorage.getItem(e);if(null!=i){const e=m(i);return this.onResult({data:{tileX:g.tileX,tileY:g.tileY,navMeshData:e}}),void t({})}}else localStorage.removeItem(e);const i=h(g);if(!i.success)return console.error("error"in i?i.error:"Unknown navmesh tile build error"),void t(i);if(!i.data)return localStorage.removeItem(e),this.onResult({data:{tileX:g.tileX,tileY:g.tileY}}),void t(i);const s=i.data.toTypedArray();localStorage.setItem(e,M(s)),this.onResult({data:{tileX:g.tileX,tileY:g.tileY,navMeshData:s}}),t(i)})})}buildAllTiles(t,e){const{tileWidth:i,tileHeight:s}=this;for(let o=0;o<s;o++)for(let s=0;s<i;s++)this.buildTile(t,e,[s,o])}getTileForWorldPosition(t){return[Math.floor((t.x-this.navMeshBoundsMin.x)/this.tcs),Math.floor((t.z-this.navMeshBoundsMin.z)/this.tcs)]}getTilesForBounds(t){if(!t.intersectsBox(new n.Box3(this.navMeshBoundsMin,this.navMeshBoundsMax)))return[];const e=this.getTileForWorldPosition(t.min),i=this.getTileForWorldPosition(t.max);e[0]=Math.max(0,Math.min(this.tileWidth-1,e[0])),e[1]=Math.max(0,Math.min(this.tileHeight-1,e[1])),i[0]=Math.max(0,Math.min(this.tileWidth-1,i[0])),i[1]=Math.max(0,Math.min(this.tileHeight-1,i[1]));const s=[];for(let t=e[1];t<=i[1];t++)for(let o=e[0];o<=i[0];o++)s.push([o,t]);return s}destroy(){this.abortController.abort(),this.navMesh.destroy(),this.onNavMeshUpdate.complete()}}const M=function(t){return btoa(String.fromCharCode(...t))},m=function(t){if(!t)return null;const e=atob(t),i=e.length,s=new Uint8Array(i);for(let t=0;t<i;t++)s[t]=e.charCodeAt(t);return s};/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import type { BuildTileMeshProps } from './build-tile.js';
|
|
2
|
+
import type { TaskHandler } from '../../worker/task.js';
|
|
3
|
+
export type NavMeshWorkerResult = {
|
|
4
|
+
tileX: number;
|
|
5
|
+
tileY: number;
|
|
6
|
+
navMeshData?: Uint8Array;
|
|
7
|
+
};
|
|
8
|
+
export declare const navMeshTaskHandlers: {
|
|
9
|
+
'navmesh.buildTile': TaskHandler<BuildTileMeshProps, NavMeshWorkerResult>;
|
|
10
|
+
};
|
|
2
11
|
//# sourceMappingURL=dynamic-tiled-navmesh.worker.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{init as e}from"recast-navigation";import{buildTile as t}from"./build-tile.js";let
|
|
1
|
+
import{init as e}from"recast-navigation";import{buildTile as t}from"./build-tile.js";let r;export const navMeshTaskHandlers={"navmesh.buildTile":async a=>{r??(r=e()),await r;const i=t(a);if(!i.success){const e="error"in i?i.error:"Unknown navmesh tile build error";throw new Error(`NavMesh tile build failed (${a.tileX}, ${a.tileY}): ${e}`)}if(!i.data)return{payload:{tileX:a.tileX,tileY:a.tileY}};const l=i.data.toTypedArray();return i.data.destroy(),{payload:{tileX:a.tileX,tileY:a.tileY,navMeshData:l},transfer:[l.buffer]}}};/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export*from"./navigation.js";export*from"./behavior-tree/bt.js";export*from"./behavior-tree/move.js";/*
|
|
1
|
+
export*from"./navigation.js";export*from"./navigation-areas.js";export*from"./behavior-tree/bt.js";export*from"./behavior-tree/move.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,22 @@
|
|
|
1
|
+
import type { Vector3Tuple } from 'recast-navigation';
|
|
2
|
+
export declare enum NavigationArea {
|
|
3
|
+
Ground = 0,
|
|
4
|
+
Water = 1
|
|
5
|
+
}
|
|
6
|
+
export declare enum NavigationTraversalFlag {
|
|
7
|
+
Walk = 1,
|
|
8
|
+
Swim = 2
|
|
9
|
+
}
|
|
10
|
+
/** Worker-safe description of a convex navigation-area prism. */
|
|
11
|
+
export type NavigationAreaModifier = {
|
|
12
|
+
area: NavigationArea;
|
|
13
|
+
vertices: Vector3Tuple[];
|
|
14
|
+
minHeight: number;
|
|
15
|
+
maxHeight: number;
|
|
16
|
+
};
|
|
17
|
+
/** Internal extension point for actors which classify generated navigation spans. */
|
|
18
|
+
export interface NavigationAreaContributor {
|
|
19
|
+
getNavigationAreaModifiers(): NavigationAreaModifier[];
|
|
20
|
+
}
|
|
21
|
+
export declare function isNavigationAreaContributor(value: unknown): value is NavigationAreaContributor;
|
|
22
|
+
//# sourceMappingURL=navigation-areas.d.ts.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export var NavigationArea;!function(a){a[a.Ground=0]="Ground",a[a.Water=1]="Water"}(NavigationArea||(NavigationArea={}));export var NavigationTraversalFlag;!function(a){a[a.Walk=1]="Walk",a[a.Swim=2]="Swim"}(NavigationTraversalFlag||(NavigationTraversalFlag={}));export function isNavigationAreaContributor(a){return"function"==typeof a?.getNavigationAreaModifiers}/*
|
|
2
|
+
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
|
+
* See the LICENSE.md file for details.
|
|
4
|
+
*/
|
|
@@ -1,14 +1,38 @@
|
|
|
1
1
|
import { Vector3 } from "three";
|
|
2
|
+
import { NavigationArea, NavigationTraversalFlag } from './navigation-areas.js';
|
|
3
|
+
export type NavigationQueryProfile = Readonly<{
|
|
4
|
+
name: string;
|
|
5
|
+
includeFlags: number;
|
|
6
|
+
excludeFlags: number;
|
|
7
|
+
areaCosts?: Readonly<Partial<Record<NavigationArea, number>>>;
|
|
8
|
+
}>;
|
|
9
|
+
export declare const NavigationQueryProfiles: Readonly<{
|
|
10
|
+
Walker: Readonly<{
|
|
11
|
+
name: "Walker";
|
|
12
|
+
includeFlags: NavigationTraversalFlag.Walk;
|
|
13
|
+
excludeFlags: NavigationTraversalFlag.Swim;
|
|
14
|
+
}>;
|
|
15
|
+
Swimmer: Readonly<{
|
|
16
|
+
name: "Swimmer";
|
|
17
|
+
includeFlags: number;
|
|
18
|
+
excludeFlags: 0;
|
|
19
|
+
areaCosts: {
|
|
20
|
+
1: number;
|
|
21
|
+
};
|
|
22
|
+
}>;
|
|
23
|
+
}>;
|
|
2
24
|
export declare class Navigation {
|
|
3
25
|
private navMeshActor;
|
|
4
26
|
private query?;
|
|
5
27
|
private world;
|
|
28
|
+
private filters;
|
|
6
29
|
constructor();
|
|
30
|
+
private getFilter;
|
|
7
31
|
private ensureQuery;
|
|
8
32
|
/**
|
|
9
33
|
* Finds a path from a start point to an end point.
|
|
10
34
|
*/
|
|
11
|
-
findPath(start: Vector3, end: Vector3): {
|
|
35
|
+
findPath(start: Vector3, end: Vector3, profile?: NavigationQueryProfile): {
|
|
12
36
|
success: boolean;
|
|
13
37
|
path: Vector3[];
|
|
14
38
|
};
|
|
@@ -16,12 +40,12 @@ export declare class Navigation {
|
|
|
16
40
|
* Finds the closest point on the navmesh to a given position.
|
|
17
41
|
* Returns null if it can't be found or if nav mesh has not been generated
|
|
18
42
|
*/
|
|
19
|
-
findClosestPoint(position: Vector3): Vector3 | null;
|
|
20
|
-
findRandomPoint(): {
|
|
43
|
+
findClosestPoint(position: Vector3, profile?: NavigationQueryProfile): Vector3 | null;
|
|
44
|
+
findRandomPoint(profile?: NavigationQueryProfile): {
|
|
21
45
|
success: boolean;
|
|
22
46
|
randomPoint: Vector3;
|
|
23
47
|
};
|
|
24
|
-
findRandomPointAroundCircle(position: Vector3, radius: number): {
|
|
48
|
+
findRandomPointAroundCircle(position: Vector3, radius: number, profile?: NavigationQueryProfile): {
|
|
25
49
|
success: boolean;
|
|
26
50
|
randomPoint: Vector3;
|
|
27
51
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as e,__metadata as t}from"tslib";import{NavMeshQuery as n}from"recast-navigation";import{Vector3 as
|
|
1
|
+
import{__decorate as e,__metadata as t}from"tslib";import{NavMeshQuery as n,QueryFilter as s}from"recast-navigation";import{Vector3 as r}from"three";import{inject as i,Service as o,World as l}from"../index.js";import a,{safeRecastInit as u}from"../actors/builtin/navmesh-actor.js";import{NavigationArea as c,NavigationTraversalFlag as h}from"./navigation-areas.js";export const NavigationQueryProfiles=Object.freeze({Walker:Object.freeze({name:"Walker",includeFlags:h.Walk,excludeFlags:h.Swim}),Swimmer:Object.freeze({name:"Swimmer",includeFlags:h.Walk|h.Swim,excludeFlags:0,areaCosts:{[c.Water]:2}})});let d=class{constructor(){this.world=i(l),this.filters=new Map,u().then(()=>{const e=this.world.actors.find(e=>e instanceof a);null!=e&&(this.navMeshActor=e,this.query=null),this.world.actorAdded.subscribe(e=>{e instanceof a&&(this.navMeshActor=e,this.query=null)}),this.world.actorRemoved.subscribe(e=>{e instanceof a&&e.id===this.navMeshActor?.id&&(this.navMeshActor=null,this.query=null)})})}getFilter(e){let t=this.filters.get(e);if(null!=t)return t;t=new s,t.includeFlags=e.includeFlags,t.excludeFlags=e.excludeFlags;for(const[n,s]of Object.entries(e.areaCosts??{}))null!=s&&t.setAreaCost(Number(n),s);return this.filters.set(e,t),t}ensureQuery(){null==this.query&&null!=this.navMeshActor&&null!=this.navMeshActor.navMesh&&(this.query=new n(this.navMeshActor.navMesh))}findPath(e,t,n=NavigationQueryProfiles.Walker){if(this.ensureQuery(),null==this.query)return y("NavMesh has not been generated yet"),f;const s=this.findClosestPoint(e,n);if(null==s)return y("Start point could not be found on NavMesh",e),f;const i=this.findClosestPoint(t,n);if(null==i)return y("End point could not be found on NavMesh",t),f;const{success:o,error:l,path:a}=this.query.computePath(s,i,{filter:this.getFilter(n)}),u=a.at(-1),c=null!=u&&function(e,t){const n=e.x-t.x,s=e.y-t.y,r=e.z-t.z;return n*n+s*s+r*r}(u,i)<=.01;return o&&c?{success:!0,path:a.map(e=>new r(e.x,e.y,e.z))}:(y("Failed to generate path",l),f)}findClosestPoint(e,t=NavigationQueryProfiles.Walker){if(this.ensureQuery(),null==this.query)return y("NavMesh has not been generated yet"),null;const{success:n,point:s}=this.query.findClosestPoint(e,{filter:this.getFilter(t)});return n?new r(s.x,s.y,s.z):null}findRandomPoint(e=NavigationQueryProfiles.Walker){if(this.ensureQuery(),null==this.query)return y("NavMesh has not been generated yet"),null;const t=this.query.findRandomPoint({filter:this.getFilter(e)});return{success:t.success,randomPoint:(new r).copy(t.randomPoint)}}findRandomPointAroundCircle(e,t,n=NavigationQueryProfiles.Walker){if(this.ensureQuery(),null==this.query)return y("NavMesh has not been generated yet"),null;const s=this.query.findRandomPointAroundCircle(e,t,{filter:this.getFilter(n)});return{success:s.success,randomPoint:(new r).copy(s.randomPoint)}}};d=e([o(),t("design:paramtypes",[])],d);export{d as Navigation};const f={success:!1,path:[]};let m=new Set;function y(e,...t){m.has(e)||(m.add(e),setTimeout(()=>m.delete(e),2e3),console.warn(e,...t))}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ContainerInstance } from 'typedi';
|
|
2
|
+
import type { BaseActor } from './actors/actor.js';
|
|
2
3
|
type Constructable<T> = abstract new (...args: any[]) => T;
|
|
3
4
|
export declare function inject<T>(type: Constructable<T>): T;
|
|
5
|
+
/** Resolve a service from the scoped container that created an actor. */
|
|
6
|
+
export declare function injectFromActor<T>(actor: BaseActor, type: Constructable<T>): T;
|
|
4
7
|
export declare function withInjectionContext<T>(containerInstance: ContainerInstance, fn: (diContainer: ContainerInstance) => T): T;
|
|
5
8
|
export {};
|
|
6
9
|
//# sourceMappingURL=inject.d.ts.map
|
package/dist/gameplay/inject.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import t from"typedi";import{activeContainerInstance as e}from"./actors/internal/container-map.js";export function inject(
|
|
1
|
+
import t from"typedi";import{activeContainerInstance as n}from"./actors/internal/container-map.js";import{containerRefMap as e}from"./actors/internal/container-map.js";export function inject(e){return n.value?.get(e)??t.get(e)}export function injectFromActor(t,n){return e.get(t)?.get(n)??inject(n)}export function withInjectionContext(t,e){const r=n.value;n.value=t;try{return e(t)}finally{n.value=r}}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BaseActor } from '../actors/actor.js';
|
|
2
|
+
/** Direct, allocation-free-per-frame dispatch for framework-managed actor updates. */
|
|
3
|
+
export declare class ActorUpdateRegistry {
|
|
4
|
+
private readonly updatePhase;
|
|
5
|
+
private readonly lateUpdatePhase;
|
|
6
|
+
private readonly registrationsByActor;
|
|
7
|
+
register(actor: BaseActor, update: (deltaTime: number) => void, late?: boolean): void;
|
|
8
|
+
unregister(actor: BaseActor): void;
|
|
9
|
+
update(deltaTime: number): void;
|
|
10
|
+
lateUpdate(deltaTime: number): void;
|
|
11
|
+
dispose(): void;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=actor-update-registry.d.ts.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
class t{constructor(){this.registrations=[],this.needsCompaction=!1}add(t){const s={update:t,phase:this,index:this.registrations.length};return this.registrations.push(s),s}remove(t){this.registrations[t.index]===t&&(this.registrations[t.index]=null,t.index=-1,this.needsCompaction=!0)}dispatch(t){for(let s=0,e=this.registrations.length;s<e;s++){const e=this.registrations[s];null!==e&&e.update(t)}this.needsCompaction&&this.compact()}clear(){this.registrations.length=0,this.needsCompaction=!1}compact(){let t=0;for(let s=0;s<this.registrations.length;s++){const e=this.registrations[s];null!=e&&(this.registrations[t]=e,e.index=t++)}this.registrations.length=t,this.needsCompaction=!1}}export class ActorUpdateRegistry{constructor(){this.updatePhase=new t,this.lateUpdatePhase=new t,this.registrationsByActor=new Map}register(t,s,e=!1){const i=(e?this.lateUpdatePhase:this.updatePhase).add(s);let r=this.registrationsByActor.get(t);null==r&&(r={registrations:[],disposal:t.disposed.subscribe(()=>this.unregister(t))},this.registrationsByActor.set(t,r)),r.registrations.push(i)}unregister(t){const s=this.registrationsByActor.get(t);if(null!=s){for(const t of s.registrations)t.phase.remove(t);s.disposal.unsubscribe(),this.registrationsByActor.delete(t)}}update(t){this.updatePhase.dispatch(t)}lateUpdate(t){this.lateUpdatePhase.dispatch(t)}dispose(){this.updatePhase.clear(),this.lateUpdatePhase.clear();for(const t of this.registrationsByActor.values())t.disposal.unsubscribe();this.registrationsByActor.clear()}}/*
|
|
2
|
+
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
|
+
* See the LICENSE.md file for details.
|
|
4
|
+
*/
|
|
@@ -24,6 +24,7 @@ export declare class ViewController {
|
|
|
24
24
|
private view;
|
|
25
25
|
private readonly tick;
|
|
26
26
|
private readonly lateTick;
|
|
27
|
+
private readonly actorUpdates;
|
|
27
28
|
readonly audioListener: THREE.AudioListener;
|
|
28
29
|
readonly pausedChanged: BehaviorSubject<boolean>;
|
|
29
30
|
readonly mutedChanged: BehaviorSubject<boolean>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as e,__metadata as t}from"tslib";import{Service as a}from"typedi";import{Camera as i}from"three";import{RenderingView as r}from"../../rendering.js";import{BehaviorSubject as s,Subject as n,takeUntil as h}from"rxjs";import*as o from"three";import{GameTimeScheduler as m}from"./game-time-scheduler.js";import{createCameraShakeSample as l,resetCameraShakeSample as u,sampleCameraShake as c}from"./camera-shake.js";export{defaultPostProcessEffectStage,postProcessEffectStages}from"../../rendering/post-process-effect.js";let d=class{constructor(e){this.view=e,this.tick=new n,this.lateTick=new n,this.audioListener=new o.AudioListener,this.pausedChanged=new s(!1),this.mutedChanged=new s(!1),this._muted=!1,this._masterVolume=1,this._cameraShakeIntensity=1,this._timeScale=1,this._hitStopRemaining=0,this.scheduler=new m,this.cameraOverrideId=0,this.cameraOverrides=[],this.cameraShakeId=0,this.gameplayCameraShakes=[],this.cameraShakeContributions=new Map,this.presentationCamera=null,this.accumulatedCameraShakeSample=l(),this.scratchCameraShakeSample=l(),this.outlined=[],this.baseCamera=e.camera,this.sourceCamera=e.camera,e.onLoop(e=>{this.handleFrame(e)}),e.camera.add(this.audioListener),window.hology_view=this}get activeBackend(){return this.view.activeBackend}get requestedBackend(){return this.view.requestedBackend}get vfxBackendPreference(){return this.view.vfxBackendPreference}get webgpuVfxInstancesPerTemplate(){return this.view.webgpuVfxInstancesPerTemplate}get webgpuVfxTemplateMemoryBudgetBytes(){return this.view.webgpuVfxTemplateMemoryBudgetBytes}get webgpuVfxWorldMemoryBudgetBytes(){return this.view.webgpuVfxWorldMemoryBudgetBytes}get webgpuVfxColdTemplateSeconds(){return this.view.webgpuVfxColdTemplateSeconds}get experimentalWebGpuDevice(){return this.view.experimentalWebGpuDevice}registerExperimentalWebGpuRenderGraphExtension(e){return this.view.registerExperimentalWebGpuRenderGraphExtension(e)}handleFrame(e){let t=this._timeScale,a=0;this._hitStopRemaining>0?(this._hitStopRemaining-=1e3*e,this._hitStopRemaining<=0&&(this._hitStopRemaining=0),t=0):a=e*this._timeScale,this.advanceGameplayCameraShakes(a),this.tick.next(a),this.scheduler.update(1e3*a),this.lateTick.next(a),this.view.simulationTimeScale=t,this.refreshCameraPresentation()}set timeScale(e){this._timeScale=Math.max(0,e)}get timeScale(){return this._timeScale}applyHitStop(e){this._hitStopRemaining=Math.max(this._hitStopRemaining,e)}set fpsCap(e){this.view.fpsCap=e}get fpsCap(){return this.view.fpsCap}set showStats(e){this.view.showStats=e}get showStats(){return this.view.showStats}setLightVolume(e){this.view.lightVolume=e}getLightVolume(){return this.view.lightVolume}addPostProcessVolume(e){this.view.addPostProcessVolume(e)}removePostProcessVolume(e){this.view.removePostProcessVolume(e)}addPostProcessEffect(e,t){return this.view.addPostProcessEffect(e,t)}onUpdate(e){return null!=e&&this.tick.pipe(h(e.disposed)),this.tick}onLateUpdate(e){return null!=e&&this.lateTick.pipe(h(e.disposed)),this.lateTick}setCamera(e){const t=e instanceof i?e:e.camera.instance;this.baseCamera=t,0===this.cameraOverrides.length&&(this.sourceCamera=t,this.refreshCameraPresentation())}getCamera(){return this.view.camera}getSourceCamera(){return this.sourceCamera}getBaseCamera(){return this.baseCamera}applyCameraShake(e){const t=function(e){return{duration:Math.max(0,e.duration??0),attack:e.attack??0,decay:e.decay??0,frequency:e.frequency??24,positionAmplitude:e.positionAmplitude?[...e.positionAmplitude]:[0,0,0],rotationAmplitude:e.rotationAmplitude?[...e.rotationAmplitude]:[0,0,0],seed:e.seed}}(e);if(t.duration<=0)return{release:()=>{}};const a={id:++this.cameraShakeId,request:t,elapsedTime:0,seedOverride:t.seed};this.gameplayCameraShakes.push(a),this.refreshCameraPresentation();let i=!1;return{release:()=>{i||(i=!0,this.releaseGameplayCameraShake(a.id))}}}setCameraShakeContribution(e,t,a,i,r){if(a.duration<=0)return void this.removeCameraShakeContribution(e,t);let s=this.cameraShakeContributions.get(e);s||(s=new Map,this.cameraShakeContributions.set(e,s));const n=s.get(t);n?(n.request=a,n.elapsedTime=Math.max(0,i),n.seedOverride=r):s.set(t,{key:t,request:a,elapsedTime:Math.max(0,i),seedOverride:r}),this.refreshCameraPresentation()}removeCameraShakeContribution(e,t){const a=this.cameraShakeContributions.get(e);a?.delete(t)&&(0===a.size&&this.cameraShakeContributions.delete(e),this.refreshCameraPresentation())}clearCameraShakeContributionsForOwner(e){this.cameraShakeContributions.delete(e)&&this.refreshCameraPresentation()}pushCameraOverride(e,t,a=0){const i={id:++this.cameraOverrideId,camera:e,owner:t,priority:a};this.cameraOverrides.push(i),this.applyTopCameraOverride();let r=!1;return{release:()=>{r||(r=!0,this.releaseCameraOverrideEntry(i))}}}releaseCameraOverridesForOwner(e){const t=this.cameraOverrides.filter(t=>t.owner!==e);t.length!==this.cameraOverrides.length&&(this.cameraOverrides=t,this.applyTopCameraOverride())}releaseCameraOverrideEntry(e){const t=this.cameraOverrides.indexOf(e);-1!==t&&(this.cameraOverrides.splice(t,1),this.applyTopCameraOverride())}applyTopCameraOverride(){const e=this.getTopCameraOverride();this.sourceCamera=e?.camera??this.baseCamera,this.refreshCameraPresentation()}getTopCameraOverride(){let e;for(const t of this.cameraOverrides)(!e||t.priority>e.priority||t.priority===e.priority&&t.id>e.id)&&(e=t);return e}applyRenderedCamera(e){this.view.camera!==e&&this.view.setCamera(e),e.add(this.audioListener)}releaseGameplayCameraShake(e){const t=this.gameplayCameraShakes.findIndex(t=>t.id===e);-1!==t&&(this.gameplayCameraShakes.splice(t,1),this.refreshCameraPresentation())}advanceGameplayCameraShakes(e){if(!(e<=0||0===this.gameplayCameraShakes.length))for(let t=this.gameplayCameraShakes.length-1;t>=0;t--){const a=this.gameplayCameraShakes[t];a.elapsedTime+=e,a.elapsedTime>a.request.duration&&this.gameplayCameraShakes.splice(t,1)}}refreshCameraPresentation(){if(!this.hasActiveCameraShake())return void this.applyRenderedCamera(this.sourceCamera);const e=this.ensurePresentationCamera();this.copyCameraState(this.sourceCamera,e),this.accumulateCameraShakeSamples(),this.applyCameraShakeSample(e),this.applyRenderedCamera(e)}hasActiveCameraShake(){if(this.gameplayCameraShakes.length>0)return!0;for(const e of this.cameraShakeContributions.values())if(e.size>0)return!0;return!1}ensurePresentationCamera(){return this.presentationCamera||(this.presentationCamera=new o.PerspectiveCamera,this.presentationCamera.name="View Controller Presentation Camera"),this.presentationCamera}copyCameraState(e,t){e.updateWorldMatrix(!0,!1),t.position.copy(e.getWorldPosition(v)),t.quaternion.copy(e.getWorldQuaternion(w)),t.scale.copy(e.getWorldScale(f)),t.layers.mask=e.layers.mask,e instanceof o.PerspectiveCamera?(t.fov=e.fov,t.near=e.near,t.far=e.far,t.aspect=e.aspect,t.zoom=e.zoom,t.focus=e.focus,t.filmGauge=e.filmGauge,t.filmOffset=e.filmOffset,t.view=null==e.view?null:{enabled:e.view.enabled,fullWidth:e.view.fullWidth,fullHeight:e.view.fullHeight,offsetX:e.view.offsetX,offsetY:e.view.offsetY,width:e.view.width,height:e.view.height},t.updateProjectionMatrix()):(t.projectionMatrix.copy(e.projectionMatrix),t.projectionMatrixInverse.copy(e.projectionMatrixInverse))}accumulateCameraShakeSamples(){u(this.accumulatedCameraShakeSample);for(let e=0;e<this.gameplayCameraShakes.length;e++)this.accumulateCameraShakeEntry(this.gameplayCameraShakes[e]);for(const e of this.cameraShakeContributions.values())for(const t of e.values())this.accumulateCameraShakeEntry(t)}accumulateCameraShakeEntry(e){const t=c(e.request,e.elapsedTime,this.scratchCameraShakeSample,e.seedOverride);if(t)for(let e=0;e<3;e++)this.accumulatedCameraShakeSample.position[e]+=t.position[e],this.accumulatedCameraShakeSample.rotation[e]+=t.rotation[e]}applyCameraShakeSample(e){k.copy(e.quaternion),y.set(this.accumulatedCameraShakeSample.position[0]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.position[1]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.position[2]*this._cameraShakeIntensity),y.applyQuaternion(k),e.position.add(y),x.set(this.accumulatedCameraShakeSample.rotation[0]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.rotation[1]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.rotation[2]*this._cameraShakeIntensity),O.setFromEuler(x),e.quaternion.copy(k).multiply(O),e.updateMatrixWorld(!0)}setMuted(e){this._muted=e,this.applyAudioGain(),this.mutedChanged.next(e)}getMuted(){return this._muted}set masterVolume(e){this._masterVolume=Math.min(Math.max(e,0),1),this.applyAudioGain()}get masterVolume(){return this._masterVolume}set cameraShakeIntensity(e){this._cameraShakeIntensity=Math.min(Math.max(e,0),1),this.refreshCameraPresentation()}get cameraShakeIntensity(){return this._cameraShakeIntensity}applyAudioGain(){this.audioListener.gain.gain.setValueAtTime(this._muted?0:this._masterVolume,this.audioListener.context.currentTime)}setOutlined(e){this.outlined.length=0;const t=this.outlined;for(let a=0;a<e.length;a++)t[a]=e[a];this.view.setSelectedObjects(t),this.view.setEnableOutlines(e.length>0)}getOutlined(){return this.outlined}addOutlined(e){this.outlined.includes(e)||(this.outlined.push(e),this.view.setSelectedObjects(this.outlined),this.view.setEnableOutlines(!0))}removeOutlined(e){const t=this.outlined.indexOf(e);-1!==t&&(this.outlined.splice(t,1),this.view.setSelectedObjects(this.outlined))}setOutlineColor(e){this.view.outlinePass?.visibleEdgeColor.copy(e)}getOutlineColor(){return this.view.outlinePass?.visibleEdgeColor}setOutlineThickness(e){this.view.outlinePass.edgeThickness=e}getOutlineThickness(){return this.view.outlinePass.edgeThickness}get htmlElement(){return this.view.container}getViewportSize(e=S){return this.view.getClientSize(e)}get paused(){return this.view.paused}set paused(e){e!=this.paused&&(e?this.pauseRendering():this.unpauseRendering())}pauseRendering(){this.view.paused=!0,this.pausedChanged.next(this.view.paused),this.scheduler.pause()}unpauseRendering(){this.view.paused=!1,this.pausedChanged.next(this.view.paused),this.scheduler.resume()}setInterval(e,t,a){return this.scheduler.setInterval(e,t,a)}clearInterval(e){this.scheduler.clearInterval(e)}dispose(){this.view.running&&this.view.stop(),this.scheduler.dispose(),this.cameraOverrides.length=0,this.gameplayCameraShakes.length=0,this.cameraShakeContributions.clear(),this.audioListener.removeFromParent(),this.tick.complete(),this.lateTick.complete(),this.paused=!0}createTimer(e){return this.scheduler.createTimer(e)}getScreenPosition(e,t=C,a=this.getViewportSize()){const i=this.getCamera();return p.multiplyMatrices(i.projectionMatrix,i.matrixWorldInverse),i instanceof o.PerspectiveCamera&&(g.setFromProjectionMatrix(p),!g.containsPoint(e))?null:(t.copy(e),t.project(i),t.x=(t.x+1)/2*a.x,t.y=(1-t.y)/2*a.y,t)}};d=e([a(),t("design:paramtypes",[r])],d);export{d as ViewController};const p=new o.Matrix4,C=new o.Vector3,S=new o.Vector2,g=new o.Frustum,v=new o.Vector3,f=new o.Vector3,w=new o.Quaternion,k=new o.Quaternion,y=new o.Vector3,x=new o.Euler(0,0,0,"XYZ"),O=new o.Quaternion;/*
|
|
1
|
+
import{__decorate as e,__metadata as t}from"tslib";import{Service as a}from"typedi";import{Camera as i}from"three";import{RenderingView as r}from"../../rendering.js";import{BehaviorSubject as s,Subject as n,takeUntil as h}from"rxjs";import*as o from"three";import{GameTimeScheduler as m}from"./game-time-scheduler.js";import{ActorUpdateRegistry as l}from"./actor-update-registry.js";import{createCameraShakeSample as u,resetCameraShakeSample as c,sampleCameraShake as p}from"./camera-shake.js";export{defaultPostProcessEffectStage,postProcessEffectStages}from"../../rendering/post-process-effect.js";let d=class{constructor(e){this.view=e,this.tick=new n,this.lateTick=new n,this.actorUpdates=new l,this.audioListener=new o.AudioListener,this.pausedChanged=new s(!1),this.mutedChanged=new s(!1),this._muted=!1,this._masterVolume=1,this._cameraShakeIntensity=1,this._timeScale=1,this._hitStopRemaining=0,this.scheduler=new m,this.cameraOverrideId=0,this.cameraOverrides=[],this.cameraShakeId=0,this.gameplayCameraShakes=[],this.cameraShakeContributions=new Map,this.presentationCamera=null,this.accumulatedCameraShakeSample=u(),this.scratchCameraShakeSample=u(),this.outlined=[],this.baseCamera=e.camera,this.sourceCamera=e.camera,e.onLoop(e=>{this.handleFrame(e)}),e.camera.add(this.audioListener),window.hology_view=this}get activeBackend(){return this.view.activeBackend}get requestedBackend(){return this.view.requestedBackend}get vfxBackendPreference(){return this.view.vfxBackendPreference}get webgpuVfxInstancesPerTemplate(){return this.view.webgpuVfxInstancesPerTemplate}get webgpuVfxTemplateMemoryBudgetBytes(){return this.view.webgpuVfxTemplateMemoryBudgetBytes}get webgpuVfxWorldMemoryBudgetBytes(){return this.view.webgpuVfxWorldMemoryBudgetBytes}get webgpuVfxColdTemplateSeconds(){return this.view.webgpuVfxColdTemplateSeconds}get experimentalWebGpuDevice(){return this.view.experimentalWebGpuDevice}registerExperimentalWebGpuRenderGraphExtension(e){return this.view.registerExperimentalWebGpuRenderGraphExtension(e)}handleFrame(e){let t=this._timeScale,a=0;this._hitStopRemaining>0?(this._hitStopRemaining-=1e3*e,this._hitStopRemaining<=0&&(this._hitStopRemaining=0),t=0):a=e*this._timeScale,this.advanceGameplayCameraShakes(a),this.tick.next(a),this.actorUpdates.update(a),this.scheduler.update(1e3*a),this.lateTick.next(a),this.actorUpdates.lateUpdate(a),this.view.simulationTimeScale=t,this.refreshCameraPresentation()}set timeScale(e){this._timeScale=Math.max(0,e)}get timeScale(){return this._timeScale}applyHitStop(e){this._hitStopRemaining=Math.max(this._hitStopRemaining,e)}set fpsCap(e){this.view.fpsCap=e}get fpsCap(){return this.view.fpsCap}set showStats(e){this.view.showStats=e}get showStats(){return this.view.showStats}setLightVolume(e){this.view.lightVolume=e}getLightVolume(){return this.view.lightVolume}addPostProcessVolume(e){this.view.addPostProcessVolume(e)}removePostProcessVolume(e){this.view.removePostProcessVolume(e)}addPostProcessEffect(e,t){return this.view.addPostProcessEffect(e,t)}onUpdate(e){return null==e?this.tick:this.tick.pipe(h(e.disposed))}onLateUpdate(e){return null==e?this.lateTick:this.lateTick.pipe(h(e.disposed))}registerActorUpdate(e,t,a=!1){this.actorUpdates.register(e,t,a)}unregisterActorUpdates(e){this.actorUpdates.unregister(e)}setCamera(e){const t=e instanceof i?e:e.camera.instance;this.baseCamera=t,0===this.cameraOverrides.length&&(this.sourceCamera=t,this.refreshCameraPresentation())}getCamera(){return this.view.camera}getSourceCamera(){return this.sourceCamera}getBaseCamera(){return this.baseCamera}applyCameraShake(e){const t=function(e){return{duration:Math.max(0,e.duration??0),attack:e.attack??0,decay:e.decay??0,frequency:e.frequency??24,positionAmplitude:e.positionAmplitude?[...e.positionAmplitude]:[0,0,0],rotationAmplitude:e.rotationAmplitude?[...e.rotationAmplitude]:[0,0,0],seed:e.seed}}(e);if(t.duration<=0)return{release:()=>{}};const a={id:++this.cameraShakeId,request:t,elapsedTime:0,seedOverride:t.seed};this.gameplayCameraShakes.push(a),this.refreshCameraPresentation();let i=!1;return{release:()=>{i||(i=!0,this.releaseGameplayCameraShake(a.id))}}}setCameraShakeContribution(e,t,a,i,r){if(a.duration<=0)return void this.removeCameraShakeContribution(e,t);let s=this.cameraShakeContributions.get(e);s||(s=new Map,this.cameraShakeContributions.set(e,s));const n=s.get(t);n?(n.request=a,n.elapsedTime=Math.max(0,i),n.seedOverride=r):s.set(t,{key:t,request:a,elapsedTime:Math.max(0,i),seedOverride:r}),this.refreshCameraPresentation()}removeCameraShakeContribution(e,t){const a=this.cameraShakeContributions.get(e);a?.delete(t)&&(0===a.size&&this.cameraShakeContributions.delete(e),this.refreshCameraPresentation())}clearCameraShakeContributionsForOwner(e){this.cameraShakeContributions.delete(e)&&this.refreshCameraPresentation()}pushCameraOverride(e,t,a=0){const i={id:++this.cameraOverrideId,camera:e,owner:t,priority:a};this.cameraOverrides.push(i),this.applyTopCameraOverride();let r=!1;return{release:()=>{r||(r=!0,this.releaseCameraOverrideEntry(i))}}}releaseCameraOverridesForOwner(e){const t=this.cameraOverrides.filter(t=>t.owner!==e);t.length!==this.cameraOverrides.length&&(this.cameraOverrides=t,this.applyTopCameraOverride())}releaseCameraOverrideEntry(e){const t=this.cameraOverrides.indexOf(e);-1!==t&&(this.cameraOverrides.splice(t,1),this.applyTopCameraOverride())}applyTopCameraOverride(){const e=this.getTopCameraOverride();this.sourceCamera=e?.camera??this.baseCamera,this.refreshCameraPresentation()}getTopCameraOverride(){let e;for(const t of this.cameraOverrides)(!e||t.priority>e.priority||t.priority===e.priority&&t.id>e.id)&&(e=t);return e}applyRenderedCamera(e){this.view.camera!==e&&this.view.setCamera(e),e.add(this.audioListener)}releaseGameplayCameraShake(e){const t=this.gameplayCameraShakes.findIndex(t=>t.id===e);-1!==t&&(this.gameplayCameraShakes.splice(t,1),this.refreshCameraPresentation())}advanceGameplayCameraShakes(e){if(!(e<=0||0===this.gameplayCameraShakes.length))for(let t=this.gameplayCameraShakes.length-1;t>=0;t--){const a=this.gameplayCameraShakes[t];a.elapsedTime+=e,a.elapsedTime>a.request.duration&&this.gameplayCameraShakes.splice(t,1)}}refreshCameraPresentation(){if(!this.hasActiveCameraShake())return void this.applyRenderedCamera(this.sourceCamera);const e=this.ensurePresentationCamera();this.copyCameraState(this.sourceCamera,e),this.accumulateCameraShakeSamples(),this.applyCameraShakeSample(e),this.applyRenderedCamera(e)}hasActiveCameraShake(){if(this.gameplayCameraShakes.length>0)return!0;for(const e of this.cameraShakeContributions.values())if(e.size>0)return!0;return!1}ensurePresentationCamera(){return this.presentationCamera||(this.presentationCamera=new o.PerspectiveCamera,this.presentationCamera.name="View Controller Presentation Camera"),this.presentationCamera}copyCameraState(e,t){e.updateWorldMatrix(!0,!1),t.position.copy(e.getWorldPosition(f)),t.quaternion.copy(e.getWorldQuaternion(k)),t.scale.copy(e.getWorldScale(w)),t.layers.mask=e.layers.mask,e instanceof o.PerspectiveCamera?(t.fov=e.fov,t.near=e.near,t.far=e.far,t.aspect=e.aspect,t.zoom=e.zoom,t.focus=e.focus,t.filmGauge=e.filmGauge,t.filmOffset=e.filmOffset,t.view=null==e.view?null:{enabled:e.view.enabled,fullWidth:e.view.fullWidth,fullHeight:e.view.fullHeight,offsetX:e.view.offsetX,offsetY:e.view.offsetY,width:e.view.width,height:e.view.height},t.updateProjectionMatrix()):(t.projectionMatrix.copy(e.projectionMatrix),t.projectionMatrixInverse.copy(e.projectionMatrixInverse))}accumulateCameraShakeSamples(){c(this.accumulatedCameraShakeSample);for(let e=0;e<this.gameplayCameraShakes.length;e++)this.accumulateCameraShakeEntry(this.gameplayCameraShakes[e]);for(const e of this.cameraShakeContributions.values())for(const t of e.values())this.accumulateCameraShakeEntry(t)}accumulateCameraShakeEntry(e){const t=p(e.request,e.elapsedTime,this.scratchCameraShakeSample,e.seedOverride);if(t)for(let e=0;e<3;e++)this.accumulatedCameraShakeSample.position[e]+=t.position[e],this.accumulatedCameraShakeSample.rotation[e]+=t.rotation[e]}applyCameraShakeSample(e){y.copy(e.quaternion),x.set(this.accumulatedCameraShakeSample.position[0]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.position[1]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.position[2]*this._cameraShakeIntensity),x.applyQuaternion(y),e.position.add(x),O.set(this.accumulatedCameraShakeSample.rotation[0]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.rotation[1]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.rotation[2]*this._cameraShakeIntensity),P.setFromEuler(O),e.quaternion.copy(y).multiply(P),e.updateMatrixWorld(!0)}setMuted(e){this._muted=e,this.applyAudioGain(),this.mutedChanged.next(e)}getMuted(){return this._muted}set masterVolume(e){this._masterVolume=Math.min(Math.max(e,0),1),this.applyAudioGain()}get masterVolume(){return this._masterVolume}set cameraShakeIntensity(e){this._cameraShakeIntensity=Math.min(Math.max(e,0),1),this.refreshCameraPresentation()}get cameraShakeIntensity(){return this._cameraShakeIntensity}applyAudioGain(){this.audioListener.gain.gain.setValueAtTime(this._muted?0:this._masterVolume,this.audioListener.context.currentTime)}setOutlined(e){this.outlined.length=0;const t=this.outlined;for(let a=0;a<e.length;a++)t[a]=e[a];this.view.setSelectedObjects(t),this.view.setEnableOutlines(e.length>0)}getOutlined(){return this.outlined}addOutlined(e){this.outlined.includes(e)||(this.outlined.push(e),this.view.setSelectedObjects(this.outlined),this.view.setEnableOutlines(!0))}removeOutlined(e){const t=this.outlined.indexOf(e);-1!==t&&(this.outlined.splice(t,1),this.view.setSelectedObjects(this.outlined))}setOutlineColor(e){this.view.outlinePass?.visibleEdgeColor.copy(e)}getOutlineColor(){return this.view.outlinePass?.visibleEdgeColor}setOutlineThickness(e){this.view.outlinePass.edgeThickness=e}getOutlineThickness(){return this.view.outlinePass.edgeThickness}get htmlElement(){return this.view.container}getViewportSize(e=S){return this.view.getClientSize(e)}get paused(){return this.view.paused}set paused(e){e!=this.paused&&(e?this.pauseRendering():this.unpauseRendering())}pauseRendering(){this.view.paused=!0,this.pausedChanged.next(this.view.paused),this.scheduler.pause()}unpauseRendering(){this.view.paused=!1,this.pausedChanged.next(this.view.paused),this.scheduler.resume()}setInterval(e,t,a){return this.scheduler.setInterval(e,t,a)}clearInterval(e){this.scheduler.clearInterval(e)}dispose(){this.view.running&&this.view.stop(),this.scheduler.dispose(),this.cameraOverrides.length=0,this.gameplayCameraShakes.length=0,this.cameraShakeContributions.clear(),this.audioListener.removeFromParent(),this.tick.complete(),this.lateTick.complete(),this.actorUpdates.dispose(),this.paused=!0}createTimer(e){return this.scheduler.createTimer(e)}getScreenPosition(e,t=g,a=this.getViewportSize()){const i=this.getCamera();return C.multiplyMatrices(i.projectionMatrix,i.matrixWorldInverse),i instanceof o.PerspectiveCamera&&(v.setFromProjectionMatrix(C),!v.containsPoint(e))?null:(t.copy(e),t.project(i),t.x=(t.x+1)/2*a.x,t.y=(1-t.y)/2*a.y,t)}};d=e([a(),t("design:paramtypes",[r])],d);export{d as ViewController};const C=new o.Matrix4,g=new o.Vector3,S=new o.Vector2,v=new o.Frustum,f=new o.Vector3,w=new o.Vector3,k=new o.Quaternion,y=new o.Quaternion,x=new o.Vector3,O=new o.Euler(0,0,0,"XYZ"),P=new o.Quaternion;/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as t}from"tslib";import{Service as e}from"typedi";import{BaseActor as r}from"../actors/actor.js";import{ActorFactory as s}from"../actors/factory.js";import{Group as i,Vector3 as n}from"three";import{ActorComponent as o}from"../../gameplay/actors/component.js";import{Subject as a}from"rxjs";import{RenderingView as c}from"../../rendering.js";import{randomUUID as h}from"../../utils/uuid.js";import{PrefabInstance as
|
|
1
|
+
import{__decorate as t}from"tslib";import{Service as e}from"typedi";import{BaseActor as r}from"../actors/actor.js";import{ActorFactory as s}from"../actors/factory.js";import{Group as i,Vector3 as n}from"three";import{ActorComponent as o}from"../../gameplay/actors/component.js";import{Subject as a}from"rxjs";import{RenderingView as c}from"../../rendering.js";import{randomUUID as h}from"../../utils/uuid.js";import{PrefabInstance as l}from"../../scene/objects/prefab.js";import{PhysicsSystem as p}from"./physics/physics-system.js";import{inject as d}from"../../gameplay/inject.js";import{isComponentEnabledInEnvironment as m}from"../actors/internal/component-init.js";import{NetRole as f}from"../net/service/net-actor-role.js";import{BasePlayerController as v}from"../actors/controller/actor-controller.js";import{EventGraphInstance as y}from"../event-graph/runtime.js";import{ViewController as w}from"./render.js";import{AssetsProvider as u}from"../../scene/assets-provider.js";import{WorldEventGraphAssetRuntime as b}from"../event-graph/runtime-assets.js";import{hasExecutableEventGraphDocument as E}from"../event-graph/model.js";const A=new n;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?A:this.view.csm.lights[0].position}}let G=class{constructor(){this.actorFactory=d(s),this.view=d(c),this.viewController=d(w),this.physics=d(p),this.assetsProvider=d(u),this.eventGraphAssets=new b(this,this.assetsProvider),this.actors=[],this.actorAdded=new a,this.actorRemoved=new a,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),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),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 v&&(o.isLocallyControlled=r===f.autonomousProxy),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.isLocallyControlled=!0,this.actorFactory.initActor(s),this.addActor(s,e,r),s}spawnActorSync(t,e,r){if(null==t)throw new Error("Cannot spawn actor with null type");const s=this.actorFactory.createSync(t,e,r);return this.addActor(s,e,r),s}addActor(t,e,r){e&&t.object.position.copy(e),r&&t.object.rotation.copy(r),null==t.object.parent&&this.scene.add(t.object),this.actors.push(t),j(t,t=>t.onBeginPlay(),this.actorFactory.inEditor),this.actorAdded.next(t)}async preloadVisualEffects(t){await(this.materializer?.preloadVisualEffects(t))}removeActor(t){for(const[e,r]of[...this.sceneEventGraphs])r.binding.mainActor===t&&this.deactivateSceneEventGraph(e);j(t,t=>t.onEndPlay(),this.actorFactory.inEditor);const e=this.actors.indexOf(t);e>=0&&this.actors.splice(e,1),t.object.removeFromParent(),this.viewController.unregisterActorUpdates(t),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))}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,r){if(null==this.materializer)return console.error("Internal error: Materializer is missing on World"),null;const s=new l,n=new i;null!=e&&n.position.copy(e),null!=r&&n.rotation.copy(r),this.materializer.setScene(this.scene),this.materializer.inEditor=this.actorFactory.inEditor;const o=null!=t.asset.id?await(this.assetsProvider?.getAsset(t.asset.id))??t.asset:t.asset,{object:a,actors:c,actorsById:p,mainActor:d,instanceId:m}=await this.materializer.createFromPrefabAsset(o,{sceneObjectChain:["r-"+h()],runtimeSpawn:!0},n,void 0,!1);s.object=a,s.actors=c,s.mainActor=d,s.__materializerInstanceId=m,this.scene.attach(a),c.forEach(t=>{this.addActor(t)}),this.physics.addFromScene(a);const f=o.prefab?.eventGraph;if(!this.actorFactory.inEditor&&E(f)){s.eventGraph=new y(f,{world:this,actorsById:p,mainActor:d,updates:this.viewController.onUpdate(),timer:this.viewController,physics:this.physics,assets:this.eventGraphAssets});try{await s.eventGraph.beginPlay()}catch(t){throw this.removePrefab(s),t}}return s}removePrefab(t){if(t.destroyed)return;t.destroyed=!0,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)}};G=t([e()],G);export{G as World};function j(t,e,s=!1,i=new WeakSet){if(!i.has(t))return i.add(t),e(t),t instanceof r&&t.attachedComponents.forEach(t=>{m(t,s)&&j(t,e,s,i)}),Object.entries(t).filter(([t,e])=>e instanceof o&&m(e,s)).forEach(([t,r])=>{j(r,e,s,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
|
@@ -5,6 +5,9 @@ export { AssetMeshInstance } from './scene/asset-resource-loader.js';
|
|
|
5
5
|
export { registerWorker, initWorker } from './worker/index.js';
|
|
6
6
|
export * from './shader/index.js';
|
|
7
7
|
export * from './gameplay/actors/builtin/post-process-volume-actor.js';
|
|
8
|
+
export * from './gameplay/actors/builtin/water-volume-actor.js';
|
|
9
|
+
export * from './gameplay/actors/builtin/water-spline-volume-actor.js';
|
|
10
|
+
export * from './gameplay/actors/builtin/components/water-post-process-settings.js';
|
|
8
11
|
export * from './scene/objects/prefab.js';
|
|
9
12
|
export * from './scene/objects/data-asset.js';
|
|
10
13
|
export * from './effects/sequence/index.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"./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"./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
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as e from"three";import{NodeMaterialWebGpuResolver as t}from"./node-material-webgpu-resolver.js";import{float as i,varyingTransformed as n,vec4 as o}from"three-shader-graph";import{NodeShaderMaterial as r}from"../shader-nodes/index.js";import{sceneMapUniformName as s}from"../shader-nodes/scene-sample.js";import{MATERIAL_FLAG_ALPHA_TEST as a,MATERIAL_FLAG_RECEIVE_DECALS as l}from"../shader-nodes/material-flags.js";import{createMotionVectorOutput as d}from"./motion-vector-node.js";export class ExperimentalWebGpuBackend{constructor(e,t,i,n,o,r,s,a,l,d,c,h,u,p,m,g,f,v,S){this.renderer=e,this.fallbackMaterial=t,this.nodeMaterialResolver=i,this.gBufferFallbackSource=n,this.bloom=o,this.fxaa=r,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.antiAliasingState=v,this.renderScale=1,this.pmremGenerator=null,this.pmremResults=new WeakMap,this.ownedPmremResults=new Set,this.warnedUnsupportedPostProcessStages=new Set,this.canvas=S}static async create(u={}){const p=await import("@hology/webgpu-renderer"),m=document.createElement("canvas");m.dataset.hologyRenderer="webgpu-experimental";const g=new p.WGSLShaderMaterial({vertexShader:c,fragmentShader:h});g.side=p.DoubleSide;const f=p,v=null!=u.motionBlur&&!1!==u.motionBlur,S=!1!==u.gtao&&(null==u.gtao||!1!==u.gtao.temporal),P=!1!==u.volumetricFog&&(null==u.volumetricFog||!1!==u.volumetricFog.temporal),x=null!=(w=u).antiAliasing?w.antiAliasing:null!=w.taa&&!1!==w.taa?"taa":null!=w.smaa&&!1!==w.smaa?"smaa":null!=w.fxaa&&!1!==w.fxaa?"fxaa":!1===w.taa?"off":"taa";var w;const b=null!=u.antiAliasing,y=v||S||P||(b||"taa"===x),F=new t(f,(e,t)=>"opaque"===t?[e.outputColor.xyz.rgba(i(e.alphaTest>0?a:0).add(!1===e.userData.receiveDecals?0:l)),o((e.outputNormal??n.normal).multiplyScalar(.5).addScalar(.5),e.outputRoughness??i(1)),...y?[d(e)]:[]]:void 0),C=new e.MeshStandardMaterial({color:8688803,roughness:1});if(null==F.resolve(C,"opaque"))throw g.dispose(),C.dispose(),new Error("Failed to compile the WebGPU G-buffer fallback material.");const L=!1===u.gtao?null:new p.GTAOEffect({normalSpace:"view",...p.GTAO_PRESETS.high,...u.gtao??{}}),M=!1===u.ssr||null==u.ssr?null:new p.SSREffect({normalSpace:"view",resolutionScale:.5,...u.ssr??{}}),O=!1===u.bloom?null:new p.BloomEffect({threshold:1,strength:.9,radius:.2,...u.bloom??{}}),E=b||!1!==u.fxaa&&"fxaa"===x?new p.FXAAEffect(!1===u.fxaa?{}:u.fxaa):null,G=b||!1!==u.smaa&&"smaa"===x?new p.SMAAEffect(!1===u.smaa?{}:u.smaa):null,R=b||!1!==u.taa&&"taa"===x?new p.TemporalAAEffect({renderScale:1,...!1===u.taa?{}:u.taa}):null,D={mode:x},B=!1===u.motionBlur||null==u.motionBlur?null:new p.MotionBlurEffect(u.motionBlur),A=u.volumetricFog||void 0,T=!1===u.volumetricFog?null:new p.VolumetricFogEffect(void 0,{...p.VOLUMETRIC_FOG_PRESETS.high,maxDistance:64,spatialJitter:!0,scatteringIntensity:Math.PI,...A,temporal:!1!==A?.temporal&&{...p.VOLUMETRIC_FOG_PRESETS.high.temporal,...A?.temporal},blur:!1!==A?.blur&&{...!1===p.VOLUMETRIC_FOG_PRESETS.high.blur?{}:p.VOLUMETRIC_FOG_PRESETS.high.blur,depthAwareUpsample:!0,...A?.blur}}),V=!1===u.colorGrading?null:new p.ColorGradingEffect({enabled:!1,...u.colorGrading??{}}),W=!1===u.depthOfField?null:new p.DepthOfFieldEffect({enabled:!1,...u.depthOfField??{}}),k=!1===u.outline?null:new p.OutlineEffect({enabled:!1,...u.outline??{}}),I=[],N=new WeakMap,U=new Set,j=[],H=(e,t,i)=>{let n=i;const o=I.filter(e=>e.enabled&&e.stage===t).sort((e,t)=>e.priority-t.priority||e.id-t.id);for(const t of o){if(!(t.material instanceof r)){const e=`${t.id}:material`;U.has(e)||(U.add(e),console.warn(`[Hology WebGPU experiment] Skipping custom post-process effect ${t.id}: only NodeShaderMaterial effects are supported.`));continue}try{let i=N.get(t.material);null!=i&&i.version===t.material.version||(i={version:t.material.version,shader:F.compileFullscreen(t.material)},N.set(t.material,i));const o=e.createColorTarget(`hologyCustomPostProcess${t.id}`);e.addNodeFullscreenPass({name:`HologyCustomPostProcess${t.id}`,shader:i.shader,inputOverrides:{[s]:n},outputs:o}),n=o}catch(e){const i=`${t.id}:compile:${t.material.version}`;U.has(i)||(U.add(i),console.warn(`[Hology WebGPU experiment] Skipping custom post-process effect ${t.id}: ${e instanceof Error?e.message:String(e)}`,e))}}return n},$=new p.WebGpuRenderer({canvas:m,fallbackMaterial:g,materialResolver:(e,t)=>F.resolve(e,t),invokeObjectRenderCallbacks:!1,hiZOcclusionCulling:!0,renderGraphProfiling:!1,sampleCount:1,multiDrawIndirect:!1});$.setRenderScale("taa"===x?R?.renderScale??1:1),$.setRenderGraphBuilder((e,t)=>{const i=new p.FrameGraphBuilder(t),n=i.createColorTarget("hologyNormalRoughness",{format:"rgba16float"}),o=y?i.createColorTarget("hologyMotionVectors",{format:"rgba16float"}):null;for(const e of j)"beforeScene"===e.stage&&e(i,i.sceneColor);i.addDirectionalShadowPass(),i.addPointShadowPass(),i.addSpotShadowPass(),i.addDepthPrepass(),i.addHiZPasses(),i.addVisibilityPass(),i.addMaterialPass({name:"OpaqueGBufferPass",outputs:null==o?[i.sceneColor,n]:[i.sceneColor,n,o],drawStage:"opaque",loadDepth:!0}),i.addHiZHistoryPass("OpaqueGBufferPass");let r=i.sceneColor;if(L?.enabled){const e=L.addPasses(i,{depth:i.mainDepth,normal:n,velocity:o??void 0});r=L.addCompositePass(i,r,e)}null!=M&&(r=M.addPasses(i,{sceneColor:r,depth:i.mainDepth,normalRoughness:n}));const s=new Set(j.flatMap(e=>e.getSceneTextureReads?.()??[]));i.addSceneTextureCopyPass(r,{sceneColor:s.has("sceneColorSnapshot"),sceneDepth:s.has("sceneDepthSnapshot")}),i.addLatePass(r);for(const e of j)"beforeScene"!==e.stage&&e(i,r);r=H(i,"beforeFog",r),null!=T&&(r=T.addPasses(i,{sceneColor:r,depth:i.mainDepth,velocity:o??void 0,volumeObjects:t.fogVolumeObjects,lights:t.extractedLights})),null!=B&&null!=o&&(r=B.addPass(i,r,o,i.mainDepth)),null!=O&&(r=O.addPass(i,r)),r=H(i,"beforeOutline",r),null!=k&&(r=k.addPass(i,r)),r=H(i,"beforeDepthOfField",r),null!=W&&(r=W.addPass(i,r,i.mainDepth)),r=H(i,"beforeColorAdjustment",r),null!=V&&(r=V.addPass(i,r)),r=H(i,"beforeAntiAliasing",r);let a="taa"===D.mode&&null!=R?R.addPass(i,r,i.mainDepth,o??void 0):r;"smaa"===D.mode&&null!=G?a=G.addPass(i,a):"fxaa"===D.mode&&null!=E&&(a=E.addPass(i,a)),a=H(i,"beforeOutput",a),i.present(a)});try{return await $.initialize(),new ExperimentalWebGpuBackend($,g,F,C,O,E,G,L,M,R,B,T,V,W,k,I,j,D,m)}catch(e){throw $.destroy(),g.dispose(),F.dispose(),C.dispose(),L?.dispose(),G?.dispose(),B?.dispose(),T?.dispose(),k?.dispose(),e}}static supportsCamera(t){return t instanceof e.PerspectiveCamera||t instanceof e.OrthographicCamera}setSize(e,t,i,n=1){const o=this.canvas.width,r=this.canvas.height,s=Math.min(1,Math.max(.25,n)),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!==o||this.canvas.height!==r)&&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)}setWorldEnvironment(t){if(null==t)return void this.nodeMaterialResolver.setWorldEnvironment(null);let i=t.source;if(i.mapping!==e.CubeUVReflectionMapping){let e=this.pmremResults.get(i);null==e&&(this.pmremGenerator??(this.pmremGenerator=this.renderer.createPMREMGenerator()),e=this.pmremGenerator.fromEquirectangular(i),this.pmremResults.set(i,e),this.ownedPmremResults.add(e)),i=e.texture}this.nodeMaterialResolver.setWorldEnvironment({texture:i,intensity:t.intensity})}resetViewHistory(){this.renderer.resetViewHistory(),this.taa?.reset(),this.motionBlur?.reset(),this.gtao?.reset(),this.volumetricFog?.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.motionBlur?.setJitterUv(n?.currentJitterUv.x??0,n?.currentJitterUv.y??0);let o=!1;try{this.renderer.render(e,t,i),o=!0}finally{n?.finishCamera(t),o&&this.motionBlur?.commitCamera()}}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()}inspect(e,t){return inspectWebGpuScene(e,t,this.nodeMaterialResolver)}destroy(){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 o=Array.isArray(t.material)?t.material:[t.material];for(const e of o){if(null==e)continue;e.transparent&&n.transparentMaterials++;const t=!0===e.isWGSLShaderMaterial||!0===e.isLineMaterial,o=!t&&!0===i?.canResolve(e);t||o||(n.materialsReplaced++,e.isShaderMaterial&&n.shaderMaterialsReplaced++)}}),n}export function formatWebGpuSceneCapabilitySummary(e){return`[Hology WebGPU experiment] ${JSON.stringify(e)}`}const c="\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",h="\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{float as i,varyingTransformed as n,vec4 as o}from"three-shader-graph";import{NodeShaderMaterial as r}from"../shader-nodes/index.js";import{sceneMapUniformName as s}from"../shader-nodes/scene-sample.js";import{depthUniformName as a}from"../shader-nodes/depth.js";import{MATERIAL_FLAG_ALPHA_TEST as l,MATERIAL_FLAG_RECEIVE_DECALS as d}from"../shader-nodes/material-flags.js";import{createMotionVectorOutput as c}from"./motion-vector-node.js";export class ExperimentalWebGpuBackend{constructor(e,t,i,n,o,r,s,a,l,d,c,h,u,p,m,g,f,v,S){this.renderer=e,this.fallbackMaterial=t,this.nodeMaterialResolver=i,this.gBufferFallbackSource=n,this.bloom=o,this.fxaa=r,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.antiAliasingState=v,this.renderScale=1,this.pmremGenerator=null,this.pmremResults=new WeakMap,this.ownedPmremResults=new Set,this.warnedUnsupportedPostProcessStages=new Set,this.canvas=S}static async create(p={}){const m=await import("@hology/webgpu-renderer"),g=document.createElement("canvas");g.dataset.hologyRenderer="webgpu-experimental";const f=new m.WGSLShaderMaterial({vertexShader:h,fragmentShader:u});f.side=m.DoubleSide;const v=m,S=null!=p.motionBlur&&!1!==p.motionBlur,P=!1!==p.gtao&&(null==p.gtao||!1!==p.gtao.temporal),x=!1!==p.volumetricFog&&(null==p.volumetricFog||!1!==p.volumetricFog.temporal),w=null!=(b=p).antiAliasing?b.antiAliasing:null!=b.taa&&!1!==b.taa?"taa":null!=b.smaa&&!1!==b.smaa?"smaa":null!=b.fxaa&&!1!==b.fxaa?"fxaa":!1===b.taa?"off":"taa";var b;const y=null!=p.antiAliasing,F=S||P||x||(y||"taa"===w),C=new t(v,(e,t)=>"opaque"===t?[e.outputColor.xyz.rgba(i(e.alphaTest>0?l:0).add(!1===e.userData.receiveDecals?0:d)),o((e.outputNormal??n.normal).multiplyScalar(.5).addScalar(.5),e.outputRoughness??i(1)),...F?[c(e)]:[]]:void 0),L=new e.MeshStandardMaterial({color:8688803,roughness:1});if(null==C.resolve(L,"opaque"))throw f.dispose(),L.dispose(),new Error("Failed to compile the WebGPU G-buffer fallback material.");const M=!1===p.gtao?null:new m.GTAOEffect({normalSpace:"view",...m.GTAO_PRESETS.high,...p.gtao??{}}),O=!1===p.ssr||null==p.ssr?null:new m.SSREffect({normalSpace:"view",resolutionScale:.5,...p.ssr??{}}),E=!1===p.bloom?null:new m.BloomEffect({threshold:1,strength:.9,radius:.2,...p.bloom??{}}),G=y||!1!==p.fxaa&&"fxaa"===w?new m.FXAAEffect(!1===p.fxaa?{}:p.fxaa):null,R=y||!1!==p.smaa&&"smaa"===w?new m.SMAAEffect(!1===p.smaa?{}:p.smaa):null,D=y||!1!==p.taa&&"taa"===w?new m.TemporalAAEffect({renderScale:1,...!1===p.taa?{}:p.taa}):null,B={mode:w},A=!1===p.motionBlur||null==p.motionBlur?null:new m.MotionBlurEffect(p.motionBlur),T=p.volumetricFog||void 0,V=!1===p.volumetricFog?null:new m.VolumetricFogEffect(void 0,{...m.VOLUMETRIC_FOG_PRESETS.high,maxDistance:64,spatialJitter:!0,scatteringIntensity:Math.PI,...T,temporal:!1!==T?.temporal&&{...m.VOLUMETRIC_FOG_PRESETS.high.temporal,...T?.temporal},blur:!1!==T?.blur&&{...!1===m.VOLUMETRIC_FOG_PRESETS.high.blur?{}:m.VOLUMETRIC_FOG_PRESETS.high.blur,depthAwareUpsample:!0,...T?.blur}}),W=!1===p.colorGrading?null:new m.ColorGradingEffect({enabled:!1,...p.colorGrading??{}}),k=!1===p.depthOfField?null:new m.DepthOfFieldEffect({enabled:!1,...p.depthOfField??{}}),I=!1===p.outline?null:new m.OutlineEffect({enabled:!1,...p.outline??{}}),N=[],U=new WeakMap,j=new Set,H=[],$=(e,t,i)=>{let n=i;const o=N.filter(e=>e.enabled&&e.stage===t).sort((e,t)=>e.priority-t.priority||e.id-t.id);for(const t of o){if(!(t.material instanceof r)){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=U.get(t.material);null!=i&&i.version===t.material.version||(i={version:t.material.version,shader:C.compileFullscreen(t.material)},U.set(t.material,i));const o=e.createColorTarget(`hologyCustomPostProcess${t.id}`);e.addNodeFullscreenPass({name:`HologyCustomPostProcess${t.id}`,shader:i.shader,inputOverrides:{[s]:n},outputs:o}),n=o}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},_=new m.WebGpuRenderer({canvas:g,fallbackMaterial:f,materialResolver:(e,t)=>C.resolve(e,t),invokeObjectRenderCallbacks:!1,hiZOcclusionCulling:!0,renderGraphProfiling:!1,sampleCount:1,multiDrawIndirect:!1});_.setRenderScale("taa"===w?D?.renderScale??1:1),_.setRenderGraphBuilder((e,t)=>{const i=new m.FrameGraphBuilder(t),n=i.createColorTarget("hologyNormalRoughness",{format:"rgba16float"}),o=F?i.createColorTarget("hologyMotionVectors",{format:"rgba16float"}):null;for(const e of H)"beforeScene"===e.stage&&e(i,i.sceneColor);i.addDirectionalShadowPass(),i.addPointShadowPass(),i.addSpotShadowPass(),i.addDepthPrepass(),i.addHiZPasses(),i.addVisibilityPass(),i.addMaterialPass({name:"OpaqueGBufferPass",outputs:null==o?[i.sceneColor,n]:[i.sceneColor,n,o],drawStage:"opaque",loadDepth:!0}),i.addHiZHistoryPass("OpaqueGBufferPass");let r=i.sceneColor;if(M?.enabled){const e=M.addPasses(i,{depth:i.mainDepth,normal:n,velocity:o??void 0});r=M.addCompositePass(i,r,e)}null!=O&&(r=O.addPasses(i,{sceneColor:r,depth:i.mainDepth,normalRoughness:n}));const s=new Set(H.flatMap(e=>e.getSceneTextureReads?.()??[]));i.addSceneTextureCopyPass(r,{sceneColor:s.has("sceneColorSnapshot"),sceneDepth:s.has("sceneDepthSnapshot")||N.some(e=>e.enabled&&null!=e.material.uniforms[a])}),r=$(i,"beforeFog",r),i.addLatePass(r);for(const e of H)"beforeScene"!==e.stage&&e(i,r);null!=V&&(r=V.addPasses(i,{sceneColor:r,depth:i.mainDepth,velocity:o??void 0,volumeObjects:t.fogVolumeObjects,lights:t.extractedLights})),null!=A&&null!=o&&(r=A.addPass(i,r,o,i.mainDepth)),null!=E&&(r=E.addPass(i,r)),r=$(i,"beforeOutline",r),null!=I&&(r=I.addPass(i,r)),r=$(i,"beforeDepthOfField",r),null!=k&&(r=k.addPass(i,r,i.mainDepth)),r=$(i,"beforeColorAdjustment",r),null!=W&&(r=W.addPass(i,r)),r=$(i,"beforeAntiAliasing",r);let l="taa"===B.mode&&null!=D?D.addPass(i,r,i.mainDepth,o??void 0):r;"smaa"===B.mode&&null!=R?l=R.addPass(i,l):"fxaa"===B.mode&&null!=G&&(l=G.addPass(i,l)),l=$(i,"beforeOutput",l),i.present(l)});try{return await _.initialize(),new ExperimentalWebGpuBackend(_,f,C,L,E,G,R,M,O,D,A,V,W,k,I,N,H,B,g)}catch(e){throw _.destroy(),f.dispose(),C.dispose(),L.dispose(),M?.dispose(),R?.dispose(),A?.dispose(),V?.dispose(),I?.dispose(),e}}static supportsCamera(t){return t instanceof e.PerspectiveCamera||t instanceof e.OrthographicCamera}setSize(e,t,i,n=1){const o=this.canvas.width,r=this.canvas.height,s=Math.min(1,Math.max(.25,n)),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!==o||this.canvas.height!==r)&&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)}setWorldEnvironment(t){if(null==t)return void this.nodeMaterialResolver.setWorldEnvironment(null);let i=t.source;if(i.mapping!==e.CubeUVReflectionMapping){let e=this.pmremResults.get(i);null==e&&(this.pmremGenerator??(this.pmremGenerator=this.renderer.createPMREMGenerator()),e=this.pmremGenerator.fromEquirectangular(i),this.pmremResults.set(i,e),this.ownedPmremResults.add(e)),i=e.texture}this.nodeMaterialResolver.setWorldEnvironment({texture:i,intensity:t.intensity})}resetViewHistory(){this.renderer.resetViewHistory(),this.taa?.reset(),this.motionBlur?.reset(),this.gtao?.reset(),this.volumetricFog?.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.motionBlur?.setJitterUv(n?.currentJitterUv.x??0,n?.currentJitterUv.y??0);let o=!1;try{this.renderer.render(e,t,i),o=!0}finally{n?.finishCamera(t),o&&this.motionBlur?.commitCamera()}}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()}inspect(e,t){return inspectWebGpuScene(e,t,this.nodeMaterialResolver)}destroy(){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 o=Array.isArray(t.material)?t.material:[t.material];for(const e of o){if(null==e)continue;e.transparent&&n.transparentMaterials++;const t=!0===e.isWGSLShaderMaterial||!0===e.isLineMaterial,o=!t&&!0===i?.canResolve(e);t||o||(n.materialsReplaced++,e.isShaderMaterial&&n.shaderMaterialsReplaced++)}}),n}export function formatWebGpuSceneCapabilitySummary(e){return`[Hology WebGPU experiment] ${JSON.stringify(e)}`}const h="\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",u="\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
|
@@ -6,6 +6,7 @@ import { CSM } from "three/examples/jsm/csm/CSM.js";
|
|
|
6
6
|
import { GTAOPass } from "three/examples/jsm/postprocessing/GTAOPass.js";
|
|
7
7
|
import { OutlinePass } from './utils/three/outline-pass.js';
|
|
8
8
|
import { type PostProcessSettings, type PostProcessVolume } from "./gameplay/actors/builtin/post-process-volume-actor.js";
|
|
9
|
+
import type { WaterPostProcessVolume } from './gameplay/actors/builtin/components/water-post-process-settings.js';
|
|
9
10
|
import { type PostProcessEffect, type PostProcessEffectOptions } from "./rendering/post-process-effect.js";
|
|
10
11
|
import { Cluster } from "./rendering/cluster-generator.js";
|
|
11
12
|
import { LightVolume } from "./rendering/light-probes/light-volume-capture.js";
|
|
@@ -163,6 +164,10 @@ export declare class RenderingView {
|
|
|
163
164
|
get fpsCap(): number | null;
|
|
164
165
|
set fpsCap(fps: number | null | undefined);
|
|
165
166
|
postProcessVolumes: PostProcessVolume[];
|
|
167
|
+
private readonly waterPostProcessVolumes;
|
|
168
|
+
private readonly underwaterPostProcessEffects;
|
|
169
|
+
private activeUnderwaterPostProcessEffect;
|
|
170
|
+
private activeWaterPostProcessVolume;
|
|
166
171
|
postProcessSettings: PostProcessSettings;
|
|
167
172
|
baseToneMapping: THREE.ToneMapping;
|
|
168
173
|
baseToneMappingExposure: number;
|
|
@@ -244,6 +249,9 @@ export declare class RenderingView {
|
|
|
244
249
|
private previousPresentationPixelRatio;
|
|
245
250
|
resizeRender(force?: boolean): void;
|
|
246
251
|
addPostProcessVolume(volume: PostProcessVolume): void;
|
|
252
|
+
addWaterPostProcessVolume(volume: WaterPostProcessVolume): void;
|
|
253
|
+
removeWaterPostProcessVolume(volume: WaterPostProcessVolume): void;
|
|
254
|
+
private updateUnderwaterPostProcessing;
|
|
247
255
|
removePostProcessVolume(volume: PostProcessVolume): void;
|
|
248
256
|
addPostProcessEffect(source: PostProcessMaterialSource, options?: PostProcessEffectOptions): PostProcessEffect;
|
|
249
257
|
private syncExperimentalWebGpuPostProcessEffects;
|