@octanejs/three 0.1.0 → 0.1.2
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/README.md +100 -16
- package/UPSTREAM.md +52 -3
- package/package.json +15 -8
- package/src/config.ts +5 -0
- package/src/core/catalogue.ts +31 -2
- package/src/core/driver.ts +299 -15
- package/src/core/hooks.ts +4 -1
- package/src/core/index.ts +16 -0
- package/src/core/portal.ts +187 -82
- package/src/core/root.ts +180 -10
- package/src/core/store.ts +34 -2
- package/src/index.ts +25 -0
- package/src/intrinsics.ts +20 -0
- package/src/scheduling.ts +83 -0
- package/src/web/DOMRegion.ts +58 -0
- package/src/web/dom-region.ts +104 -0
package/src/core/driver.ts
CHANGED
|
@@ -11,9 +11,11 @@ import type {
|
|
|
11
11
|
UniversalEventListenerDescriptor,
|
|
12
12
|
UniversalEventPriority,
|
|
13
13
|
UniversalHostBatch,
|
|
14
|
+
UniversalHostCommand,
|
|
14
15
|
UniversalHostDriver,
|
|
15
16
|
UniversalListenerDescriptor,
|
|
16
17
|
UniversalPortalTargetHandle,
|
|
18
|
+
UniversalPreparedHostBatch,
|
|
17
19
|
} from 'octane/universal';
|
|
18
20
|
import {
|
|
19
21
|
getInitialRootStore,
|
|
@@ -22,7 +24,12 @@ import {
|
|
|
22
24
|
swapInteractivity,
|
|
23
25
|
type RootStore,
|
|
24
26
|
} from './store.js';
|
|
25
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
createThreeObject,
|
|
29
|
+
registerThreeNamespace,
|
|
30
|
+
resolveThreeConstructor,
|
|
31
|
+
THREE_RENDERER_ID,
|
|
32
|
+
} from './catalogue.js';
|
|
26
33
|
import {
|
|
27
34
|
attachString,
|
|
28
35
|
detachAttachment,
|
|
@@ -39,6 +46,9 @@ const STORE_CONTAINERS = new WeakMap<RootStore, ThreeHostContainer>();
|
|
|
39
46
|
const ACTIVE_EVENT_SCOPES = new WeakSet<RootStore>();
|
|
40
47
|
const THREE_PORTAL_TARGET = Symbol('octane.three.portal-target');
|
|
41
48
|
const EXTERNAL_PORTAL_TARGET_LEASES = new WeakMap<THREE.Object3D, ExternalTargetLease>();
|
|
49
|
+
const EMPTY_THREE_EVENTS = new Map<string, UniversalEventListenerDescriptor>();
|
|
50
|
+
const EMPTY_THREE_CALLBACKS = new Map<string, UniversalListenerDescriptor>();
|
|
51
|
+
const THREE_VECTOR3_FROM_ARRAY = THREE.Vector3.prototype.fromArray;
|
|
42
52
|
|
|
43
53
|
const THREE_EVENT_PRIORITIES: Readonly<Record<string, UniversalEventPriority>> = Object.freeze({
|
|
44
54
|
onClick: 'discrete',
|
|
@@ -126,12 +136,13 @@ interface ThreeHostInstance {
|
|
|
126
136
|
readonly children: number[];
|
|
127
137
|
owned: boolean;
|
|
128
138
|
visible: boolean;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
139
|
+
events: Map<string, UniversalEventListenerDescriptor>;
|
|
140
|
+
lifecycles: Map<string, UniversalListenerDescriptor>;
|
|
141
|
+
localCallbacks: Map<string, UniversalListenerDescriptor>;
|
|
132
142
|
readonly localCleanups: Map<string, () => void>;
|
|
133
143
|
store: RootStore | undefined;
|
|
134
144
|
physical: PhysicalPlacement | null;
|
|
145
|
+
directLeaf: boolean;
|
|
135
146
|
}
|
|
136
147
|
|
|
137
148
|
interface SimulatedInstance {
|
|
@@ -155,6 +166,7 @@ interface StagedObject {
|
|
|
155
166
|
interface ThreeDriverState {
|
|
156
167
|
readonly instances: Map<number, ThreeHostInstance>;
|
|
157
168
|
readonly rootChildren: number[];
|
|
169
|
+
directInstances: readonly ThreeHostInstance[] | null;
|
|
158
170
|
readonly portalChildren: Map<string | number, number[]>;
|
|
159
171
|
readonly portalTargets: Map<string | number, ThreePortalTargetDomain>;
|
|
160
172
|
readonly portalTargetCache: WeakMap<RootStore, WeakMap<THREE.Object3D, ThreePortalTargetDomain>>;
|
|
@@ -192,6 +204,8 @@ export function createThreePortalTarget(
|
|
|
192
204
|
export interface ThreeHostEnvironment {
|
|
193
205
|
/** Called once after an accepted host batch, without requiring WebGL. */
|
|
194
206
|
invalidate?(): void;
|
|
207
|
+
/** Set false to omit accepted batches from the public diagnostic history. */
|
|
208
|
+
recordCommits?: boolean;
|
|
195
209
|
/** Root state associated with a configured managed scene. */
|
|
196
210
|
readonly store?: RootStore;
|
|
197
211
|
/** Run all Three handlers from one platform event in one universal scope. */
|
|
@@ -257,6 +271,7 @@ export function createThreeContainer(
|
|
|
257
271
|
const state: ThreeDriverState = {
|
|
258
272
|
instances: new Map(),
|
|
259
273
|
rootChildren: [],
|
|
274
|
+
directInstances: null,
|
|
260
275
|
portalChildren: new Map(),
|
|
261
276
|
portalTargets: new Map(),
|
|
262
277
|
portalTargetCache: new WeakMap(),
|
|
@@ -310,6 +325,254 @@ function cloneSimulation(state: ThreeDriverState): Map<number, SimulatedInstance
|
|
|
310
325
|
return simulation;
|
|
311
326
|
}
|
|
312
327
|
|
|
328
|
+
/**
|
|
329
|
+
* Retained prop patches do not change the logical or physical Three graph. Keep
|
|
330
|
+
* them transactional by validating every target up front, but avoid cloning and
|
|
331
|
+
* republishing the entire scene merely to apply ordinary object properties.
|
|
332
|
+
*
|
|
333
|
+
* String attachments are deliberately excluded: a parent prop patch can replace
|
|
334
|
+
* any segment of an attached child's path, which requires the general shadow
|
|
335
|
+
* validation and physical synchronization below. Interactive targets are also
|
|
336
|
+
* excluded because changing `raycast` can alter their root-store membership.
|
|
337
|
+
*/
|
|
338
|
+
interface DirectMeshProps extends Readonly<Record<string, unknown>> {
|
|
339
|
+
readonly name: string;
|
|
340
|
+
readonly position: readonly [number, number, number];
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function isDirectMeshProps(props: Readonly<Record<string, unknown>>): props is DirectMeshProps {
|
|
344
|
+
let nextName = 'name';
|
|
345
|
+
for (const name in props) {
|
|
346
|
+
if (!Object.prototype.hasOwnProperty.call(props, name)) continue;
|
|
347
|
+
if (name !== nextName) return false;
|
|
348
|
+
nextName = name === 'name' ? 'position' : '';
|
|
349
|
+
}
|
|
350
|
+
const position = props.position;
|
|
351
|
+
// Eligibility must not read authored array indices: the general driver does
|
|
352
|
+
// not observe those values until an accepted batch is applied.
|
|
353
|
+
return nextName === '' && typeof props.name === 'string' && Array.isArray(position);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function prepareDirectLeafMountBatch(
|
|
357
|
+
container: ThreeHostContainer,
|
|
358
|
+
batch: UniversalHostBatch,
|
|
359
|
+
): UniversalPreparedHostBatch | null {
|
|
360
|
+
const state = container[THREE_DRIVER_STATE];
|
|
361
|
+
if (
|
|
362
|
+
state.instances.size !== 0 ||
|
|
363
|
+
state.rootChildren.length !== 0 ||
|
|
364
|
+
state.portalTargets.size !== 0 ||
|
|
365
|
+
state.portalChildren.size !== 0 ||
|
|
366
|
+
container.scene.parent !== null ||
|
|
367
|
+
container.scene.children.length !== 0 ||
|
|
368
|
+
resolveThreeConstructor('mesh') !== THREE.Mesh
|
|
369
|
+
) {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
let createCount = 0;
|
|
374
|
+
while (batch.commands[createCount]?.op === 'create') createCount++;
|
|
375
|
+
if (createCount === 0 || batch.commands.length !== createCount * 2) return null;
|
|
376
|
+
const ids = new Set<number>();
|
|
377
|
+
for (let index = 0; index < createCount; index++) {
|
|
378
|
+
const create = batch.commands[index];
|
|
379
|
+
const placement = batch.commands[createCount + index];
|
|
380
|
+
if (
|
|
381
|
+
create.op !== 'create' ||
|
|
382
|
+
create.type !== 'mesh' ||
|
|
383
|
+
ids.has(create.id) ||
|
|
384
|
+
!isDirectMeshProps(create.props) ||
|
|
385
|
+
placement.op !== 'insert' ||
|
|
386
|
+
placement.id !== create.id ||
|
|
387
|
+
placement.parent !== null ||
|
|
388
|
+
placement.before !== null
|
|
389
|
+
) {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
ids.add(create.id);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const staged: ThreeHostInstance[] = [];
|
|
396
|
+
try {
|
|
397
|
+
for (let index = 0; index < createCount; index++) {
|
|
398
|
+
const command = batch.commands[index] as Extract<
|
|
399
|
+
UniversalHostCommand,
|
|
400
|
+
{ readonly op: 'create' }
|
|
401
|
+
>;
|
|
402
|
+
const object = new THREE.Mesh();
|
|
403
|
+
staged.push(
|
|
404
|
+
createHostInstance(
|
|
405
|
+
container,
|
|
406
|
+
command.id,
|
|
407
|
+
{ object, owned: true, type: 'mesh', propsApplied: true },
|
|
408
|
+
command.props,
|
|
409
|
+
true,
|
|
410
|
+
),
|
|
411
|
+
);
|
|
412
|
+
applyThreeProps(object, command.props, undefined, {
|
|
413
|
+
colorSpace: container.environment.linear !== true,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
} catch (error) {
|
|
417
|
+
for (const instance of staged) disposeOwnedNow(instance.object);
|
|
418
|
+
throw error;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
let status: 'prepared' | 'applied' | 'aborted' = 'prepared';
|
|
422
|
+
return {
|
|
423
|
+
apply() {
|
|
424
|
+
if (status !== 'prepared') return;
|
|
425
|
+
status = 'applied';
|
|
426
|
+
state.directInstances = staged;
|
|
427
|
+
let failed = false;
|
|
428
|
+
let firstError: unknown;
|
|
429
|
+
for (const instance of staged) {
|
|
430
|
+
instance.directLeaf = true;
|
|
431
|
+
state.instances.set(instance.id, instance);
|
|
432
|
+
state.rootChildren.push(instance.id);
|
|
433
|
+
instance.parent = null;
|
|
434
|
+
OBJECT_INSTANCES.set(instance.object, instance);
|
|
435
|
+
try {
|
|
436
|
+
container.scene.add(instance.object);
|
|
437
|
+
instance.physical = {
|
|
438
|
+
kind: 'object3d',
|
|
439
|
+
object: instance.object,
|
|
440
|
+
parent: container.scene,
|
|
441
|
+
};
|
|
442
|
+
} catch (error) {
|
|
443
|
+
if (!failed) {
|
|
444
|
+
failed = true;
|
|
445
|
+
firstError = error;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (container.environment.recordCommits !== false) {
|
|
450
|
+
try {
|
|
451
|
+
container.commits.push(batch);
|
|
452
|
+
} catch (error) {
|
|
453
|
+
if (!failed) {
|
|
454
|
+
failed = true;
|
|
455
|
+
firstError = error;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
try {
|
|
460
|
+
container.environment.invalidate?.();
|
|
461
|
+
} catch (error) {
|
|
462
|
+
if (!failed) {
|
|
463
|
+
failed = true;
|
|
464
|
+
firstError = error;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
if (failed) throw firstError;
|
|
468
|
+
},
|
|
469
|
+
abort() {
|
|
470
|
+
if (status !== 'prepared') return;
|
|
471
|
+
status = 'aborted';
|
|
472
|
+
for (const instance of staged) disposeOwnedNow(instance.object);
|
|
473
|
+
},
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function prepareUpdateOnlyBatch(
|
|
478
|
+
container: ThreeHostContainer,
|
|
479
|
+
batch: UniversalHostBatch,
|
|
480
|
+
): UniversalPreparedHostBatch | null {
|
|
481
|
+
if (batch.commands.length === 0) return null;
|
|
482
|
+
|
|
483
|
+
const state = container[THREE_DRIVER_STATE];
|
|
484
|
+
const instances = state.directInstances;
|
|
485
|
+
if (
|
|
486
|
+
instances === null ||
|
|
487
|
+
batch.commands.length !== state.rootChildren.length ||
|
|
488
|
+
batch.commands.length !== instances.length ||
|
|
489
|
+
state.rootChildren.length !== state.instances.size ||
|
|
490
|
+
container.scene.children.length !== state.rootChildren.length ||
|
|
491
|
+
container.scene.parent !== null
|
|
492
|
+
) {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
for (let index = 0; index < batch.commands.length; index++) {
|
|
496
|
+
const command = batch.commands[index];
|
|
497
|
+
if (command.op !== 'update') return null;
|
|
498
|
+
const instance = instances[index];
|
|
499
|
+
if (command.id !== instance.id) return null;
|
|
500
|
+
const nameDescriptor = Object.getOwnPropertyDescriptor(instance.object, 'name');
|
|
501
|
+
if (
|
|
502
|
+
!instance.directLeaf ||
|
|
503
|
+
instance.physical?.kind !== 'object3d' ||
|
|
504
|
+
instance.physical.parent !== container.scene ||
|
|
505
|
+
instance.object.parent !== container.scene ||
|
|
506
|
+
container.scene.children[index] !== instance.object ||
|
|
507
|
+
instance.object.visible !== true ||
|
|
508
|
+
nameDescriptor === undefined ||
|
|
509
|
+
!('value' in nameDescriptor) ||
|
|
510
|
+
nameDescriptor.writable !== true ||
|
|
511
|
+
instance.object.position.fromArray !== THREE_VECTOR3_FROM_ARRAY
|
|
512
|
+
) {
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
if (!isDirectMeshProps(command.props)) return null;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
let status: 'prepared' | 'applied' | 'aborted' = 'prepared';
|
|
519
|
+
return {
|
|
520
|
+
apply() {
|
|
521
|
+
if (status !== 'prepared') return;
|
|
522
|
+
status = 'applied';
|
|
523
|
+
let failed = false;
|
|
524
|
+
let firstError: unknown;
|
|
525
|
+
for (let index = 0; index < instances.length; index++) {
|
|
526
|
+
try {
|
|
527
|
+
const instance = instances[index];
|
|
528
|
+
const previous = instance.props as DirectMeshProps;
|
|
529
|
+
const next = (
|
|
530
|
+
batch.commands[index] as Extract<UniversalHostCommand, { readonly op: 'update' }>
|
|
531
|
+
).props as DirectMeshProps;
|
|
532
|
+
instance.props = next;
|
|
533
|
+
if (previous.name !== next.name) instance.object.name = next.name;
|
|
534
|
+
const previousPosition = previous.position;
|
|
535
|
+
const nextPosition = next.position;
|
|
536
|
+
if (
|
|
537
|
+
previousPosition[0] !== nextPosition[0] ||
|
|
538
|
+
previousPosition[1] !== nextPosition[1] ||
|
|
539
|
+
previousPosition[2] !== nextPosition[2]
|
|
540
|
+
) {
|
|
541
|
+
instance.object.position.fromArray(nextPosition);
|
|
542
|
+
}
|
|
543
|
+
} catch (error) {
|
|
544
|
+
if (!failed) {
|
|
545
|
+
failed = true;
|
|
546
|
+
firstError = error;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
if (container.environment.recordCommits !== false) {
|
|
551
|
+
try {
|
|
552
|
+
container.commits.push(batch);
|
|
553
|
+
} catch (error) {
|
|
554
|
+
if (!failed) {
|
|
555
|
+
failed = true;
|
|
556
|
+
firstError = error;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
try {
|
|
561
|
+
container.environment.invalidate?.();
|
|
562
|
+
} catch (error) {
|
|
563
|
+
if (!failed) {
|
|
564
|
+
failed = true;
|
|
565
|
+
firstError = error;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
if (failed) throw firstError;
|
|
569
|
+
},
|
|
570
|
+
abort() {
|
|
571
|
+
if (status === 'prepared') status = 'aborted';
|
|
572
|
+
},
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
313
576
|
function isPortalParent(parent: ParentId): parent is UniversalPortalTargetHandle {
|
|
314
577
|
return (
|
|
315
578
|
parent !== null &&
|
|
@@ -774,6 +1037,7 @@ function createHostInstance(
|
|
|
774
1037
|
id: number,
|
|
775
1038
|
staged: StagedObject,
|
|
776
1039
|
props: Readonly<Record<string, unknown>>,
|
|
1040
|
+
directLeaf = false,
|
|
777
1041
|
): ThreeHostInstance {
|
|
778
1042
|
return {
|
|
779
1043
|
id,
|
|
@@ -785,12 +1049,13 @@ function createHostInstance(
|
|
|
785
1049
|
children: [],
|
|
786
1050
|
owned: staged.owned,
|
|
787
1051
|
visible: true,
|
|
788
|
-
events: new Map(),
|
|
789
|
-
lifecycles: new Map(),
|
|
790
|
-
localCallbacks: new Map(),
|
|
1052
|
+
events: directLeaf ? EMPTY_THREE_EVENTS : new Map(),
|
|
1053
|
+
lifecycles: directLeaf ? EMPTY_THREE_CALLBACKS : new Map(),
|
|
1054
|
+
localCallbacks: directLeaf ? EMPTY_THREE_CALLBACKS : new Map(),
|
|
791
1055
|
localCleanups: new Map(),
|
|
792
1056
|
store: container.environment.store,
|
|
793
1057
|
physical: null,
|
|
1058
|
+
directLeaf,
|
|
794
1059
|
};
|
|
795
1060
|
}
|
|
796
1061
|
|
|
@@ -947,7 +1212,12 @@ export function createThreeDriver(
|
|
|
947
1212
|
registerThreeNamespace();
|
|
948
1213
|
return {
|
|
949
1214
|
id: renderer,
|
|
950
|
-
capabilities: {
|
|
1215
|
+
capabilities: {
|
|
1216
|
+
text: 'ignore',
|
|
1217
|
+
localHostCallbacks: true,
|
|
1218
|
+
visibility: true,
|
|
1219
|
+
compilerLeafProps: true,
|
|
1220
|
+
},
|
|
951
1221
|
events: {
|
|
952
1222
|
classify(name) {
|
|
953
1223
|
const priority = getThreeHostEventPriority(name);
|
|
@@ -1121,6 +1391,10 @@ export function createThreeDriver(
|
|
|
1121
1391
|
);
|
|
1122
1392
|
}
|
|
1123
1393
|
const state = container[THREE_DRIVER_STATE];
|
|
1394
|
+
const directLeafMount = prepareDirectLeafMountBatch(container, batch);
|
|
1395
|
+
if (directLeafMount !== null) return directLeafMount;
|
|
1396
|
+
const updateOnly = prepareUpdateOnlyBatch(container, batch);
|
|
1397
|
+
if (updateOnly !== null) return updateOnly;
|
|
1124
1398
|
const simulation = cloneSimulation(state);
|
|
1125
1399
|
const rootChildren = [...state.rootChildren];
|
|
1126
1400
|
const portalChildren = clonePortalChildren(state);
|
|
@@ -1487,6 +1761,7 @@ export function createThreeDriver(
|
|
|
1487
1761
|
}
|
|
1488
1762
|
|
|
1489
1763
|
tasks.push(() => {
|
|
1764
|
+
state.directInstances = null;
|
|
1490
1765
|
state.rootChildren.splice(0, state.rootChildren.length, ...rootChildren);
|
|
1491
1766
|
state.portalChildren.clear();
|
|
1492
1767
|
for (const [target, children] of portalChildren) {
|
|
@@ -1499,12 +1774,19 @@ export function createThreeDriver(
|
|
|
1499
1774
|
instance.parent = simulated.parent;
|
|
1500
1775
|
instance.children.splice(0, instance.children.length, ...simulated.children);
|
|
1501
1776
|
instance.visible = simulated.visible;
|
|
1502
|
-
instance.
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1777
|
+
if (instance.directLeaf) {
|
|
1778
|
+
instance.events = new Map(simulated.events);
|
|
1779
|
+
instance.lifecycles = new Map(simulated.lifecycles);
|
|
1780
|
+
instance.localCallbacks = new Map(simulated.localCallbacks);
|
|
1781
|
+
} else {
|
|
1782
|
+
instance.events.clear();
|
|
1783
|
+
for (const entry of simulated.events) instance.events.set(...entry);
|
|
1784
|
+
instance.lifecycles.clear();
|
|
1785
|
+
for (const entry of simulated.lifecycles) instance.lifecycles.set(...entry);
|
|
1786
|
+
instance.localCallbacks.clear();
|
|
1787
|
+
for (const entry of simulated.localCallbacks) instance.localCallbacks.set(...entry);
|
|
1788
|
+
}
|
|
1789
|
+
instance.directLeaf = false;
|
|
1508
1790
|
instance.store = storeForInstance(
|
|
1509
1791
|
id,
|
|
1510
1792
|
simulation,
|
|
@@ -1560,7 +1842,9 @@ export function createThreeDriver(
|
|
|
1560
1842
|
});
|
|
1561
1843
|
}
|
|
1562
1844
|
|
|
1563
|
-
|
|
1845
|
+
if (container.environment.recordCommits !== false) {
|
|
1846
|
+
tasks.push(() => container.commits.push(batch));
|
|
1847
|
+
}
|
|
1564
1848
|
tasks.push(() => container.environment.invalidate?.());
|
|
1565
1849
|
runAll(tasks);
|
|
1566
1850
|
},
|
package/src/core/hooks.ts
CHANGED
|
@@ -17,7 +17,9 @@ import {
|
|
|
17
17
|
import { getThreeEventStore, getThreeInstance, type Instance } from './driver.js';
|
|
18
18
|
import {
|
|
19
19
|
getRootObjectStore,
|
|
20
|
+
readRootStoreRenderSnapshot,
|
|
20
21
|
RootStoreContext,
|
|
22
|
+
RootStoreRenderSnapshotContext,
|
|
21
23
|
useRootStoreSelector,
|
|
22
24
|
type RenderCallback,
|
|
23
25
|
type RootState,
|
|
@@ -86,6 +88,7 @@ export function useThree<T = RootState>(...args: unknown[]): T {
|
|
|
86
88
|
const selector = (typeof userArgs[0] === 'function' ? userArgs[0] : identity) as Selector<T>;
|
|
87
89
|
const equalityFn = (typeof userArgs[1] === 'function' ? userArgs[1] : Object.is) as EqualityFn<T>;
|
|
88
90
|
const store = useStore();
|
|
91
|
+
const renderSnapshot = useContext(RootStoreRenderSnapshotContext);
|
|
89
92
|
const cache = useRef<
|
|
90
93
|
| {
|
|
91
94
|
readonly state: RootState;
|
|
@@ -96,7 +99,7 @@ export function useThree<T = RootState>(...args: unknown[]): T {
|
|
|
96
99
|
>(undefined, subSlot(slot, 'useThree:cache'));
|
|
97
100
|
|
|
98
101
|
const getSnapshot = (): T => {
|
|
99
|
-
const state = store
|
|
102
|
+
const state = readRootStoreRenderSnapshot(store, renderSnapshot);
|
|
100
103
|
const previous = cache.current;
|
|
101
104
|
if (
|
|
102
105
|
previous !== undefined &&
|
package/src/core/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ export {
|
|
|
12
12
|
export { useLoader } from './loader.js';
|
|
13
13
|
export { createPortal } from './portal.js';
|
|
14
14
|
export { dispose } from './props.js';
|
|
15
|
+
export type { Disposable } from './props.js';
|
|
15
16
|
export {
|
|
16
17
|
addAfterEffect,
|
|
17
18
|
addEffect,
|
|
@@ -26,15 +27,26 @@ export { createEvents } from './events.js';
|
|
|
26
27
|
export type {
|
|
27
28
|
Args,
|
|
28
29
|
Attach,
|
|
30
|
+
AttachFnType,
|
|
31
|
+
AttachType,
|
|
29
32
|
Catalogue,
|
|
33
|
+
Color,
|
|
30
34
|
ConstructorRepresentation,
|
|
35
|
+
ElementProps,
|
|
36
|
+
Euler,
|
|
31
37
|
EventProps,
|
|
32
38
|
InstanceProps,
|
|
39
|
+
Layers,
|
|
33
40
|
MathRepresentation,
|
|
41
|
+
MathProps,
|
|
34
42
|
MathType,
|
|
35
43
|
MathTypes,
|
|
44
|
+
Matrix3,
|
|
45
|
+
Matrix4,
|
|
36
46
|
PrimitiveProps,
|
|
47
|
+
Quaternion,
|
|
37
48
|
RaycastableRepresentation,
|
|
49
|
+
ReactProps,
|
|
38
50
|
ThreeElement,
|
|
39
51
|
ThreeElements,
|
|
40
52
|
ThreeInstanceProps,
|
|
@@ -42,6 +54,9 @@ export type {
|
|
|
42
54
|
ThreeRef,
|
|
43
55
|
ThreeToElements,
|
|
44
56
|
ThreeToJSXElements,
|
|
57
|
+
Vector2,
|
|
58
|
+
Vector3,
|
|
59
|
+
Vector4,
|
|
45
60
|
VectorRepresentation,
|
|
46
61
|
} from './catalogue.js';
|
|
47
62
|
export type {
|
|
@@ -66,6 +81,7 @@ export type {
|
|
|
66
81
|
CanvasLike,
|
|
67
82
|
DefaultGLProps,
|
|
68
83
|
GLProps,
|
|
84
|
+
ThreeRoot as ReconcilerRoot,
|
|
69
85
|
RenderProps,
|
|
70
86
|
ThreeRoot,
|
|
71
87
|
} from './root.js';
|