Needle Engine

Changes between version 3.28.8 and 3.29.0
Files changed (14) hide show
  1. plugins/vite/meta.js +3 -1
  2. src/engine-components/Collider.ts +1 -16
  3. src/engine/engine_addressables.ts +16 -0
  4. src/engine/engine_gizmos.ts +326 -321
  5. src/engine/engine_gltf_builtin_components.ts +18 -5
  6. src/engine/engine_networking_instantiate.ts +1 -1
  7. src/engine/engine_physics_rapier.ts +24 -10
  8. src/engine/engine_physics.ts +2 -1
  9. src/engine/engine_scenelighting.ts +3 -28
  10. src/engine/engine_types.ts +15 -4
  11. src/engine/engine_utils.ts +8 -0
  12. src/engine/extensions/NEEDLE_lighting_settings.ts +0 -20
  13. src/engine/extensions/NEEDLE_techniques_webgl.ts +3 -1
  14. src/engine/codegen/register_types.ts +2 -2
plugins/vite/meta.js CHANGED
@@ -117,7 +117,9 @@
117
117
 
118
118
 
119
119
  function updateUrlMetaTag(html, url) {
120
- html = html.replace(`<meta name="url" content="http://needle.tools">`, `<meta name="url" content="${url}">`);
120
+ const result = `<meta name="url" content="${url}">`;
121
+ html = html.replace(`<meta name="url" content="https://needle.tools">`, result);
122
+ html = html.replace(`<meta name="url" content="http://needle.tools">`, result);
121
123
  return html;
122
124
  }
123
125
 
src/engine-components/Collider.ts CHANGED
@@ -19,31 +19,16 @@
19
19
  @serializable(Rigidbody)
20
20
  attachedRigidbody: Rigidbody | null = null;
21
21
 
22
- /**
23
- * Note: Make sure to call updatePhysicsMaterial after having changed this property
24
- */
25
22
  @serializable()
26
23
  isTrigger: boolean = false;
27
24
 
28
- /**
29
- * The physics material determines how the collider interacts with other colliders (e.g. bouncyness)
30
- * Note: Make sure to call updatePhysicsMaterial after having changed this property
31
- */
32
25
  @serializable()
33
26
  sharedMaterial?: PhysicsMaterial;
34
27
 
35
- /** The collider membership indicates what groups the collider is part of (e.g. group 2 and 3)
36
- * An empty array indicates that the collider is part of all groups
37
- * Note: Make sure to call updateProperties after having changed this property
38
- */
39
28
  @serializable()
40
29
  membership: number[] = [0];
41
- /** The collider filter indicates what groups the collider can interact with (e.g. group 3 and 4)
42
- * An empty array indicates that the collider can interact with all groups
43
- * Note: Make sure to call updateProperties after having changed this property
44
- */
45
30
  @serializable()
46
- filter: number[] = [0];
31
+ filter?: number[];
47
32
 
