@solidrt/3d 0.0.59 → 0.0.61
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/AGENTS.md +436 -139
- package/examples/cascades.tsx +10 -4
- package/examples/pick.tsx +6 -4
- package/examples/sprites.tsx +7 -1
- package/package.json +4 -3
- package/src/components/context.tsx +13 -2
- package/src/components/first-person-camera.tsx +1 -1
- package/src/components/index.ts +2 -0
- package/src/components/instanced-mesh.tsx +25 -6
- package/src/components/lod.tsx +3 -1
- package/src/components/orbit-camera.tsx +105 -20
- package/src/components/shots.tsx +93 -0
- package/src/first-person.ts +48 -16
- package/src/gltf.ts +316 -62
- package/src/index.ts +8 -3
- package/src/input.ts +61 -17
- package/src/light.ts +25 -2
- package/src/material.ts +101 -25
- package/src/math.ts +11 -0
- package/src/mesh.ts +68 -21
- package/src/model-data.ts +27 -0
- package/src/model-file.ts +112 -37
- package/src/model.ts +90 -29
- package/src/node.ts +1 -1
- package/src/orbit.ts +526 -108
- package/src/scene-shadows.ts +4 -3
- package/src/scene.ts +201 -189
- package/src/shots.ts +63 -0
- package/src/skeleton.ts +39 -7
- package/tools/environment.ts +1 -0
- package/tools/model.ts +4 -3
- package/tools/node-env.d.ts +25 -0
- package/src/motion.ts +0 -17
- package/src/order.ts +0 -58
package/src/scene.ts
CHANGED
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
// and pointer subsystems are scene-shadows.ts / scene-pointer.ts,
|
|
37
37
|
// built here with the scene's seams as their deps.
|
|
38
38
|
|
|
39
|
-
import { addDraw, createCubeDrawTarget, createDrawTarget, depthTexture, destroyProgram, destroyRenderPipeline, destroyTexture, removeDraw, renderTarget, setDrawBuffers,
|
|
39
|
+
import { addDraw, createCubeDrawTarget, createDrawTarget, depthTexture, destroyProgram, destroyRenderPipeline, destroyTexture, removeDraw, renderTarget, setDrawBuffers, setDrawParams, setDrawRange, setDrawTextures, setTargetParams, setTargetRect, setTargetSize, setTargetTextures } from "@solidrt/core/gpu"
|
|
40
40
|
import * as spatial from "flux:spatial"
|
|
41
|
-
import type { Impact as CoreImpact, NodeId, QueryFilter } from "flux:spatial"
|
|
41
|
+
import type { BindDrawOptions, Impact as CoreImpact, NodeId, QueryFilter } from "flux:spatial"
|
|
42
42
|
import type { BufferId, DrawId, FilterMode, ProgramId, RenderPipelineId, ShaderParams, TextureBindings, TextureId, WrapMode } from "@solidrt/core/gpu"
|
|
43
43
|
import { getOwner, onCleanup } from "@solidrt/core"
|
|
44
44
|
import type { PointerEvent as ElementPointerEvent, WheelEvent as ElementWheelEvent } from "@solidrt/core"
|
|
@@ -59,8 +59,7 @@ import type { BloomOptions, ResolveInput, ResolveOptions, ResolveRecord } from "
|
|
|
59
59
|
import { createEnvironmentPlaceholder, createPrefilter, bufferFormat } from "./environment.ts"
|
|
60
60
|
import type { Prefilter } from "./environment.ts"
|
|
61
61
|
import type { Material } from "./material.ts"
|
|
62
|
-
import {
|
|
63
|
-
import { activateMorph, fillTransform, freeLeaving, leaveScene, makeNode, setTransition, worldInto } from "./node.ts"
|
|
62
|
+
import { activateMorph, fillTransform, freeLeaving, leaveScene, makeNode, setTransition } from "./node.ts"
|
|
64
63
|
import type { SceneHooks, SceneNode, ScenePointerListener } from "./node.ts"
|
|
65
64
|
import { checkInstancePairing, checkMask, instanceBinding, localBounds, publishRecords } from "./mesh.ts"
|
|
66
65
|
import type { InstancedMesh, InstanceNode, Mesh } from "./mesh.ts"
|
|
@@ -117,9 +116,6 @@ function packVolume(volume: Volume, site: string): "capsule" | "box" {
|
|
|
117
116
|
|
|
118
117
|
// pick()'s camera-ray scratch.
|
|
119
118
|
let pickDir: Vec3 = [0, 0, 0]
|
|
120
|
-
// worldInto's out for the light/center readbacks below; nothing here
|
|
121
|
-
// outlives a single call.
|
|
122
|
-
let worldScratch = mat4()
|
|
123
119
|
|
|
124
120
|
/** One picking intersection, Three's intersect result: the mesh, the
|
|
125
121
|
* camera-ray distance in world units, the world-space point, and for a
|
|
@@ -410,6 +406,22 @@ export type ToneMapping = "none" | "aces" | "agx" | "neutral"
|
|
|
410
406
|
|
|
411
407
|
// The uToneMapping value per mode; the RESOLVE set branches on it.
|
|
412
408
|
const TONE_MAPPING_CODE: Record<ToneMapping, number> = { none: 0, aces: 1, agx: 2, neutral: 3 }
|
|
409
|
+
// The core's draw queues (flux:spatial bindDraw), drawn in this order.
|
|
410
|
+
const QUEUE_BACKGROUND = 0
|
|
411
|
+
const QUEUE_OPAQUE = 1
|
|
412
|
+
const QUEUE_CUTOUT = 2
|
|
413
|
+
const QUEUE_TRANSPARENT = 3
|
|
414
|
+
let drawQueue = (m: Material): number =>
|
|
415
|
+
m.transparent === true ? QUEUE_TRANSPARENT : m.cutout === true ? QUEUE_CUTOUT : QUEUE_OPAQUE
|
|
416
|
+
// A mesh entry's draw sink: what the core writes into it and where it
|
|
417
|
+
// sorts, from the material the entry draws with.
|
|
418
|
+
let drawBinding = (material: Material, mesh: Mesh, inst: { count: number } | null): BindDrawOptions => ({
|
|
419
|
+
normal: material.normalMatrix === true,
|
|
420
|
+
count: inst !== null ? inst.count : 1,
|
|
421
|
+
fade: material.lodFade === true,
|
|
422
|
+
queue: drawQueue(material),
|
|
423
|
+
renderOrder: mesh.renderOrder,
|
|
424
|
+
})
|
|
413
425
|
|
|
414
426
|
/** The RESOLVE set's params for a resolve pass of your own (an app-owned
|
|
415
427
|
* atlas tiled views render into, resolved through `resolveFragment` from
|
|
@@ -575,11 +587,24 @@ export type ViewHandle = {
|
|
|
575
587
|
/** Replace the view's layer mask (bitmask): entries for newly admitted
|
|
576
588
|
* meshes attach, masked-out ones detach. */
|
|
577
589
|
setLayers(mask: number): void
|
|
590
|
+
/** The level a LOD group draws in this view (scene.lodLevel through
|
|
591
|
+
* the view's camera). */
|
|
592
|
+
lodLevel(group: SceneNode): number | null
|
|
578
593
|
/** The camera ray through one of the VIEW's pixels, hits nearest first:
|
|
579
594
|
* scene.pick through this view's camera and size, so a mesh under a
|
|
580
595
|
* minimap is picked where the minimap shows it (the scene's query
|
|
581
596
|
* filters apply: visibility, the view's own layer mask is not one). */
|
|
582
597
|
pick(x: number, y: number): Hit[]
|
|
598
|
+
/** scene.project through this view's camera and size: a world point
|
|
599
|
+
* to VIEW pixels. */
|
|
600
|
+
project(point: Vec3): { x: number; y: number; w: number } | null
|
|
601
|
+
/** scene.unproject through this view's camera and size. */
|
|
602
|
+
unproject(x: number, y: number, w: number, out?: Vec3): Vec3
|
|
603
|
+
/** scene.screenRay through this view's camera and size. */
|
|
604
|
+
screenRay(x: number, y: number): ScreenRay
|
|
605
|
+
/** Exactly scene.raycast: the query is the scene's, a view has no
|
|
606
|
+
* camera in it. */
|
|
607
|
+
raycast(origin: Vec3, direction: Vec3, opts?: QueryOptions): Hit[]
|
|
583
608
|
/** Element pointer handlers for the element showing `texture`: the
|
|
584
609
|
* scene's dispatch with THIS VIEW as the root of the walk - nodes get
|
|
585
610
|
* their ordinary handlers, picked through the view's camera, the
|
|
@@ -807,6 +832,13 @@ export type Scene = {
|
|
|
807
832
|
* measured projected size, for the scene, its views and its shadow
|
|
808
833
|
* tiles. Below 1 switches to far levels sooner. */
|
|
809
834
|
setLodBias(bias: number): void
|
|
835
|
+
/** The level a LOD group draws in the scene's own render (the thing to
|
|
836
|
+
* read while tuning thresholds): the index into its levels, nearest
|
|
837
|
+
* first, `levels.length` when culled past the last one, null before
|
|
838
|
+
* the scene measured it. Views read their own through view.lodLevel;
|
|
839
|
+
* an InstancedLod picks per instance, so this is its group's own
|
|
840
|
+
* pick, not any instance's. Throws for a node with no levels. */
|
|
841
|
+
lodLevel(group: SceneNode): number | null
|
|
810
842
|
/**
|
|
811
843
|
* Project a world point to scene pixels: origin top-left, y down - the
|
|
812
844
|
* output texture's own coordinate space, ready for overlay layout (HUD
|
|
@@ -1008,6 +1040,14 @@ function checkLayout(material: Material, geometry: Geometry, what: string): void
|
|
|
1008
1040
|
}
|
|
1009
1041
|
}
|
|
1010
1042
|
|
|
1043
|
+
// The debug name a mesh's draw entries report in the resource inventory
|
|
1044
|
+
// (`/gpu`): its geometry's label (a model part's name, a generator's) -
|
|
1045
|
+
// the same name whether the part is a plain mesh or a population - else a
|
|
1046
|
+
// population's own label, else none.
|
|
1047
|
+
function drawLabel(mesh: Mesh): string | undefined {
|
|
1048
|
+
return mesh.geometry.label ?? mesh._instances?.label
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1011
1051
|
// The index range a mesh's entries draw: its own (setDrawRange) or the
|
|
1012
1052
|
// whole index list.
|
|
1013
1053
|
function meshRange(mesh: Mesh): { firstIndex: number; indexCount: number } {
|
|
@@ -1116,6 +1156,7 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1116
1156
|
label,
|
|
1117
1157
|
autoFree: false,
|
|
1118
1158
|
})
|
|
1159
|
+
spatial.setDrawSort(texture, true)
|
|
1119
1160
|
let resolve = makeResolve(texture, width, height, opts?.resolve, { filter: opts?.filter, wrap: opts?.wrap }, label)
|
|
1120
1161
|
let disposed = false
|
|
1121
1162
|
let scheduled = false
|
|
@@ -1185,12 +1226,13 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1185
1226
|
}
|
|
1186
1227
|
|
|
1187
1228
|
// Live meshes (those holding a draw entry) in add order; the background
|
|
1188
|
-
// entry never joins this list. Draw order is
|
|
1189
|
-
//
|
|
1190
|
-
//
|
|
1191
|
-
//
|
|
1229
|
+
// entry never joins this list. Draw order is the core's (setDrawSort on
|
|
1230
|
+
// every sorted target): each mesh's bind carries its sort key (setDrawKey
|
|
1231
|
+
// re-keys on a renderOrder change), and a flush that moved a node,
|
|
1232
|
+
// changed a binding or moved a target's view re-keys that target's
|
|
1233
|
+
// entries.
|
|
1192
1234
|
let meshes: Mesh[] = []
|
|
1193
|
-
let
|
|
1235
|
+
let keyDraw = (mesh: Mesh) => spatial.setDrawKey(mesh._node!, drawQueue(mesh.material), mesh.renderOrder)
|
|
1194
1236
|
// Attached lights in attach order (= light index); any change to the
|
|
1195
1237
|
// set, a light's fields, or a light's world matrix rewrites the shared
|
|
1196
1238
|
// light params at the end of the sync - one write, however many meshes.
|
|
@@ -1319,60 +1361,34 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1319
1361
|
// matrix too: rewrite the whole array once.
|
|
1320
1362
|
shadowSys.markMatricesDirty()
|
|
1321
1363
|
}
|
|
1322
|
-
let orderDirty = false
|
|
1323
|
-
// The order last handed to the engine: a resort that lands on the same
|
|
1324
|
-
// permutation (the common case under a moving camera) issues nothing.
|
|
1325
|
-
let lastOrder: DrawId[] = []
|
|
1326
1364
|
// `skybox`: the entry runs the library's cube-map fragment (its params
|
|
1327
1365
|
// and textures are rewritable in place); false = app GLSL.
|
|
1328
1366
|
// `sky` is the skybox source (its cube and knobs) for the bake, null
|
|
1329
1367
|
// for a GLSL background.
|
|
1330
1368
|
// `entries` is the background's draw entry per target that draws it:
|
|
1331
|
-
// the scene's own and every sky view's (a reflection probe)
|
|
1332
|
-
// in
|
|
1369
|
+
// the scene's own and every sky view's (a reflection probe). Each is
|
|
1370
|
+
// bound to the scene root in the background queue, so the core's sort
|
|
1371
|
+
// keeps it first and its visibility follows the root's; the root's
|
|
1372
|
+
// matrix is not written (the program has no uModel).
|
|
1333
1373
|
let background: { pipeline: RenderPipelineId; program: ProgramId; sky: SkyboxOptions | null; entries: Map<TextureId, DrawId> } | null = null
|
|
1334
|
-
|
|
1335
|
-
// first mesh entry; undefined appends, right for a target with none yet).
|
|
1336
|
-
let attachBackground = (target: TextureId, before: DrawId | undefined) => {
|
|
1374
|
+
let attachBackground = (target: TextureId) => {
|
|
1337
1375
|
if (background === null) return
|
|
1338
1376
|
let sky = background.sky
|
|
1339
1377
|
let params = sky === null ? null : skyboxParams(sky, "scene.setBackground")
|
|
1340
1378
|
let textures = sky === null ? undefined : { uSky: sky.cube }
|
|
1341
|
-
|
|
1379
|
+
let entry = addDraw(target, background.pipeline, params, { vertexCount: 3, textures, instanceCount: 0 })
|
|
1380
|
+
background.entries.set(target, entry)
|
|
1381
|
+
spatial.bindDraw(root._node!, target, entry, { params: false, queue: QUEUE_BACKGROUND })
|
|
1382
|
+
}
|
|
1383
|
+
let detachBackground = (target: TextureId, entry: DrawId) => {
|
|
1384
|
+
if (root._node !== null) spatial.unbindDraw(root._node, target)
|
|
1385
|
+
removeDraw(target, entry)
|
|
1342
1386
|
}
|
|
1343
1387
|
// The environment cube bound as uEnv on every receiving target (the
|
|
1344
1388
|
// light rewrite seeds it on new targets); null binds the placeholder,
|
|
1345
1389
|
// the scene's own 1x1 black cube.
|
|
1346
1390
|
let environment: TextureId | null = null
|
|
1347
1391
|
let envPlaceholder = createEnvironmentPlaceholder(label + "-env-none")
|
|
1348
|
-
let sortEntries = () => {
|
|
1349
|
-
orderDirty = false
|
|
1350
|
-
let order = orderEntries(meshes, camera.view, background?.entries.get(texture))
|
|
1351
|
-
if (order.length === lastOrder.length && order.every((id, i) => id === lastOrder[i])) return
|
|
1352
|
-
lastOrder = order
|
|
1353
|
-
setDrawOrder(texture, order)
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
|
-
// The transparent sort keys: each transparent mesh's local-bounds
|
|
1357
|
-
// center carried through its world matrix (read from the core), at
|
|
1358
|
-
// sort time only - opaque meshes never need one.
|
|
1359
|
-
let refreshCenters = () => {
|
|
1360
|
-
if (transparentCount < 2) return
|
|
1361
|
-
for (let mesh of meshes) {
|
|
1362
|
-
if (!mesh._transparent || mesh._node === null) continue
|
|
1363
|
-
let b = localBounds(mesh)
|
|
1364
|
-
let m = worldInto(worldScratch, mesh)
|
|
1365
|
-
let cx = 0, cy = 0, cz = 0
|
|
1366
|
-
if (b !== null) {
|
|
1367
|
-
cx = (b[0]! + b[3]!) / 2
|
|
1368
|
-
cy = (b[1]! + b[4]!) / 2
|
|
1369
|
-
cz = (b[2]! + b[5]!) / 2
|
|
1370
|
-
}
|
|
1371
|
-
mesh._center[0] = m[0] * cx + m[4] * cy + m[8] * cz + m[12]
|
|
1372
|
-
mesh._center[1] = m[1] * cx + m[5] * cy + m[9] * cz + m[13]
|
|
1373
|
-
mesh._center[2] = m[2] * cx + m[6] * cy + m[10] * cz + m[14]
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1376
1392
|
let camera = makeCamera()
|
|
1377
1393
|
let clip: Vec4 = [0, 0, 0, 0]
|
|
1378
1394
|
let pickOrigin: Vec3 = [0, 0, 0]
|
|
@@ -1417,6 +1433,26 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1417
1433
|
pickDir[2] = -v[10]
|
|
1418
1434
|
}
|
|
1419
1435
|
let pixelRay = (x: number, y: number): void => pixelRayOf(camera, width, height, x, y)
|
|
1436
|
+
// project() for any camera over a tw x th target (the scene's, a view's).
|
|
1437
|
+
let projectWith = (cam: Camera, tw: number, th: number, point: Vec3): { x: number; y: number; w: number } | null => {
|
|
1438
|
+
ensureCamera(cam, tw, th)
|
|
1439
|
+
transformPoint(clip, cam.viewProj, point)
|
|
1440
|
+
let w = clip[3]
|
|
1441
|
+
if (w < 1e-6) return null
|
|
1442
|
+
// perspective() bakes the y-down clip flip, so NDC maps straight to
|
|
1443
|
+
// top-left-origin pixels with no negation here.
|
|
1444
|
+
let x = ((clip[0] / w) * 0.5 + 0.5) * tw
|
|
1445
|
+
let y = ((clip[1] / w) * 0.5 + 0.5) * th
|
|
1446
|
+
if (cam.ortho !== null) {
|
|
1447
|
+
// An orthographic clip w is 1 everywhere (every point projects, the
|
|
1448
|
+
// divides above are no-ops) and carries no depth, so `w` reports
|
|
1449
|
+
// the camera-forward distance off the view row instead - the same
|
|
1450
|
+
// meaning as the perspective clip w, and unproject's exact input.
|
|
1451
|
+
let v = cam.view
|
|
1452
|
+
w = -(v[2] * point[0] + v[6] * point[1] + v[10] * point[2] + v[14])
|
|
1453
|
+
}
|
|
1454
|
+
return { x, y, w }
|
|
1455
|
+
}
|
|
1420
1456
|
|
|
1421
1457
|
// Views (scene.createView): more targets drawing the same meshes from
|
|
1422
1458
|
// their own cameras. A view holds one entry per mesh in its target,
|
|
@@ -1457,11 +1493,6 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1457
1493
|
ownNames: Set<string>
|
|
1458
1494
|
camera: Camera
|
|
1459
1495
|
entries: Map<Mesh, DrawId>
|
|
1460
|
-
orderDirty: boolean
|
|
1461
|
-
lastOrder: DrawId[]
|
|
1462
|
-
/** A shadow tile measures LOD by the SCENE camera: set once at
|
|
1463
|
-
* creation, then on every scene camera move. */
|
|
1464
|
-
lodViewSet: boolean
|
|
1465
1496
|
/** A reflection probe's target is a cube draw target of this face
|
|
1466
1497
|
* edge, rendered face by face through probe.update(); null for a
|
|
1467
1498
|
* 2D view. */
|
|
@@ -1515,18 +1546,17 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1515
1546
|
...meshRange(mesh),
|
|
1516
1547
|
textures: entryTextures(mesh.material, mesh, morph),
|
|
1517
1548
|
instanceCount: 0,
|
|
1549
|
+
label: drawLabel(mesh),
|
|
1518
1550
|
})
|
|
1519
1551
|
// The core turns the entry on (with the world matrix) at the next
|
|
1520
1552
|
// flush, and off again whenever the node or an ancestor hides.
|
|
1521
|
-
spatial.bindDraw(mesh._node!, texture, mesh._entry, mesh.material
|
|
1522
|
-
orderDirty = true
|
|
1553
|
+
spatial.bindDraw(mesh._node!, texture, mesh._entry, drawBinding(mesh.material, mesh, inst))
|
|
1523
1554
|
}
|
|
1524
1555
|
let detachScene = (mesh: Mesh) => {
|
|
1525
1556
|
if (mesh._entry === null) return
|
|
1526
1557
|
if (mesh._node !== null) spatial.unbindDraw(mesh._node, texture)
|
|
1527
1558
|
if (!disposed) removeDraw(texture, mesh._entry)
|
|
1528
1559
|
mesh._entry = null
|
|
1529
|
-
orderDirty = true
|
|
1530
1560
|
}
|
|
1531
1561
|
// The material a view draws a mesh with: a shadow view lets a caster's
|
|
1532
1562
|
// material pick its own depth variant (its cull side, cutout, skinning
|
|
@@ -1568,10 +1598,10 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1568
1598
|
...meshRange(mesh),
|
|
1569
1599
|
textures: entryTextures(material, mesh, morph),
|
|
1570
1600
|
instanceCount: 0,
|
|
1601
|
+
label: drawLabel(mesh),
|
|
1571
1602
|
})
|
|
1572
|
-
spatial.bindDraw(mesh._node!, v.texture, entry, material
|
|
1603
|
+
spatial.bindDraw(mesh._node!, v.texture, entry, drawBinding(material, mesh, inst))
|
|
1573
1604
|
v.entries.set(mesh, entry)
|
|
1574
|
-
v.orderDirty = true
|
|
1575
1605
|
}
|
|
1576
1606
|
let detachView = (v: ViewRecord, mesh: Mesh) => {
|
|
1577
1607
|
v.skipped.delete(mesh)
|
|
@@ -1580,25 +1610,15 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1580
1610
|
v.entries.delete(mesh)
|
|
1581
1611
|
if (mesh._node !== null) spatial.unbindDraw(mesh._node, v.texture)
|
|
1582
1612
|
if (!v.disposed) removeDraw(v.texture, entry)
|
|
1583
|
-
v.orderDirty = true
|
|
1584
|
-
}
|
|
1585
|
-
let sortView = (v: ViewRecord) => {
|
|
1586
|
-
v.orderDirty = false
|
|
1587
|
-
if (v.override !== null) return
|
|
1588
|
-
let order = orderEntries(meshes, v.camera.view, background?.entries.get(v.texture), m => v.entries.get(m as Mesh) ?? null)
|
|
1589
|
-
if (order.length === v.lastOrder.length && order.every((id, i) => id === v.lastOrder[i])) return
|
|
1590
|
-
v.lastOrder = order
|
|
1591
|
-
setDrawOrder(v.texture, order)
|
|
1592
1613
|
}
|
|
1593
1614
|
let disposeView = (v: ViewRecord) => {
|
|
1594
1615
|
if (v.disposed) return
|
|
1595
1616
|
v.disposed = true
|
|
1596
|
-
background?.entries.delete(v.texture)
|
|
1617
|
+
if (background?.entries.delete(v.texture) && root._node !== null) spatial.unbindDraw(root._node, v.texture)
|
|
1597
1618
|
for (let mesh of v.entries.keys()) if (mesh._node !== null) spatial.unbindDraw(mesh._node, v.texture)
|
|
1598
1619
|
v.entries.clear()
|
|
1599
1620
|
for (let light of lights) if (light.type === "directional" && light._node !== null) spatial.unbindSlot(light._node, v.texture)
|
|
1600
|
-
spatial.
|
|
1601
|
-
spatial.setLodView(v.texture, null)
|
|
1621
|
+
spatial.setView(v.texture, null)
|
|
1602
1622
|
// Drain the zeroed direction slots while the target still exists.
|
|
1603
1623
|
spatial.flush()
|
|
1604
1624
|
if (v.resolve !== null) disposeResolve(v.resolve)
|
|
@@ -1670,6 +1690,13 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1670
1690
|
x: vopts.x,
|
|
1671
1691
|
y: vopts.y,
|
|
1672
1692
|
})
|
|
1693
|
+
// An override view (a shadow tile, a depth or id pass) draws in add
|
|
1694
|
+
// order; every other view sorts like the scene.
|
|
1695
|
+
if (override === null) spatial.setDrawSort(buffer, true)
|
|
1696
|
+
// A shadow tile measures LOD by the scene camera; every other view
|
|
1697
|
+
// by its own, under the scene's bias.
|
|
1698
|
+
if (shadowFilter !== null) spatial.setLodReference(buffer, texture)
|
|
1699
|
+
else spatial.setLodBias(buffer, lodBias)
|
|
1673
1700
|
let v: ViewRecord = {
|
|
1674
1701
|
texture: buffer,
|
|
1675
1702
|
// A shadow view is depth only, a probe's faces are read as a cube,
|
|
@@ -1690,18 +1717,13 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1690
1717
|
ownNames: new Set(),
|
|
1691
1718
|
camera: makeCamera(),
|
|
1692
1719
|
entries: new Map(),
|
|
1693
|
-
orderDirty: true,
|
|
1694
|
-
lastOrder: [],
|
|
1695
|
-
lodViewSet: false,
|
|
1696
1720
|
cube,
|
|
1697
1721
|
probeCube: null,
|
|
1698
1722
|
sky,
|
|
1699
1723
|
disposed: false,
|
|
1700
1724
|
}
|
|
1701
1725
|
views.push(v)
|
|
1702
|
-
|
|
1703
|
-
// later sort keeps it.
|
|
1704
|
-
if (sky) attachBackground(v.texture, undefined)
|
|
1726
|
+
if (sky) attachBackground(v.texture)
|
|
1705
1727
|
// The light rewrite seeds the shadow set (maps, casts, biases,
|
|
1706
1728
|
// matrices) on the new target too.
|
|
1707
1729
|
lightsDirty = true
|
|
@@ -1737,36 +1759,25 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1737
1759
|
schedule: () => hooks._schedule(),
|
|
1738
1760
|
})
|
|
1739
1761
|
|
|
1740
|
-
// A camera write is also the target's
|
|
1741
|
-
//
|
|
1742
|
-
// flush below, which switches the
|
|
1743
|
-
//
|
|
1744
|
-
//
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1762
|
+
// A camera write is also the target's view for the core: what it culls
|
|
1763
|
+
// by, measures projected size with (eye, forward, focal, ortho) and
|
|
1764
|
+
// sorts depth against - set before the flush below, which switches the
|
|
1765
|
+
// entries outside it off. The scene and each view measure by their own
|
|
1766
|
+
// camera; a shadow tile by the SCENE camera, through the LOD reference
|
|
1767
|
+
// set at its creation, so a caster draws the level the camera sees and
|
|
1768
|
+
// its shadow matches. Cube face renders (probes) set it per face: six
|
|
1769
|
+
// cameras share one target, so each face sets its view and flushes
|
|
1770
|
+
// before it renders.
|
|
1771
|
+
let viewScratch = new Float32Array(16)
|
|
1772
|
+
let projScratch = new Float32Array(16)
|
|
1773
|
+
let setView = (target: TextureId, cam: Camera) => {
|
|
1774
|
+
viewScratch.set(cam.view)
|
|
1775
|
+
projScratch.set(cam.proj)
|
|
1776
|
+
spatial.setView(target, viewScratch, projScratch)
|
|
1749
1777
|
}
|
|
1750
|
-
// A camera write is also the target's LOD view - eye, vertical focal
|
|
1751
|
-
// factor (the magnitude of proj[5] under either projection: the
|
|
1752
|
-
// projections bake in a y-down clip flip, so the element is negative),
|
|
1753
|
-
// the ortho flag and the scene's bias - what the core measures
|
|
1754
|
-
// projected size with. The scene and each view measure by their own
|
|
1755
|
-
// camera; a shadow tile by the SCENE camera, so a caster draws the
|
|
1756
|
-
// level the camera sees and its shadow matches.
|
|
1757
1778
|
let lodBias = opts?.lodBias ?? 1
|
|
1758
1779
|
if (!(lodBias > 0 && Number.isFinite(lodBias))) throw new Error("createScene: lodBias must be a positive number, got " + lodBias)
|
|
1759
|
-
|
|
1760
|
-
let lodScratch = new Float32Array(6)
|
|
1761
|
-
let setLodView = (target: TextureId, cam: Camera) => {
|
|
1762
|
-
lodScratch[0] = cam.eye[0]
|
|
1763
|
-
lodScratch[1] = cam.eye[1]
|
|
1764
|
-
lodScratch[2] = cam.eye[2]
|
|
1765
|
-
lodScratch[3] = Math.abs(cam.proj[5]!)
|
|
1766
|
-
lodScratch[4] = cam.ortho !== null ? 1 : 0
|
|
1767
|
-
lodScratch[5] = lodBias
|
|
1768
|
-
spatial.setLodView(target, lodScratch)
|
|
1769
|
-
}
|
|
1780
|
+
spatial.setLodBias(texture, lodBias)
|
|
1770
1781
|
let sync = () => {
|
|
1771
1782
|
scheduled = false
|
|
1772
1783
|
if (disposed) return
|
|
@@ -1802,37 +1813,20 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1802
1813
|
if (budgetError === null) budgetError = shadowError
|
|
1803
1814
|
ensureCamera(camera, width, height)
|
|
1804
1815
|
let cameraMoved = camera.pending
|
|
1805
|
-
let biasChanged = lodBiasDirty
|
|
1806
|
-
lodBiasDirty = false
|
|
1807
1816
|
if (camera.pending) {
|
|
1808
1817
|
camera.pending = false
|
|
1809
1818
|
setTargetParams(texture, cameraParams(camera))
|
|
1810
|
-
|
|
1811
|
-
if (transparentCount > 1) orderDirty = true
|
|
1819
|
+
setView(texture, camera)
|
|
1812
1820
|
}
|
|
1813
|
-
if (cameraMoved || biasChanged) setLodView(texture, camera)
|
|
1814
1821
|
shadowSys.placeCameras(cameraMoved)
|
|
1815
1822
|
for (let v of views) {
|
|
1816
1823
|
ensureCamera(v.camera, v.width, v.height)
|
|
1817
|
-
|
|
1818
|
-
if (viewMoved) {
|
|
1824
|
+
if (v.camera.pending) {
|
|
1819
1825
|
v.camera.pending = false
|
|
1820
1826
|
setTargetParams(v.texture, cameraParams(v.camera))
|
|
1821
|
-
|
|
1822
|
-
if (transparentCount > 1) v.orderDirty = true
|
|
1827
|
+
setView(v.texture, v.camera)
|
|
1823
1828
|
if (v.shadowFilter !== null) shadowSys.markMatricesDirty()
|
|
1824
1829
|
}
|
|
1825
|
-
// Probe faces measure nothing (six cameras, one target), like their
|
|
1826
|
-
// frustum.
|
|
1827
|
-
if (v.cube !== null) continue
|
|
1828
|
-
if (v.shadowFilter !== null) {
|
|
1829
|
-
if (cameraMoved || biasChanged || !v.lodViewSet) {
|
|
1830
|
-
v.lodViewSet = true
|
|
1831
|
-
setLodView(v.texture, camera)
|
|
1832
|
-
}
|
|
1833
|
-
} else if (viewMoved || biasChanged) {
|
|
1834
|
-
setLodView(v.texture, v.camera)
|
|
1835
|
-
}
|
|
1836
1830
|
}
|
|
1837
1831
|
// Light bookkeeping first, so a fresh direction-slot bind is seeded
|
|
1838
1832
|
// by the flush below in the same sync.
|
|
@@ -1857,21 +1851,9 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1857
1851
|
// uModel/uNormal, visibility switch and direction slots.
|
|
1858
1852
|
spatial.flush()
|
|
1859
1853
|
if (moved.length > 0) {
|
|
1860
|
-
// Which meshes moved is the core's knowledge now, so any move with
|
|
1861
|
-
// two or more transparent meshes re-sorts (sortEntries issues nothing
|
|
1862
|
-
// when the permutation is unchanged).
|
|
1863
|
-
if (transparentCount > 1) {
|
|
1864
|
-
orderDirty = true
|
|
1865
|
-
for (let v of views) v.orderDirty = true
|
|
1866
|
-
}
|
|
1867
1854
|
for (let n of moved) n._moved = false
|
|
1868
1855
|
moved.length = 0
|
|
1869
1856
|
}
|
|
1870
|
-
// The sort keys are world-space and camera-independent: refreshed once
|
|
1871
|
-
// for every sort this sync.
|
|
1872
|
-
if (orderDirty || views.some(v => v.orderDirty)) refreshCenters()
|
|
1873
|
-
if (orderDirty) sortEntries()
|
|
1874
|
-
for (let v of views) if (v.orderDirty) sortView(v)
|
|
1875
1857
|
if (budgetError !== null) {
|
|
1876
1858
|
if (opts?.onError !== undefined) opts.onError(budgetError)
|
|
1877
1859
|
else throw budgetError
|
|
@@ -1957,6 +1939,15 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1957
1939
|
)
|
|
1958
1940
|
}
|
|
1959
1941
|
if (inst !== null) checkInstancePairing(mesh.material, inst, "Mesh material")
|
|
1942
|
+
// An anchored population's records are relative to the anchor, so
|
|
1943
|
+
// it must be an ancestor (the core's contract: an anchor move then
|
|
1944
|
+
// restages every record); anything else binds records that never
|
|
1945
|
+
// follow it - errors here, at add().
|
|
1946
|
+
if (inst !== null && inst.anchor !== null) {
|
|
1947
|
+
let p: SceneNode | null = mesh.parent
|
|
1948
|
+
while (p !== null && p !== inst.anchor) p = p.parent
|
|
1949
|
+
if (p === null) throw new Error("An instanced mesh's anchor must be one of its ancestors when it is added to a scene")
|
|
1950
|
+
}
|
|
1960
1951
|
let bufs = acquireGeometryBuffers(mesh.geometry)
|
|
1961
1952
|
mesh._buffers = bufs
|
|
1962
1953
|
attachScene(mesh)
|
|
@@ -1966,20 +1957,27 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1966
1957
|
if (inst !== null && inst.streams.some(s => s.dirty !== null)) recordsDirty.add(mesh)
|
|
1967
1958
|
// Picking: an ordinary mesh's node carries its geometry's shape,
|
|
1968
1959
|
// which is its box in the core index (following every
|
|
1969
|
-
// updateVertices) and its triangle narrowphase.
|
|
1970
|
-
//
|
|
1971
|
-
//
|
|
1972
|
-
//
|
|
1973
|
-
//
|
|
1974
|
-
// is
|
|
1960
|
+
// updateVertices) and its triangle narrowphase. An instanced mesh's
|
|
1961
|
+
// instances are the leaves that pick, each with the shape, so its
|
|
1962
|
+
// own node stays OUT of the index: explicit population bounds are
|
|
1963
|
+
// its cull box only (else the cull group of its instances). A
|
|
1964
|
+
// record mesh's records are opaque, so its explicit bounds are its
|
|
1965
|
+
// box in the index (without them it is not picked at all), as is a
|
|
1966
|
+
// sprite's unit box (its triangles lie wherever the camera is, not
|
|
1967
|
+
// where the geometry says).
|
|
1975
1968
|
if (inst === null && !mesh._sprite) spatial.setShape(mesh._node!, bufs.shape)
|
|
1969
|
+
else if (inst !== null && inst.nodes !== null) spatial.setCullBounds(mesh._node!, inst.bounds)
|
|
1976
1970
|
else spatial.setBounds(mesh._node!, localBounds(mesh))
|
|
1977
1971
|
// A rebuilt entry (setGeometry) re-shapes the live instances to the
|
|
1978
1972
|
// new geometry; their record bindings are untouched. On a first
|
|
1979
|
-
// entry none has a core node yet - they enter after the mesh
|
|
1973
|
+
// entry none has a core node yet - they enter after the mesh -
|
|
1974
|
+
// except under an anchor, where an instance in an earlier branch
|
|
1975
|
+
// of the anchor's subtree is already in and waiting to be bound.
|
|
1980
1976
|
if (inst !== null && inst.nodes !== null) {
|
|
1981
1977
|
for (let n of inst.nodes.slots) {
|
|
1982
|
-
if (n
|
|
1978
|
+
if (n === null || n._node === null) continue
|
|
1979
|
+
if (inst.anchor !== null && !byNode.has(n._node)) this._attachInstance(n)
|
|
1980
|
+
else spatial.setShape(n._node, bufs.shape)
|
|
1983
1981
|
}
|
|
1984
1982
|
}
|
|
1985
1983
|
// Culling: the gate and margin only when off the defaults, and a
|
|
@@ -1997,9 +1995,6 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
1997
1995
|
if (inst !== null && inst.nodes !== null && inst.bounds === null) groupDirty.add(mesh as InstancedMesh)
|
|
1998
1996
|
byNode.set(mesh._node!, mesh)
|
|
1999
1997
|
meshes.push(mesh)
|
|
2000
|
-
mesh._transparent = mesh.material.transparent === true
|
|
2001
|
-
if (mesh._transparent) transparentCount++
|
|
2002
|
-
orderDirty = true
|
|
2003
1998
|
this._schedule()
|
|
2004
1999
|
},
|
|
2005
2000
|
_detach(mesh) {
|
|
@@ -2010,15 +2005,14 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
2010
2005
|
detachScene(mesh)
|
|
2011
2006
|
if (mesh._node !== null) {
|
|
2012
2007
|
spatial.setShape(mesh._node, null)
|
|
2013
|
-
spatial.
|
|
2008
|
+
if (mesh._instances !== null && mesh._instances.nodes !== null) spatial.setCullBounds(mesh._node, null)
|
|
2009
|
+
else spatial.setBounds(mesh._node, null)
|
|
2014
2010
|
byNode.delete(mesh._node)
|
|
2015
2011
|
}
|
|
2016
2012
|
releaseGeometryBuffers(mesh._buffers)
|
|
2017
2013
|
mesh._buffers = null
|
|
2018
2014
|
let i = meshes.indexOf(mesh)
|
|
2019
2015
|
if (i >= 0) meshes.splice(i, 1)
|
|
2020
|
-
if (mesh._transparent) transparentCount--
|
|
2021
|
-
orderDirty = true
|
|
2022
2016
|
}
|
|
2023
2017
|
mesh._entry = null
|
|
2024
2018
|
},
|
|
@@ -2029,17 +2023,23 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
2029
2023
|
// geometry buffers and core node are live - a mesh masked out of
|
|
2030
2024
|
// the scene included, whose instances still pick.
|
|
2031
2025
|
if (mesh === null || mesh._buffers === null || mesh._node === null || instance._node === null) return
|
|
2026
|
+
// A population with an anchor may enter AFTER an instance living
|
|
2027
|
+
// elsewhere in the anchor's subtree (a later sibling branch); the
|
|
2028
|
+
// mesh's own attach then binds the instances it finds waiting.
|
|
2029
|
+
let anchor = (mesh._instances.anchor ?? mesh)._node
|
|
2030
|
+
if (anchor === null || byNode.has(instance._node)) return
|
|
2032
2031
|
spatial.setShape(instance._node, mesh._buffers.shape)
|
|
2033
2032
|
spatial.setLayers(instance._node, mesh.layers)
|
|
2034
|
-
// The record is the instance's placement
|
|
2035
|
-
//
|
|
2036
|
-
//
|
|
2037
|
-
//
|
|
2033
|
+
// The record is the instance's placement relative to the anchor -
|
|
2034
|
+
// the mesh node itself, or the ancestor the mesh sits under at
|
|
2035
|
+
// identity - staged by the core at the next flush. One buffer per
|
|
2036
|
+
// LOD level of an instanced LOD (the population is the group and
|
|
2037
|
+
// the anchor, its levels its children at identity).
|
|
2038
2038
|
let levels = mesh._instances.levels
|
|
2039
2039
|
instanceBuffers.length = 0
|
|
2040
2040
|
if (levels === null) instanceBuffers.push(mesh._instances.matrix)
|
|
2041
2041
|
else for (let l of levels) instanceBuffers.push(l._instances.matrix)
|
|
2042
|
-
spatial.bindMatrixRecord(instance._node, instanceBuffers, instance._slot,
|
|
2042
|
+
spatial.bindMatrixRecord(instance._node, instanceBuffers, instance._slot, anchor)
|
|
2043
2043
|
// A morphing population: the instance's register takes its slot's
|
|
2044
2044
|
// row now (a recycled slot's row is overwritten, not inherited).
|
|
2045
2045
|
if (mesh._instances.morph !== null) activateMorph(instance)
|
|
@@ -2124,9 +2124,9 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
2124
2124
|
recordsDirty.add(mesh)
|
|
2125
2125
|
this._schedule()
|
|
2126
2126
|
},
|
|
2127
|
-
_reorder() {
|
|
2128
|
-
|
|
2129
|
-
|
|
2127
|
+
_reorder(mesh) {
|
|
2128
|
+
if (disposed || mesh._buffers === null || mesh._node === null) return
|
|
2129
|
+
keyDraw(mesh)
|
|
2130
2130
|
this._schedule()
|
|
2131
2131
|
},
|
|
2132
2132
|
_moved(node) {
|
|
@@ -2234,9 +2234,15 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
2234
2234
|
if (!(bias > 0 && Number.isFinite(bias))) throw new Error("scene.setLodBias: bias must be a positive number, got " + bias)
|
|
2235
2235
|
if (disposed || bias === lodBias) return
|
|
2236
2236
|
lodBias = bias
|
|
2237
|
-
|
|
2237
|
+
spatial.setLodBias(texture, bias)
|
|
2238
|
+
// Shadow tiles take the scene's through their LOD reference.
|
|
2239
|
+
for (let v of views) if (v.shadowFilter === null && !v.disposed) spatial.setLodBias(v.texture, bias)
|
|
2238
2240
|
hooks._schedule()
|
|
2239
2241
|
},
|
|
2242
|
+
lodLevel(group) {
|
|
2243
|
+
if (disposed || group._node === null) return null
|
|
2244
|
+
return spatial.lodLevel(group._node, texture)
|
|
2245
|
+
},
|
|
2240
2246
|
setLayers(mask) {
|
|
2241
2247
|
checkMask(mask, "scene.setLayers")
|
|
2242
2248
|
if (disposed || sceneMask === mask) return
|
|
@@ -2273,7 +2279,7 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
2273
2279
|
let sky = source === null || typeof source === "string" ? null : source
|
|
2274
2280
|
if (sky !== null) skyboxParams(sky, "scene.setBackground")
|
|
2275
2281
|
if (background !== null) {
|
|
2276
|
-
for (let [target, entry] of background.entries)
|
|
2282
|
+
for (let [target, entry] of background.entries) detachBackground(target, entry)
|
|
2277
2283
|
destroyRenderPipeline(background.pipeline)
|
|
2278
2284
|
destroyProgram(background.program)
|
|
2279
2285
|
background = null
|
|
@@ -2281,11 +2287,8 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
2281
2287
|
if (source === null) return
|
|
2282
2288
|
let built = sky === null ? backgroundPipeline(source as string, label + "-background") : backgroundPipeline(SKYBOX_FRAGMENT, label + "-skybox")
|
|
2283
2289
|
background = { pipeline: built.pipeline, program: built.program, sky, entries: new Map() }
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
// later sort keeps it there.
|
|
2287
|
-
attachBackground(texture, meshes.find(m => m._entry !== null)?._entry ?? undefined)
|
|
2288
|
-
for (let v of views) if (v.sky && !v.disposed) attachBackground(v.texture, v.lastOrder[0] ?? v.entries.values().next().value)
|
|
2290
|
+
attachBackground(texture)
|
|
2291
|
+
for (let v of views) if (v.sky && !v.disposed) attachBackground(v.texture)
|
|
2289
2292
|
},
|
|
2290
2293
|
setEnvironment(env) {
|
|
2291
2294
|
if (disposed) return
|
|
@@ -2318,23 +2321,7 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
2318
2321
|
hooks._schedule()
|
|
2319
2322
|
},
|
|
2320
2323
|
project(point) {
|
|
2321
|
-
|
|
2322
|
-
transformPoint(clip, camera.viewProj, point)
|
|
2323
|
-
let w = clip[3]
|
|
2324
|
-
if (w < 1e-6) return null
|
|
2325
|
-
// perspective() bakes the y-down clip flip, so NDC maps straight to
|
|
2326
|
-
// top-left-origin pixels with no negation here.
|
|
2327
|
-
let x = ((clip[0] / w) * 0.5 + 0.5) * width
|
|
2328
|
-
let y = ((clip[1] / w) * 0.5 + 0.5) * height
|
|
2329
|
-
if (camera.ortho !== null) {
|
|
2330
|
-
// An orthographic clip w is 1 everywhere (every point projects, the
|
|
2331
|
-
// divides above are no-ops) and carries no depth, so `w` reports
|
|
2332
|
-
// the camera-forward distance off the view row instead - the same
|
|
2333
|
-
// meaning as the perspective clip w, and unproject's exact input.
|
|
2334
|
-
let v = camera.view
|
|
2335
|
-
w = -(v[2] * point[0] + v[6] * point[1] + v[10] * point[2] + v[14])
|
|
2336
|
-
}
|
|
2337
|
-
return { x, y, w }
|
|
2324
|
+
return projectWith(camera, width, height, point)
|
|
2338
2325
|
},
|
|
2339
2326
|
viewProj(out) {
|
|
2340
2327
|
ensureCamera(camera, width, height)
|
|
@@ -2482,11 +2469,35 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
2482
2469
|
}
|
|
2483
2470
|
hooks._schedule()
|
|
2484
2471
|
},
|
|
2472
|
+
lodLevel(group) {
|
|
2473
|
+
if (v.disposed || group._node === null) return null
|
|
2474
|
+
return spatial.lodLevel(group._node, v.texture)
|
|
2475
|
+
},
|
|
2485
2476
|
pick(x, y) {
|
|
2486
2477
|
if (v.disposed) return []
|
|
2487
2478
|
pixelRayOf(v.camera, v.width, v.height, x, y)
|
|
2488
2479
|
return raycastAs(v.texture, pickOrigin, pickDir, undefined)
|
|
2489
2480
|
},
|
|
2481
|
+
project(point) {
|
|
2482
|
+
return projectWith(v.camera, v.width, v.height, point)
|
|
2483
|
+
},
|
|
2484
|
+
unproject(x, y, w, out = [0, 0, 0]) {
|
|
2485
|
+
pixelRayOf(v.camera, v.width, v.height, x, y)
|
|
2486
|
+
out[0] = pickOrigin[0] + w * pickDir[0]
|
|
2487
|
+
out[1] = pickOrigin[1] + w * pickDir[1]
|
|
2488
|
+
out[2] = pickOrigin[2] + w * pickDir[2]
|
|
2489
|
+
return out
|
|
2490
|
+
},
|
|
2491
|
+
screenRay(x, y) {
|
|
2492
|
+
pixelRayOf(v.camera, v.width, v.height, x, y)
|
|
2493
|
+
return {
|
|
2494
|
+
origin: [pickOrigin[0], pickOrigin[1], pickOrigin[2]],
|
|
2495
|
+
direction: [pickDir[0], pickDir[1], pickDir[2]],
|
|
2496
|
+
}
|
|
2497
|
+
},
|
|
2498
|
+
raycast(origin, direction, rayOpts) {
|
|
2499
|
+
return raycastAs(texture, origin, direction, rayOpts)
|
|
2500
|
+
},
|
|
2490
2501
|
get handlers() {
|
|
2491
2502
|
return viewPointer.handlers
|
|
2492
2503
|
},
|
|
@@ -2544,8 +2555,7 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
2544
2555
|
spatial.destroyNode(root._node)
|
|
2545
2556
|
root._node = null
|
|
2546
2557
|
}
|
|
2547
|
-
spatial.
|
|
2548
|
-
spatial.setLodView(texture, null)
|
|
2558
|
+
spatial.setView(texture, null)
|
|
2549
2559
|
// Drain the zeroed direction slots the teardown queued while the
|
|
2550
2560
|
// targets still exist; afterwards their groups are gone.
|
|
2551
2561
|
spatial.flush()
|
|
@@ -2601,10 +2611,12 @@ export function createScene(width: number, height: number, opts?: SceneOptions):
|
|
|
2601
2611
|
// The scene's pending state (lights, params, the fan-out to this
|
|
2602
2612
|
// target) lands before the faces read it.
|
|
2603
2613
|
sync()
|
|
2604
|
-
renderCubeFaces(v.texture, v.camera, size, position, chain !== null,
|
|
2605
|
-
//
|
|
2606
|
-
//
|
|
2607
|
-
|
|
2614
|
+
renderCubeFaces(v.texture, v.camera, size, position, chain !== null, () => {
|
|
2615
|
+
// Each face looks a different way: its own view, flushed so
|
|
2616
|
+
// the core culls, sorts and measures the face before it
|
|
2617
|
+
// renders.
|
|
2618
|
+
setView(v.texture, v.camera)
|
|
2619
|
+
spatial.flush()
|
|
2608
2620
|
})
|
|
2609
2621
|
chain?.run()
|
|
2610
2622
|
},
|