@needle-tools/engine 2.67.12-pre → 2.67.14-pre

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/needle-engine.js +5104 -5041
  3. package/dist/needle-engine.min.js +4812 -0
  4. package/dist/needle-engine.umd.cjs +278 -278
  5. package/lib/engine/engine_input.js +5 -2
  6. package/lib/engine/engine_input.js.map +1 -1
  7. package/lib/engine/engine_physics.d.ts +5 -0
  8. package/lib/engine/engine_physics.js +39 -4
  9. package/lib/engine/engine_physics.js.map +1 -1
  10. package/lib/engine/engine_types.d.ts +1 -0
  11. package/lib/engine/engine_types.js.map +1 -1
  12. package/lib/engine/engine_utils.js +4 -8
  13. package/lib/engine/engine_utils.js.map +1 -1
  14. package/lib/engine-components/Animation.d.ts +1 -1
  15. package/lib/engine-components/Animation.js +3 -3
  16. package/lib/engine-components/Animation.js.map +1 -1
  17. package/lib/engine-components/CharacterController.js +2 -2
  18. package/lib/engine-components/CharacterController.js.map +1 -1
  19. package/lib/engine-components/Renderer.d.ts +1 -1
  20. package/lib/engine-components/Renderer.js +15 -4
  21. package/lib/engine-components/Renderer.js.map +1 -1
  22. package/lib/engine-components/WebXR.js +12 -2
  23. package/lib/engine-components/WebXR.js.map +1 -1
  24. package/lib/engine-components/WebXRController.js +1 -1
  25. package/lib/engine-components/WebXRController.js.map +1 -1
  26. package/lib/engine-components/api.d.ts +1 -0
  27. package/lib/engine-components/api.js +2 -0
  28. package/lib/engine-components/api.js.map +1 -0
  29. package/lib/needle-engine.d.ts +1 -0
  30. package/lib/needle-engine.js +1 -0
  31. package/lib/needle-engine.js.map +1 -1
  32. package/lib/tsconfig.tsbuildinfo +1 -1
  33. package/package.json +3 -4
  34. package/plugins/vite/copyfiles.js +37 -10
  35. package/plugins/vite/reload.js +2 -1
  36. package/src/engine/codegen/register_types.js +2 -2
  37. package/src/engine/engine_input.ts +4 -2
  38. package/src/engine/engine_physics.ts +41 -4
  39. package/src/engine/engine_types.ts +1 -0
  40. package/src/engine/engine_utils.ts +4 -8
  41. package/src/engine-components/Animation.ts +3 -3
  42. package/src/engine-components/CharacterController.ts +2 -2
  43. package/src/engine-components/Renderer.ts +19 -5
  44. package/src/engine-components/WebXR.ts +12 -3
  45. package/src/engine-components/WebXRController.ts +1 -1
  46. package/src/engine-components/api.ts +1 -0
  47. package/src/needle-engine.ts +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/engine",
3
- "version": "2.67.12-pre",
3
+ "version": "2.67.14-pre",
4
4
  "description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development, and can be deployed anywhere. It is flexible, extensible, and collaboration and XR come naturally.",
5
5
  "main": "dist/needle-engine.umd.cjs",
6
6
  "module": "lib/needle-engine.js",
@@ -59,8 +59,8 @@
59
59
  "flatbuffers": "2.0.4",
60
60
  "md5": "^2.3.0",
61
61
  "peerjs": "1.3.2",
62
- "simplex-noise": "^4.0.1",
63
62
  "postprocessing": "^6.30.1",
63
+ "simplex-noise": "^4.0.1",
64
64
  "stats.js": "^0.17.0",
65
65
  "three": "npm:@needle-tools/three@^0.146.7",
66
66
  "three-mesh-ui": "^6.4.5",
@@ -75,10 +75,9 @@
75
75
  "@needle-tools/helper": "^0.4.5",
76
76
  "@needle-tools/needle-component-compiler": "1.9.3",
77
77
  "@types/three": "0.146.0",
78
- "copy-files-from-to": "^3.7.0",
79
78
  "esbuild": "^0.15.10",
80
79
  "esbuild-node-externals": "^1.5.0",
