@gnsx/three 0.185.14 → 0.185.16
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/build/three.cjs +682 -0
- package/build/three.core.js +748 -7
- package/build/three.core.min.js +1 -1
- package/build/three.module.js +1 -1
- package/build/three.module.min.js +1 -1
- package/build/three.webgpu.js +1 -1
- package/build/three.webgpu.min.js +1 -1
- package/build/three.webgpu.nodes.js +1 -1
- package/build/three.webgpu.nodes.min.js +1 -1
- package/package.json +1 -1
- package/src/Three.Core.js +17 -0
- package/src/core/NodePath.js +208 -0
- package/src/core/Object3D.js +191 -0
- package/src/core/nodeId.js +168 -0
- package/src/loaders/ObjectLoader.js +4 -0
- package/src/math/XorShift32.js +119 -0
- package/src/utils.js +79 -6
package/package.json
CHANGED
package/src/Three.Core.js
CHANGED
|
@@ -102,6 +102,20 @@ export { InstancedBufferAttribute } from './core/InstancedBufferAttribute.js';
|
|
|
102
102
|
export { GLBufferAttribute } from './core/GLBufferAttribute.js';
|
|
103
103
|
export * from './core/BufferAttribute.js';
|
|
104
104
|
export { Object3D } from './core/Object3D.js';
|
|
105
|
+
// WITH_GENESYS
|
|
106
|
+
export { NodePath } from './core/NodePath.js';
|
|
107
|
+
export {
|
|
108
|
+
isValidNodeId,
|
|
109
|
+
nodeIdToString,
|
|
110
|
+
nodeIdFromString,
|
|
111
|
+
configureNodeIdSeed,
|
|
112
|
+
generateNodeId,
|
|
113
|
+
allocateNodeId,
|
|
114
|
+
nodeIdFromKey,
|
|
115
|
+
collectSiblingNodeIds,
|
|
116
|
+
ensureUniqueNodeIdAmongParentChildren
|
|
117
|
+
} from './core/nodeId.js';
|
|
118
|
+
// !WITH_GENESYS
|
|
105
119
|
export { Raycaster } from './core/Raycaster.js';
|
|
106
120
|
export { Layers } from './core/Layers.js';
|
|
107
121
|
export { EventDispatcher } from './core/EventDispatcher.js';
|
|
@@ -115,6 +129,9 @@ export { BezierInterpolant } from './math/interpolants/BezierInterpolant.js';
|
|
|
115
129
|
export { Interpolant } from './math/Interpolant.js';
|
|
116
130
|
export { Triangle } from './math/Triangle.js';
|
|
117
131
|
export { MathUtils } from './math/MathUtils.js';
|
|
132
|
+
// WITH_GENESYS
|
|
133
|
+
export { hashStringToUint32, xorshift32, randomSeedUint32, XorShift32 } from './math/XorShift32.js';
|
|
134
|
+
// !WITH_GENESYS
|
|
118
135
|
export { Spherical } from './math/Spherical.js';
|
|
119
136
|
export { Cylindrical } from './math/Cylindrical.js';
|
|
120
137
|
export { Plane } from './math/Plane.js';
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// WITH_GENESYS
|
|
2
|
+
/**
|
|
3
|
+
* Path of sibling-local nodeIds identifying a node in the scene tree.
|
|
4
|
+
* String form: `"a1b2/c3d4"` (4-hex segments separated by `/`).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { isValidNodeId, nodeIdFromString, nodeIdToString } from './nodeId.js';
|
|
8
|
+
|
|
9
|
+
class NodePath {
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {readonly number[]} [ids]
|
|
13
|
+
*/
|
|
14
|
+
constructor( ids = [] ) {
|
|
15
|
+
|
|
16
|
+
const normalized = [];
|
|
17
|
+
for ( const id of ids ) {
|
|
18
|
+
|
|
19
|
+
if ( ! isValidNodeId( id ) ) {
|
|
20
|
+
|
|
21
|
+
throw new Error( `[NodePath] Invalid NodeId segment: ${ String( id ) }` );
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
normalized.push( id & 0xffff );
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.ids = normalized;
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {...number} ids
|
|
35
|
+
* @return {NodePath}
|
|
36
|
+
*/
|
|
37
|
+
static fromIds( ...ids ) {
|
|
38
|
+
|
|
39
|
+
return new NodePath( ids );
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Parses `"a1b2/c3d4"`. Empty / whitespace-only string yields {@link NodePath.empty}.
|
|
45
|
+
* @param {string} path
|
|
46
|
+
* @return {NodePath}
|
|
47
|
+
*/
|
|
48
|
+
static fromString( path ) {
|
|
49
|
+
|
|
50
|
+
const trimmed = path.trim();
|
|
51
|
+
if ( trimmed.length === 0 ) {
|
|
52
|
+
|
|
53
|
+
return NodePath.empty;
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const parts = trimmed.split( '/' );
|
|
58
|
+
const ids = [];
|
|
59
|
+
for ( const part of parts ) {
|
|
60
|
+
|
|
61
|
+
if ( part.length === 0 ) {
|
|
62
|
+
|
|
63
|
+
throw new Error( `[NodePath] Empty segment in path "${ path }"` );
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const id = nodeIdFromString( part );
|
|
68
|
+
if ( id === null ) {
|
|
69
|
+
|
|
70
|
+
throw new Error( `[NodePath] Invalid segment "${ part }" in path "${ path }"` );
|
|
71
|
+
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
ids.push( id );
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return new NodePath( ids );
|
|
79
|
+
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {NodePath|string} path
|
|
84
|
+
* @return {NodePath}
|
|
85
|
+
*/
|
|
86
|
+
static coerce( path ) {
|
|
87
|
+
|
|
88
|
+
return typeof path === 'string' ? NodePath.fromString( path ) : path;
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** @return {number} */
|
|
93
|
+
get length() {
|
|
94
|
+
|
|
95
|
+
return this.ids.length;
|
|
96
|
+
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** @return {boolean} */
|
|
100
|
+
get isEmpty() {
|
|
101
|
+
|
|
102
|
+
return this.ids.length === 0;
|
|
103
|
+
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** @return {string} */
|
|
107
|
+
toString() {
|
|
108
|
+
|
|
109
|
+
return this.ids.map( nodeIdToString ).join( '/' );
|
|
110
|
+
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {NodePath} other
|
|
115
|
+
* @return {boolean}
|
|
116
|
+
*/
|
|
117
|
+
equals( other ) {
|
|
118
|
+
|
|
119
|
+
if ( this.ids.length !== other.ids.length ) {
|
|
120
|
+
|
|
121
|
+
return false;
|
|
122
|
+
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for ( let i = 0; i < this.ids.length; i ++ ) {
|
|
126
|
+
|
|
127
|
+
if ( this.ids[ i ] !== other.ids[ i ] ) {
|
|
128
|
+
|
|
129
|
+
return false;
|
|
130
|
+
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return true;
|
|
136
|
+
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @param {NodePath} prefix
|
|
141
|
+
* @return {boolean}
|
|
142
|
+
*/
|
|
143
|
+
startsWith( prefix ) {
|
|
144
|
+
|
|
145
|
+
if ( prefix.ids.length > this.ids.length ) {
|
|
146
|
+
|
|
147
|
+
return false;
|
|
148
|
+
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
for ( let i = 0; i < prefix.ids.length; i ++ ) {
|
|
152
|
+
|
|
153
|
+
if ( this.ids[ i ] !== prefix.ids[ i ] ) {
|
|
154
|
+
|
|
155
|
+
return false;
|
|
156
|
+
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return true;
|
|
162
|
+
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @param {number} start
|
|
167
|
+
* @param {number} [end]
|
|
168
|
+
* @return {NodePath}
|
|
169
|
+
*/
|
|
170
|
+
slice( start, end ) {
|
|
171
|
+
|
|
172
|
+
return new NodePath( this.ids.slice( start, end ) );
|
|
173
|
+
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @param {NodePath} relative
|
|
178
|
+
* @return {NodePath}
|
|
179
|
+
*/
|
|
180
|
+
concat( relative ) {
|
|
181
|
+
|
|
182
|
+
return new NodePath( [ ...this.ids, ...relative.ids ] );
|
|
183
|
+
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* If this path starts with `ancestor`, returns the remainder; otherwise `null`.
|
|
188
|
+
* @param {NodePath} ancestor
|
|
189
|
+
* @return {?NodePath}
|
|
190
|
+
*/
|
|
191
|
+
relativeFrom( ancestor ) {
|
|
192
|
+
|
|
193
|
+
if ( ! this.startsWith( ancestor ) ) {
|
|
194
|
+
|
|
195
|
+
return null;
|
|
196
|
+
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return this.slice( ancestor.length );
|
|
200
|
+
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
NodePath.empty = new NodePath( [] );
|
|
206
|
+
|
|
207
|
+
export { NodePath };
|
|
208
|
+
// !WITH_GENESYS
|
package/src/core/Object3D.js
CHANGED
|
@@ -7,6 +7,15 @@ import { Layers } from './Layers.js';
|
|
|
7
7
|
import { Matrix3 } from '../math/Matrix3.js';
|
|
8
8
|
import { generateUUID } from '../math/MathUtils.js';
|
|
9
9
|
import { error } from '../utils.js';
|
|
10
|
+
// WITH_GENESYS
|
|
11
|
+
import {
|
|
12
|
+
allocateNodeId,
|
|
13
|
+
ensureUniqueNodeIdAmongParentChildren,
|
|
14
|
+
generateNodeId,
|
|
15
|
+
isValidNodeId
|
|
16
|
+
} from './nodeId.js';
|
|
17
|
+
import { NodePath } from './NodePath.js';
|
|
18
|
+
// !WITH_GENESYS
|
|
10
19
|
|
|
11
20
|
let _object3DId = 0;
|
|
12
21
|
|
|
@@ -96,6 +105,16 @@ class Object3D extends EventDispatcher {
|
|
|
96
105
|
*/
|
|
97
106
|
this.uuid = generateUUID();
|
|
98
107
|
|
|
108
|
+
// WITH_GENESYS
|
|
109
|
+
/**
|
|
110
|
+
* Sibling-local 16-bit identity for scene-graph addressing ({@link NodePath}).
|
|
111
|
+
* `0` is valid. Reminted on parent attach when colliding with a sibling.
|
|
112
|
+
*
|
|
113
|
+
* @type {number}
|
|
114
|
+
*/
|
|
115
|
+
this.nodeId = generateNodeId();
|
|
116
|
+
// !WITH_GENESYS
|
|
117
|
+
|
|
99
118
|
/**
|
|
100
119
|
* The name of the 3D object.
|
|
101
120
|
*
|
|
@@ -778,6 +797,9 @@ class Object3D extends EventDispatcher {
|
|
|
778
797
|
if ( object && object.isObject3D ) {
|
|
779
798
|
|
|
780
799
|
object.removeFromParent();
|
|
800
|
+
// WITH_GENESYS
|
|
801
|
+
ensureUniqueNodeIdAmongParentChildren( object, this );
|
|
802
|
+
// !WITH_GENESYS
|
|
781
803
|
object.parent = this;
|
|
782
804
|
this.children.push( object );
|
|
783
805
|
|
|
@@ -903,6 +925,9 @@ class Object3D extends EventDispatcher {
|
|
|
903
925
|
object.applyMatrix4( _m1 );
|
|
904
926
|
|
|
905
927
|
object.removeFromParent();
|
|
928
|
+
// WITH_GENESYS
|
|
929
|
+
ensureUniqueNodeIdAmongParentChildren( object, this );
|
|
930
|
+
// !WITH_GENESYS
|
|
906
931
|
object.parent = this;
|
|
907
932
|
this.children.push( object );
|
|
908
933
|
|
|
@@ -918,6 +943,166 @@ class Object3D extends EventDispatcher {
|
|
|
918
943
|
|
|
919
944
|
}
|
|
920
945
|
|
|
946
|
+
// WITH_GENESYS
|
|
947
|
+
/**
|
|
948
|
+
* Assigns a {@link nodeId} when unset or outside the uint16 range.
|
|
949
|
+
* @return {number}
|
|
950
|
+
*/
|
|
951
|
+
ensureNodeId() {
|
|
952
|
+
|
|
953
|
+
if ( ! isValidNodeId( this.nodeId ) ) {
|
|
954
|
+
|
|
955
|
+
this.nodeId = generateNodeId();
|
|
956
|
+
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
return this.nodeId;
|
|
960
|
+
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* Replaces {@link nodeId} with a newly generated value (e.g. prefab instance roots).
|
|
965
|
+
* @param {Iterable<number>} [existingSiblingIds]
|
|
966
|
+
* @return {number}
|
|
967
|
+
*/
|
|
968
|
+
remintNodeId( existingSiblingIds ) {
|
|
969
|
+
|
|
970
|
+
this.nodeId = existingSiblingIds !== undefined
|
|
971
|
+
? allocateNodeId( existingSiblingIds )
|
|
972
|
+
: generateNodeId();
|
|
973
|
+
return this.nodeId;
|
|
974
|
+
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/**
|
|
978
|
+
* Direct child with the given {@link nodeId}, or `null`.
|
|
979
|
+
* @param {number} id
|
|
980
|
+
* @return {?Object3D}
|
|
981
|
+
*/
|
|
982
|
+
findChildByNodeId( id ) {
|
|
983
|
+
|
|
984
|
+
for ( let i = 0; i < this.children.length; i ++ ) {
|
|
985
|
+
|
|
986
|
+
const child = this.children[ i ];
|
|
987
|
+
if ( child.nodeId === id ) {
|
|
988
|
+
|
|
989
|
+
return child;
|
|
990
|
+
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
return null;
|
|
996
|
+
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Resolves a path relative to this node. An empty path returns `this`.
|
|
1001
|
+
* @param {NodePath|string} path
|
|
1002
|
+
* @return {?Object3D}
|
|
1003
|
+
*/
|
|
1004
|
+
resolvePath( path ) {
|
|
1005
|
+
|
|
1006
|
+
const nodePath = NodePath.coerce( path );
|
|
1007
|
+
if ( nodePath.isEmpty ) {
|
|
1008
|
+
|
|
1009
|
+
return this;
|
|
1010
|
+
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
let current = this;
|
|
1014
|
+
for ( let i = 0; i < nodePath.ids.length; i ++ ) {
|
|
1015
|
+
|
|
1016
|
+
const next = current.findChildByNodeId( nodePath.ids[ i ] );
|
|
1017
|
+
if ( ! next ) {
|
|
1018
|
+
|
|
1019
|
+
return null;
|
|
1020
|
+
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
current = next;
|
|
1024
|
+
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
return current;
|
|
1028
|
+
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* Builds a {@link NodePath} for this node.
|
|
1033
|
+
* - Without `root`: absolute path from the topmost non-Scene ancestor down to this node.
|
|
1034
|
+
* - With `root`: path relative to `root` (empty when `this === root`); `null` when not under `root`.
|
|
1035
|
+
* @param {Object3D} [root]
|
|
1036
|
+
* @return {?NodePath}
|
|
1037
|
+
*/
|
|
1038
|
+
getNodePath( root ) {
|
|
1039
|
+
|
|
1040
|
+
if ( root !== undefined ) {
|
|
1041
|
+
|
|
1042
|
+
if ( this === root ) {
|
|
1043
|
+
|
|
1044
|
+
return NodePath.empty;
|
|
1045
|
+
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
const absolute = this.getNodePath();
|
|
1049
|
+
const rootAbsolute = root.getNodePath();
|
|
1050
|
+
if ( ! absolute || ! rootAbsolute ) {
|
|
1051
|
+
|
|
1052
|
+
return null;
|
|
1053
|
+
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
return absolute.relativeFrom( rootAbsolute );
|
|
1057
|
+
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
const ids = [];
|
|
1061
|
+
let current = this;
|
|
1062
|
+
while ( current ) {
|
|
1063
|
+
|
|
1064
|
+
ids.push( current.ensureNodeId() );
|
|
1065
|
+
const parent = current.parent;
|
|
1066
|
+
// Scene / World containers are not part of authored NodePaths.
|
|
1067
|
+
if ( parent === null || parent.isScene === true ) {
|
|
1068
|
+
|
|
1069
|
+
break;
|
|
1070
|
+
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
current = parent;
|
|
1074
|
+
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
ids.reverse();
|
|
1078
|
+
return new NodePath( ids );
|
|
1079
|
+
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
/**
|
|
1083
|
+
* Ensures every Object3D in this subtree has a valid {@link nodeId}, unique among its siblings.
|
|
1084
|
+
*/
|
|
1085
|
+
ensureNodeIdsInSubtree() {
|
|
1086
|
+
|
|
1087
|
+
this.ensureNodeId();
|
|
1088
|
+
const taken = new Set();
|
|
1089
|
+
for ( let i = 0; i < this.children.length; i ++ ) {
|
|
1090
|
+
|
|
1091
|
+
const child = this.children[ i ];
|
|
1092
|
+
if ( ! isValidNodeId( child.nodeId ) || taken.has( child.nodeId ) ) {
|
|
1093
|
+
|
|
1094
|
+
child.nodeId = allocateNodeId( taken );
|
|
1095
|
+
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
taken.add( child.nodeId );
|
|
1099
|
+
child.ensureNodeIdsInSubtree();
|
|
1100
|
+
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
}
|
|
1104
|
+
// !WITH_GENESYS
|
|
1105
|
+
|
|
921
1106
|
/**
|
|
922
1107
|
* Searches through the 3D object and its children, starting with the 3D object
|
|
923
1108
|
* itself, and returns the first with a matching ID.
|
|
@@ -1320,6 +1505,10 @@ class Object3D extends EventDispatcher {
|
|
|
1320
1505
|
object.uuid = this.uuid;
|
|
1321
1506
|
object.type = this.type;
|
|
1322
1507
|
|
|
1508
|
+
// WITH_GENESYS
|
|
1509
|
+
object.nodeId = this.nodeId;
|
|
1510
|
+
// !WITH_GENESYS
|
|
1511
|
+
|
|
1323
1512
|
if ( this.name !== '' ) object.name = this.name;
|
|
1324
1513
|
if ( this.castShadow === true ) object.castShadow = true;
|
|
1325
1514
|
if ( this.receiveShadow === true ) object.receiveShadow = true;
|
|
@@ -1630,6 +1819,8 @@ class Object3D extends EventDispatcher {
|
|
|
1630
1819
|
this.visible = source.visible;
|
|
1631
1820
|
// WITH_GENESYS
|
|
1632
1821
|
this.selfHidden = source.selfHidden;
|
|
1822
|
+
// Keep nodeId; reminted on attach if it collides with a sibling.
|
|
1823
|
+
this.nodeId = source.nodeId;
|
|
1633
1824
|
// !WITH_GENESYS
|
|
1634
1825
|
|
|
1635
1826
|
this.castShadow = source.castShadow;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// WITH_GENESYS
|
|
2
|
+
/**
|
|
3
|
+
* Sibling-local 16-bit node identities.
|
|
4
|
+
*
|
|
5
|
+
* `null` / `undefined` mean unset. `0` is a valid NodeId.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { hashStringToUint32, randomSeedUint32, XorShift32 } from '../math/XorShift32.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* True when `id` is a finite integer in the uint16 range (including 0).
|
|
12
|
+
* @param {unknown} id
|
|
13
|
+
* @return {boolean}
|
|
14
|
+
*/
|
|
15
|
+
function isValidNodeId( id ) {
|
|
16
|
+
|
|
17
|
+
return typeof id === 'number' && Number.isInteger( id ) && id >= 0 && id <= 0xffff;
|
|
18
|
+
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Canonical display form: 4 lowercase hex characters.
|
|
23
|
+
* @param {number} id
|
|
24
|
+
* @return {string}
|
|
25
|
+
*/
|
|
26
|
+
function nodeIdToString( id ) {
|
|
27
|
+
|
|
28
|
+
return ( id & 0xffff ).toString( 16 ).padStart( 4, '0' );
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Parses a hex node-id segment (1–4 hex digits).
|
|
34
|
+
* @param {string} value
|
|
35
|
+
* @return {?number}
|
|
36
|
+
*/
|
|
37
|
+
function nodeIdFromString( value ) {
|
|
38
|
+
|
|
39
|
+
const trimmed = value.trim();
|
|
40
|
+
if ( ! /^[0-9a-fA-F]{1,4}$/.test( trimmed ) ) {
|
|
41
|
+
|
|
42
|
+
return null;
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return Number.parseInt( trimmed, 16 ) & 0xffff;
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const defaultNodeIdRng = new XorShift32();
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Seeds the process-wide NodeId RNG for deterministic construction.
|
|
54
|
+
* Pass `undefined` / omit to restore a non-deterministic stream.
|
|
55
|
+
* @param {string} [seed]
|
|
56
|
+
*/
|
|
57
|
+
function configureNodeIdSeed( seed ) {
|
|
58
|
+
|
|
59
|
+
if ( seed == null ) {
|
|
60
|
+
|
|
61
|
+
defaultNodeIdRng.setState( randomSeedUint32() );
|
|
62
|
+
return;
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
defaultNodeIdRng.setState( hashStringToUint32( seed ) );
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Generates a random NodeId from the process-wide default RNG.
|
|
72
|
+
* @param {XorShift32} [rng]
|
|
73
|
+
* @return {number}
|
|
74
|
+
*/
|
|
75
|
+
function generateNodeId( rng = defaultNodeIdRng ) {
|
|
76
|
+
|
|
77
|
+
return rng.nextUint16();
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Allocates a NodeId not present in `existing`.
|
|
83
|
+
* @param {Iterable<number>|ReadonlySet<number>} existing
|
|
84
|
+
* @param {XorShift32} [rng]
|
|
85
|
+
* @return {number}
|
|
86
|
+
*/
|
|
87
|
+
function allocateNodeId( existing, rng = defaultNodeIdRng ) {
|
|
88
|
+
|
|
89
|
+
const taken = existing instanceof Set ? existing : new Set( existing );
|
|
90
|
+
if ( taken.size >= 0x10000 ) {
|
|
91
|
+
|
|
92
|
+
throw new Error( '[nodeId] Cannot allocate NodeId: all 65536 sibling ids are in use' );
|
|
93
|
+
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let id = rng.nextUint16();
|
|
97
|
+
while ( taken.has( id ) ) {
|
|
98
|
+
|
|
99
|
+
id = rng.nextUint16();
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return id;
|
|
104
|
+
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Deterministic NodeId from a stable key (first-try candidate for remints).
|
|
109
|
+
* @param {string} key
|
|
110
|
+
* @return {number}
|
|
111
|
+
*/
|
|
112
|
+
function nodeIdFromKey( key ) {
|
|
113
|
+
|
|
114
|
+
return new XorShift32( key ).nextUint16();
|
|
115
|
+
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Collects nodeId values from a sibling set.
|
|
120
|
+
* @param {Iterable<{nodeId?: ?number}>} nodes
|
|
121
|
+
* @param {Object} [except]
|
|
122
|
+
* @return {Set<number>}
|
|
123
|
+
*/
|
|
124
|
+
function collectSiblingNodeIds( nodes, except ) {
|
|
125
|
+
|
|
126
|
+
const taken = new Set();
|
|
127
|
+
for ( const node of nodes ) {
|
|
128
|
+
|
|
129
|
+
if ( node !== except && isValidNodeId( node.nodeId ) ) {
|
|
130
|
+
|
|
131
|
+
taken.add( node.nodeId );
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return taken;
|
|
138
|
+
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Ensures `object.nodeId` is unique among `parent.children`.
|
|
143
|
+
* @param {import('./Object3D.js').Object3D} object
|
|
144
|
+
* @param {import('./Object3D.js').Object3D} parent
|
|
145
|
+
*/
|
|
146
|
+
function ensureUniqueNodeIdAmongParentChildren( object, parent ) {
|
|
147
|
+
|
|
148
|
+
const taken = collectSiblingNodeIds( parent.children, object );
|
|
149
|
+
if ( ! isValidNodeId( object.nodeId ) || taken.has( object.nodeId ) ) {
|
|
150
|
+
|
|
151
|
+
object.nodeId = allocateNodeId( taken );
|
|
152
|
+
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export {
|
|
158
|
+
isValidNodeId,
|
|
159
|
+
nodeIdToString,
|
|
160
|
+
nodeIdFromString,
|
|
161
|
+
configureNodeIdSeed,
|
|
162
|
+
generateNodeId,
|
|
163
|
+
allocateNodeId,
|
|
164
|
+
nodeIdFromKey,
|
|
165
|
+
collectSiblingNodeIds,
|
|
166
|
+
ensureUniqueNodeIdAmongParentChildren
|
|
167
|
+
};
|
|
168
|
+
// !WITH_GENESYS
|
|
@@ -1128,6 +1128,10 @@ class ObjectLoader extends Loader {
|
|
|
1128
1128
|
|
|
1129
1129
|
object.uuid = data.uuid;
|
|
1130
1130
|
|
|
1131
|
+
// WITH_GENESYS
|
|
1132
|
+
if ( data.nodeId !== undefined ) object.nodeId = data.nodeId;
|
|
1133
|
+
// !WITH_GENESYS
|
|
1134
|
+
|
|
1131
1135
|
if ( data.name !== undefined ) object.name = data.name;
|
|
1132
1136
|
|
|
1133
1137
|
if ( data.matrix !== undefined ) {
|