48
33
  awake() {
49
34
  super.awake();
src/engine/engine_addressables.ts CHANGED
@@ -60,6 +60,14 @@
60
60
 
61
61
  const $assetReference = Symbol("assetReference");
62
62
 
63
+ /** ### AssetReferences can be used to easily load glTF or GLB assets
64
+ * You can use `AssetReference.getOrCreate` to get an AssetReference for a URL to be easily loaded.
65
+ * When using the same URL multiple times the same AssetReference will be returned, this avoids loading or creating the same asset multiple times.
66
+ * - `myAssetReference.preload()` to load the asset binary without creating an instance yet.
67
+ * - `myAssetReference.loadAssetAsync()` to load the asset and create an instance.
68
+ * - `myAssetReference.instantiate()` to load the asset and create a new instance.
69
+ * - `myAssetReference.unload()` to dispose allocated memory and destroy the asset instance.
70
+ */
63
71
  export class AssetReference {
64
72
 
65
73
  /**
@@ -144,8 +152,10 @@
144
152
  return !this.asset || this.asset.__destroyed === true || isDestroyed(this.asset) === true;
145
153
  }
146
154
 
155
+ /** has the asset been loaded (via preload) or does it exist already (assigned to `asset`) */
147
156
  isLoaded() { return this._rawBinary || this.asset !== undefined }
148
157
 
158
+ /** frees previously allocated memory and destroys the current `asset` instance (if any) */
149
159
  unload() {
150
160
  if (this.asset) {
151
161
  if (debug) console.log("Unload", this.asset);
@@ -163,6 +173,7 @@
163
173
  }
164
174
  }
165
175
 
176
+ /** loads the asset binary without creating an instance */
166
177
  async preload(): Promise<ArrayBuffer | null> {
167
178
  if (!this.mustLoad) return null;
168
179
  if (this._isLoadingRawBinary) return null;
@@ -178,6 +189,9 @@
178
189
  }
179
190
 
180
191
  // TODO: we need a way to abort loading a resource
192
+ /** Loads the asset and creates one instance (assigned to `asset`)
193
+ * @returns the loaded asset
194
+ */
181
195
  async loadAssetAsync(prog?: ProgressCallback | null) {
182
196
  if (debug)
183
197
  console.log("loadAssetAsync", this.uri);
@@ -230,10 +244,12 @@
230
244
  }
231
245
  }
232
246
 
247
+ /** loads and returns a new instance of `asset` */
233
248
  async instantiate(parent?: Object3D | InstantiateOptions) {
234
249
  return this.onInstantiate(parent, false);
235
250
  }
236
251
 
252
+ /** loads and returns a new instance of `asset` - this call is networked so an instance will be created on all connected users */
237
253
  async instantiateSynced(parent?: Object3D | InstantiateOptions, saveOnServer: boolean = true) {
238
254
  return this.onInstantiate(parent, true, saveOnServer);
239
255
  }
src/engine/engine_gizmos.ts CHANGED
@@ -1,322 +1,327 @@
1
- import { BufferAttribute, Line, BoxGeometry, EdgesGeometry, Color, LineSegments, LineBasicMaterial, Object3D, Mesh, SphereGeometry, type ColorRepresentation, Vector3, Box3, Quaternion, CylinderGeometry, AxesHelper } from 'three';
2
- import { Context } from './engine_setup.js';
3
- import { getWorldPosition, lookAtObject, setWorldPositionXYZ } from './engine_three_utils.js';
4
- import type { Vec3, Vec4 } from './engine_types.js';
5
- import ThreeMeshUI, { Inline, Text } from "three-mesh-ui"
6
- import { getParam } from './engine_utils.js';
7
- import { type Options } from 'three-mesh-ui/build/types/core/elements/MeshUIBaseElement.js';
8
-
9
- const _tmp = new Vector3();
10
- const _tmp2 = new Vector3();
11
- const _quat = new Quaternion();
12
-
13
- const debug = getParam("debuggizmos");
14
- const defaultColor: ColorRepresentation = 0x888888;
15
-
16
- export type LabelHandle = {
17
- setText(str: string);
18
- }
19
- declare type ColorWithAlpha = Color & { a: number };
20
-
21
- export class Gizmos {
22
-
23
- /**
24
- * Draw a label in the scene or attached to an object (if a parent is provided)
25
- * @returns a handle to the label that can be used to change the text
26
- */
27
- static DrawLabel(position: Vec3, text: string, size: number = .1, duration: number = 9999, color?: ColorRepresentation, backgroundColor?: ColorRepresentation | ColorWithAlpha, parent?: Object3D,) {
28
- if (!color) color = defaultColor;
29
- const element = Internal.getTextLabel(duration, text, size, color, backgroundColor);
30
- if (parent instanceof Object3D) parent.add(element);
31
- element.position.x = position.x;
32
- element.position.y = position.y;
33
- element.position.z = position.z;
34
- return element as LabelHandle;
35
- }
36
-
37
- static DrawRay(origin: Vec3, dir: Vec3, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true) {
38
- const obj = Internal.getLine(duration);
39
- const positions = obj.geometry.getAttribute("position");
40
- positions.setXYZ(0, origin.x, origin.y, origin.z);
41
- _tmp.set(dir.x, dir.y, dir.z).multiplyScalar(999999999);
42
- positions.setXYZ(1, origin.x + _tmp.x, origin.y + _tmp.y, origin.z + _tmp.z);
43
- positions.needsUpdate = true;
44
- obj.material["color"].set(color);
45
- obj.material["depthTest"] = depthTest;
46
- obj.material["depthWrite"] = false;
47
- }
48
-
49
- static DrawDirection(pt: Vec3, direction: Vec3 | Vec4, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true, lengthFactor: number = 1) {
50
- const obj = Internal.getLine(duration);
51
- const positions = obj.geometry.getAttribute("position");
52
- positions.setXYZ(0, pt.x, pt.y, pt.z);
53
- if (direction["w"] !== undefined) {
54
- _tmp.set(0, 0, -lengthFactor);
55
- _quat.set(direction["x"], direction["y"], direction["z"], direction["w"]);
56
- _tmp.applyQuaternion(_quat);
57
- }
58
- else {
59
- _tmp.set(direction.x, direction.y, direction.z);
60
- _tmp.multiplyScalar(lengthFactor);
61
- }
62
- positions.setXYZ(1, pt.x + _tmp.x, pt.y + _tmp.y, pt.z + _tmp.z);
63
- positions.needsUpdate = true;
64
- obj.material["color"].set(color);
65
- obj.material["depthTest"] = depthTest;
66
- obj.material["depthWrite"] = false;
67
-
68
- }
69
-
70
- static DrawLine(pt0: Vec3, pt1: Vec3, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true) {
71
- const obj = Internal.getLine(duration);
72
-
73
- const positions = obj.geometry.getAttribute("position");
74
- positions.setXYZ(0, pt0.x, pt0.y, pt0.z);
75
- positions.setXYZ(1, pt1.x, pt1.y, pt1.z);
76
- positions.needsUpdate = true;
77
- obj.material["color"].set(color);
78
- obj.material["depthTest"] = depthTest;
79
- obj.material["depthWrite"] = false;
80
- }
81
-
82
- static DrawWireSphere(center: Vec3, radius: number, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true) {
83
- const obj = Internal.getSphere(radius, duration, true);
84
- setWorldPositionXYZ(obj, center.x, center.y, center.z);
85
- obj.material["color"].set(color);
86
- obj.material["depthTest"] = depthTest;
87
- obj.material["depthWrite"] = false;
88
- }
89
-
90
- static DrawSphere(center: Vec3, radius: number, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true) {
91
- const obj = Internal.getSphere(radius, duration, false);
92
- setWorldPositionXYZ(obj, center.x, center.y, center.z);
93
- obj.material["color"].set(color);
94
- obj.material["depthTest"] = depthTest;
95
- obj.material["depthWrite"] = false;
96
- }
97
-
98
- static DrawWireBox(center: Vec3, size: Vec3, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true) {
99
- const obj = Internal.getBox(duration);
100
- obj.position.set(center.x, center.y, center.z);
101
- obj.scale.set(size.x, size.y, size.z);
102
- obj.material["color"].set(color);
103
- obj.material["depthTest"] = depthTest;
104
- obj.material["wireframe"] = true;
105
- obj.material["depthWrite"] = false;
106
- }
107
-
108
- static DrawWireBox3(box: Box3, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true) {
109
- const obj = Internal.getBox(duration);
110
- obj.position.copy(box.getCenter(_tmp));
111
- obj.scale.copy(box.getSize(_tmp));
112
- obj.material["color"].set(color);
113
- obj.material["depthTest"] = depthTest;
114
- obj.material["wireframe"] = true;
115
- obj.material["depthWrite"] = false;
116
- }
117
-
118
- private static _up = new Vector3(0, 1, 0);
119
- static DrawArrow(pt0: Vec3, pt1: Vec3, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true, wireframe: boolean = false) {
120
- const obj = Internal.getArrowHead(duration);
121
- obj.position.set(pt1.x, pt1.y, pt1.z);
122
- obj.quaternion.setFromUnitVectors(this._up.set(0, 1, 0), _tmp.set(pt1.x, pt1.y, pt1.z).sub(_tmp2.set(pt0.x, pt0.y, pt0.z)).normalize());
123
- const dist = _tmp.set(pt1.x, pt1.y, pt1.z).sub(_tmp2.set(pt0.x, pt0.y, pt0.z)).length();
124
- const scale = dist * 0.1;
125
- obj.scale.set(scale, scale, scale);
126
- obj.material["color"].set(color);
127
- obj.material["depthTest"] = depthTest;
128
- obj.material["wireframe"] = wireframe;
129
- this.DrawLine(pt0, pt1, color, duration, depthTest);
130
- }
131
- }
132
-
133
- const box: BoxGeometry = new BoxGeometry(1, 1, 1);
134
- export function CreateWireCube(col: ColorRepresentation | null = null): LineSegments {
135
- const color = new Color(col ?? 0xdddddd);
136
- // const material = new MeshBasicMaterial();
137
- // material.color = new Color(col ?? 0xdddddd);
138
- // material.wireframe = true;
139
- // const box = new Mesh(box, material);
140
- // box.name = "BOX_GIZMO";
141
- const edges = new EdgesGeometry(box);
142
- const line = new LineSegments(edges, new LineBasicMaterial({ color: color }));
143
- return line;
144
- }
145
-
146
-
147
-
148
- const $cacheSymbol = Symbol("GizmoCache");
149
- class Internal {
150
- // private static createdLines: number = 0;
151
-
152
- private static readonly familyName = "needle-gizmos";
153
- private static ensureFont() {
154
- let fontFamily = ThreeMeshUI.FontLibrary.getFontFamily(this.familyName);
155
-
156
- if (!fontFamily) {
157
- fontFamily = ThreeMeshUI.FontLibrary.addFontFamily(this.familyName);
158
- const variant = fontFamily.addVariant("normal", "normal", "./include/needle/arial-msdf.json", "./include/needle/arial.png") as any as ThreeMeshUI.FontVariant;
159
- variant?.addEventListener('ready', () => {
160
- ThreeMeshUI.update();
161
- });
162
- }
163
- }
164
-
165
- static getTextLabel(duration: number, text: string, size: number, color: ColorRepresentation, backgroundColor?: ColorRepresentation | ColorWithAlpha): Text & LabelHandle {
166
- this.ensureFont();
167
- let element = this.textLabelCache.pop();
168
-
169
- let opacity = 1;
170
- if (backgroundColor && typeof backgroundColor === "string" && backgroundColor?.length >= 8 && backgroundColor.startsWith("#")) {
171
- opacity = parseInt(backgroundColor.substring(7), 16) / 255;
172
- backgroundColor = backgroundColor.substring(0, 7);
173
- if (debug)
174
- console.log(backgroundColor, opacity);
175
- }
176
- else if (typeof backgroundColor === "object" && backgroundColor["a"] !== undefined) {
177
- opacity = backgroundColor["a"]
178
- }
179
-
180
- const props: Options = {
181
- boxSizing: 'border-box',
182
- fontFamily: this.familyName,
183
- width: "auto",
184
- fontSize: size,
185
- color: color,
186
- lineHeight: .75,
187
- backgroundColor: backgroundColor ?? undefined,
188
- backgroundOpacity: opacity,
189
- textContent: text,
190
- borderRadius: 1 * size,
191
- padding: 1 * size,
192
- };
193
-
194
- if (!element) {
195
- element = new Text(props);
196
- const global = this;
197
- const labelHandle = element as LabelHandle & Text;
198
- labelHandle.setText = function (str: string) {
199
- this.set({ textContent: str, whiteSpace: 'pre' });
200
- global.tmuiNeedsUpdate = true;
201
- };
202
- }
203
- else {
204
- element.set(props);
205
- // const handle = element as any as LabelHandle;
206
- // handle.setText(text);
207
- }
208
- this.tmuiNeedsUpdate = true;
209
- element.layers.disableAll();
210
- element.layers.enable(2);
211
- this.registerTimedObject(Context.Current, element, duration, this.textLabelCache);
212
- return element as Text & LabelHandle;
213
- }
214
-
215
- static getBox(duration: number): Mesh {
216
- let box = this.boxesCache.pop();
217
- if (!box) {
218
- const geo: BoxGeometry = new BoxGeometry(1, 1, 1);
219
- box = new Mesh(geo);
220
- }
221
- this.registerTimedObject(Context.Current, box, duration, this.boxesCache);
222
- return box;
223
- }
224
-
225
- static getLine(duration: number): Line {
226
- let line = this.linesCache.pop();
227
- if (!line) {
228
- line = new Line();
229
- let positions = line.geometry.getAttribute("position");
230
- if (!positions) {
231
- positions = new BufferAttribute(new Float32Array(2 * 3), 3);
232
- line.geometry.setAttribute("position", positions);
233
- }
234
- }
235
- this.registerTimedObject(Context.Current, line, duration, this.linesCache);
236
- return line;
237
- }
238
-
239
- static getSphere(radius: number, duration: number, wireframe: boolean): Mesh {
240
- let sphere = this.spheresCache.pop();
241
- if (!sphere) {
242
- sphere = new Mesh(new SphereGeometry(1, 8, 8));
243
- }
244
- sphere.scale.set(radius, radius, radius);
245
- sphere.material["wireframe"] = wireframe;
246
- this.registerTimedObject(Context.Current, sphere, duration, this.spheresCache);
247
- return sphere;
248
- }
249
-
250
- static getArrowHead(duration: number): Mesh {
251
- let arrowHead = this.arrowHeadsCache.pop();
252
- if (!arrowHead) {
253
- arrowHead = new Mesh(new CylinderGeometry(0, .5, 1, 8));
254
- }
255
- this.registerTimedObject(Context.Current, arrowHead, duration, this.arrowHeadsCache);
256
- return arrowHead;
257
- }
258
-
259
- private static linesCache: Array<Line> = [];
260
- private static spheresCache: Mesh[] = [];
261
- private static boxesCache: Mesh[] = [];
262
- private static arrowHeadsCache: Mesh[] = [];
263
- private static textLabelCache: Array<Text> = [];
264
-
265
- private static registerTimedObject(context: Context, object: Object3D, duration: number, cache: Array<Object3D>) {
266
- if (!this.contextPostRenderCallbacks.get(context)) {
267
- const cb = () => { this.onPostRender(context, this.timedObjectsBuffer, this.timesBuffer) };
268
- this.contextPostRenderCallbacks.set(context, cb);
269
- context.post_render_callbacks.push(cb);
270
- }
271
- if (!this.contextBeforeRenderCallbacks.get(context)) {
272
- const cb = () => { this.onBeforeRender(context, this.timedObjectsBuffer) };
273
- this.contextBeforeRenderCallbacks.set(context, cb);
274
- context.pre_render_callbacks.push(cb);
275
- }
276
- object.renderOrder = 999999;
277
- object.layers.disableAll();
278
- object.layers.enable(2);
279
- object[$cacheSymbol] = cache;
280
- this.timedObjectsBuffer.push(object);
281
- this.timesBuffer.push(Context.Current.time.time + duration);
282
- context.scene.add(object);
283
- }
284
-
285
-
286
- private static timedObjectsBuffer = new Array<Object3D>();
287
- private static timesBuffer = new Array<number>();
288
- private static contextPostRenderCallbacks = new Map<Context, () => void>();
289
- private static contextBeforeRenderCallbacks = new Map<Context, () => void>();
290
- private static tmuiNeedsUpdate = false;
291
-
292
- private static onBeforeRender(ctx: Context, objects: Array<Object3D>) {
293
- // const cameraWorldPosition = getWorldPosition(ctx.mainCamera!, _tmp);
294
- if (this.tmuiNeedsUpdate) {
295
- ThreeMeshUI.update();
296
- }
297
- for (let i = 0; i < objects.length; i++) {
298
- const obj = objects[i];
299
- if (ctx.mainCamera && obj instanceof ThreeMeshUI.MeshUIBaseElement) {
300
- const isInXR = ctx.isInVR;
301
- const keepUp = isInXR;
302
- const copyRotation = !isInXR;
303
- lookAtObject(obj, ctx.mainCamera, keepUp, copyRotation);
304
- }
305
- }
306
- }
307
-
308
- private static onPostRender(ctx: Context, objects: Array<Object3D>, times: Array<number>) {
309
- const time = ctx.time.time;
310
- for (let i = 0; i < objects.length; i++) {
311
- const obj = objects[i];
312
- if (time > times[i]) {
313
- const cache = obj[$cacheSymbol];
314
- cache.push(obj);
315
- obj.removeFromParent();
316
- objects.splice(i, 1);
317
- times.splice(i, 1);
318
- }
319
- }
320
- }
321
-
1
+ import { BufferAttribute, Line, BoxGeometry, EdgesGeometry, Color, LineSegments, LineBasicMaterial, Object3D, Mesh, SphereGeometry, type ColorRepresentation, Vector3, Box3, Quaternion, CylinderGeometry, AxesHelper } from 'three';
2
+ import { Context } from './engine_setup.js';
3
+ import { getWorldPosition, lookAtObject, setWorldPositionXYZ } from './engine_three_utils.js';
4
+ import type { Vec3, Vec4 } from './engine_types.js';
5
+ import ThreeMeshUI, { Inline, Text } from "three-mesh-ui"
6
+ import { getParam } from './engine_utils.js';
7
+ import { type Options } from 'three-mesh-ui/build/types/core/elements/MeshUIBaseElement.js';
8
+
9
+ const _tmp = new Vector3();
10
+ const _tmp2 = new Vector3();
11
+ const _quat = new Quaternion();
12
+
13
+ const debug = getParam("debuggizmos");
14
+ const defaultColor: ColorRepresentation = 0x888888;
15
+
16
+ export type LabelHandle = {
17
+ setText(str: string);
18
+ }
19
+ declare type ColorWithAlpha = Color & { a: number };
20
+
21
+ export class Gizmos {
22
+
23
+ static isGizmo(obj: Object3D) {
24
+ return obj[$cacheSymbol] !== undefined;
25
+ }
26
+
27
+ /**
28
+ * Draw a label in the scene or attached to an object (if a parent is provided)
29
+ * @returns a handle to the label that can be used to change the text
30
+ */
31
+ static DrawLabel(position: Vec3, text: string, size: number = .1, duration: number = 9999, color?: ColorRepresentation, backgroundColor?: ColorRepresentation | ColorWithAlpha, parent?: Object3D,) {
32
+ if (!color) color = defaultColor;
33
+ const element = Internal.getTextLabel(duration, text, size, color, backgroundColor);
34
+ if (parent instanceof Object3D) parent.add(element);
35
+ element.position.x = position.x;
36
+ element.position.y = position.y;
37
+ element.position.z = position.z;
38
+ return element as LabelHandle;
39
+ }
40
+
41
+ static DrawRay(origin: Vec3, dir: Vec3, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true) {
42
+ const obj = Internal.getLine(duration);
43
+ const positions = obj.geometry.getAttribute("position");
44
+ positions.setXYZ(0, origin.x, origin.y, origin.z);
45
+ _tmp.set(dir.x, dir.y, dir.z).multiplyScalar(999999999);
46
+ positions.setXYZ(1, origin.x + _tmp.x, origin.y + _tmp.y, origin.z + _tmp.z);
47
+ positions.needsUpdate = true;
48
+ obj.material["color"].set(color);
49
+ obj.material["depthTest"] = depthTest;
50
+ obj.material["depthWrite"] = false;
51
+ }
52
+
53
+ static DrawDirection(pt: Vec3, direction: Vec3 | Vec4, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true, lengthFactor: number = 1) {
54
+ const obj = Internal.getLine(duration);
55
+ const positions = obj.geometry.getAttribute("position");
56
+ positions.setXYZ(0, pt.x, pt.y, pt.z);
57
+ if (direction["w"] !== undefined) {
58
+ _tmp.set(0, 0, -lengthFactor);
59
+ _quat.set(direction["x"], direction["y"], direction["z"], direction["w"]);
60
+ _tmp.applyQuaternion(_quat);
61
+ }
62
+ else {
63
+ _tmp.set(direction.x, direction.y, direction.z);
64
+ _tmp.multiplyScalar(lengthFactor);
65
+ }
66
+ positions.setXYZ(1, pt.x + _tmp.x, pt.y + _tmp.y, pt.z + _tmp.z);
67
+ positions.needsUpdate = true;
68
+ obj.material["color"].set(color);
69
+ obj.material["depthTest"] = depthTest;
70
+ obj.material["depthWrite"] = false;
71
+
72
+ }
73
+
74
+ static DrawLine(pt0: Vec3, pt1: Vec3, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true) {
75
+ const obj = Internal.getLine(duration);
76
+
77
+ const positions = obj.geometry.getAttribute("position");
78
+ positions.setXYZ(0, pt0.x, pt0.y, pt0.z);
79
+ positions.setXYZ(1, pt1.x, pt1.y, pt1.z);
80
+ positions.needsUpdate = true;
81
+ obj.material["color"].set(color);
82
+ obj.material["depthTest"] = depthTest;
83
+ obj.material["depthWrite"] = false;
84
+ }
85
+
86
+ static DrawWireSphere(center: Vec3, radius: number, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true) {
87
+ const obj = Internal.getSphere(radius, duration, true);
88
+ setWorldPositionXYZ(obj, center.x, center.y, center.z);
89
+ obj.material["color"].set(color);
90
+ obj.material["depthTest"] = depthTest;
91
+ obj.material["depthWrite"] = false;
92
+ }
93
+
94
+ static DrawSphere(center: Vec3, radius: number, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true) {
95
+ const obj = Internal.getSphere(radius, duration, false);
96
+ setWorldPositionXYZ(obj, center.x, center.y, center.z);
97
+ obj.material["color"].set(color);
98
+ obj.material["depthTest"] = depthTest;
99
+ obj.material["depthWrite"] = false;
100
+ }
101
+
102
+ static DrawWireBox(center: Vec3, size: Vec3, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true) {
103
+ const obj = Internal.getBox(duration);
104
+ obj.position.set(center.x, center.y, center.z);
105
+ obj.scale.set(size.x, size.y, size.z);
106
+ obj.material["color"].set(color);
107
+ obj.material["depthTest"] = depthTest;
108
+ obj.material["wireframe"] = true;
109
+ obj.material["depthWrite"] = false;
110
+ }
111
+
112
+ static DrawWireBox3(box: Box3, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true) {
113
+ const obj = Internal.getBox(duration);
114
+ obj.position.copy(box.getCenter(_tmp));
115
+ obj.scale.copy(box.getSize(_tmp));
116
+ obj.material["color"].set(color);
117
+ obj.material["depthTest"] = depthTest;
118
+ obj.material["wireframe"] = true;
119
+ obj.material["depthWrite"] = false;
120
+ }
121
+
122
+ private static _up = new Vector3(0, 1, 0);
123
+ static DrawArrow(pt0: Vec3, pt1: Vec3, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true, wireframe: boolean = false) {
124
+ const obj = Internal.getArrowHead(duration);
125
+ obj.position.set(pt1.x, pt1.y, pt1.z);
126
+ obj.quaternion.setFromUnitVectors(this._up.set(0, 1, 0), _tmp.set(pt1.x, pt1.y, pt1.z).sub(_tmp2.set(pt0.x, pt0.y, pt0.z)).normalize());
127
+ const dist = _tmp.set(pt1.x, pt1.y, pt1.z).sub(_tmp2.set(pt0.x, pt0.y, pt0.z)).length();
128
+ const scale = dist * 0.1;
129
+ obj.scale.set(scale, scale, scale);
130
+ obj.material["color"].set(color);
131
+ obj.material["depthTest"] = depthTest;
132
+ obj.material["wireframe"] = wireframe;
133
+ this.DrawLine(pt0, pt1, color, duration, depthTest);
134
+ }
135
+ }
136
+
137
+ const box: BoxGeometry = new BoxGeometry(1, 1, 1);
138
+ export function CreateWireCube(col: ColorRepresentation | null = null): LineSegments {
139
+ const color = new Color(col ?? 0xdddddd);
140
+ // const material = new MeshBasicMaterial();
141
+ // material.color = new Color(col ?? 0xdddddd);
142
+ // material.wireframe = true;
143
+ // const box = new Mesh(box, material);
144
+ // box.name = "BOX_GIZMO";
145
+ const edges = new EdgesGeometry(box);
146
+ const line = new LineSegments(edges, new LineBasicMaterial({ color: color }));
147
+ return line;
148
+ }
149
+
150
+
151
+
152
+ const $cacheSymbol = Symbol("GizmoCache");
153
+ class Internal {
154
+ // private static createdLines: number = 0;
155
+
156
+ private static readonly familyName = "needle-gizmos";
157
+ private static ensureFont() {
158
+ let fontFamily = ThreeMeshUI.FontLibrary.getFontFamily(this.familyName);
159
+
160
+ if (!fontFamily) {
161
+ fontFamily = ThreeMeshUI.FontLibrary.addFontFamily(this.familyName);
162
+ const variant = fontFamily.addVariant("normal", "normal", "./include/needle/arial-msdf.json", "./include/needle/arial.png") as any as ThreeMeshUI.FontVariant;
163
+ variant?.addEventListener('ready', () => {
164
+ ThreeMeshUI.update();
165
+ });
166
+ }
167
+ }
168
+
169
+ static getTextLabel(duration: number, text: string, size: number, color: ColorRepresentation, backgroundColor?: ColorRepresentation | ColorWithAlpha): Text & LabelHandle {
170
+ this.ensureFont();
171
+ let element = this.textLabelCache.pop();
172
+
173
+ let opacity = 1;
174
+ if (backgroundColor && typeof backgroundColor === "string" && backgroundColor?.length >= 8 && backgroundColor.startsWith("#")) {
175
+ opacity = parseInt(backgroundColor.substring(7), 16) / 255;
176
+ backgroundColor = backgroundColor.substring(0, 7);
177
+ if (debug)
178
+ console.log(backgroundColor, opacity);
179
+ }
180
+ else if (typeof backgroundColor === "object" && backgroundColor["a"] !== undefined) {
181
+ opacity = backgroundColor["a"]
182
+ }
183
+
184
+ const props: Options = {
185
+ boxSizing: 'border-box',
186
+ fontFamily: this.familyName,
187
+ width: "auto",
188
+ fontSize: size,
189
+ color: color,
190
+ lineHeight: .75,
191
+ backgroundColor: backgroundColor ?? undefined,
192
+ backgroundOpacity: opacity,
193
+ textContent: text,
194
+ borderRadius: 1 * size,
195
+ padding: 1 * size,
196
+ };
197
+
198
+ if (!element) {
199
+ element = new Text(props);
200
+ const global = this;
201
+ const labelHandle = element as LabelHandle & Text;
202
+ labelHandle.setText = function (str: string) {
203
+ this.set({ textContent: str, whiteSpace: 'pre' });
204
+ global.tmuiNeedsUpdate = true;
205
+ };
206
+ }
207
+ else {
208
+ element.set(props);
209
+ // const handle = element as any as LabelHandle;
210
+ // handle.setText(text);
211
+ }
212
+ this.tmuiNeedsUpdate = true;
213
+ element.layers.disableAll();
214
+ element.layers.enable(2);
215
+ this.registerTimedObject(Context.Current, element, duration, this.textLabelCache);
216
+ return element as Text & LabelHandle;
217
+ }
218
+
219
+ static getBox(duration: number): Mesh {
220
+ let box = this.boxesCache.pop();
221
+ if (!box) {
222
+ const geo: BoxGeometry = new BoxGeometry(1, 1, 1);
223
+ box = new Mesh(geo);
224
+ }
225
+ this.registerTimedObject(Context.Current, box, duration, this.boxesCache);
226
+ return box;
227
+ }
228
+
229
+ static getLine(duration: number): Line {
230
+ let line = this.linesCache.pop();
231
+ if (!line) {
232
+ line = new Line();
233
+ let positions = line.geometry.getAttribute("position");
234
+ if (!positions) {
235
+ positions = new BufferAttribute(new Float32Array(2 * 3), 3);
236
+ line.geometry.setAttribute("position", positions);
237
+ }
238
+ }
239
+ this.registerTimedObject(Context.Current, line, duration, this.linesCache);
240
+ return line;
241
+ }
242
+
243
+ static getSphere(radius: number, duration: number, wireframe: boolean): Mesh {
244
+ let sphere = this.spheresCache.pop();
245
+ if (!sphere) {
246
+ sphere = new Mesh(new SphereGeometry(1, 8, 8));
247
+ }
248
+ sphere.scale.set(radius, radius, radius);
249
+ sphere.material["wireframe"] = wireframe;
250
+ this.registerTimedObject(Context.Current, sphere, duration, this.spheresCache);
251
+ return sphere;
252
+ }
253
+
254
+ static getArrowHead(duration: number): Mesh {
255
+ let arrowHead = this.arrowHeadsCache.pop();
256
+ if (!arrowHead) {
257
+ arrowHead = new Mesh(new CylinderGeometry(0, .5, 1, 8));
258
+ }
259
+ this.registerTimedObject(Context.Current, arrowHead, duration, this.arrowHeadsCache);
260
+ return arrowHead;
261
+ }
262
+
263
+ private static linesCache: Array<Line> = [];
264
+ private static spheresCache: Mesh[] = [];
265
+ private static boxesCache: Mesh[] = [];
266
+ private static arrowHeadsCache: Mesh[] = [];
267
+ private static textLabelCache: Array<Text> = [];
268
+
269
+ private static registerTimedObject(context: Context, object: Object3D, duration: number, cache: Array<Object3D>) {
270
+ if (!this.contextPostRenderCallbacks.get(context)) {
271
+ const cb = () => { this.onPostRender(context, this.timedObjectsBuffer, this.timesBuffer) };
272
+ this.contextPostRenderCallbacks.set(context, cb);
273
+ context.post_render_callbacks.push(cb);
274
+ }
275
+ if (!this.contextBeforeRenderCallbacks.get(context)) {
276
+ const cb = () => { this.onBeforeRender(context, this.timedObjectsBuffer) };
277
+ this.contextBeforeRenderCallbacks.set(context, cb);
278
+ context.pre_render_callbacks.push(cb);
279
+ }
280
+
281
+ object.renderOrder = 999999;
282
+ object.layers.disableAll();
283
+ object.layers.enable(2);
284
+ object[$cacheSymbol] = cache;
285
+ this.timedObjectsBuffer.push(object);
286
+ this.timesBuffer.push(Context.Current.time.realtimeSinceStartup + duration);
287
+ context.scene.add(object);
288
+ }
289
+
290
+
291
+ private static readonly timedObjectsBuffer = new Array<Object3D>();
292
+ private static readonly timesBuffer = new Array<number>();
293
+ private static readonly contextPostRenderCallbacks = new Map<Context, () => void>();
294
+ private static readonly contextBeforeRenderCallbacks = new Map<Context, () => void>();
295
+ private static tmuiNeedsUpdate = false;
296
+
297
+ private static onBeforeRender(ctx: Context, objects: Array<Object3D>) {
298
+ // const cameraWorldPosition = getWorldPosition(ctx.mainCamera!, _tmp);
299
+ if (this.tmuiNeedsUpdate) {
300
+ ThreeMeshUI.update();
301
+ }
302
+ for (let i = 0; i < objects.length; i++) {
303
+ const obj = objects[i];
304
+ if (ctx.mainCamera && obj instanceof ThreeMeshUI.MeshUIBaseElement) {
305
+ const isInXR = ctx.isInVR;
306
+ const keepUp = isInXR;
307
+ const copyRotation = !isInXR;
308
+ lookAtObject(obj, ctx.mainCamera, keepUp, copyRotation);
309
+ }
310
+ }
311
+ }
312
+
313
+ private static onPostRender(ctx: Context, objects: Array<Object3D>, times: Array<number>) {
314
+ const time = ctx.time.realtimeSinceStartup;
315
+ for (let i = objects.length - 1; i >= 0; i--) {
316
+ const obj = objects[i];
317
+ if (time > times[i]) {
318
+ const cache = obj[$cacheSymbol];
319
+ cache.push(obj);
320
+ obj.removeFromParent();
321
+ objects.splice(i, 1);
322
+ times.splice(i, 1);
323
+ }
324
+ }
325
+ }
326
+
322
327
  }
src/engine/engine_gltf_builtin_components.ts CHANGED
@@ -122,15 +122,28 @@
122
122
  * That way the order of components in the scene doesnt affect the result GUID
123
123
  */
124
124
  // TODO: clear this when re-loading the context
125
- const componentIdProviderCache = new Map<string, InstantiateIdProvider>();
125
+ const idProviderCache = new Map<string, InstantiateIdProvider>();
126
126
 
127
127
  function recursiveCreateGuids(obj: IGameObject, idProvider: UIDProvider | null, guidsMap: GuidsMap, resolveGuids: IHasResolveGuids[]) {
128
128
  if (idProvider === null) return;
129
129
  if (!obj) return;
130
130
  const prev = obj.guid;
131
- obj.guid = idProvider.generateUUID();
131
+
132
+ // we also want to use the idproviderCache for objects because they might be removed or re-ordered in the scene hierarchy
133
+ // in which case we dont want to e.g. change the syncedInstantiate objects that get created because suddenly another object has that guid
134
+ const idProviderKey = obj.guid;
135
+ if (idProviderKey?.length) {
136
+ if (!idProviderCache.has(idProviderKey)) {
137
+ if (debug) console.log("Creating InstanceIdProvider with key \"" + idProviderKey + "\" for object " + obj.name);
138
+ idProviderCache.set(idProviderKey, new InstantiateIdProvider(idProviderKey));
139
+ }
140
+ }
141
+ const objectIdProvider = idProviderKey && idProviderCache.get(idProviderKey) || idProvider;
142
+
143
+ obj.guid = objectIdProvider.generateUUID();
132
144
  if (prev && prev !== "invalid")
133
145
  guidsMap[prev] = obj.guid;
146
+
134
147
  // console.log(obj);
135
148
  if (obj && obj.userData && obj.userData.components) {
136
149
  for (const comp of obj.userData.components) {
@@ -140,13 +153,13 @@
140
153
  // this is to prevent cases where multiple GLBs are loaded with the same component guid
141
154
  const idProviderKey = comp.guid;
142
155
  if (idProviderKey) {
143
- if (!componentIdProviderCache.has(idProviderKey)) {
156
+ if (!idProviderCache.has(idProviderKey)) {
144
157
  if (debug) console.log("Creating InstanceIdProvider with key \"" + idProviderKey + "\" for component " + comp[originalComponentNameKey]);
145
- componentIdProviderCache.set(idProviderKey, new InstantiateIdProvider(idProviderKey));
158
+ idProviderCache.set(idProviderKey, new InstantiateIdProvider(idProviderKey));
146
159
  }
147
160
  }
148
161
  else if(debug) console.warn("Can not create IdProvider: component " + comp[originalComponentNameKey] + " has no guid", comp.guid);
149
- const componentIdProvider = componentIdProviderCache.get(idProviderKey) || idProvider
162
+ const componentIdProvider = idProviderCache.get(idProviderKey) || idProvider
150
163
 
151
164
  const prev = comp.guid;
152
165
  comp.guid = componentIdProvider.generateUUID();
src/engine/engine_networking_instantiate.ts CHANGED
@@ -56,7 +56,7 @@
56
56
  this._seed = this._originalSeed;
57
57
  }
58
58
 
59
- generateUUID(str?: string) {
59
+ generateUUID(str?: string): string {
60
60
  if (typeof str === "string") {
61
61
  return v5(str, ID_NAMESPACE);
62
62
  }
src/engine/engine_physics_rapier.ts CHANGED
@@ -29,6 +29,7 @@
29
29
  const debugColliderPlacement = getParam("debugcolliderplacement");
30
30
  const debugCollisions = getParam("debugcollisions");
31
31
  const showColliders = getParam("showcolliders");
32
+ const showPhysicsRaycasts = getParam("debugraycasts");
32
33
 
33
34
 
34
35
  /** on physics body and references the needle component */
@@ -65,8 +66,8 @@
65
66
 
66
67
  export class RapierPhysics implements IPhysicsEngine {
67
68
 
68
- /** Enable to draw collider shapes */
69
69
  debugRenderColliders: boolean = false;
70
+ debugRenderRaycasts: boolean = false;
70
71
 
71
72
  removeBody(obj: IComponent) {
72
73
  if (!obj) return;
@@ -297,6 +298,8 @@
297
298
  const ray = this.getPhysicsRay(this.rapierRay, origin, direction);
298
299
  if (!ray) return null;
299
300
 
301
+ if (this.debugRenderRaycasts || showPhysicsRaycasts) Gizmos.DrawRay(ray.origin, ray.dir, 0x0000ff, 1);
302
+
300
303
  const hit = this.world?.castRay(ray, maxDistance, solid, undefined, undefined, undefined, undefined, (c) => {
301
304
  // ignore objects in the IgnoreRaycast=2 layer
302
305
  return !c[$componentKey]?.gameObject.layers.isEnabled(2);
@@ -320,6 +323,8 @@
320
323
  const ray = this.getPhysicsRay(this.rapierRay, origin, direction);
321
324
  if (!ray) return null;
322
325
 
326
+ if (this.debugRenderRaycasts || showPhysicsRaycasts) Gizmos.DrawRay(ray.origin, ray.dir, 0x0000ff, 1);
327
+
323
328
  const hit = this.world?.castRayAndGetNormal(ray, maxDistance, solid, undefined, undefined, undefined, undefined, (c) => {
324
329
  // ignore objects in the IgnoreRaycast=2 layer
325
330
  return !c[$componentKey]?.gameObject.layers.isEnabled(2);
@@ -405,15 +410,24 @@
405
410
  this.rapierSphere = new Ball(radius);
406
411
  this.rapierSphere.radius = radius;
407
412
 
408
- this.world.intersectionsWithShape(point, this.rapierIdentityRotation, this.rapierSphere, col => {
409
- const collider = col[$componentKey] as ICollider
410
- // if (collider.gameObject.layers.isEnabled(2)) return true;
411
- const intersection = new SphereOverlapResult(collider.gameObject, collider);
412
- this.rapierColliderArray.push(intersection);
413
- return true; // Return `false` instead if we want to stop searching for other colliders that contain this point.
414
- }, QueryFilterFlags.EXCLUDE_SENSORS, undefined, undefined, undefined,
413
+ if (this.debugRenderRaycasts || showPhysicsRaycasts) Gizmos.DrawWireSphere(point, radius, 0x3344ff, 1);
414
+ this.world.intersectionsWithShape(point,
415
+ this.rapierIdentityRotation,
416
+ this.rapierSphere,
415
417
  col => {
416
418
  const collider = col[$componentKey] as ICollider
419
+ // if (collider.gameObject.layers.isEnabled(2)) return true;
420
+ const intersection = new SphereOverlapResult(collider.gameObject, collider);
421
+ this.rapierColliderArray.push(intersection);
422
+ return true; // Return `false` instead if we want to stop searching for other colliders that contain this point.
423
+ },
424
+ // TODO: it seems as QueryFilterFlags.EXCLUDE_SENSORS also excludes DYNAMIC Rigidbodies (only if they're set to kinematic)
425
+ undefined, // QueryFilterFlags.EXCLUDE_SENSORS,
426
+ undefined, undefined, undefined,
427
+ col => {
428
+ // we don't want to raycast against triggers (see comment about Exclude Sensors above)
429
+ if (col.isSensor()) return false;
430
+ const collider = col[$componentKey] as ICollider
417
431
  return collider.gameObject.layers.isEnabled(2) == false
418
432
  }
419
433
  );
@@ -787,7 +801,7 @@
787
801
  const body = collider[$bodyKey];
788
802
  const members = collider.membership;
789
803
  let memberMask = 0;
790
- if (members.length <= 0) {
804
+ if (members == undefined) {
791
805
  memberMask = 0xffffffff;
792
806
  }
793
807
  else {
@@ -799,7 +813,7 @@
799
813
 
800
814
  const mask = collider.filter;
801
815
  let filterMask = 0;
802
- if (mask.length <= 0) {
816
+ if (mask == undefined) {
803
817
  filterMask = 0xffffffff;
804
818
  }
805
819
  else {
src/engine/engine_physics.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  import { getWorldPosition } from "./engine_three_utils.js"
5
5
  import type { Vec2, Vec3, } from './engine_types.js';
6
6
  import type { IPhysicsEngine } from './engine_types.js';
7
+ import { Gizmos } from './engine_gizmos.js';
7
8
 
8
9
  const debugPhysics = getParam("debugphysics");
9
10
  const layerMaskHelper: Layers = new Layers();
@@ -161,7 +162,7 @@
161
162
  }
162
163
  private tempBoundingBox: Box3 = new Box3();
163
164
  private onSphereOverlap(obj: Object3D, sp: Sphere, mask: Layers, results: Array<Intersection>, traverseChildsAfterHit: boolean): void {
164
- if (obj.type === "Mesh" && obj.layers.test(mask)) {
165
+ if (obj.type === "Mesh" && obj.layers.test(mask) && !Gizmos.isGizmo(obj)) {
165
166
  const mesh = obj as Mesh;
166
167
  const geo = mesh.geometry;
167
168
  if (!geo.boundingBox)
src/engine/engine_scenelighting.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { Vector4, EquirectangularReflectionMapping, WebGLCubeRenderTarget, Texture, LightProbe, SphericalHarmonics3, SRGBColorSpace } from "three";
2
- import { LightProbeGenerator } from "three/examples/jsm/lights/LightProbeGenerator.js"
3
2
  import { Context } from "./engine_setup.js";
4
3
  import { SceneLightSettings } from "./extensions/NEEDLE_lighting_settings.js";
5
4
  import { createFlatTexture, createTrilightTexture } from "./engine_shaders.js";
6
5
  import { getParam } from "./engine_utils.js";
7
6
  import { type SourceIdentifier } from "./engine_types.js";
8
7
  import { AssetReference } from "./engine_addressables.js";
8
+ // import { LightProbeGenerator } from "three/examples/jsm/lights/LightProbeGenerator.js"
9
9
 
10
10
  const debug = getParam("debugenvlight");
11
11
 
@@ -218,33 +218,6 @@
218
218
  scene.environment = null;
219
219
  }
220
220
 
221
- /** @internal */
222
- async internalGetSceneLightingData(sourceId: SourceIdentifier): Promise<SphericalHarmonicsData> {
223
- if (debug)
224
- console.log("GET SCENE LIGHT DATA", sourceId);
225
-
226
- // const existing = this.getReflection(sourceId);
227
- // const sh = existing?.getSphericalHarmonicsArray(this.sceneLightSettings?.ambientIntensity ?? 1);
228
- // if (sh) {
229
- // console.log("HAS EXISTING", sh, existing);
230
- // return sh;
231
- // }
232
-
233
- // fallback
234
- if (this._waitPromise) return this._waitPromise;
235
- this._waitPromise = new Promise((res, _rej) => {
236
- const interval = setInterval(async () => {
237
- const ex = this.internalGetReflection(sourceId);
238
- if (ex) {
239
- clearInterval(interval);
240
- res(ex.getSphericalHarmonicsArray(this.environmentIntensity ?? 1)!);
241
- }
242
- }, 10);
243
- });
244
- return this._waitPromise;
245
- }
246
-
247
- private _waitPromise?: Promise<SphericalHarmonicsData>;
248
221
  private _lighting: { [sourceId: SourceIdentifier]: LightData } = {};
249
222
 
250
223
  }
@@ -269,6 +242,7 @@
269
242
  tex.colorSpace = SRGBColorSpace;
270
243
  }
271
244
 
245
+ /* REMOVED, no LightProbe / custom shader lighting support for now
272
246
  getSphericalHarmonicsArray(intensityFactor: number = 1): SphericalHarmonicsData | null {
273
247
  if (this._sphericalHarmonicsArray?.length && this._source) {
274
248
  return { array: this._sphericalHarmonicsArray, texture: this._source, lightProbe: this._lightProbe };
@@ -311,4 +285,5 @@
311
285
 
312
286
  return null;
313
287
  }
288
+ */
314
289
  }
src/engine/engine_types.ts CHANGED
@@ -272,19 +272,28 @@
272
272
  export declare interface ICollider extends IComponent {
273
273
  get isCollider();
274
274
  attachedRigidbody: IRigidbody | null;
275
+ /**
276
+ * Note: Make sure to call updatePhysicsMaterial after having changed this property
277
+ */
275
278
  isTrigger: boolean;
279
+ /**
280
+ * The physics material determines how the collider interacts with other colliders (e.g. bouncyness)
281
+ * Note: Make sure to call updatePhysicsMaterial after having changed this property
282
+ */
276
283
  sharedMaterial?: PhysicsMaterial;
277
284
  center?: Vec3 & { multiply(vec: Vec3) };
278
285
  updateProperties(): void;
279
286
  updatePhysicsMaterial(): void;
280
287
  /** The collider membership indicates what groups the collider is part of (e.g. group 2 and 3)
281
- * An empty array indicates that the collider is part of all groups
288
+ * An `undefined` array indicates that the collider is part of all groups
289
+ * Note: Make sure to call updateProperties after having changed this property
282
290
  */
283
- membership: number[];
291
+ membership?: number[];
284
292
  /** The collider filter indicates what groups the collider can interact with (e.g. group 3 and 4)
285
- * An empty array indicates that the collider can interact with all groups
293
+ * An `undefined` array indicates that the collider can interact with all groups
294
+ * Note: Make sure to call updateProperties after having changed this property
286
295
  */
287
- filter: number[];
296
+ filter?: number[];
288
297
  }
289
298
 
290
299
  export declare interface ISphereCollider extends ICollider {
@@ -495,4 +504,6 @@
495
504
 
496
505
  /** Enable to render collider shapes */
497
506
  debugRenderColliders: boolean;
507
+ /** Enable to visualize raycasts in the scene with gizmos */
508
+ debugRenderRaycasts: boolean;
498
509
  }
src/engine/engine_utils.ts CHANGED
@@ -437,6 +437,14 @@
437
437
  };
438
438
 
439
439
 
440
+ /** Is MacOS or Windows (and not hololens) */
441
+ export function isDesktop() {
442
+ const ua = window.navigator.userAgent;
443
+ const standalone = /Windows|MacOS/.test(ua);
444
+ const isHololens = /Windows NT/.test(ua) && /Edg/.test(ua) && !/Win64/.test(ua);
445
+ return standalone && !isHololens;
446
+ }
447
+
440
448
  export function isMobileDevice() {
441
449
  return (typeof window.orientation !== "undefined") || (navigator.userAgent.indexOf('IEMobile') !== -1);
442
450
  }
src/engine/extensions/NEEDLE_lighting_settings.ts CHANGED
@@ -170,30 +170,10 @@
170
170
  this._ambientLightObj.removeFromParent();
171
171
  if (this._hemisphereLightObj)
172
172
  this._hemisphereLightObj.removeFromParent();
173
-
174
- // create light probe object
175
- if (!this._lightProbeObj) {
176
- if (this.sourceId) {
177
- this.context.sceneLighting.internalGetSceneLightingData(this.sourceId).then(data => {
178
- if (!data) return;
179
- this._lightProbeObj = data.lightProbe;
180
- if (this.enabled && !this.destroyed && this._lightProbeObj) {
181
- if (debug) console.log("Add", this.sourceId, data);
182
- this.gameObject.add(this._lightProbeObj);
183
- }
184
- });
185
- }
186
- }
187
- else {
188
- if (this.enabled && this.destroyed && this._lightProbeObj) {
189
- this.gameObject.add(this._lightProbeObj);
190
- }
191
- }
192
173
  }
193
174
 
194
175
  if (this.sourceId)
195
176
  this.context.sceneLighting.internalEnableReflection(this.sourceId);
196
-
197
177
  }
198
178
 
199
179
  onDisable() {
src/engine/extensions/NEEDLE_techniques_webgl.ts CHANGED
@@ -97,7 +97,7 @@
97
97
  this.uniforms[this._viewProjectionName] = { value: [] };
98
98
 
99
99
  if (this.uniforms[this._sphericalHarmonicsName]) {
100
- this.waitForLighting();
100
+ // this.waitForLighting();
101
101
  }
102
102
 
103
103
  if (this.depthTextureUniform || this.opaqueTextureUniform) {
@@ -112,6 +112,7 @@
112
112
  Context.Current.pre_render_callbacks.splice(index, 1);
113
113
  }
114
114
 
115
+ /* REMOVED, we don't have Lit shader support for now
115
116
  async waitForLighting() {
116
117
  const context: Context = Context.Current;
117
118
  if (!context) {
@@ -136,6 +137,7 @@
136
137
  // this.uniformsNeedUpdate = true;
137
138
  if (debug) console.log("Set environment lighting", this.uniforms);
138
139
  }
140
+ */
139
141
 
140
142
  private _sphericalHarmonicsName = "unity_SpecCube0";
141
143
 
src/engine/codegen/register_types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { TypeStore } from "./../engine_typestore.js"
2
-
2
+
3
3
  // Import types
4
4
  import { __Ignore } from "../../engine-components/codegen/components.js";
5
5
  import { ActionBuilder } from "../../engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js";
@@ -219,7 +219,7 @@
219
219
  import { XRGrabRendering } from "../../engine-components/webxr/WebXRGrabRendering.js";
220
220
  import { XRRig } from "../../engine-components/webxr/WebXRRig.js";
221
221
  import { XRState } from "../../engine-components/XRFlag.js";
222
-
222
+
223
223
  // Register types
224
224
  TypeStore.add("__Ignore", __Ignore);
225
225
  TypeStore.add("ActionBuilder", ActionBuilder);