81
- "fs-extra": "^11.1.0",
80
+ "fs-extra": "^11.1.1",
82
81
  "jsdoc-babel": "^0.5.0",
83
82
  "jsdoc-to-markdown": "^7.1.1",
84
83
  "madge": "^5.0.1",
@@ -1,7 +1,6 @@
1
1
 
2
- import { copy } from 'fs-extra'
3
- import { resolve } from 'path'
4
- import { existsSync } from 'fs';
2
+ import { resolve, join } from 'path'
3
+ import { existsSync, statSync, mkdirSync, readdirSync, copyFileSync, mkdir } from 'fs';
5
4
 
6
5
 
7
6
  /** copy files on build from assets to dist */
@@ -11,32 +10,60 @@ export const needleCopyFiles = (command, config, userSettings) => {
11
10
  return;
12
11
  }
13
12
 
13
+ const copyIncludesFromEngine = config?.copyIncludesFromEngine ?? true;
14
+
14
15
  return {
15
16
  name: 'needle-copy-files',
16
17
  apply: 'build',
17
18
  async closeBundle() {
18
19
  const baseDir = process.cwd();
20
+ const pluginName = "needle-copy-files";
19
21
 
20
22
  const outdirName = "dist";
21
23
  const outDir = resolve(baseDir, outdirName);
22
24
  if (!existsSync(outDir)) {
23
- console.log(`No ${outdirName} folder found, skipping copy files: ${outDir}`);
24
- return;
25
+ mkdirSync(outDir);
26
+ }
27
+
28
+ if (copyIncludesFromEngine !== false) {
29
+ // copy include from engine
30
+ const engineIncludeDir = resolve(baseDir, 'node_modules', '@needle-tools', 'engine', 'src', 'include');
31
+ if (existsSync(engineIncludeDir)) {
32
+ console.log(`[${pluginName}] - Copy engine include to ${baseDir}/include`)
33
+ const targetDir = resolve(baseDir, 'include');
34
+ copyRecursiveSync(engineIncludeDir, targetDir);
35
+ }
25
36
  }
37
+
26
38
  // copy assets dir
27
39
  const assetsDir = resolve(baseDir, 'assets');
28
40
  if (existsSync(assetsDir)) {
29
- console.log(`Copy assets to ${outdirName}/assets`)
41
+ console.log(`[${pluginName}] - Copy assets to ${outdirName}/assets`)
30
42
  const targetDir = resolve(outDir, 'assets');
31
- await copy(assetsDir, targetDir, { overwrite: true });
43
+ copyRecursiveSync(assetsDir, targetDir);
32
44
  }
33
45
  // copy include dir
34
46
  const includeDir = resolve(baseDir, 'include');
35
47
  if (existsSync(includeDir)) {
36
- console.log(`Copy include to ${outdirName}/include`)
48
+ console.log(`[${pluginName}] - Copy include to ${outdirName}/include`)
37
49
  const targetDir = resolve(outDir, 'include');
38
- await copy(includeDir, targetDir, { overwrite: true });
50
+ copyRecursiveSync(includeDir, targetDir);
39
51
  }
40
52
  }
41
53
  }
42
- }
54
+ }
55
+
56
+ function copyRecursiveSync(src, dest) {
57
+ var exists = existsSync(src);
58
+ var stats = exists && statSync(src);
59
+ var isDirectory = exists && stats.isDirectory();
60
+ if (isDirectory) {
61
+ if (!existsSync(dest))
62
+ mkdirSync(dest);
63
+ readdirSync(src).forEach(function (childItemName) {
64
+ copyRecursiveSync(join(src, childItemName), join(dest, childItemName));
65
+ });
66
+ } else {
67
+ copyFileSync(src, dest);
68
+ }
69
+ };
@@ -43,7 +43,8 @@ export const needleReload = (command, config, userSettings) => {
43
43
  else if (!config.server.watch.ignored) config.server.watch.ignored = [];
44
44
  for (const pattern of ignorePatterns)
45
45
  config.server.watch.ignored.push(pattern);
46
- setTimeout(() => console.log("Updated server ignore patterns: ", config.server.watch.ignored), 100);
46
+ if(config?.debug === true || userSettings?.debug === true)
47
+ setTimeout(() => console.log("Updated server ignore patterns: ", config.server.watch.ignored), 100);
47
48
  },
48
49
  handleHotUpdate(args) {
49
50
  args.buildDirectory = buildDirectory;
@@ -1,5 +1,5 @@
1
1
  import { TypeStore } from "./../engine_typestore"
2
-
2
+
3
3
  // Import types
4
4
  import { __Ignore } from "../../engine-components/codegen/components";
5
5
  import { AlignmentConstraint } from "../../engine-components/AlignmentConstraint";
@@ -179,7 +179,7 @@ import { XRGrabModel } from "../../engine-components/WebXRGrabRendering";
179
179
  import { XRGrabRendering } from "../../engine-components/WebXRGrabRendering";
180
180
  import { XRRig } from "../../engine-components/WebXRRig";
181
181
  import { XRState } from "../../engine-components/XRFlag";
182
-
182
+
183
183
  // Register types
184
184
  TypeStore.add("__Ignore", __Ignore);
185
185
  TypeStore.add("AlignmentConstraint", AlignmentConstraint);
@@ -478,6 +478,8 @@ export class Input extends EventTarget {
478
478
 
479
479
  while (evt.button >= this._pointerPositionDown.length) this._pointerPositionDown.push(new THREE.Vector2());
480
480
  this._pointerPositionDown[evt.button].set(evt.clientX, evt.clientY);
481
+ while (evt.button >= this._pointerPositions.length) this._pointerPositions.push(new THREE.Vector2());
482
+ this._pointerPositions[evt.button].set(evt.clientX, evt.clientY);
481
483
 
482
484
  if (evt.button >= this._pointerDownTime.length) this._pointerDownTime.push(0);
483
485
  this._pointerDownTime[evt.button] = this.context.time.time;
@@ -552,8 +554,8 @@ export class Input extends EventTarget {
552
554
 
553
555
  const lf = this._pointerPositionsLastFrame[evt.button];
554
556
  lf.copy(this._pointerPositions[evt.button]);
555
- const dx = evt.movementX !== undefined ? evt.movementX : evt.clientX - lf.x;
556
- const dy = evt.movementY !== undefined ? evt.movementY : evt.clientY - lf.y;
557
+ const dx = evt.clientX - lf.x;
558
+ const dy = evt.clientY - lf.y;
557
559
  this._pointerPositionsDelta[evt.button].set(dx, dy);
558
560
 
559
561
  this._pointerPositions[evt.button].x = evt.clientX;
@@ -263,7 +263,10 @@ export class Physics {
263
263
  const ray = this.getPhysicsRay(this.rapierRay, origin, direction);
264
264
  if (!ray) return null;
265
265
 
266
- const hit = this.world?.castRay(ray, maxDistance, solid);
266
+ const hit = this.world?.castRay(ray, maxDistance, solid, undefined, undefined, undefined, undefined, (c) => {
267
+ // ignore objects in the IgnoreRaycast=2 layer
268
+ return !c[$componentKey]?.gameObject.layers.isEnabled(2);
269
+ });
267
270
  if (hit) {
268
271
  const point = ray.pointAt(hit.toi);
269
272
  const vec = this.raycastVectorsBuffer.get();
@@ -274,6 +277,28 @@ export class Physics {
274
277
  return null;
275
278
  }
276
279
 
280
+ public raycastPhysicsFastAndGetNormal(origin: Vec2 | Vec3, direction: Vec3 | undefined = undefined, maxDistance: number = Infinity, solid: boolean = true)
281
+ : null | { point: Vector3, normal: Vector3, collider: ICollider } {
282
+
283
+ const ray = this.getPhysicsRay(this.rapierRay, origin, direction);
284
+ if (!ray) return null;
285
+
286
+ const hit = this.world?.castRayAndGetNormal(ray, maxDistance, solid, undefined, undefined, undefined, undefined, (c) => {
287
+ // ignore objects in the IgnoreRaycast=2 layer
288
+ return !c[$componentKey]?.gameObject.layers.isEnabled(2);
289
+ });
290
+ if (hit) {
291
+ const point = ray.pointAt(hit.toi);
292
+ const normal = hit.normal;
293
+ const vec = this.raycastVectorsBuffer.get();
294
+ const nor = this.raycastVectorsBuffer.get();
295
+ vec.set(point.x, point.y, point.z);
296
+ nor.set(normal.x, normal.y, normal.z);
297
+ return { point: vec, normal: nor, collider: hit.collider[$componentKey] };
298
+ }
299
+ return null;
300
+ }
301
+
277
302
  private getPhysicsRay(ray: RAPIER.Ray, origin: Vec2 | Vec3, direction: Vec3 | undefined = undefined): RAPIER.Ray | null {
278
303
  const cam = this.context.mainCamera;
279
304
  // if we get origin in 2d space we need to project it to 3d space
@@ -283,11 +308,13 @@ export class Physics {
283
308
  return null;
284
309
  }
285
310
  const vec3 = this.raycastVectorsBuffer.get();
311
+ vec3.x = origin.x;
312
+ vec3.y = origin.y;
313
+ vec3.z = 0;
286
314
  // if the origin is in screen space we need to convert it to raycaster space
287
- if (origin.x > 1 || origin.y > 1 || origin.y < -1 || origin.x < -1) {
288
- this.context.input.convertScreenspaceToRaycastSpace(origin);
315
+ if (vec3.x > 1 || vec3.y > 1 || vec3.y < -1 || vec3.x < -1) {
316
+ this.context.input.convertScreenspaceToRaycastSpace(vec3);
289
317
  }
290
- vec3.set(origin.x, origin.y, -1);
291
318
  vec3.unproject(cam);
292
319
  origin = vec3;
293
320
  }
@@ -466,9 +493,15 @@ export class Physics {
466
493
  if (scale.z < 0)
467
494
  scale.z = Math.abs(scale.z);
468
495
 
496
+ // prevent zero scale - seems normals are flipped otherwise
497
+ if (scale.x == 0) scale.x = 0.0000001;
498
+ if (scale.y == 0) scale.y = 0.0000001;
499
+ if (scale.z == 0) scale.z = 0.0000001;
500
+
469
501
  const desc = ColliderDesc.cuboid(scale.x, scale.y, scale.z);
470
502
  // const objectLayerMask = collider.gameObject.layers.mask;
471
503
  // const mask = objectLayerMask & ~2;
504
+ // TODO: https://rapier.rs/docs/user_guides/javascript/colliders/#collision-groups-and-solver-groups
472
505
  // desc.setCollisionGroups(objectLayerMask);
473
506
  this.createCollider(collider, desc, center);
474
507
  }
@@ -612,6 +645,10 @@ export class Physics {
612
645
  col[$componentKey] = collider;
613
646
  collider[$bodyKey] = col;
614
647
  col.setActiveEvents(ActiveEvents.COLLISION_EVENTS);
648
+
649
+ // const objectLayerMask = collider.gameObject.layers.mask;
650
+ // const mask = objectLayerMask & ~2;
651
+ // col.setCollisionGroups(objectLayerMask);
615
652
  this.objects.push(collider);
616
653
  this.bodies.push(col);
617
654
  return col;
@@ -172,6 +172,7 @@ export declare interface ICamera extends IComponent {
172
172
  backgroundColor: RGBAColor | null;
173
173
  backgroundBlurriness: number | undefined;
174
174
  clearFlags: number;
175
+ cullingMask: number;
175
176
  aspect: number;
176
177
  fieldOfView?: number;
177
178
  screenPointToRay(x: number, y: number, ray?: Ray): Ray;
@@ -26,14 +26,10 @@ export class CircularBuffer<T> {
26
26
  }
27
27
 
28
28
  get(): T {
29
- let i = this._index++;
30
- if (i >= this._cache.length) {
31
- if (i >= this._maxSize) {
32
- i = this._index = 0;
33
- }
34
- else {
35
- this._cache.push(this._factory());
36
- }
29
+ const i = this._index % this._maxSize;
30
+ this._index++;
31
+ if (this._cache.length <= i) {
32
+ this._cache[i] = this._factory();
37
33
  }
38
34
  return this._cache[i];
39
35
  }
@@ -145,7 +145,7 @@ export class Animation extends Behaviour {
145
145
  return false;
146
146
  }
147
147
 
148
- play(clipOrNumber: AnimationClip | number | string | undefined, options?: PlayOptions): Promise<AnimationAction> | void {
148
+ play(clipOrNumber: AnimationClip | number | string | undefined = 0, options?: PlayOptions): Promise<AnimationAction> | void {
149
149
  if (debug) console.log("PLAY", clipOrNumber)
150
150
  this.init();
151
151
  if (!this.mixer) {
@@ -171,8 +171,8 @@ export class Animation extends Behaviour {
171
171
  if (!options) options = {};
172
172
  if (!options.minMaxOffsetNormalized) options.minMaxOffsetNormalized = this.minMaxOffsetNormalized;
173
173
  if (!options.minMaxSpeed) options.minMaxSpeed = this.minMaxSpeed;
174
- if (!options.loop) options.loop = this.loop;
175
- if (!options.clampWhenFinished) options.clampWhenFinished = this.clampWhenFinished;
174
+ if (options.loop === undefined) options.loop = this.loop;
175
+ if (options.clampWhenFinished === undefined) options.clampWhenFinished = this.clampWhenFinished;
176
176
  for (const act of this.actions) {
177
177
  if (act.getClip() === clip) {
178
178
  return this.internalOnPlay(act, options);
@@ -120,7 +120,7 @@ export class CharacterControllerInput extends Behaviour {
120
120
  // if (jumpDown) this._jumpDownTime = this.context.time.time;
121
121
  // const jumpUp = this.context.input.isKeyUp(" ");
122
122
 
123
- const step = forward ? 1 : 0 + backward ? -1 : 0;
123
+ const step = (forward ? 1 : 0) + (backward ? -1 : 0);
124
124
  this._currentSpeed.z += step * this.movementSpeed * this.context.time.deltaTime;
125
125
 
126
126
  // if (!this.controller || this.controller.isGrounded)
@@ -132,7 +132,7 @@ export class CharacterControllerInput extends Behaviour {
132
132
  if (this.controller) this.controller.move(this._temp);
133
133
  else this.gameObject.position.add(this._temp);
134
134
 
135
- const rotation = rotateLeft ? 1 : 0 + rotateRight ? -1 : 0;
135
+ const rotation = (rotateLeft ? 1 : 0) + (rotateRight ? -1 : 0);
136
136
  this._currentAngularSpeed.y += Mathf.toRadians(rotation * this.rotationSpeed) * this.context.time.deltaTime;
137
137
  if (this.lookForward && Math.abs(this._currentAngularSpeed.y) < .01) {
138
138
  const forwardVector = this.context.mainCameraComponent!.forward;
@@ -639,7 +639,7 @@ export class Renderer extends Behaviour implements IRenderer {
639
639
  }
640
640
  }
641
641
 
642
- private applySettings(go: THREE.Object3D) {
642
+ applySettings(go: THREE.Object3D) {
643
643
  go.receiveShadow = this.receiveShadows;
644
644
  if (this.shadowCastingMode == ShadowCastingMode.On) {
645
645
  go.castShadow = true;
@@ -734,15 +734,16 @@ class InstancingHandler {
734
734
  public setup(renderer: Renderer, obj: THREE.Object3D, context: Context, handlesArray: InstanceHandle[] | null, args: InstancingSetupArgs, level: number = 0)
735
735
  : InstanceHandle[] | null {
736
736
 
737
+ // make sure setting casting settings are applied so when we add the mesh to the InstancedMesh we can ask for the correct cast shadow setting
738
+ renderer.applySettings(obj);
737
739
  const res = this.tryCreateOrAddInstance(obj, context, args);
738
740
  if (res) {
739
741
  renderer.loadProgressiveTextures(res.instancer.material);
740
742
  if (handlesArray === null) handlesArray = [];
741
743
  handlesArray.push(res);
742
- return handlesArray;
743
744
  }
744
745
 
745
- if (level <= 0 && obj.type !== "Mesh") {
746
+ else if (level <= 0 && obj.type !== "Mesh") {
746
747
  const nextLevel = level + 1;
747
748
  for (const ch of obj.children) {
748
749
  handlesArray = this.setup(renderer, ch, context, handlesArray, args, nextLevel);
@@ -799,10 +800,8 @@ class InstancingHandler {
799
800
  let previousMatrix: THREE.Matrix4 = obj.matrixWorld.clone();
800
801
  const matrixChangeWrapper = (a: Matrix4, b: Matrix4) => {
801
802
  const newMatrixWorld = original(a, b);
802
- // console.warn("MULT", obj.matrixWorldNeedsUpdate);
803
803
  if (obj[NEED_UPDATE_INSTANCE_KEY] || previousMatrix.equals(newMatrixWorld) === false) {
804
804
  previousMatrix.copy(newMatrixWorld)
805
- // handle.setMatrix(newMatrixWorld);
806
805
  obj[NEED_UPDATE_INSTANCE_KEY] = true;
807
806
  }
808
807
  return newMatrixWorld;
@@ -865,6 +864,13 @@ class InstanceHandle {
865
864
 
866
865
  class InstancedMeshRenderer {
867
866
 
867
+ set castShadow(val: boolean) {
868
+ this.inst.castShadow = val;
869
+ }
870
+ set receiveShadow(val: boolean) {
871
+ this.inst.receiveShadow = val;
872
+ }
873
+
868
874
  public name: string = "";
869
875
  public geo: THREE.BufferGeometry;
870
876
  public material: THREE.Material;
@@ -915,6 +921,14 @@ class InstancedMeshRenderer {
915
921
  return null;
916
922
  }
917
923
  const handle = new InstanceHandle(-1, obj, this);
924
+
925
+ if (obj.castShadow === true && this.inst.castShadow === false) {
926
+ this.inst.castShadow = true;
927
+ }
928
+ if (obj.receiveShadow === true && this.inst.receiveShadow === false) {
929
+ this.inst.receiveShadow = true;
930
+ }
931
+
918
932
  this.add(handle);
919
933
  return handle;
920
934
  }
@@ -390,10 +390,19 @@ export class WebXR extends Behaviour {
390
390
  if (this.context.mainCamera) {
391
391
  //@ts-ignore
392
392
  const cam = xr.getCamera(this.context.mainCamera) as ArrayCamera;
393
- for (const c of cam.cameras) {
394
- c.layers.enableAll();
393
+ const cull = this.context.mainCameraComponent?.cullingMask;
394
+ if(cull !== undefined){
395
+ for (const c of cam.cameras) {
396
+ c.layers.mask = cull;
397
+ }
398
+ cam.layers.mask = cull;
399
+ }
400
+ else {
401
+ for (const c of cam.cameras) {
402
+ c.layers.enableAll();
403
+ }
404
+ cam.layers.enableAll();
395
405
  }
396
-
397
406
  this.rig.add(this.context.mainCamera);
398
407
  if (this._requestedAR) {
399
408
  this.context.scene.add(this.rig);
@@ -769,7 +769,7 @@ export class WebXRController extends Behaviour {
769
769
  public raycast(): Intersection[] {
770
770
  const opts = new RaycastOptions();
771
771
  opts.layerMask = new Layers();
772
- opts.layerMask.set(0);
772
+ opts.layerMask.enableAll();
773
773
  opts.layerMask.disable(2);
774
774
  opts.ray = this.getRay();
775
775
  const hits = this.context.physics.raycast(opts);
@@ -0,0 +1 @@
1
+ export * from "./ui/PointerEvents"
@@ -11,6 +11,7 @@ export { GameObject, Behaviour } from "./engine-components/Component";
11
11
  export { serializable, serializeable } from "./engine/engine_serialization_decorator";
12
12
  export { Collision } from "./engine/engine_types";
13
13
  export * from "./engine/api";
14
+ export * from "./engine-components/api";
14
15
  export * from "./engine-components/codegen/components";
15
16
  export * from "./engine-components/js-extensions/Object3D";
16
17