Needle Engine

Changes between version 3.2.3-alpha and 3.2.4-alpha
Files changed (35) hide show
  1. src/engine/engine_fileloader.js +2 -2
  2. plugins/vite/license.js +2 -2
  3. src/engine/codegen/register_types.js +2 -2
  4. src/engine-components/Animation.ts +17 -19
  5. src/engine-components/Animator.ts +4 -5
  6. src/engine-components/AnimatorController.ts +16 -17
  7. src/engine-components/api.ts +2 -0
  8. src/engine/api.ts +5 -1
  9. src/engine-components/AudioListener.ts +4 -4
  10. src/engine-components/AudioSource.ts +8 -8
  11. src/engine-components/AvatarLoader.ts +15 -16
  12. src/engine-components/AxesHelper.ts +1 -1
  13. src/engine-components/BasicIKConstraint.ts +2 -2
  14. src/engine-components/BoxHelperComponent.ts +13 -13
  15. src/engine-components/Component.ts +61 -62
  16. src/engine-components/DragControls.ts +42 -43
  17. src/engine/engine_addressables.ts +6 -7
  18. src/engine/engine_context.ts +31 -28
  19. src/engine/engine_gameobject.ts +21 -21
  20. src/engine/engine_gizmos.ts +5 -6
  21. src/engine/engine_gltf_builtin_components.ts +9 -10
  22. src/engine/engine_input.ts +17 -18
  23. src/engine/engine_license.ts +2 -2
  24. src/engine/engine_mainloop_utils.ts +5 -5
  25. src/engine/engine_networking_auto.ts +1 -1
  26. src/engine/engine_networking_files.ts +10 -11
  27. src/engine/engine_three_utils.ts +37 -37
  28. src/engine/extensions/NEEDLE_components.ts +7 -6
  29. src/engine/extensions/NEEDLE_lighting_settings.ts +15 -6
  30. src/engine/extensions/NEEDLE_techniques_webgl.ts +29 -30
  31. src/needle-engine.ts +5 -8
  32. src/engine-components/ParticleSystemModules.ts +1483 -1483
  33. src/engine-components-experimental/networking/PlayerSync.ts +17 -4
  34. src/engine-components/ui/Utils.ts +4 -4
  35. src/engine-components-experimental/api.ts +1 -0
src/engine/engine_fileloader.js CHANGED
@@ -1,6 +1,6 @@
1
- import * as THREE from "three";
1
+ import { FileLoader } from "three";
2
2
 
3
- export const loader = new THREE.FileLoader();
3
+ export const loader = new FileLoader();
4
4
 
5
5
  export async function loadFileAsync(url) {
6
6
  return new Promise((resolve, reject) => {
plugins/vite/license.js CHANGED
@@ -7,11 +7,11 @@
7
7
  name: "needle-license",
8
8
  enforce: 'pre',
9
9
  async transform(src, id) {
10
- if (id.includes("engine/engine_license.ts")) {
10
+ if (id.includes("engine/engine_license") || id.includes("needle-tools_engine.js")) {
11
11
  const needleConfig = await loadConfig();
12
12
  if (needleConfig) {
13
13
  if (needleConfig.hasProLicense !== undefined && typeof needleConfig.hasProLicense === "boolean") {
14
- src = src.replace("const HAS_LICENSE = false;", "const HAS_LICENSE = " + needleConfig.hasProLicense + ";");
14
+ src = src.replace("NEEDLE_ENGINE_COMMERCIAL_USE_LICENSE = false;", "NEEDLE_ENGINE_COMMERCIAL_USE_LICENSE = " + needleConfig.hasProLicense + ";");
15
15
  return { code: src, map: null }
16
16
  }
17
17
  }
src/engine/codegen/register_types.js CHANGED
@@ -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";
@@ -184,7 +184,7 @@
184
184
  import { XRGrabRendering } from "../../engine-components/WebXRGrabRendering";
185
185
  import { XRRig } from "../../engine-components/WebXRRig";
186
186
  import { XRState } from "../../engine-components/XRFlag";
187
-
187
+
188
188
  // Register types
189
189
  TypeStore.add("__Ignore", __Ignore);
190
190
  TypeStore.add("AlignmentConstraint", AlignmentConstraint);
src/engine-components/Animation.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  import { Behaviour } from "./Component";
2
- import * as THREE from 'three'
3
- import { AnimationAction, AnimationClip, Vector2 } from "three";
2
+ import { AnimationAction, AnimationClip, AnimationMixer, LoopOnce, LoopRepeat } from "three";
4
3
  import { MixerEvent } from "./Animator";
5
4
  import { serializable } from "../engine/engine_serialization_decorator";
6
- import { InstancingUtil } from "../engine/engine_instancing";
7
5
  import { Mathf } from "../engine/engine_math";
8
6
  import { Vec2 } from "../engine/engine_types";
9
7
  import { getParam } from "../engine/engine_utils";
@@ -63,7 +61,7 @@
63
61
  this.animations = animations;
64
62
  }
65
63
 
66
- set animations(animations: THREE.AnimationClip[]) {
64
+ set animations(animations: AnimationClip[]) {
67
65
  if (debug) console.log("assign animations", animations);
68
66
  this.gameObject.animations = animations;
69
67
  }
@@ -74,29 +72,29 @@
74
72
  /**
75
73
  * @deprecated Currently unsupported
76
74
  */
77
- get currentAction(): THREE.AnimationAction | null {
75
+ get currentAction(): AnimationAction | null {
78
76
  return this._currentActions[0];
79
77
  }
80
78
 
81
79
  /**
82
80
  * @deprecated Currently unsupported
83
81
  */
84
- get currentActions(): THREE.AnimationAction[] {
82
+ get currentActions(): AnimationAction[] {
85
83
  return this._currentActions;
86
84
  }
87
85
 
88
- private mixer: THREE.AnimationMixer | undefined = undefined;
89
- get actions(): Array<THREE.AnimationAction> {
86
+ private mixer: AnimationMixer | undefined = undefined;
87
+ get actions(): Array<AnimationAction> {
90
88
  return this._actions;
91
89
  }
92
- set actions(val: Array<THREE.AnimationAction>) {
90
+ set actions(val: Array<AnimationAction>) {
93
91
  this._actions = val;
94
92
  }
95
- private _actions: Array<THREE.AnimationAction> = [];
93
+ private _actions: Array<AnimationAction> = [];
96
94
 
97
- // private _currentAction: THREE.AnimationAction | null = null;
95
+ // private _currentAction: AnimationAction | null = null;
98
96
 
99
- private _currentActions: THREE.AnimationAction[] = [];
97
+ private _currentActions: AnimationAction[] = [];
100
98
  private _handles: AnimationHandle[] = [];
101
99
 
102
100
  awake() {
@@ -133,7 +131,7 @@
133
131
  // InstancingUtil.markDirty(this.gameObject);
134
132
  }
135
133
 
136
- getAction(name: string): THREE.AnimationAction | undefined | null {
134
+ getAction(name: string): AnimationAction | undefined | null {
137
135
  return this.actions?.find(a => a.getClip().name === name);
138
136
  }
139
137
 
@@ -210,8 +208,8 @@
210
208
  if (options?.startTime !== undefined) action.time = options.startTime;
211
209
 
212
210
  if (options?.loop !== undefined)
213
- action.loop = options.loop ? THREE.LoopRepeat : THREE.LoopOnce;
214
- else action.loop = THREE.LoopOnce;
211
+ action.loop = options.loop ? LoopRepeat : LoopOnce;
212
+ else action.loop = LoopOnce;
215
213
  action.play();
216
214
  if (debug)
217
215
  console.log("PLAY", action.getClip().name, action)
@@ -239,14 +237,14 @@
239
237
  this._didInit = true;
240
238
  if (!this.gameObject) return;
241
239
  this.actions = [];
242
- this.mixer = new THREE.AnimationMixer(this.gameObject);
240
+ this.mixer = new AnimationMixer(this.gameObject);
243
241
  }
244
242
  }
245
243
 
246
244
 
247
245
  class AnimationHandle {
248
- mixer: THREE.AnimationMixer;
249
- action: THREE.AnimationAction;
246
+ mixer: AnimationMixer;
247
+ action: AnimationAction;
250
248
  promise: Promise<AnimationAction> | null = null;
251
249
  resolve: Function | null = null;
252
250
  reject: Function | null = null;
@@ -258,7 +256,7 @@
258
256
  private _finishedCallback?: any;
259
257
  private _resolvedOrRejectedCallback?: (AnimationHandle) => void;
260
258
 
261
- constructor(action: THREE.AnimationAction, mixer: THREE.AnimationMixer, opts?: PlayOptions, cb?: (handle: AnimationHandle) => void) {
259
+ constructor(action: AnimationAction, mixer: AnimationMixer, opts?: PlayOptions, cb?: (handle: AnimationHandle) => void) {
262
260
  this.action = action;
263
261
  this.mixer = mixer;
264
262
  this._resolvedOrRejectedCallback = cb;
src/engine-components/Animator.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { Behaviour } from "./Component";
2
- import * as THREE from 'three'
3
- import { LoopOnce, AnimationActionLoopStyles, AnimationAction } from "three";
4
- import { getParam, deepClone } from "../engine/engine_utils";
2
+ import { AnimationActionLoopStyles, AnimationAction, AnimationMixer } from "three";
3
+ import { getParam } from "../engine/engine_utils";
5
4
  import { AnimatorControllerModel } from "../engine/extensions/NEEDLE_animator_controller_model";
6
5
  import { AnimatorController } from "./AnimatorController";
7
6
  import { serializable } from "../engine/engine_serialization_decorator";
@@ -12,9 +11,9 @@
12
11
 
13
12
  export declare class MixerEvent {
14
13
  type: string;
15
- action: THREE.AnimationAction;
14
+ action: AnimationAction;
16
15
  loopDelta: number;
17
- target: THREE.AnimationMixer;
16
+ target: AnimationMixer;
18
17
  }
19
18
 
20
19
  export declare class PlayOptions {
src/engine-components/AnimatorController.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  import { Animator } from "./Animator";
2
2
  import { AnimatorConditionMode, AnimatorControllerModel, AnimatorControllerParameterType, AnimatorStateInfo, Condition, createMotion, State, StateMachineBehaviour } from "../engine/extensions/NEEDLE_animator_controller_model";
3
- import { AnimationAction, AnimationClip, AnimationMixer, AxesHelper, Euler, KeyframeTrack, LoopOnce, LoopRepeat, Matrix4, Object3D, Quaternion, Vector3 } from "three";
3
+ import { AnimationAction, AnimationClip, AnimationMixer, AxesHelper, Euler, KeyframeTrack, LoopOnce, Object3D, Quaternion, Vector3 } from "three";
4
4
  import { deepClone, getParam } from "../engine/engine_utils";
5
5
  import { Context } from "../engine/engine_setup";
6
- import * as THREE from "three";
7
6
  import { TypeStore } from "../engine/engine_typestore";
8
7
  import { assign } from "../engine/engine_serialization_core";
9
8
  import { Mathf } from "../engine/engine_math";
@@ -110,7 +109,7 @@
110
109
  get context(): Context | undefined | null { return this.animator?.context; }
111
110
 
112
111
 
113
- // applyRootMotion(obj: THREE.Object3D) {
112
+ // applyRootMotion(obj: Object3D) {
114
113
  // // this.internalApplyRootMotion(obj);
115
114
  // }
116
115
 
@@ -160,7 +159,7 @@
160
159
  }
161
160
 
162
161
 
163
- private _mixer!: THREE.AnimationMixer;
162
+ private _mixer!: AnimationMixer;
164
163
  private _activeState?: State;
165
164
 
166
165
  constructor(model: AnimatorControllerModel) {
@@ -541,10 +540,10 @@
541
540
  private rootMotionHandler?: RootMotionHandler;
542
541
 
543
542
 
544
- // private findRootBone(obj: THREE.Object3D): THREE.Object3D | null {
543
+ // private findRootBone(obj: Object3D): Object3D | null {
545
544
  // if (this.animationRoot) return this.animationRoot;
546
545
  // if (obj.type === "Bone") {
547
- // this.animationRoot = obj as THREE.Bone;
546
+ // this.animationRoot = obj as Bone;
548
547
  // return this.animationRoot;
549
548
  // }
550
549
  // if (obj.children) {
@@ -601,16 +600,16 @@
601
600
 
602
601
  class RootMotionAction {
603
602
 
604
- private static lastObjPosition: { [key: string]: THREE.Vector3 } = {};
605
- static lastObjRotation: { [key: string]: THREE.Quaternion } = {};
603
+ private static lastObjPosition: { [key: string]: Vector3 } = {};
604
+ static lastObjRotation: { [key: string]: Quaternion } = {};
606
605
 
607
606
  // we remove the first keyframe rotation from the space rotation when updating
608
- private static firstKeyframeRotation: { [key: string]: THREE.Quaternion } = {};
607
+ private static firstKeyframeRotation: { [key: string]: Quaternion } = {};
609
608
  // this is used to rotate the space on clip end / start (so the transform direction is correct)
610
- private static spaceRotation: { [key: string]: THREE.Quaternion } = {};
611
- private static effectiveSpaceRotation: { [key: string]: THREE.Quaternion } = {};
609
+ private static spaceRotation: { [key: string]: Quaternion } = {};
610
+ private static effectiveSpaceRotation: { [key: string]: Quaternion } = {};
612
611
 
613
- private static clipOffsetRotation: { [key: string]: THREE.Quaternion } = {};
612
+ private static clipOffsetRotation: { [key: string]: Quaternion } = {};
614
613
 
615
614
 
616
615
  set action(val: AnimationAction) {
@@ -631,14 +630,14 @@
631
630
  positionChange: Vector3 = new Vector3();
632
631
  rotationChange: Quaternion = new Quaternion();
633
632
 
634
- constructor(context: Context, root: THREE.Object3D, clip: AnimationClip, positionTrack: KeyframeTrack | null, rotationTrack: KeyframeTrack | null) {
633
+ constructor(context: Context, root: Object3D, clip: AnimationClip, positionTrack: KeyframeTrack | null, rotationTrack: KeyframeTrack | null) {
635
634
  // console.log(this, positionTrack, rotationTrack);
636
635
  this.context = context;
637
636
  this.root = root;
638
637
  this.clip = clip;
639
638
 
640
639
  if (!RootMotionAction.firstKeyframeRotation[clip.uuid])
641
- RootMotionAction.firstKeyframeRotation[clip.uuid] = new THREE.Quaternion();
640
+ RootMotionAction.firstKeyframeRotation[clip.uuid] = new Quaternion();
642
641
  if (rotationTrack) {
643
642
  const values = rotationTrack.values;
644
643
  RootMotionAction.firstKeyframeRotation[clip.uuid]
@@ -672,7 +671,7 @@
672
671
 
673
672
  if (debugRootMotion)
674
673
  {
675
- const euler = new THREE.Euler().setFromQuaternion(lastRotation);
674
+ const euler = new Euler().setFromQuaternion(lastRotation);
676
675
  console.log("START", this.clip.name, Mathf.toDegrees(euler.y));
677
676
  }
678
677
  }
@@ -799,13 +798,13 @@
799
798
 
800
799
  private controller: AnimatorController;
801
800
  private handler: RootMotionAction[] = [];
802
- private root!: THREE.Object3D;
801
+ private root!: Object3D;
803
802
 
804
803
  constructor(controller: AnimatorController) {
805
804
  this.controller = controller;
806
805
  }
807
806
 
808
- createClip(mixer: AnimationMixer, root: THREE.Object3D, clip: AnimationClip): AnimationAction {
807
+ createClip(mixer: AnimationMixer, root: Object3D, clip: AnimationClip): AnimationAction {
809
808
  this.root = root;
810
809
  let rootName = "";
811
810
  if (root && "name" in root) {
src/engine-components/api.ts CHANGED
@@ -1,1 +1,3 @@
1
+ export * from "./codegen/components";
2
+ export * from "./js-extensions/Object3D";
1
3
  export * from "./ui/PointerEvents"
src/engine/api.ts CHANGED
@@ -3,9 +3,12 @@
3
3
  export * from "./engine_context_registry";
4
4
  export * from "./extensions/extensions"
5
5
  export { InstancingUtil } from "./engine_instancing";
6
+ export * from "./engine_types"
6
7
  export * from "./engine_gameobject";
7
8
  export * from "./engine_components";
8
9
  export * from "./engine_components_internal";
10
+ export * from "./engine_input"
11
+ export * from "./engine_coroutine"
9
12
  export { AssetReference, ImageReference } from "./engine_addressables";
10
13
  export { Context, FrameEvent } from "./engine_setup";
11
14
  export * from "./debug/debug";
@@ -14,7 +17,8 @@
14
17
  export * from "./engine_scenetools";
15
18
  export * from "./engine_math"
16
19
  export * from "./js-extensions"
17
- export { hasProLicense } from "./engine_license"
20
+ export { hasProLicense } from "./engine_license";
21
+ export { syncField } from "./engine_networking_auto";
18
22
 
19
23
  export {
20
24
  // url params
src/engine-components/AudioListener.ts CHANGED
@@ -1,18 +1,18 @@
1
1
  import { Behaviour, GameObject } from "./Component";
2
- import * as THREE from "three";
2
+ import { AudioListener as ThreeAudioListener } from "three";
3
3
  import { AudioSource } from "./AudioSource";
4
4
  import { Camera } from "./Camera";
5
5
 
6
6
 
7
7
  export class AudioListener extends Behaviour {
8
8
 
9
- get listener(): THREE.AudioListener {
9
+ get listener(): ThreeAudioListener {
10
10
  if (this._listener == null)
11
- this._listener = new THREE.AudioListener();
11
+ this._listener = new ThreeAudioListener();
12
12
  return this._listener;
13
13
  }
14
14
 
15
- private _listener: THREE.AudioListener | null = null;
15
+ private _listener: ThreeAudioListener | null = null;
16
16
 
17
17
  awake() {
18
18
  AudioSource.registerWaitForAllowAudio(() => {
src/engine-components/AudioSource.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { Behaviour, GameObject } from "./Component";
2
- import * as THREE from "three";
3
2
  import { PositionalAudioHelper } from 'three/examples/jsm/helpers/PositionalAudioHelper.js';
4
3
  import { AudioListener } from "./AudioListener";
5
4
  import * as utils from "../engine/engine_utils";
6
5
  import { serializable } from "../engine/engine_serialization_decorator";
7
- import { Application, ApplicationEvents } from "../engine/engine_application";
6
+ import { ApplicationEvents } from "../engine/engine_application";
7
+ import { AudioLoader, PositionalAudio } from "three";
8
8
 
9
9
 
10
10
  const debug = utils.getParam("debugaudio");
@@ -146,20 +146,20 @@
146
146
  playInBackground: boolean = true;
147
147
 
148
148
  private _loop: boolean = false;
149
- private sound: THREE.PositionalAudio | null = null;
149
+ private sound: PositionalAudio | null = null;
150
150
  private helper: PositionalAudioHelper | null = null;
151
151
  private wasPlaying = false;
152
- private audioLoader: THREE.AudioLoader | null = null;
152
+ private audioLoader: AudioLoader | null = null;
153
153
  private shouldPlay: boolean = false;
154
154
  // set this from audio context time, used to set clip offset when setting "time" property
155
155
  // there is maybe a better way to set a audio clip current time?!
156
156
  private _lastClipStartedLoading: string | null = null;
157
157
 
158
- public get Sound(): THREE.PositionalAudio | null {
158
+ public get Sound(): PositionalAudio | null {
159
159
  if (!this.sound && AudioSource._userInteractionRegistered) {
160
160
  const listener = GameObject.getComponent(this.context.mainCamera, AudioListener) ?? GameObject.findObjectOfType(AudioListener, this.context);
161
161
  if (listener?.listener) {
162
- this.sound = new THREE.PositionalAudio(listener.listener);
162
+ this.sound = new PositionalAudio(listener.listener);
163
163
  this.gameObject.add(this.sound);
164
164
  }
165
165
  }
@@ -170,7 +170,7 @@
170
170
 
171
171
 
172
172
  awake() {
173
- this.audioLoader = new THREE.AudioLoader();
173
+ this.audioLoader = new AudioLoader();
174
174
  if (this.playOnAwake) this.shouldPlay = true;
175
175
  }
176
176
 
@@ -294,7 +294,7 @@
294
294
  console.log(this.clip);
295
295
  if (this.clip.endsWith(".mp3") || this.clip.endsWith(".wav")) {
296
296
  if (!this.audioLoader)
297
- this.audioLoader = new THREE.AudioLoader();
297
+ this.audioLoader = new AudioLoader();
298
298
  this.shouldPlay = true;
299
299
  if (this._lastClipStartedLoading === this.clip) {
300
300
  if (debug) console.log("Is currently loading:", this._lastClipStartedLoading, this)
src/engine-components/AvatarLoader.ts CHANGED
@@ -1,28 +1,27 @@
1
- import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
2
- import * as THREE from "three";
1
+ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
3
2
  import * as utils from "../engine/engine_utils"
4
- // import * as object from "../engine/engine_gltf_builtin_components";
5
3
  import * as loaders from "../engine/engine_loaders"
6
4
  import { Context } from "../engine/engine_setup";
7
5
  import { GameObject } from "./Component";
8
6
  import { download_file } from "../engine/engine_web_api";
9
7
  import { getLoader } from "../engine/engine_gltf";
10
8
  import { InstantiateOptions } from "../engine/engine_gameobject";
9
+ import { Box3, Object3D, Vector3 } from "three";
11
10
 
12
11
  const debug = utils.getParam("debugavatar");
13
12
 
14
13
  export class AvatarModel {
15
- root: THREE.Object3D;
16
- head: THREE.Object3D;
17
- leftHand: THREE.Object3D | null;
18
- rigthHand: THREE.Object3D | null;
14
+ root: Object3D;
15
+ head: Object3D;
16
+ leftHand: Object3D | null;
17
+ rigthHand: Object3D | null;
19
18
 
20
19
 
21
20
  get isValid(): boolean {
22
21
  return this.head !== null && this.head !== undefined;
23
22
  }
24
23
 
25
- constructor(root: THREE.Object3D, head: THREE.Object3D, leftHand: THREE.Object3D | null, rigthHand: THREE.Object3D | null) {
24
+ constructor(root: Object3D, head: Object3D, leftHand: Object3D | null, rigthHand: Object3D | null) {
26
25
  this.root = root;
27
26
  this.head = head;
28
27
  this.leftHand = leftHand;
@@ -40,14 +39,14 @@
40
39
  // private loader: GLTFLoader | null;
41
40
  // private avatarModelCache: Map<string, AvatarModel | null> = new Map<string, AvatarModel | null>();
42
41
 
43
- public async getOrCreateNewAvatarInstance(context: Context, avatarId: string | THREE.Object3D): Promise<AvatarModel | null> {
42
+ public async getOrCreateNewAvatarInstance(context: Context, avatarId: string | Object3D): Promise<AvatarModel | null> {
44
43
 
45
44
  if (!avatarId) {
46
45
  console.error("Can not create avatar: failed to provide id or root object");
47
46
  return null;
48
47
  }
49
48
 
50
- let root: THREE.Object3D | null = null;
49
+ let root: Object3D | null = null;
51
50
  if (typeof avatarId === "string") {
52
51
  root = await this.loadAvatar(context, avatarId);
53
52
  if (!root) {
@@ -77,7 +76,7 @@
77
76
  }
78
77
 
79
78
 
80
- private async loadAvatar(context: Context, avatarId: string): Promise<THREE.Object3D | null> {
79
+ private async loadAvatar(context: Context, avatarId: string): Promise<Object3D | null> {
81
80
 
82
81
  console.assert(avatarId !== undefined && avatarId !== null && typeof avatarId === "string", "Avatar id must not be null");
83
82
  if (avatarId.length <= 0) return null;
@@ -146,9 +145,9 @@
146
145
  }
147
146
 
148
147
  // TODO this should be burned to the ground once 🤞 we have proper extras that define object relations.
149
- private findAvatar(obj: THREE.Object3D): AvatarModel {
148
+ private findAvatar(obj: Object3D): AvatarModel {
150
149
 
151
- const root: THREE.Object3D = obj;
150
+ const root: Object3D = obj;
152
151
  let searchIn = root;
153
152
  // some GLTFs have a "scene" root it seems, others don't, we skip the root here if there's only one child
154
153
  if (searchIn.children.length == 1)
@@ -163,8 +162,8 @@
163
162
  head = root;
164
163
 
165
164
  // normalize size, if the object isn't properly setup the scale might be totally off
166
- const boundsSize = new THREE.Vector3();
167
- new THREE.Box3().setFromObject(head).getSize(boundsSize);
165
+ const boundsSize = new Vector3();
166
+ new Box3().setFromObject(head).getSize(boundsSize);
168
167
  const maxAxis = Math.max(boundsSize.x, boundsSize.y, boundsSize.z);
169
168
  console.warn("[Custom Avatar] " + "Normalizing head scale, it's too big: " + maxAxis + " meters! Should be < 0.3m");
170
169
  if (maxAxis > 0.3) {
@@ -177,7 +176,7 @@
177
176
  }
178
177
 
179
178
 
180
- private findAvatarPart(obj: THREE.Object3D, searchString: string[]): THREE.Object3D | null {
179
+ private findAvatarPart(obj: Object3D, searchString: string[]): Object3D | null {
181
180
 
182
181
  const name = obj.name.toLowerCase();
183
182
  let matchesAll = true;
src/engine-components/AxesHelper.ts CHANGED
@@ -11,7 +11,7 @@
11
11
  @serializable()
12
12
  public isGizmo:boolean = true;
13
13
 
14
- private _axes: THREE.AxesHelper | null = null;
14
+ private _axes: _AxesHelper | null = null;
15
15
 
16
16
  onEnable(): void {
17
17
  if (this.isGizmo && !params.showGizmos) return;
src/engine-components/BasicIKConstraint.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Behaviour, GameObject } from "./Component";
2
2
  import * as utils from "./../engine/engine_three_utils";
3
- import * as THREE from "three"
3
+ import { Vector3 } from "three";
4
4
 
5
5
  export class BasicIKConstraint extends Behaviour {
6
6
 
@@ -33,7 +33,7 @@
33
33
  let hintDir = utils.getWorldPosition(this.hint).clone();
34
34
  hintDir.sub(center);
35
35
 
36
- let offsetDir = new THREE.Vector3();
36
+ let offsetDir = new Vector3();
37
37
  offsetDir.crossVectors(hintDir, dir0);
38
38
  offsetDir.crossVectors(dir0, offsetDir);
39
39
  offsetDir.normalize();
src/engine-components/BoxHelperComponent.ts CHANGED
@@ -1,28 +1,28 @@
1
1
  import { Behaviour } from "./Component";
2
- import * as THREE from "three";
3
2
  import { getParam } from "../engine/engine_utils";
4
3
  import { CreateWireCube, Gizmos } from "../engine/engine_gizmos";
5
4
  import { getWorldPosition, getWorldScale } from "../engine/engine_three_utils";
5
+ import { Box3, Color, ColorRepresentation, LineSegments, Object3D, Vector3 } from "three";
6
6
 
7
7
  const gizmos = getParam("gizmos");
8
8
  const debug = getParam("debugboxhelper");
9
9
 
10
10
  export class BoxHelperComponent extends Behaviour {
11
11
 
12
- private box: THREE.Box3 | null = null;
13
- private static testBox: THREE.Box3 = new THREE.Box3();
12
+ private box: Box3 | null = null;
13
+ private static testBox: Box3 = new Box3();
14
14
  private _lastMatrixUpdateFrame: number = -1;
15
- private static _position: THREE.Vector3 = new THREE.Vector3();
16
- private static _size: THREE.Vector3 = new THREE.Vector3(.01, .01, .01);
15
+ private static _position: Vector3 = new Vector3();
16
+ private static _size: Vector3 = new Vector3(.01, .01, .01);
17
17
 
18
- public isInBox(obj: THREE.Object3D, scaleFactor?: number): boolean | undefined {
18
+ public isInBox(obj: Object3D, scaleFactor?: number): boolean | undefined {
19
19
  if (!obj) return undefined;
20
20
 
21
21
  // if (!obj.geometry.boundingBox) obj.geometry.computeBoundingBox();
22
22
  // if (!obj.geometry.boundingBox) return undefined;
23
23
 
24
24
  if (!this.box) {
25
- this.box = new THREE.Box3();
25
+ this.box = new Box3();
26
26
  }
27
27
 
28
28
 
@@ -56,14 +56,14 @@
56
56
  return intersects;
57
57
  }
58
58
 
59
- public intersects(box: THREE.Box3): boolean {
59
+ public intersects(box: Box3): boolean {
60
60
  if (!box) return false;
61
61
  return this.updateBox(false).intersectsBox(box);
62
62
  }
63
63
 
64
- public updateBox(force: boolean = false): THREE.Box3 {
64
+ public updateBox(force: boolean = false): Box3 {
65
65
  if (!this.box) {
66
- this.box = new THREE.Box3();
66
+ this.box = new Box3();
67
67
  }
68
68
  if (force || this.context.time.frameCount != this._lastMatrixUpdateFrame) {
69
69
  const firstUpdate = this._lastMatrixUpdateFrame < 0;
@@ -77,8 +77,8 @@
77
77
  }
78
78
 
79
79
 
80
- private _helper: THREE.LineSegments | null = null;
81
- private _color: THREE.Color | null = null;
80
+ private _helper: LineSegments | null = null;
81
+ private _color: Color | null = null;
82
82
 
83
83
  awake(): void {
84
84
  this._helper = null;
@@ -86,7 +86,7 @@
86
86
  this.box = null;
87
87
  }
88
88
 
89
- public showHelper(col: THREE.ColorRepresentation | null = null, force: boolean = false) {
89
+ public showHelper(col: ColorRepresentation | null = null, force: boolean = false) {
90
90
  if (!gizmos && !force) return;
91
91
  if (this._helper) {
92
92
  if (col)
src/engine-components/Component.ts CHANGED
@@ -1,15 +1,14 @@
1
- import * as THREE from "three";
2
1
  import { Mathf } from "../engine/engine_math";
3
2
  import * as threeutils from "../engine/engine_three_utils";
4
3
  import { activeInHierarchyFieldName } from "../engine/engine_constants";
5
4
  import { Context, FrameEvent } from "../engine/engine_setup";
6
5
  import * as main from "../engine/engine_mainloop_utils";
7
- import { Object3D } from "three";
8
6
  import { syncDestroy, syncInstantiate } from "../engine/engine_networking_instantiate";
9
- import { ConstructorConcrete, SourceIdentifier, IComponent, IGameObject, Constructor, GuidsMap, UIDProvider, Collision, ICollider } from "../engine/engine_types";
7
+ import { ConstructorConcrete, SourceIdentifier, IComponent, IGameObject, Constructor, GuidsMap, Collision, ICollider } from "../engine/engine_types";
10
8
  import { addNewComponent, destroyComponentInstance, findObjectOfType, findObjectsOfType, getComponent, getComponentInChildren, getComponentInParent, getComponents, getComponentsInChildren, getComponentsInParent, getOrAddComponent, moveComponentInstance, removeComponent } from "../engine/engine_components";
11
9
  import { findByGuid, destroy, InstantiateOptions, instantiate, HideFlags, foreachComponent, markAsInstancedRendered, isActiveInHierarchy, isActiveSelf, isUsingInstancing, setActive, isDestroyed } from "../engine/engine_gameobject";
12
10
 
11
+ import { Euler, Object3D, Quaternion, Scene, Vector3 } from "three";
13
12
 
14
13
  // export interface ISerializationCallbackReceiver {
15
14
  // onBeforeSerialize?(): object | void;
@@ -19,7 +18,7 @@
19
18
  // onDeserialize?(key: string, value: any): any | void;
20
19
  // }
21
20
 
22
- abstract class GameObject extends THREE.Object3D implements THREE.Object3D, IGameObject {
21
+ abstract class GameObject extends Object3D implements Object3D, IGameObject {
23
22
 
24
23
  guid: string | undefined;
25
24
 
@@ -27,13 +26,13 @@
27
26
  abstract destroy();
28
27
 
29
28
  // The actual implementation / prototype of threejs is modified in js-extensions/Object3D
30
- abstract get transform(): THREE.Object3D;
29
+ abstract get transform(): Object3D;
31
30
 
32
- public static isDestroyed(go: THREE.Object3D): boolean {
31
+ public static isDestroyed(go: Object3D): boolean {
33
32
  return isDestroyed(go);
34
33
  }
35
34
 
36
- public static setActive(go: THREE.Object3D, active: boolean, processStart: boolean = true) {
35
+ public static setActive(go: Object3D, active: boolean, processStart: boolean = true) {
37
36
  if (!go) return;
38
37
  setActive(go, active);
39
38
 
@@ -44,29 +43,29 @@
44
43
  }
45
44
 
46
45
  /** If the object is active (same as go.visible) */
47
- public static isActiveSelf(go: THREE.Object3D): boolean {
46
+ public static isActiveSelf(go: Object3D): boolean {
48
47
  return isActiveSelf(go);
49
48
  }
50
49
 
51
50
  /** If the object is active in the hierarchy (e.g. if any parent is invisible or not in the scene it will be false)
52
51
  * @param go object to check
53
52
  */
54
- public static isActiveInHierarchy(go: THREE.Object3D): boolean {
53
+ public static isActiveInHierarchy(go: Object3D): boolean {
55
54
  return isActiveInHierarchy(go);
56
55
  }
57
56
 
58
- public static markAsInstancedRendered(go: THREE.Object3D, instanced: boolean) {
57
+ public static markAsInstancedRendered(go: Object3D, instanced: boolean) {
59
58
  markAsInstancedRendered(go, instanced);
60
59
  }
61
60
 
62
- public static isUsingInstancing(instance: THREE.Object3D): boolean { return isUsingInstancing(instance); }
61
+ public static isUsingInstancing(instance: Object3D): boolean { return isUsingInstancing(instance); }
63
62
 
64
63
  /** Run a callback for all components of the provided type on the provided object and its children (if recursive is true)
65
64
  * @param instance object to run the method on
66
65
  * @param cb callback to run on each component
67
66
  * @param recursive if true, the method will be run on all children as well
68
67
  */
69
- public static foreachComponent(instance: THREE.Object3D, cb: (comp: Component) => any, recursive: boolean = true): any {
68
+ public static foreachComponent(instance: Object3D, cb: (comp: Component) => any, recursive: boolean = true): any {
70
69
  return foreachComponent(instance, cb as (comp: IComponent) => any, recursive);
71
70
  }
72
71
 
@@ -90,7 +89,7 @@
90
89
  /** Destroys a object on all connected clients (if you are in a networked session)
91
90
  * @param instance object to destroy
92
91
  */
93
- public static destroySynced(instance: THREE.Object3D | Component, context?: Context, recursive: boolean = true) {
92
+ public static destroySynced(instance: Object3D | Component, context?: Context, recursive: boolean = true) {
94
93
  if (!instance) return;
95
94
  const go = instance as GameObject;
96
95
  context = context ?? Context.Current;
@@ -101,14 +100,14 @@
101
100
  * @param instance object to destroy
102
101
  * @param recursive if true, all children will be destroyed as well
103
102
  */
104
- public static destroy(instance: THREE.Object3D | Component, recursive: boolean = true, isRoot: boolean = true) {
103
+ public static destroy(instance: Object3D | Component, recursive: boolean = true, isRoot: boolean = true) {
105
104
  return destroy(instance, recursive, isRoot);
106
105
  }
107
106
 
108
107
  /**
109
108
  * Add an object to parent and also ensure all components are being registered
110
109
  */
111
- public static add(instance: THREE.Object3D | null | undefined, parent: THREE.Object3D, context?: Context) {
110
+ public static add(instance: Object3D | null | undefined, parent: Object3D, context?: Context) {
112
111
  if (!instance || !parent) return;
113
112
  if (instance === parent) {
114
113
  console.warn("Can not add object to self", instance);
@@ -137,7 +136,7 @@
137
136
  /**
138
137
  * Removes the object from its parent and deactivates all of its components
139
138
  */
140
- public static remove(instance: THREE.Object3D | null | undefined) {
139
+ public static remove(instance: Object3D | null | undefined) {
141
140
  if (!instance) return;
142
141
  instance.parent?.remove(instance);
143
142
  setActive(instance, false);
@@ -148,7 +147,7 @@
148
147
  }
149
148
 
150
149
  /** Invokes a method on all components including children (if a method with that name exists) */
151
- public static invokeOnChildren(go: THREE.Object3D | null | undefined, functionName: string, ...args: any) {
150
+ public static invokeOnChildren(go: Object3D | null | undefined, functionName: string, ...args: any) {
152
151
  this.invoke(go, functionName, true, args);
153
152
  }
154
153
 
@@ -156,7 +155,7 @@
156
155
  * @param go object to invoke the method on all components
157
156
  * @param functionName name of the method to invoke
158
157
  */
159
- public static invoke(go: THREE.Object3D | null | undefined, functionName: string, children: boolean = false, ...args: any) {
158
+ public static invoke(go: Object3D | null | undefined, functionName: string, children: boolean = false, ...args: any) {
160
159
  if (!go) return;
161
160
  this.foreachComponent(go, c => {
162
161
  const fn = c[functionName];
@@ -172,7 +171,7 @@
172
171
  * @param type type of the component to add
173
172
  * @param callAwake if true, the component will be added and awake will be called immediately
174
173
  */
175
- public static addNewComponent<T>(go: GameObject | THREE.Object3D, type: ConstructorConcrete<T>, callAwake: boolean = true): T {
174
+ public static addNewComponent<T>(go: IGameObject | Object3D, type: ConstructorConcrete<T>, callAwake: boolean = true): T {
176
175
  const instance = new type();
177
176
  //@ts-ignore
178
177
  addNewComponent(go, instance, callAwake);
@@ -185,7 +184,7 @@
185
184
  * @param go component to move the component to
186
185
  * @param instance component to move to the GO
187
186
  */
188
- public static addComponent(go: GameObject, instance: Component): void {
187
+ public static addComponent(go: IGameObject, instance: Component): void {
189
188
  return this.moveComponent(go, instance);
190
189
  }
191
190
 
@@ -194,7 +193,7 @@
194
193
  * @param go component to move the component to
195
194
  * @param instance component to move to the GO
196
195
  */
197
- public static moveComponent(go: GameObject, instance: Component): void {
196
+ public static moveComponent(go: IGameObject, instance: Component): void {
198
197
  if (instance.gameObject == null) {
199
198
  throw new Error("Did you mean to create a new component? Use addNewComponent");
200
199
  }
@@ -209,12 +208,12 @@
209
208
  return instance;
210
209
  }
211
210
 
212
- public static getOrAddComponent<T>(go: GameObject | THREE.Object3D, typeName: ConstructorConcrete<T>): T {
211
+ public static getOrAddComponent<T>(go: IGameObject | Object3D, typeName: ConstructorConcrete<T>): T {
213
212
  return getOrAddComponent<any>(go, typeName);
214
213
  }
215
214
 
216
215
  /** Gets a component on the provided object */
217
- public static getComponent<T>(go: GameObject | THREE.Object3D | null, typeName: Constructor<T> | null): T | null {
216
+ public static getComponent<T>(go: IGameObject | Object3D | null, typeName: Constructor<T> | null): T | null {
218
217
  if (go === null) return null;
219
218
  // if names are minified we could also use the type store and work with strings everywhere
220
219
  // not ideal, but I dont know a good/sane way to do this otherwise
@@ -223,49 +222,49 @@
223
222
  return getComponent(go, typeName as any);
224
223
  }
225
224
 
226
- public static getComponents<T>(go: GameObject | THREE.Object3D | null, typeName: Constructor<T>, arr: T[] | null = null): T[] {
225
+ public static getComponents<T>(go: IGameObject | Object3D | null, typeName: Constructor<T>, arr: T[] | null = null): T[] {
227
226
  if (go === null) return arr ?? [];
228
227
  return getComponents(go, typeName, arr);
229
228
  }
230
229
 
231
- public static findByGuid(guid: string, hierarchy: THREE.Object3D): GameObject | Component | null | undefined {
230
+ public static findByGuid(guid: string, hierarchy: Object3D): GameObject | Component | null | undefined {
232
231
  const res = findByGuid(guid, hierarchy);
233
232
  return res as GameObject | Component | null | undefined;
234
233
  }
235
234
 
236
- public static findObjectOfType<T>(typeName: Constructor<T>, context?: Context | THREE.Object3D, includeInactive: boolean = true): T | null {
235
+ public static findObjectOfType<T>(typeName: Constructor<T>, context?: Context | Object3D, includeInactive: boolean = true): T | null {
237
236
  return findObjectOfType(typeName, context ?? Context.Current, includeInactive);
238
237
  }
239
238
 
240
- public static findObjectsOfType<T>(typeName: Constructor<T>, context?: Context | THREE.Object3D): Array<T> {
239
+ public static findObjectsOfType<T>(typeName: Constructor<T>, context?: Context | Object3D): Array<T> {
241
240
  const arr = [];
242
241
  findObjectsOfType(typeName, arr, context);
243
242
  return arr;
244
243
  }
245
244
 
246
- public static getComponentInChildren<T>(go: GameObject | THREE.Object3D, typeName: Constructor<T>): T | null {
245
+ public static getComponentInChildren<T>(go: IGameObject | Object3D, typeName: Constructor<T>): T | null {
247
246
  return getComponentInChildren(go, typeName);
248
247
  }
249
248
 
250
- public static getComponentsInChildren<T>(go: GameObject | THREE.Object3D, typeName: Constructor<T>, arr: T[] | null = null): Array<T> {
249
+ public static getComponentsInChildren<T>(go: IGameObject | Object3D, typeName: Constructor<T>, arr: T[] | null = null): Array<T> {
251
250
  return getComponentsInChildren<T>(go, typeName, arr ?? undefined) as T[]
252
251
  }
253
252
 
254
- public static getComponentInParent<T>(go: GameObject | THREE.Object3D, typeName: Constructor<T>): T | null {
253
+ public static getComponentInParent<T>(go: IGameObject | Object3D, typeName: Constructor<T>): T | null {
255
254
  return getComponentInParent(go, typeName);
256
255
  }
257
256
 
258
- public static getComponentsInParent<T>(go: GameObject | THREE.Object3D, typeName: Constructor<T>, arr: Array<T> | null = null): Array<T> {
257
+ public static getComponentsInParent<T>(go: IGameObject | Object3D, typeName: Constructor<T>, arr: Array<T> | null = null): Array<T> {
259
258
  return getComponentsInParent(go, typeName, arr);
260
259
  }
261
260
 
262
- public static getAllComponents(go: GameObject | THREE.Object3D): Behaviour[] {
261
+ public static getAllComponents(go: IGameObject | Object3D): Behaviour[] {
263
262
  const componentsList = go.userData?.components;
264
263
  const newList = [...componentsList];
265
264
  return newList;
266
265
  }
267
266
 
268
- public static *iterateComponents(go: GameObject | THREE.Object3D) {
267
+ public static *iterateComponents(go: IGameObject | Object3D) {
269
268
  const list = go?.userData?.components;
270
269
  if (list && Array.isArray(list)) {
271
270
  for (let i = 0; i < list.length; i++) {
@@ -300,7 +299,7 @@
300
299
  set context(context: Context) {
301
300
  this.__context = context;
302
301
  }
303
- get scene(): THREE.Scene { return this.context.scene; }
302
+ get scene(): Scene { return this.context.scene; }
304
303
 
305
304
  get layer(): number {
306
305
  return this.gameObject?.userData?.layer;
@@ -367,7 +366,7 @@
367
366
  gameObject!: GameObject;
368
367
  guid: string = "invalid";
369
368
  sourceId?: SourceIdentifier;
370
- // transform: THREE.Object3D = nullObject;
369
+ // transform: Object3D = nullObject;
371
370
 
372
371
  /** called on a component with a map of old to new guids (e.g. when instantiate generated new guids and e.g. timeline track bindings needs to remape them) */
373
372
  resolveGuids?(guidsMap: GuidsMap): void;
@@ -521,24 +520,24 @@
521
520
 
522
521
  // TODO move this to threeutils
523
522
  // we need a copy for modifying the values to local space
524
- private static _worldPositionBuffer: THREE.Vector3 = new THREE.Vector3();
525
- private static _worldQuaternionBuffer: THREE.Quaternion = new THREE.Quaternion();
526
- private static _worldEulerBuffer: THREE.Euler = new THREE.Euler();
523
+ private static _worldPositionBuffer: Vector3 = new Vector3();
524
+ private static _worldQuaternionBuffer: Quaternion = new Quaternion();
525
+ private static _worldEulerBuffer: Euler = new Euler();
527
526
 
528
- private _worldPosition: THREE.Vector3 | undefined = undefined;
529
- private _worldQuaternion: THREE.Quaternion | undefined = undefined;
530
- private static _tempQuaternionBuffer2: THREE.Quaternion = new THREE.Quaternion();
531
- private _worldEuler: THREE.Euler | undefined = undefined;
532
- private _worldRotation: THREE.Vector3 | undefined = undefined;
527
+ private _worldPosition: Vector3 | undefined = undefined;
528
+ private _worldQuaternion: Quaternion | undefined = undefined;
529
+ private static _tempQuaternionBuffer2: Quaternion = new Quaternion();
530
+ private _worldEuler: Euler | undefined = undefined;
531
+ private _worldRotation: Vector3 | undefined = undefined;
533
532
 
534
- get worldPosition(): THREE.Vector3 {
535
- if (!this._worldPosition) this._worldPosition = new THREE.Vector3();
533
+ get worldPosition(): Vector3 {
534
+ if (!this._worldPosition) this._worldPosition = new Vector3();
536
535
  threeutils.getWorldPosition(this.gameObject, this._worldPosition);
537
536
  // this.gameObject.getWorldPosition(this._worldPosition);
538
537
  return this._worldPosition;
539
538
  }
540
539
 
541
- set worldPosition(val: THREE.Vector3) {
540
+ set worldPosition(val: Vector3) {
542
541
  threeutils.setWorldPosition(this.gameObject, val);
543
542
  }
544
543
 
@@ -548,11 +547,11 @@
548
547
  }
549
548
 
550
549
 
551
- get worldQuaternion(): THREE.Quaternion {
552
- if (!this._worldQuaternion) this._worldQuaternion = new THREE.Quaternion();
550
+ get worldQuaternion(): Quaternion {
551
+ if (!this._worldQuaternion) this._worldQuaternion = new Quaternion();
553
552
  return threeutils.getWorldQuaternion(this.gameObject, this._worldQuaternion);
554
553
  }
555
- set worldQuaternion(val: THREE.Quaternion) {
554
+ set worldQuaternion(val: Quaternion) {
556
555
  threeutils.setWorldQuaternion(this.gameObject, val);
557
556
  }
558
557
  setWorldQuaternion(x: number, y: number, z: number, w: number) {
@@ -562,23 +561,23 @@
562
561
 
563
562
 
564
563
  // world euler (in radians)
565
- get worldEuler(): THREE.Euler {
566
- if (!this._worldEuler) this._worldEuler = new THREE.Euler();
564
+ get worldEuler(): Euler {
565
+ if (!this._worldEuler) this._worldEuler = new Euler();
567
566
  this._worldEuler.setFromQuaternion(this.worldQuaternion);
568
567
  return this._worldEuler;
569
568
  }
570
569
 
571
570
  // world euler (in radians)
572
- set worldEuler(val: THREE.Euler) {
573
- if (!this._worldQuaternion) this._worldQuaternion = new THREE.Quaternion();
571
+ set worldEuler(val: Euler) {
572
+ if (!this._worldQuaternion) this._worldQuaternion = new Quaternion();
574
573
  this._worldQuaternion?.setFromEuler(val);
575
574
  this.worldQuaternion = this._worldQuaternion;
576
575
  }
577
576
 
578
577
  // returns rotation in degrees
579
- get worldRotation(): THREE.Vector3 {
578
+ get worldRotation(): Vector3 {
580
579
  const rot = this.worldEuler;
581
- if (!this._worldRotation) this._worldRotation = new THREE.Vector3();
580
+ if (!this._worldRotation) this._worldRotation = new Vector3();
582
581
  const wr = this._worldRotation;
583
582
  wr.set(rot.x, rot.y, rot.z);
584
583
  wr.x = Mathf.toDegrees(wr.x);
@@ -587,7 +586,7 @@
587
586
  return wr;
588
587
  }
589
588
 
590
- set worldRotation(val: THREE.Vector3) {
589
+ set worldRotation(val: Vector3) {
591
590
  this.setWorldRotation(val.x, val.y, val.z, true);
592
591
  }
593
592
 
@@ -602,16 +601,16 @@
602
601
  this.worldQuaternion = Component._worldQuaternionBuffer;
603
602
  }
604
603
 
605
- private static _forward: THREE.Vector3 = new THREE.Vector3();
606
- public get forward(): THREE.Vector3 {
604
+ private static _forward: Vector3 = new Vector3();
605
+ public get forward(): Vector3 {
607
606
  return Component._forward.set(0, 0, -1).applyQuaternion(this.worldQuaternion);
608
607
  }
609
- private static _right: THREE.Vector3 = new THREE.Vector3();
610
- public get right(): THREE.Vector3 {
608
+ private static _right: Vector3 = new Vector3();
609
+ public get right(): Vector3 {
611
610
  return Component._right.set(1, 0, 0).applyQuaternion(this.worldQuaternion);
612
611
  }
613
- private static _up: THREE.Vector3 = new THREE.Vector3();
614
- public get up(): THREE.Vector3 {
612
+ private static _up: Vector3 = new Vector3();
613
+ public get up(): Vector3 {
615
614
  return Component._up.set(0, 1, 0).applyQuaternion(this.worldQuaternion);
616
615
  }
617
616
 
src/engine-components/DragControls.ts CHANGED
@@ -1,19 +1,18 @@
1
- import { Behaviour, GameObject } from "./Component";
2
- // import { DragControls as Control } from "../include/three/DragControls";
1
+ import { GameObject } from "./Component";
3
2
  import { SyncedTransform } from "./SyncedTransform";
4
- import * as THREE from "three";
5
- import { IPointerClickHandler, IPointerDownHandler, IPointerEnterHandler, IPointerExitHandler, IPointerUpHandler, PointerEventData } from "./ui/PointerEvents";
3
+ import { IPointerDownHandler, IPointerEnterHandler, IPointerExitHandler, IPointerUpHandler, PointerEventData } from "./ui/PointerEvents";
6
4
  import { Context } from "../engine/engine_setup";
7
5
  import { Interactable, UsageMarker } from "./Interactable";
8
6
  import { Rigidbody } from "./RigidBody";
9
7
  import { WebXR } from "./WebXR";
10
8
  import { Avatar_POI } from "./avatar/Avatar_Brain_LookAt";
11
9
  import { RaycastOptions } from "../engine/engine_physics";
12
- import { getWorldPosition, getWorldQuaternion, setWorldPosition } from "../engine/engine_three_utils";
10
+ import { getWorldPosition, setWorldPosition } from "../engine/engine_three_utils";
13
11
  import { KeyCode } from "../engine/engine_input";
14
12
  import { nameofFactory } from "../engine/engine_utils";
15
13
  import { InstancingUtil } from "../engine/engine_instancing";
16
14
  import { OrbitControls } from "./OrbitControls";
15
+ import { BufferGeometry, Camera, Color, Line, LineBasicMaterial, Matrix4, Mesh, MeshBasicMaterial, Object3D, Plane, Ray, Raycaster, SphereGeometry, Vector2, Vector3 } from "three";
17
16
 
18
17
  const debug = false;
19
18
 
@@ -23,8 +22,8 @@
23
22
  }
24
23
 
25
24
  interface SelectArgs {
26
- selected: THREE.Object3D;
27
- attached: THREE.Object3D | GameObject | null;
25
+ selected: Object3D;
26
+ attached: Object3D | GameObject | null;
28
27
  }
29
28
 
30
29
 
@@ -41,7 +40,7 @@
41
40
 
42
41
  public transformSelf: boolean = true;
43
42
  // public transformGroup: boolean = true;
44
- // public targets: THREE.Object3D[] | null = null;
43
+ // public targets: Object3D[] | null = null;
45
44
 
46
45
  // private controls: Control | null = null;
47
46
  private orbit: OrbitControls | null = null;
@@ -53,7 +52,7 @@
53
52
  super();
54
53
  this.selectStartEventListener = [];
55
54
  this.selectEndEventListener = [];
56
- this._dragDelta = new THREE.Vector2();
55
+ this._dragDelta = new Vector2();
57
56
  }
58
57
 
59
58
  addDragEventListener(type: DragEvents, cb: (ctrls: DragControls, args: SelectArgs) => void | Function) {
@@ -74,10 +73,10 @@
74
73
  this.orbit = GameObject.findObjectOfType(OrbitControls, this.context);
75
74
  }
76
75
 
77
- private static lastHovered: THREE.Object3D;
76
+ private static lastHovered: Object3D;
78
77
  private _draggingRigidbodies: Rigidbody[] = [];
79
78
 
80
- private allowEdit(_obj: THREE.Object3D | null = null) {
79
+ private allowEdit(_obj: Object3D | null = null) {
81
80
  return this.context.connection.allowEditing;
82
81
  }
83
82
 
@@ -160,7 +159,7 @@
160
159
 
161
160
  private _isDragging: boolean = false;
162
161
  private _marker: UsageMarker | null = null;
163
- private _dragDelta!: THREE.Vector2;
162
+ private _dragDelta!: Vector2;
164
163
  private _didDrag: boolean = false;
165
164
  private _activePointerId?: number;
166
165
 
@@ -177,14 +176,14 @@
177
176
  if (!dc || dc !== this) return;
178
177
 
179
178
 
180
- let object: THREE.Object3D = evt.object;
179
+ let object: Object3D = evt.object;
181
180
 
182
181
  if (this.transformSelf) {
183
182
  object = this.gameObject;
184
183
  }
185
184
 
186
185
  // raise event
187
- const args: { selected: THREE.Object3D, attached: THREE.Object3D | null } = { selected: object, attached: object };
186
+ const args: { selected: Object3D, attached: Object3D | null } = { selected: object, attached: object };
188
187
  for (const listener of this.selectStartEventListener) {
189
188
  listener(this, args);
190
189
  }
@@ -285,54 +284,54 @@
285
284
  return this._selected !== null && this._selected !== undefined;
286
285
  }
287
286
 
288
- public get selected(): THREE.Object3D | null {
287
+ public get selected(): Object3D | null {
289
288
  return this._selected;
290
289
  }
291
290
 
292
- private _selected: THREE.Object3D | null = null;
291
+ private _selected: Object3D | null = null;
293
292
  private _context: Context | null = null;
294
- private _camera: THREE.Camera;;
295
- private _cameraPlane: THREE.Plane = new THREE.Plane();
293
+ private _camera: Camera;
294
+ private _cameraPlane: Plane = new Plane();
296
295
 
297
296
  private _hasGroundPlane: boolean = false;
298
- private _groundPlane: THREE.Plane = new THREE.Plane();
299
- private _groundOffset: THREE.Vector3 = new THREE.Vector3();
297
+ private _groundPlane: Plane = new Plane();
298
+ private _groundOffset: Vector3 = new Vector3();
300
299
  private _groundOffsetFactor: number = 0;
301
300
  private _groundDistance: number = 0;
302
- private _groundPlanePoint: THREE.Vector3 = new THREE.Vector3();
301
+ private _groundPlanePoint: Vector3 = new Vector3();
303
302
 
304
- private _raycaster = new THREE.Raycaster();
305
- private _cameraPlaneOffset = new THREE.Vector3();
306
- private _intersection = new THREE.Vector3();
307
- private _worldPosition = new THREE.Vector3();
308
- private _inverseMatrix = new THREE.Matrix4();
303
+ private _raycaster = new Raycaster();
304
+ private _cameraPlaneOffset = new Vector3();
305
+ private _intersection = new Vector3();
306
+ private _worldPosition = new Vector3();
307
+ private _inverseMatrix = new Matrix4();
309
308
  private _rbs: Rigidbody[] = [];
310
309
 
311
- private _groundLine: THREE.Line;
312
- private _groundMarker: THREE.Object3D;
313
- private static geometry = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, -1, 0)]);
310
+ private _groundLine: Line;
311
+ private _groundMarker: Object3D;
312
+ private static geometry = new BufferGeometry().setFromPoints([new Vector3(0, 0, 0), new Vector3(0, -1, 0)]);
314
313
 
315
- constructor(camera: THREE.Camera) {
314
+ constructor(camera: Camera) {
316
315
  this._camera = camera;
317
316
 
318
- const line = new THREE.Line(DragHelper.geometry);
319
- const mat = line.material as THREE.LineBasicMaterial;
320
- mat.color = new THREE.Color(.4, .4, .4);
317
+ const line = new Line(DragHelper.geometry);
318
+ const mat = line.material as LineBasicMaterial;
319
+ mat.color = new Color(.4, .4, .4);
321
320
  line.layers.set(2);
322
321
  line.name = 'line';
323
322
  line.scale.y = 1;
324
323
  // line.matrixAutoUpdate = false;
325
324
  this._groundLine = line;
326
325
 
327
- const geometry = new THREE.SphereGeometry(.5, 22, 22);
328
- const material = new THREE.MeshBasicMaterial({ color: mat.color });
329
- const sphere = new THREE.Mesh(geometry, material);
326
+ const geometry = new SphereGeometry(.5, 22, 22);
327
+ const material = new MeshBasicMaterial({ color: mat.color });
328
+ const sphere = new Mesh(geometry, material);
330
329
  sphere.visible = false;
331
330
  sphere.layers.set(2);
332
331
  this._groundMarker = sphere;
333
332
  }
334
333
 
335
- setSelected(newSelected: THREE.Object3D | null, context: Context) {
334
+ setSelected(newSelected: Object3D | null, context: Context) {
336
335
  if (this._selected && context) {
337
336
  for (const rb of this._rbs) {
338
337
  rb.wakeUp();
@@ -376,7 +375,7 @@
376
375
  }
377
376
  }
378
377
 
379
- private _groundOffsetVector = new THREE.Vector3(0, 1, 0);
378
+ private _groundOffsetVector = new Vector3(0, 1, 0);
380
379
  private _requireUpdateGroundPlane = true;
381
380
  private _didDragOnGroundPlaneLastFrame: boolean = false;
382
381
 
@@ -429,7 +428,7 @@
429
428
  this._requireUpdateGroundPlane = false;
430
429
  if (this._hasGroundPlane) {
431
430
  // const wp = getWorldPosition(this._selected);
432
- // const ray = new THREE.Ray(wp, new THREE.Vector3(0, -1, 0));
431
+ // const ray = new Ray(wp, new Vector3(0, -1, 0));
433
432
 
434
433
  if (this._raycaster.ray.intersectPlane(this._groundPlane, this._intersection)) {
435
434
  const y = this._intersection.y;
@@ -466,7 +465,7 @@
466
465
  }
467
466
  }
468
467
 
469
- private onUpdateWorldPosition(wp: THREE.Vector3, pointOnPlane: THREE.Vector3 | null, heightOnly: boolean) {
468
+ private onUpdateWorldPosition(wp: Vector3, pointOnPlane: Vector3 | null, heightOnly: boolean) {
470
469
  if (!this._selected) return;
471
470
  if (heightOnly) {
472
471
  const cur = getWorldPosition(this._selected);
@@ -503,7 +502,7 @@
503
502
  private onUpdateGroundPlane() {
504
503
  if (!this._selected || !this._context) return;
505
504
  const wp = getWorldPosition(this._selected);
506
- const ray = new THREE.Ray(new THREE.Vector3(0, .1, 0).add(wp), new THREE.Vector3(0, -1, 0));
505
+ const ray = new Ray(new Vector3(0, .1, 0).add(wp), new Vector3(0, -1, 0));
507
506
  const opts = new RaycastOptions();
508
507
  opts.ignore = [this._selected];
509
508
  const hits = this._context.physics.raycastFromRay(ray, opts);
@@ -512,7 +511,7 @@
512
511
  if (!hit.face || this.contains(this._selected, hit.object)) {
513
512
  continue;
514
513
  }
515
- const normal = new THREE.Vector3(0, 1, 0); // hit.face.normal
514
+ const normal = new Vector3(0, 1, 0); // hit.face.normal
516
515
  this._groundPlane.setFromNormalAndCoplanarPoint(normal, hit.point);
517
516
  break;
518
517
  }
@@ -536,7 +535,7 @@
536
535
  }
537
536
  }
538
537
 
539
- private contains(obj: THREE.Object3D, toSearch: THREE.Object3D): boolean {
538
+ private contains(obj: Object3D, toSearch: Object3D): boolean {
540
539
  if (obj === toSearch) return true;
541
540
  if (obj.children) {
542
541
  for (const child of obj.children) {
src/engine/engine_addressables.ts CHANGED
@@ -1,11 +1,10 @@
1
1
  import { getParam, resolveUrl } from "../engine/engine_utils";
2
- // import { loadSync, parseSync } from "./engine_scenetools";
3
2
  import { SerializationContext, TypeSerializer } from "./engine_serialization_core";
4
3
  import { Context } from "./engine_setup";
5
- import { Group, Object3D, Scene, Texture } from "three";
4
+ import { Group, Object3D, Texture } from "three";
6
5
  import { processNewScripts } from "./engine_mainloop_utils";
7
6
  import { registerPrefabProvider, syncInstantiate } from "./engine_networking_instantiate";
8
- import { download, hash } from "./engine_web_api";
7
+ import { download } from "./engine_web_api";
9
8
  import { getLoader } from "./engine_gltf";
10
9
  import { SourceIdentifier } from "./engine_types";
11
10
  import { destroy, instantiate, InstantiateOptions, isDestroyed } from "./engine_gameobject";
@@ -185,11 +184,11 @@
185
184
  }
186
185
  }
187
186
 
188
- async instantiate(parent?: THREE.Object3D | InstantiateOptions) {
187
+ async instantiate(parent?: Object3D | InstantiateOptions) {
189
188
  return this.onInstantiate(parent, false);
190
189
  }
191
190
 
192
- async instantiateSynced(parent?: THREE.Object3D | InstantiateOptions, saveOnServer: boolean = true) {
191
+ async instantiateSynced(parent?: Object3D | InstantiateOptions, saveOnServer: boolean = true) {
193
192
  return this.onInstantiate(parent, true, saveOnServer);
194
193
  }
195
194
 
@@ -211,7 +210,7 @@
211
210
  }
212
211
  }
213
212
 
214
- private async onInstantiate(parent?: THREE.Object3D | InstantiateOptions, networked: boolean = false, saveOnServer?: boolean) {
213
+ private async onInstantiate(parent?: Object3D | InstantiateOptions, networked: boolean = false, saveOnServer?: boolean) {
215
214
  const context = Context.Current;
216
215
  if (!parent) parent = context.scene;
217
216
  if (this.mustLoad) {
@@ -282,7 +281,7 @@
282
281
  * and call destroy on the player marker root
283
282
  * @returns the scene root object if the asset was a glb/gltf
284
283
  */
285
- private tryGetActualGameObjectRoot(asset: any): THREE.Object3D | null {
284
+ private tryGetActualGameObjectRoot(asset: any): Object3D | null {
286
285
  if (asset && asset.scene) {
287
286
  // some exporters produce additional root objects
288
287
  const scene = asset.scene as Group;
src/engine/engine_context.ts CHANGED
@@ -1,5 +1,8 @@
1
- import { Camera, Clock, DepthTexture, PerspectiveCamera, WebGLRenderer, WebGLRenderTarget } from 'three'
2
- import * as THREE from 'three'
1
+ import { BufferGeometry, Camera, Clock, Color, DepthTexture, Group,
2
+ Material, NearestFilter, NoToneMapping, Object3D, PCFSoftShadowMap,
3
+ PerspectiveCamera, RGBAFormat, Scene, sRGBEncoding,
4
+ Texture, WebGLRenderer, WebGLRenderTarget
5
+ } from 'three'
3
6
  import { Input } from './engine_input';
4
7
  import { Physics } from './engine_physics';
5
8
  import { Time } from './engine_time';
@@ -50,7 +53,7 @@
50
53
  name?: string;
51
54
  alias?: string;
52
55
  domElement: HTMLElement | null;
53
- renderer?: THREE.WebGLRenderer = undefined;
56
+ renderer?: WebGLRenderer = undefined;
54
57
  hash?: string;
55
58
 
56
59
  constructor(domElement: HTMLElement | null) {
@@ -74,7 +77,7 @@
74
77
  ImmersiveAR = "immersive-ar",
75
78
  }
76
79
 
77
- export declare type OnBeforeRenderCallback = (renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera, geometry: THREE.BufferGeometry, material: THREE.Material, group: THREE.Group) => void
80
+ export declare type OnBeforeRenderCallback = (renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, group: Group) => void
78
81
 
79
82
 
80
83
  export function registerComponent(script: IComponent, context?: Context) {
@@ -175,8 +178,8 @@
175
178
  }
176
179
  private _currentFrameEvent: FrameEvent = FrameEvent.Undefined;
177
180
 
178
- scene: THREE.Scene;
179
- renderer: THREE.WebGLRenderer;
181
+ scene: Scene;
182
+ renderer: WebGLRenderer;
180
183
  composer: EffectComposer | null = null;
181
184
 
182
185
  // all scripts
@@ -191,7 +194,7 @@
191
194
  scripts_WithCorroutines: IComponent[] = [];
192
195
  coroutines: { [FrameEvent: number]: Array<CoroutineData> } = {}
193
196
 
194
- get mainCamera(): THREE.Camera | null {
197
+ get mainCamera(): Camera | null {
195
198
  if (this.mainCameraComponent) {
196
199
  const cam = this.mainCameraComponent as ICamera;
197
200
  if (!cam.cam)
@@ -251,21 +254,21 @@
251
254
 
252
255
  // some tonemapping other than "NONE" is required for adjusting exposure with EXR environments
253
256
  this.renderer.toneMappingExposure = 1; // range [0...inf] instead of the usual -15..15
254
- this.renderer.toneMapping = THREE.NoToneMapping; // could also set to LinearToneMapping, ACESFilmicToneMapping
257
+ this.renderer.toneMapping = NoToneMapping; // could also set to LinearToneMapping, ACESFilmicToneMapping
255
258
 
256
- this.renderer.setClearColor(new THREE.Color('lightgrey'), 0);
259
+ this.renderer.setClearColor(new Color('lightgrey'), 0);
257
260
  // @ts-ignore
258
261
  this.renderer.antialias = true;
259
262
  // @ts-ignore
260
263
  this.renderer.alpha = false;
261
264
  this.renderer.shadowMap.enabled = true;
262
- this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
265
+ this.renderer.shadowMap.type = PCFSoftShadowMap;
263
266
  this.renderer.setSize(this.domWidth, this.domHeight);
264
- this.renderer.outputEncoding = THREE.sRGBEncoding;
267
+ this.renderer.outputEncoding = sRGBEncoding;
265
268
  this.renderer.physicallyCorrectLights = true;
266
269
  }
267
270
 
268
- this.scene = new THREE.Scene();
271
+ this.scene = new Scene();
269
272
 
270
273
  ContextRegistry.register(this);
271
274
 
@@ -328,7 +331,7 @@
328
331
  }
329
332
  }
330
333
 
331
- updateAspect(camera: THREE.PerspectiveCamera, width?: number, height?: number) {
334
+ updateAspect(camera: PerspectiveCamera, width?: number, height?: number) {
332
335
  if (!camera) return;
333
336
  if (width === undefined)
334
337
  width = this.domWidth;
@@ -410,7 +413,7 @@
410
413
  if (index >= 0) this._cameraStack.splice(index, 1);
411
414
  this._cameraStack.push(cam);
412
415
  this.mainCameraComponent = cam;
413
- const camera = cam.cam as THREE.PerspectiveCamera;
416
+ const camera = cam.cam as PerspectiveCamera;
414
417
  if (camera.isPerspectiveCamera)
415
418
  this.updateAspect(camera);
416
419
  (this.mainCameraComponent as ICamera)?.applyClearFlagsIfIsActiveCamera();
@@ -434,7 +437,7 @@
434
437
  private _onBeforeRenderListeners: { [key: string]: OnBeforeRenderCallback[] } = {};
435
438
 
436
439
  /** use this to subscribe to onBeforeRender events on threejs objects */
437
- addBeforeRenderListener(target: THREE.Object3D, callback: OnBeforeRenderCallback) {
440
+ addBeforeRenderListener(target: Object3D, callback: OnBeforeRenderCallback) {
438
441
  if (!this._onBeforeRenderListeners[target.uuid]) {
439
442
  this._onBeforeRenderListeners[target.uuid] = [];
440
443
  const onBeforeRenderCallback = (renderer, scene, camera, geometry, material, group) => {
@@ -450,7 +453,7 @@
450
453
  this._onBeforeRenderListeners[target.uuid].push(callback);
451
454
  }
452
455
 
453
- removeBeforeRenderListener(target: THREE.Object3D, callback: OnBeforeRenderCallback) {
456
+ removeBeforeRenderListener(target: Object3D, callback: OnBeforeRenderCallback) {
454
457
  if (this._onBeforeRenderListeners[target.uuid]) {
455
458
  const arr = this._onBeforeRenderListeners[target.uuid];
456
459
  const idx = arr.indexOf(callback);
@@ -473,11 +476,11 @@
473
476
  this._requireColorTexture = val;
474
477
  }
475
478
 
476
- get depthTexture(): THREE.DepthTexture | null {
479
+ get depthTexture(): DepthTexture | null {
477
480
  return this._renderTarget?.depthTexture || null;
478
481
  }
479
482
 
480
- get opaqueColorTexture(): THREE.Texture | null {
483
+ get opaqueColorTexture(): Texture | null {
481
484
  return this._renderTarget?.texture || null;
482
485
  }
483
486
 
@@ -822,17 +825,17 @@
822
825
  if (!this.mainCamera) return;
823
826
  if (!this._requireDepthTexture && !this._requireColorTexture) return;
824
827
  if (!this._renderTarget) {
825
- this._renderTarget = new THREE.WebGLRenderTarget(this.domWidth, this.domHeight);
828
+ this._renderTarget = new WebGLRenderTarget(this.domWidth, this.domHeight);
826
829
  if (this._requireDepthTexture) {
827
830
  const dt = new DepthTexture(this.domWidth, this.domHeight);;
828
831
  this._renderTarget.depthTexture = dt;
829
832
  }
830
833
  if (this._requireColorTexture) {
831
- this._renderTarget.texture = new THREE.Texture();
834
+ this._renderTarget.texture = new Texture();
832
835
  this._renderTarget.texture.generateMipmaps = false;
833
- this._renderTarget.texture.minFilter = THREE.NearestFilter;
834
- this._renderTarget.texture.magFilter = THREE.NearestFilter;
835
- this._renderTarget.texture.format = THREE.RGBAFormat;
836
+ this._renderTarget.texture.minFilter = NearestFilter;
837
+ this._renderTarget.texture.magFilter = NearestFilter;
838
+ this._renderTarget.texture.format = RGBAFormat;
836
839
  }
837
840
  }
838
841
  const rt = this._renderTarget;
@@ -909,24 +912,24 @@
909
912
  }
910
913
 
911
914
 
912
- // const scene = new THREE.Scene();
915
+ // const scene = new Scene();
913
916
  // const useComposer = utils.getParam("postfx");
914
917
  // const renderer = new WebGLRenderer({ antialias: true });
915
918
  // const composer = useComposer ? new EffectComposer(renderer) : undefined;
916
919
 
917
- // renderer.setClearColor(new THREE.Color('lightgrey'), 0)
920
+ // renderer.setClearColor(new Color('lightgrey'), 0)
918
921
  // renderer.antialias = true;
919
922
  // renderer.alpha = false;
920
923
  // renderer.shadowMap.enabled = true;
921
- // renderer.shadowMap.type = THREE.PCFSoftShadowMap;
924
+ // renderer.shadowMap.type = PCFSoftShadowMap;
922
925
  // renderer.setSize(window.innerWidth, window.innerHeight);
923
- // renderer.outputEncoding = THREE.sRGBEncoding;
926
+ // renderer.outputEncoding = sRGBEncoding;
924
927
  // renderer.physicallyCorrectLights = true;
925
928
  // document.body.appendChild(renderer.domElement);
926
929
 
927
930
  // // generation pushes loading requests in this array
928
931
  // const sceneData: {
929
- // mainCamera: THREE.Camera | undefined
932
+ // mainCamera: Camera | undefined
930
933
  // } = {
931
934
  // preparing: [],
932
935
  // resolving: [],
src/engine/engine_gameobject.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { BufferGeometry, InstancedBufferGeometry, Material, Mesh, Object3D, Texture } from "three";
1
+ import { Bone, Object3D, Quaternion, SkinnedMesh, Vector3 } from "three";
2
2
  import { processNewScripts } from "./engine_mainloop_utils";
3
3
  import { InstantiateIdProvider } from "./engine_networking_instantiate";
4
4
  import { Context, registerComponent } from "./engine_setup";
@@ -36,9 +36,9 @@
36
36
  parent?: string | undefined | Object3D;
37
37
  /** for duplicatable parenting */
38
38
  keepWorldPosition?: boolean
39
- position?: THREE.Vector3 | undefined;
40
- rotation?: THREE.Quaternion | undefined;
41
- scale?: THREE.Vector3 | undefined;
39
+ position?: Vector3 | undefined;
40
+ rotation?: Quaternion | undefined;
41
+ scale?: Vector3 | undefined;
42
42
 
43
43
  visible?: boolean | undefined;
44
44
 
@@ -88,14 +88,14 @@
88
88
  return go[activeInHierarchyFieldName] || isUsingInstancing(go);
89
89
  }
90
90
 
91
- export function markAsInstancedRendered(go: THREE.Object3D, instanced: boolean) {
91
+ export function markAsInstancedRendered(go: Object3D, instanced: boolean) {
92
92
  go[$isUsingInstancing] = instanced;
93
93
  }
94
94
 
95
- export function isUsingInstancing(instance: THREE.Object3D): boolean { return InstancingUtil.isUsingInstancing(instance); }
95
+ export function isUsingInstancing(instance: Object3D): boolean { return InstancingUtil.isUsingInstancing(instance); }
96
96
 
97
97
 
98
- export function findByGuid(guid: string, hierarchy: THREE.Object3D): GameObject | IComponent | null | undefined {
98
+ export function findByGuid(guid: string, hierarchy: Object3D): GameObject | IComponent | null | undefined {
99
99
  return tryFindObject(guid, hierarchy, true, true);
100
100
  }
101
101
 
@@ -159,7 +159,7 @@
159
159
 
160
160
  declare type ForEachComponentCallback = (comp: Component) => any;
161
161
 
162
- export function foreachComponent(instance: THREE.Object3D, cb: ForEachComponentCallback, recursive: boolean = true): any {
162
+ export function foreachComponent(instance: Object3D, cb: ForEachComponentCallback, recursive: boolean = true): any {
163
163
  return internalForEachComponent(instance, cb, recursive);
164
164
  }
165
165
 
@@ -198,8 +198,8 @@
198
198
  }
199
199
 
200
200
  declare class NewGameObjectReferenceInfo {
201
- original: THREE.Object3D;
202
- clone: THREE.Object3D;
201
+ original: Object3D;
202
+ clone: Object3D;
203
203
  }
204
204
 
205
205
  export function instantiate(instance: GameObject | Object3D | null, opts: InstantiateOptions | null = null): GameObject | null {
@@ -210,7 +210,7 @@
210
210
  // if x is defined assume this is a vec3 - this is just to not break everything at once and stay a little bit backwards compatible
211
211
  if (opts["x"] !== undefined) {
212
212
  options = new InstantiateOptions();
213
- options.position = opts as unknown as THREE.Vector3;
213
+ options.position = opts as unknown as Vector3;
214
214
  }
215
215
  else {
216
216
  // if (opts instanceof InstantiateOptions)
@@ -276,12 +276,12 @@
276
276
 
277
277
 
278
278
  function internalInstantiate(
279
- context: Context, instance: GameObject | THREE.Object3D, opts: InstantiateOptions | null,
279
+ context: Context, instance: GameObject | Object3D, opts: InstantiateOptions | null,
280
280
  componentsList: Array<Component>,
281
281
  newGameObjectsMap: { [key: string]: NewGameObjectReferenceInfo },
282
282
  skinnedMeshesMap: { [key: string]: NewGameObjectReferenceInfo }
283
283
  )
284
- : GameObject | THREE.Object3D | null {
284
+ : GameObject | Object3D | null {
285
285
  if (!instance) return null;
286
286
  // prepare, remove things that dont work out of the box
287
287
  // e.g. user data we want to manually clone
@@ -290,7 +290,7 @@
290
290
  instance.userData = {};
291
291
  const children = instance.children;
292
292
  instance.children = [];
293
- let clone: THREE.Object3D | GameObject;
293
+ let clone: Object3D | GameObject;
294
294
  clone = instance.clone(false);
295
295
  apply(clone);
296
296
  // if(instance[$originalGuid])
@@ -408,9 +408,9 @@
408
408
  ) {
409
409
  for (const key in skinnedMeshes) {
410
410
  const val = skinnedMeshes[key];
411
- const original = val.original as THREE.SkinnedMesh;
411
+ const original = val.original as SkinnedMesh;
412
412
  const originalSkeleton = original.skeleton;
413
- const clone = val.clone as THREE.SkinnedMesh;
413
+ const clone = val.clone as SkinnedMesh;
414
414
  // clone.updateWorldMatrix(true, true);
415
415
  if (!originalSkeleton) {
416
416
  console.warn("Skinned mesh has no skeleton?", val);
@@ -426,16 +426,16 @@
426
426
  // clone.bindMatrix.multiplyScalar(.025);
427
427
  // console.assert(originalSkeleton.uuid !== clonedSkeleton.uuid);
428
428
  // console.assert(originalBones.length === clonedSkeleton.bones.length);
429
- const bones: Array<THREE.Bone> = [];
429
+ const bones: Array<Bone> = [];
430
430
  clonedSkeleton.bones = bones;
431
431
  for (let i = 0; i < originalBones.length; i++) {
432
432
  const bone = originalBones[i];
433
433
  const newBoneInfo = newObjectsMap[bone.uuid];
434
- const clonedBone = newBoneInfo.clone as THREE.Bone;
434
+ const clonedBone = newBoneInfo.clone as Bone;
435
435
  // console.log("NEW BONE: ", clonedBone, "BEFORE", newBoneInfo.original);
436
436
  bones.push(clonedBone);
437
437
  }
438
- // clone.skeleton = new THREE.Skeleton(bones);
438
+ // clone.skeleton = new Skeleton(bones);
439
439
  // clone.skeleton.update();
440
440
  // clone.pose();
441
441
  // clone.scale.set(1,1,1);
@@ -443,7 +443,7 @@
443
443
  // console.log("ORIG", original, "CLONE", clone);
444
444
  }
445
445
  for (const key in skinnedMeshes) {
446
- const clone = skinnedMeshes[key].clone as THREE.SkinnedMesh;
446
+ const clone = skinnedMeshes[key].clone as SkinnedMesh;
447
447
  clone.skeleton.update();
448
448
  // clone.skeleton.calculateInverses();
449
449
  clone.bind(clone.skeleton, clone.bindMatrix);
@@ -532,7 +532,7 @@
532
532
 
533
533
  }
534
534
 
535
- function postProcessNewInstance(copy: THREE.Object3D, key: string, value: IComponent | Object3D | any, newObjectsMap: { [key: string]: NewGameObjectReferenceInfo }) {
535
+ function postProcessNewInstance(copy: Object3D, key: string, value: IComponent | Object3D | any, newObjectsMap: { [key: string]: NewGameObjectReferenceInfo }) {
536
536
  if (value === null || value === undefined) return;
537
537
  if ((value as IComponent).isComponent === true) {
538
538
  const originalGameObjectReference = value["gameObject"];
src/engine/engine_gizmos.ts CHANGED
@@ -1,7 +1,6 @@
1
- import * as THREE from 'three';
2
1
  import { BufferAttribute, Line, BoxGeometry, EdgesGeometry, Color, LineSegments, LineBasicMaterial, Object3D, Mesh, SphereGeometry, ColorRepresentation, Vector3, Box3, Quaternion, CylinderGeometry } from 'three';
3
2
  import { Context } from './engine_setup';
4
- import { setWorldPosition, setWorldPositionXYZ } from './engine_three_utils';
3
+ import { setWorldPositionXYZ } from './engine_three_utils';
5
4
  import { Vec3, Vec4 } from './engine_types';
6
5
 
7
6
  const _tmp = new Vector3();
@@ -109,12 +108,12 @@
109
108
  }
110
109
 
111
110
  const box: BoxGeometry = new BoxGeometry(1, 1, 1);
112
- export function CreateWireCube(col: THREE.ColorRepresentation | null = null): THREE.LineSegments {
111
+ export function CreateWireCube(col: ColorRepresentation | null = null): LineSegments {
113
112
  const color = new Color(col ?? 0xdddddd);
114
- // const material = new THREE.MeshBasicMaterial();
115
- // material.color = new THREE.Color(col ?? 0xdddddd);
113
+ // const material = new MeshBasicMaterial();
114
+ // material.color = new Color(col ?? 0xdddddd);
116
115
  // material.wireframe = true;
117
- // const box = new THREE.Mesh(box, material);
116
+ // const box = new Mesh(box, material);
118
117
  // box.name = "BOX_GIZMO";
119
118
  const edges = new EdgesGeometry(box);
120
119
  const line = new LineSegments(edges, new LineBasicMaterial({ color: color }));
src/engine/engine_gltf_builtin_components.ts CHANGED
@@ -1,7 +1,5 @@
1
1
  import "./codegen/register_types";
2
2
  import { TypeStore } from "./engine_typestore";
3
- import * as THREE from "three";
4
- // import { GameObject } from "../engine-components/Component";
5
3
  import { InstantiateIdProvider } from "./engine_networking_instantiate"
6
4
  import { Context } from "./engine_setup";
7
5
  import { deserializeObject, serializeObject } from "./engine_serialization";
@@ -15,6 +13,7 @@
15
13
  import { getParam } from "./engine_utils";
16
14
  import { LogType, showBalloonMessage } from "./debug/debug";
17
15
  import { isLocalNetwork } from "./engine_networking_utils";
16
+ import { Object3D } from "three";
18
17
 
19
18
 
20
19
  const debug = debugExtension;
@@ -43,7 +42,7 @@
43
42
 
44
43
  export async function createBuiltinComponents(context: Context, gltfId: SourceIdentifier, gltf, seed: number | null | UIDProvider = null, extension?: NEEDLE_components) {
45
44
  if (!gltf) return;
46
- const lateResolve: Array<(gltf: THREE.Object3D) => {}> = [];
45
+ const lateResolve: Array<(gltf: Object3D) => {}> = [];
47
46
 
48
47
  let idProvider: UIDProvider | null = seed as UIDProvider;
49
48
  if (typeof idProvider === "number") {
@@ -145,15 +144,15 @@
145
144
  declare class DeserializeData {
146
145
  instance: any;
147
146
  compData: IGltfbuiltinComponent;
148
- obj: THREE.Object3D;
147
+ obj: Object3D;
149
148
  }
150
149
 
151
- declare type LateResolveCallback = (gltf: THREE.Object3D) => void;
150
+ declare type LateResolveCallback = (gltf: Object3D) => void;
152
151
 
153
152
  const unknownComponentsBuffer: Array<string> = [];
154
153
 
155
154
 
156
- async function onCreateBuiltinComponents(context: SerializationContext, obj: THREE.Object3D,
155
+ async function onCreateBuiltinComponents(context: SerializationContext, obj: Object3D,
157
156
  deserialize: DeserializeData[], lateResolve: LateResolveCallback[]) {
158
157
  if (!obj) return;
159
158
 
@@ -324,13 +323,13 @@
324
323
  // function tryResolveType(type, entry): any | undefined {
325
324
  // switch (type) {
326
325
  // case "Vector2":
327
- // return new THREE.Vector2(entry.x, entry.y);
326
+ // return new Vector2(entry.x, entry.y);
328
327
  // case "Vector3":
329
- // return new THREE.Vector3(entry.x, entry.y, entry.z);
328
+ // return new Vector3(entry.x, entry.y, entry.z);
330
329
  // case "Vector4":
331
- // return new THREE.Vector4(entry.x, entry.y, entry.z, entry.w);
330
+ // return new Vector4(entry.x, entry.y, entry.z, entry.w);
332
331
  // case "Quaternion":
333
- // return new THREE.Quaternion(entry.x, entry.y, entry.z, entry.w);
332
+ // return new Quaternion(entry.x, entry.y, entry.z, entry.w);
334
333
  // }
335
334
  // return undefined;
336
335
  // }
src/engine/engine_input.ts CHANGED
@@ -1,4 +1,3 @@
1
- import * as THREE from 'three';
2
1
  import { Vector2 } from 'three';
3
2
  import { showBalloonMessage, showBalloonWarning } from './debug/debug';
4
3
  import { assign } from './engine_serialization_core';
@@ -58,8 +57,8 @@
58
57
  _doubleClickTimeThreshold = .2;
59
58
  _longPressTimeThreshold = 1;
60
59
 
61
- get mousePosition(): THREE.Vector2 { return this._pointerPositions[0]; };
62
- get mousePositionRC(): THREE.Vector2 { return this._pointerPositionsRC[0]; }
60
+ get mousePosition(): Vector2 { return this._pointerPositions[0]; };
61
+ get mousePositionRC(): Vector2 { return this._pointerPositionsRC[0]; }
63
62
  get mouseDown(): boolean { return this._pointerDown[0]; }
64
63
  get mouseUp(): boolean { return this._pointerUp[0]; }
65
64
  get mouseClick(): boolean { return this._pointerClick[0]; }
@@ -91,19 +90,19 @@
91
90
  return count;
92
91
  }
93
92
 
94
- getPointerPosition(i: number): THREE.Vector2 | null {
93
+ getPointerPosition(i: number): Vector2 | null {
95
94
  if (i >= this._pointerPositions.length) return null;
96
95
  return this._pointerPositions[i];
97
96
  }
98
- getPointerPositionLastFrame(i: number): THREE.Vector2 | null {
97
+ getPointerPositionLastFrame(i: number): Vector2 | null {
99
98
  if (i >= this._pointerPositionsLastFrame.length) return null;
100
99
  return this._pointerPositionsLastFrame[i];
101
100
  }
102
- getPointerPositionDelta(i: number): THREE.Vector2 | null {
101
+ getPointerPositionDelta(i: number): Vector2 | null {
103
102
  if (i >= this._pointerPositionsDelta.length) return null;
104
103
  return this._pointerPositionsDelta[i];
105
104
  }
106
- getPointerPositionRC(i: number): THREE.Vector2 | null {
105
+ getPointerPositionRC(i: number): Vector2 | null {
107
106
  if (i >= this._pointerPositionsRC.length) return null;
108
107
  return this._pointerPositionsRC[i];
109
108
  }
@@ -213,11 +212,11 @@
213
212
  private _pointerClick: boolean[] = [false];
214
213
  private _pointerDoubleClick: boolean[] = [false];
215
214
  private _pointerPressed: boolean[] = [false];
216
- private _pointerPositions: THREE.Vector2[] = [new THREE.Vector2()];
217
- private _pointerPositionsLastFrame: THREE.Vector2[] = [new THREE.Vector2()];
218
- private _pointerPositionsDelta: THREE.Vector2[] = [new THREE.Vector2()];
219
- private _pointerPositionsRC: THREE.Vector2[] = [new THREE.Vector2()];
220
- private _pointerPositionDown: THREE.Vector2[] = [new THREE.Vector2()];
215
+ private _pointerPositions: Vector2[] = [new Vector2()];
216
+ private _pointerPositionsLastFrame: Vector2[] = [new Vector2()];
217
+ private _pointerPositionsDelta: Vector2[] = [new Vector2()];
218
+ private _pointerPositionsRC: Vector2[] = [new Vector2()];
219
+ private _pointerPositionDown: Vector2[] = [new Vector2()];
221
220
  private _pointerDownTime: number[] = [];
222
221
  private _pointerUpTime: number[] = [];
223
222
  private _pointerUpTimestamp: number[] = [];
@@ -505,9 +504,9 @@
505
504
  while (evt.button >= this._pointerTypes.length) this._pointerTypes.push(evt.pointerType);
506
505
  this._pointerTypes[evt.button] = evt.pointerType;
507
506
 
508
- while (evt.button >= this._pointerPositionDown.length) this._pointerPositionDown.push(new THREE.Vector2());
507
+ while (evt.button >= this._pointerPositionDown.length) this._pointerPositionDown.push(new Vector2());
509
508
  this._pointerPositionDown[evt.button].set(evt.clientX, evt.clientY);
510
- while (evt.button >= this._pointerPositions.length) this._pointerPositions.push(new THREE.Vector2());
509
+ while (evt.button >= this._pointerPositions.length) this._pointerPositions.push(new Vector2());
511
510
  this._pointerPositions[evt.button].set(evt.clientX, evt.clientY);
512
511
 
513
512
  if (evt.button >= this._pointerDownTime.length) this._pointerDownTime.push(0);
@@ -577,9 +576,9 @@
577
576
 
578
577
  private updatePointerPosition(evt: PointerEventArgs) {
579
578
  // console.log("MOVE");
580
- while (evt.button >= this._pointerPositions.length) this._pointerPositions.push(new THREE.Vector2());
581
- while (evt.button >= this._pointerPositionsLastFrame.length) this._pointerPositionsLastFrame.push(new THREE.Vector2());
582
- while (evt.button >= this._pointerPositionsDelta.length) this._pointerPositionsDelta.push(new THREE.Vector2());
579
+ while (evt.button >= this._pointerPositions.length) this._pointerPositions.push(new Vector2());
580
+ while (evt.button >= this._pointerPositionsLastFrame.length) this._pointerPositionsLastFrame.push(new Vector2());
581
+ while (evt.button >= this._pointerPositionsDelta.length) this._pointerPositionsDelta.push(new Vector2());
583
582
 
584
583
  const lf = this._pointerPositionsLastFrame[evt.button];
585
584
  lf.copy(this._pointerPositions[evt.button]);
@@ -593,7 +592,7 @@
593
592
  // we want to have the position 01 on the canvas for raycasting
594
593
  const px = evt.clientX;
595
594
  const py = evt.clientY;
596
- while (evt.button >= this._pointerPositionsRC.length) this._pointerPositionsRC.push(new THREE.Vector2());
595
+ while (evt.button >= this._pointerPositionsRC.length) this._pointerPositionsRC.push(new Vector2());
597
596
  const rc = this._pointerPositionsRC[evt.button];
598
597
  rc.set(px, py);
599
598
  this.convertScreenspaceToRaycastSpace(rc);
src/engine/engine_license.ts CHANGED
@@ -7,10 +7,10 @@
7
7
 
8
8
  // This is modified by a bundler (e.g. vite)
9
9
  // Do not edit manually
10
- const HAS_LICENSE = false;
10
+ const NEEDLE_ENGINE_COMMERCIAL_USE_LICENSE = false;
11
11
 
12
12
  export function hasProLicense() {
13
- return HAS_LICENSE;
13
+ return NEEDLE_ENGINE_COMMERCIAL_USE_LICENSE;
14
14
  }
15
15
 
16
16
 
src/engine/engine_mainloop_utils.ts CHANGED
@@ -230,7 +230,7 @@
230
230
  }
231
231
  }
232
232
 
233
- function updateIsActiveInHierarchyRecursiveRuntime(go: THREE.Object3D, activeInHierarchy: boolean, allowEventCall: boolean, level: number = 0) {
233
+ function updateIsActiveInHierarchyRecursiveRuntime(go: Object3D, activeInHierarchy: boolean, allowEventCall: boolean, level: number = 0) {
234
234
  if (level > 1000) {
235
235
  console.warn("Hierarchy is too deep (> 1000 level) - will abort updating active state");
236
236
  return false;
@@ -307,7 +307,7 @@
307
307
 
308
308
  // let isRunning = false;
309
309
  // // Prevent: https://github.com/needle-tools/needle-tiny/issues/641
310
- // const temporyChildArrayBuffer: Array<Array<THREE.Object3D>> = [];
310
+ // const temporyChildArrayBuffer: Array<Array<Object3D>> = [];
311
311
  // export function* iterateChildrenSafe(obj: Object3D) {
312
312
  // if (!obj || !obj.children) yield null;
313
313
  // // if(isRunning) return;
@@ -322,9 +322,9 @@
322
322
  // temporyChildArrayBuffer.push(arr);
323
323
  // }
324
324
 
325
- export function updateActiveInHierarchyWithoutEventCall(go: THREE.Object3D) {
325
+ export function updateActiveInHierarchyWithoutEventCall(go: Object3D) {
326
326
  let activeInHierarchy = true;
327
- let current: THREE.Object3D | null = go;
327
+ let current: Object3D | null = go;
328
328
  let foundScene: boolean = false;
329
329
  while (current) {
330
330
  if (!current) break;
@@ -342,7 +342,7 @@
342
342
  go[constants.activeInHierarchyFieldName] = activeInHierarchy && foundScene;
343
343
  }
344
344
 
345
- function perComponent(go: THREE.Object3D, evt: (comp: IComponent) => void) {
345
+ function perComponent(go: Object3D, evt: (comp: IComponent) => void) {
346
346
  if (go.userData?.components) {
347
347
  for (const comp of go.userData.components) {
348
348
  evt(comp);
src/engine/engine_networking_auto.ts CHANGED
@@ -210,7 +210,7 @@
210
210
  * (for example a networked color is sent as a number and may be converted to a color in the receiver again)
211
211
  * Parameters: (newValue, previousValue)
212
212
  */
213
- export const syncField = function(onFieldChanged: string | FieldChangedCallbackFn) {
213
+ export const syncField = function(onFieldChanged?: string | FieldChangedCallbackFn) {
214
214
 
215
215
  return function (target: any, propertyKey: string) {
216
216
 
src/engine/engine_networking_files.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  import { Context } from "../engine/engine_setup";
2
- // import { loadSync, parseSync } from "../engine/engine_scenetools";
3
- import * as THREE from "three";
4
2
  import * as web from "../engine/engine_web_api";
5
3
  import { NetworkConnection } from "../engine/engine_networking";
6
4
  import { generateSeed, InstantiateIdProvider } from "../engine/engine_networking_instantiate";
@@ -11,6 +9,7 @@
11
9
  import { IGameObject } from "./engine_types";
12
10
  import { findByGuid } from "./engine_gameobject";
13
11
  import { ContextEvent, ContextRegistry } from "./engine_context_registry";
12
+ import { BoxGeometry, BoxHelper, Mesh, MeshBasicMaterial, Object3D, Vector3 } from "three";
14
13
 
15
14
  export enum File_Event {
16
15
  File_Spawned = "file-spawned",
@@ -21,15 +20,15 @@
21
20
  file_name: string;
22
21
  file_hash: string;
23
22
  file_size: number;
24
- position: THREE.Vector3 | null;
23
+ position: Vector3 | null;
25
24
  seed: number;
26
25
  sender: string;
27
26
  serverUrl: string;
28
27
  parentGuid?: string;
29
28
 
30
- boundsSize?: THREE.Vector3;
29
+ boundsSize?: Vector3;
31
30
 
32
- constructor(connectionId: string, seed: number, guid: string, name: string, hash: string, size: number, position: THREE.Vector3 | null, serverUrl: string) {
31
+ constructor(connectionId: string, seed: number, guid: string, name: string, hash: string, size: number, position: Vector3 | null, serverUrl: string) {
33
32
  this.seed = seed;
34
33
  this.guid = guid;
35
34
  this.file_name = name;
@@ -129,7 +128,7 @@
129
128
 
130
129
  // add object to proper parent
131
130
  if (evt.parentGuid) {
132
- const parent = findByGuid(evt.parentGuid, context.scene) as THREE.Object3D;
131
+ const parent = findByGuid(evt.parentGuid, context.scene) as Object3D;
133
132
  if ("add" in parent) parent.add(obj);
134
133
  }
135
134
  if (!obj.parent)
@@ -182,16 +181,16 @@
182
181
  }
183
182
 
184
183
 
185
- const previews: { [key: string]: THREE.Object3D } = {};
184
+ const previews: { [key: string]: Object3D } = {};
186
185
 
187
186
  function addPreview(evt: FileSpawnModel, context: Context) {
188
- const sphere = new THREE.BoxGeometry();
189
- const object = new THREE.Mesh(sphere, new THREE.MeshBasicMaterial({ color: 0x00ff00 }));
190
- const box = new THREE.BoxHelper(object, 0x555555);
187
+ const sphere = new BoxGeometry();
188
+ const object = new Mesh(sphere, new MeshBasicMaterial({ color: 0x00ff00 }));
189
+ const box = new BoxHelper(object, 0x555555);
191
190
  previews[evt.guid] = box;
192
191
  context.scene.add(box);
193
192
  if (evt.parentGuid) {
194
- const parent = findByGuid(evt.parentGuid, context.scene) as THREE.Object3D;
193
+ const parent = findByGuid(evt.parentGuid, context.scene) as Object3D;
195
194
  if (parent)
196
195
  parent.add(box);
197
196
  }
src/engine/engine_three_utils.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as THREE from "three";
2
2
  import { Mathf } from "./engine_math"
3
- import { WebGLRenderer, Vector3, Quaternion, Uniform, Texture, Material, ShaderMaterial, CanvasTexture, AnimationAction, Camera, PerspectiveCamera } from "three";
3
+ import { WebGLRenderer, Vector3, Quaternion, Uniform, Texture, Material, ShaderMaterial, CanvasTexture, AnimationAction, Camera, PerspectiveCamera, Object3D, Euler, PlaneGeometry, Scene, Mesh } from "three";
4
4
  import { CircularBuffer } from "./engine_utils";
5
5
 
6
6
 
@@ -12,16 +12,16 @@
12
12
  }
13
13
 
14
14
  const flipYQuat: Quaternion = new Quaternion().setFromAxisAngle(new Vector3(0, 1, 0), Math.PI);
15
- export function lookAtInverse(obj: THREE.Object3D, target: Vector3) {
15
+ export function lookAtInverse(obj: Object3D, target: Vector3) {
16
16
 
17
17
  obj.lookAt(target);
18
18
  obj.quaternion.multiply(flipYQuat);
19
19
  }
20
20
 
21
21
 
22
- const _worldPositions = new CircularBuffer(() => new THREE.Vector3(), 100);
22
+ const _worldPositions = new CircularBuffer(() => new Vector3(), 100);
23
23
 
24
- export function getWorldPosition(obj: THREE.Object3D, vec: THREE.Vector3 | null = null, updateParents: boolean = true): THREE.Vector3 {
24
+ export function getWorldPosition(obj: Object3D, vec: Vector3 | null = null, updateParents: boolean = true): Vector3 {
25
25
  const wp = vec ?? _worldPositions.get();
26
26
  if (!obj) return wp.set(0, 0, 0);
27
27
  if (!obj.parent) return wp.copy(obj.position);
@@ -33,7 +33,7 @@
33
33
  return wp;
34
34
  }
35
35
 
36
- export function setWorldPosition(obj: THREE.Object3D, val: THREE.Vector3) {
36
+ export function setWorldPosition(obj: Object3D, val: Vector3) {
37
37
  if (!obj) return;
38
38
  const wp = _worldPositions.get();
39
39
  if (val !== wp)
@@ -43,25 +43,25 @@
43
43
  obj.position.set(wp.x, wp.y, wp.z);
44
44
  }
45
45
 
46
- export function setWorldPositionXYZ(obj: THREE.Object3D, x: number, y: number, z: number) {
46
+ export function setWorldPositionXYZ(obj: Object3D, x: number, y: number, z: number) {
47
47
  const wp = _worldPositions.get();
48
48
  wp.set(x, y, z);
49
49
  setWorldPosition(obj, wp);
50
50
  }
51
51
 
52
52
 
53
- const _worldQuaternionBuffer: THREE.Quaternion = new THREE.Quaternion();
54
- const _worldQuaternion: THREE.Quaternion = new THREE.Quaternion();
55
- const _tempQuaternionBuffer2: THREE.Quaternion = new THREE.Quaternion();
53
+ const _worldQuaternionBuffer: Quaternion = new Quaternion();
54
+ const _worldQuaternion: Quaternion = new Quaternion();
55
+ const _tempQuaternionBuffer2: Quaternion = new Quaternion();
56
56
 
57
- export function getWorldQuaternion(obj: THREE.Object3D, target: THREE.Quaternion | null = null): THREE.Quaternion {
57
+ export function getWorldQuaternion(obj: Object3D, target: Quaternion | null = null): Quaternion {
58
58
  if (!obj) return _worldQuaternion.set(0, 0, 0, 1);
59
59
  const quat = target ?? _worldQuaternion;
60
60
  if (!obj.parent) return quat.copy(obj.quaternion);
61
61
  obj.getWorldQuaternion(quat);
62
62
  return quat;
63
63
  }
64
- export function setWorldQuaternion(obj: THREE.Object3D, val: THREE.Quaternion) {
64
+ export function setWorldQuaternion(obj: Object3D, val: Quaternion) {
65
65
  if (!obj) return;
66
66
  if (val !== _worldQuaternionBuffer)
67
67
  _worldQuaternionBuffer.copy(val);
@@ -74,22 +74,22 @@
74
74
  obj.quaternion.set(q.x, q.y, q.z, q.w);
75
75
  // console.error("quaternion world to local is not yet implemented");
76
76
  }
77
- export function setWorldQuaternionXYZW(obj: THREE.Object3D, x: number, y: number, z: number, w: number) {
77
+ export function setWorldQuaternionXYZW(obj: Object3D, x: number, y: number, z: number, w: number) {
78
78
  _worldQuaternionBuffer.set(x, y, z, w);
79
79
  setWorldQuaternion(obj, _worldQuaternionBuffer);
80
80
  }
81
81
 
82
- const _worldScale: THREE.Vector3 = new THREE.Vector3();
83
- const _worldScale2: THREE.Vector3 = new THREE.Vector3();
82
+ const _worldScale: Vector3 = new Vector3();
83
+ const _worldScale2: Vector3 = new Vector3();
84
84
 
85
- export function getWorldScale(obj: THREE.Object3D, vec: THREE.Vector3 | null = null): THREE.Vector3 {
85
+ export function getWorldScale(obj: Object3D, vec: Vector3 | null = null): Vector3 {
86
86
  if (!obj) return _worldScale.set(0, 0, 0);
87
87
  if (!obj.parent) return _worldScale.copy(obj.scale);
88
88
  obj.getWorldScale(vec ?? _worldScale);
89
89
  return vec ?? _worldScale;
90
90
  }
91
91
 
92
- export function setWorldScale(obj: THREE.Object3D, vec: THREE.Vector3) {
92
+ export function setWorldScale(obj: Object3D, vec: Vector3) {
93
93
  if (!obj) return;
94
94
  if (!obj.parent) {
95
95
  obj.scale.copy(vec);
@@ -102,35 +102,35 @@
102
102
  obj.scale.copy(tempVec);
103
103
  }
104
104
 
105
- const _forward = new THREE.Vector3();
106
- const _forwardQuat = new THREE.Quaternion();
107
- export function forward(obj: THREE.Object3D): THREE.Vector3 {
105
+ const _forward = new Vector3();
106
+ const _forwardQuat = new Quaternion();
107
+ export function forward(obj: Object3D): Vector3 {
108
108
  getWorldQuaternion(obj, _forwardQuat);
109
109
  return _forward.set(0, 0, 1).applyQuaternion(_forwardQuat);
110
110
  }
111
111
 
112
112
 
113
113
 
114
- const _worldEulerBuffer: THREE.Euler = new THREE.Euler();
115
- const _worldEuler: THREE.Euler = new THREE.Euler();
116
- const _worldRotation: THREE.Vector3 = new THREE.Vector3();
114
+ const _worldEulerBuffer: Euler = new Euler();
115
+ const _worldEuler: Euler = new Euler();
116
+ const _worldRotation: Vector3 = new Vector3();
117
117
 
118
118
 
119
119
 
120
120
  // world euler (in radians)
121
- export function getWorldEuler(obj: THREE.Object3D): THREE.Euler {
121
+ export function getWorldEuler(obj: Object3D): Euler {
122
122
  obj.getWorldQuaternion(_worldQuaternion);
123
123
  _worldEuler.setFromQuaternion(_worldQuaternion);
124
124
  return _worldEuler;
125
125
  }
126
126
 
127
127
  // world euler (in radians)
128
- export function setWorldEuler(obj: THREE.Object3D, val: THREE.Euler) {
128
+ export function setWorldEuler(obj: Object3D, val: Euler) {
129
129
  setWorldQuaternion(obj, _worldQuaternion.setFromEuler(val));;
130
130
  }
131
131
 
132
132
  // returns rotation in degrees
133
- export function getWorldRotation(obj: THREE.Object3D): THREE.Vector3 {
133
+ export function getWorldRotation(obj: Object3D): Vector3 {
134
134
  const rot = getWorldEuler(obj);
135
135
  const wr = _worldRotation;
136
136
  wr.set(rot.x, rot.y, rot.z);
@@ -140,11 +140,11 @@
140
140
  return wr;
141
141
  }
142
142
 
143
- export function setWorldRotation(obj: THREE.Object3D, val: THREE.Vector3) {
143
+ export function setWorldRotation(obj: Object3D, val: Vector3) {
144
144
  setWorldRotationXYZ(obj, val.x, val.y, val.z, true);
145
145
  }
146
146
 
147
- export function setWorldRotationXYZ(obj: THREE.Object3D, x: number, y: number, z: number, degrees: boolean = true) {
147
+ export function setWorldRotationXYZ(obj: Object3D, x: number, y: number, z: number, degrees: boolean = true) {
148
148
  if (degrees) {
149
149
  x = Mathf.toRadians(x);
150
150
  y = Mathf.toRadians(y);
@@ -159,18 +159,18 @@
159
159
 
160
160
 
161
161
 
162
- // from https://github.com/mrdoob/three.js/pull/10995#issuecomment-287614722
163
- export function logHierarchy(root: THREE.Object3D | null | undefined, collapsible: boolean = true) {
162
+ // from https://github.com/mrdoob/js/pull/10995#issuecomment-287614722
163
+ export function logHierarchy(root: Object3D | null | undefined, collapsible: boolean = true) {
164
164
  if (!root) return;
165
165
  if (collapsible) {
166
- (function printGraph(obj: THREE.Object3D) {
166
+ (function printGraph(obj: Object3D) {
167
167
  console.groupCollapsed((obj.name ? obj.name : '(no name : ' + obj.type + ')') + ' %o', obj);
168
168
  obj.children.forEach(printGraph);
169
169
  console.groupEnd();
170
170
  }(root));
171
171
 
172
172
  } else {
173
- root.traverse(function (obj: THREE.Object3D) {
173
+ root.traverse(function (obj: Object3D) {
174
174
  var s = '|___';
175
175
  var obj2 = obj;
176
176
  while (obj2.parent !== null) {
@@ -198,10 +198,10 @@
198
198
 
199
199
 
200
200
  export class Graphics {
201
- private static planeGeometry = new THREE.PlaneGeometry(2, 2, 1, 1);
201
+ private static planeGeometry = new PlaneGeometry(2, 2, 1, 1);
202
202
  private static renderer = new WebGLRenderer({ antialias: false });
203
- private static perspectiveCam = new THREE.PerspectiveCamera();
204
- private static scene = new THREE.Scene();
203
+ private static perspectiveCam = new PerspectiveCamera();
204
+ private static scene = new Scene();
205
205
  private static readonly vertex = `
206
206
  varying vec2 vUv;
207
207
  void main(){
@@ -230,7 +230,7 @@
230
230
  fragmentShader: fragment
231
231
  });
232
232
  }
233
- private static readonly mesh = new THREE.Mesh(this.planeGeometry, this.blipMaterial);
233
+ private static readonly mesh = new Mesh(this.planeGeometry, this.blipMaterial);
234
234
 
235
235
  static copyTexture(texture: Texture, blitMaterial?: ShaderMaterial) {
236
236
  const material = blitMaterial ?? this.blipMaterial;
@@ -292,12 +292,12 @@
292
292
  }
293
293
 
294
294
  /**@obsolete use Graphics.copyTexture */
295
- export function copyTexture(texture: THREE.Texture): THREE.Texture {
295
+ export function copyTexture(texture: Texture): Texture {
296
296
  return Graphics.copyTexture(texture);
297
297
  }
298
298
 
299
299
  /**@obsolete use Graphics.textureToCanvas */
300
- export function textureToCanvas(texture: THREE.Texture, force: boolean = false): HTMLCanvasElement | null {
300
+ export function textureToCanvas(texture: Texture, force: boolean = false): HTMLCanvasElement | null {
301
301
  return Graphics.textureToCanvas(texture, force);
302
302
  }
303
303
 
src/engine/extensions/NEEDLE_components.ts CHANGED
@@ -6,6 +6,7 @@
6
6
  import { resolveReferences } from "./extension_utils";
7
7
  import { apply } from "../../engine-components/js-extensions/Object3D";
8
8
  import { getLoader } from "../engine_gltf";
9
+ import { Object3D } from "three";
9
10
 
10
11
  export const debug = debugExtension
11
12
  const componentsArrayExportKey = "$___Export_Components";
@@ -17,11 +18,11 @@
17
18
  }
18
19
 
19
20
  class ExportData {
20
- node: THREE.Object3D;
21
+ node: Object3D;
21
22
  nodeIndex: number;
22
23
  nodeDef: any;
23
24
 
24
- constructor(node: THREE.Object3D, nodeIndex: number, nodeDef: any) {
25
+ constructor(node: Object3D, nodeIndex: number, nodeDef: any) {
25
26
  this.node = node;
26
27
  this.nodeIndex = nodeIndex;
27
28
  this.nodeDef = nodeDef;
@@ -78,7 +79,7 @@
78
79
  }
79
80
 
80
81
  // https://github.com/mrdoob/three.js/blob/efbfc67edc7f65cfcc61a389ffc5fd43ea702bc6/examples/jsm/exporters/GLTFExporter.js#L532
81
- serializeUserData(node: THREE.Object3D, _nodeDef: any) {
82
+ serializeUserData(node: Object3D, _nodeDef: any) {
82
83
  const components = node.userData?.components;
83
84
  if (!components || components.length <= 0) return;
84
85
  // delete components before serializing user data to avoid circular references
@@ -86,7 +87,7 @@
86
87
  node[componentsArrayExportKey] = components;
87
88
  }
88
89
 
89
- afterSerializeUserData(node: THREE.Object3D, _nodeDef) {
90
+ afterSerializeUserData(node: Object3D, _nodeDef) {
90
91
  if (node.type === "Scene") {
91
92
  if (debug)
92
93
  console.log("DONE", JSON.stringify(_nodeDef));
@@ -102,7 +103,7 @@
102
103
  // console.log(_nodeDef, _nodeDef.mesh);
103
104
  }
104
105
 
105
- writeNode(node: THREE.Object3D, nodeDef) {
106
+ writeNode(node: Object3D, nodeDef) {
106
107
  let nodeIndex = this.writer.json.nodes.length;
107
108
  console.log(node.name, nodeIndex, node.uuid);
108
109
  const context = new ExportData(node, nodeIndex, nodeDef);
@@ -198,7 +199,7 @@
198
199
  await Promise.all(loadComponents);
199
200
  }
200
201
 
201
- private async createComponents(obj: THREE.Object3D, data: ExtensionData) {
202
+ private async createComponents(obj: Object3D, data: ExtensionData) {
202
203
  if (!data) return;
203
204
  const componentData = data[builtinComponentKeyName];
204
205
  if (componentData) {
src/engine/extensions/NEEDLE_lighting_settings.ts CHANGED
@@ -43,11 +43,20 @@
43
43
  if (ext) {
44
44
  if (debug)
45
45
  console.log("Apply \"" + this.name + "\", src: \"" + this.sourceId + "\"", ext);
46
- // add a component to the root of the scene
47
- const lightSettings = new Object3D();
48
- lightSettings.name = "Needle LightSettings";
49
- _result.scene.add(lightSettings);
50
- const settings = GameObject.addNewComponent(lightSettings, SceneLightSettings, false);
46
+ let settings: SceneLightSettings | undefined = undefined;
47
+ // If the result scene has only one child we add the LightingSettingsComponent to that child
48
+ if (_result.scene.children.length === 1) {
49
+ // add a component to the root of the scene
50
+ settings = GameObject.addNewComponent(_result.scene.children[0], SceneLightSettings, false);
51
+ }
52
+ // if the scene already has multiple children we add it as a new object
53
+ else
54
+ {
55
+ const lightSettings = new Object3D();
56
+ lightSettings.name = "Needle LightSettings";
57
+ _result.scene.add(lightSettings);
58
+ settings = GameObject.addNewComponent(lightSettings, SceneLightSettings, false);
59
+ }
51
60
  settings.sourceId = this.sourceId;
52
61
  settings.ambientIntensity = ext.ambientIntensity;
53
62
  settings.ambientLight = new Color().fromArray(ext.ambientLight);
@@ -109,7 +118,7 @@
109
118
  }
110
119
  if (this.ambientMode == AmbientMode.Flat) {
111
120
  if (this.ambientLight && !this._ambientLightObj) {
112
- this._ambientLightObj = new AmbientLight(this.ambientLight, Math.PI * this.ambientIntensity);
121
+ this._ambientLightObj = new AmbientLight(this.ambientLight, this.ambientIntensity);
113
122
  }
114
123
  if (this._ambientLightObj) {
115
124
  this.gameObject.add(this._ambientLightObj)
src/engine/extensions/NEEDLE_techniques_webgl.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { GLTFLoaderPlugin, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader";
2
- import * as THREE from 'three';
3
2
  import { FindShaderTechniques, whiteDefaultTexture, ToUnityMatrixArray, SetUnitySphericalHarmonics } from '../engine_shaders';
4
- import { IUniform, RawShaderMaterial, Vector4 } from 'three';
3
+ import { AlwaysDepth, BackSide, Camera, DoubleSide, EqualDepth, FrontSide, GreaterDepth, GreaterEqualDepth, IUniform, LessDepth, LessEqualDepth, LinearEncoding, Material, Matrix4, NotEqualDepth, Object3D, RawShaderMaterial, Vector3, Vector4 } from 'three';
5
4
  import { Context } from '../engine_setup';
6
5
  import { getParam } from "../engine_utils";
7
6
  import * as SHADERDATA from "../shaders/shaderData"
@@ -37,13 +36,13 @@
37
36
  }
38
37
 
39
38
  class ObjectRendererData {
40
- objectToWorldMatrix: THREE.Matrix4 = new THREE.Matrix4();
41
- worldToObjectMatrix: THREE.Matrix4 = new THREE.Matrix4();
39
+ objectToWorldMatrix: Matrix4 = new Matrix4();
40
+ worldToObjectMatrix: Matrix4 = new Matrix4();
42
41
 
43
42
  objectToWorld: Array<Vector4> = new Array<Vector4>();
44
43
  worldToObject: Array<Vector4> = new Array<Vector4>();
45
44
 
46
- updateFrom(obj: THREE.Object3D) {
45
+ updateFrom(obj: Object3D) {
47
46
  this.objectToWorldMatrix.copy(obj.matrixWorld);
48
47
  ToUnityMatrixArray(this.objectToWorldMatrix, this.objectToWorld);
49
48
 
@@ -132,7 +131,7 @@
132
131
  this.uniforms["unity_SpecCube0"] = { value: envTexture };
133
132
  SetUnitySphericalHarmonics(this.uniforms, array);
134
133
  const hdr = Math.sqrt(Math.PI * .5);
135
- this.uniforms["unity_SpecCube0_HDR"] = { value: new THREE.Vector4(hdr, hdr, hdr, hdr) };
134
+ this.uniforms["unity_SpecCube0_HDR"] = { value: new Vector4(hdr, hdr, hdr, hdr) };
136
135
  // this.needsUpdate = true;
137
136
  // this.uniformsNeedUpdate = true;
138
137
  if (debug) console.log("Set environment lighting", this.uniforms);
@@ -143,20 +142,20 @@
143
142
  private _objToWorldName = "hlslcc_mtx4x4unity_ObjectToWorld";
144
143
  private _worldToObjectName = "hlslcc_mtx4x4unity_WorldToObject";
145
144
 
146
- private static viewProjection: THREE.Matrix4 = new THREE.Matrix4();
145
+ private static viewProjection: Matrix4 = new Matrix4();
147
146
  private static _viewProjectionValues: Array<Vector4> = [];
148
147
  private _viewProjectionName = "hlslcc_mtx4x4unity_MatrixVP";
149
148
 
150
- private static viewMatrix: THREE.Matrix4 = new THREE.Matrix4();
149
+ private static viewMatrix: Matrix4 = new Matrix4();
151
150
  private static _viewMatrixValues: Array<Vector4> = [];
152
151
  private _viewMatrixName = "hlslcc_mtx4x4unity_MatrixV";
153
152
 
154
153
  private static _worldSpaceCameraPosName = "_WorldSpaceCameraPos";
155
- private static _worldSpaceCameraPos: THREE.Vector3 = new THREE.Vector3();
154
+ private static _worldSpaceCameraPos: Vector3 = new Vector3();
156
155
 
157
- private static _mainLightColor: THREE.Vector4 = new THREE.Vector4();
158
- private static _mainLightPosition: THREE.Vector3 = new THREE.Vector3();
159
- private static _lightData: THREE.Vector4 = new THREE.Vector4();
156
+ private static _mainLightColor: Vector4 = new Vector4();
157
+ private static _mainLightPosition: Vector3 = new Vector3();
158
+ private static _lightData: Vector4 = new Vector4();
160
159
 
161
160
  private _rendererData = new ObjectRendererData();
162
161
 
@@ -184,7 +183,7 @@
184
183
  this.onUpdateUniforms(camera, obj);
185
184
  }
186
185
 
187
- onUpdateUniforms(camera?: THREE.Camera, obj?: any) {
186
+ onUpdateUniforms(camera?: Camera, obj?: any) {
188
187
 
189
188
  const context = Context.Current;
190
189
 
@@ -309,7 +308,7 @@
309
308
  this.identifier = identifier;
310
309
  }
311
310
 
312
- loadMaterial(index: number): Promise<THREE.Material> | null {
311
+ loadMaterial(index: number): Promise<Material> | null {
313
312
 
314
313
  const mat = this.parser.json.materials[index];
315
314
  if (!mat) {
@@ -328,7 +327,7 @@
328
327
  const technique: SHADERDATA.Technique = shaders.techniques[techniqueIndex];
329
328
  if (!technique) return null;
330
329
 
331
- return new Promise<THREE.Material>(async (resolve, reject) => {
330
+ return new Promise<Material>(async (resolve, reject) => {
332
331
  const bundle = await FindShaderTechniques(shaders, technique.program!);
333
332
  const frag = bundle?.fragmentShader;
334
333
  const vert = bundle?.vertexShader;
@@ -342,7 +341,7 @@
342
341
  const techniqueUniforms = technique.uniforms;
343
342
 
344
343
  if (vert.includes("_Time"))
345
- uniforms["_Time"] = { value: new THREE.Vector4(0, 0, 0, 0) };
344
+ uniforms["_Time"] = { value: new Vector4(0, 0, 0, 0) };
346
345
 
347
346
  for (const u in techniqueUniforms) {
348
347
  const uniformName = u;
@@ -350,7 +349,7 @@
350
349
  // const typeName = UniformType[uniformValues.type];
351
350
  switch (uniformName) {
352
351
  case "_TimeParameters":
353
- const timeUniform = new THREE.Vector4();
352
+ const timeUniform = new Vector4();
354
353
  uniforms[uniformName] = { value: timeUniform };
355
354
  break;
356
355
 
@@ -422,7 +421,7 @@
422
421
  if (texIndex >= 0) {
423
422
  const tex = await this.parser.getDependency("texture", texIndex);
424
423
  if (tex) {
425
- tex.encoding = THREE.LinearEncoding;
424
+ tex.encoding = LinearEncoding;
426
425
  tex.needsUpdate = true;
427
426
  }
428
427
  uniforms[key] = { value: tex };
@@ -436,7 +435,7 @@
436
435
  }
437
436
  }
438
437
  if (Array.isArray(val) && val.length === 4) {
439
- uniforms[key] = { value: new THREE.Vector4(val[0], val[1], val[2], val[3]) };
438
+ uniforms[key] = { value: new Vector4(val[0], val[1], val[2], val[3]) };
440
439
  continue;
441
440
  }
442
441
  uniforms[key] = { value: val };
@@ -460,16 +459,16 @@
460
459
  const culling = uniforms["_Cull"]?.value;
461
460
  switch (culling) {
462
461
  case CullMode.Off:
463
- material.side = THREE.DoubleSide;
462
+ material.side = DoubleSide;
464
463
  break;
465
464
  case CullMode.Front:
466
- material.side = THREE.BackSide;
465
+ material.side = BackSide;
467
466
  break;
468
467
  case CullMode.Back:
469
- material.side = THREE.FrontSide;
468
+ material.side = FrontSide;
470
469
  break;
471
470
  default:
472
- material.side = THREE.FrontSide;
471
+ material.side = FrontSide;
473
472
  break;
474
473
  }
475
474
 
@@ -477,31 +476,31 @@
477
476
  switch (zTest) {
478
477
  case ZTestMode.Equal:
479
478
  material.depthTest = true;
480
- material.depthFunc = THREE.EqualDepth;
479
+ material.depthFunc = EqualDepth;
481
480
  break;
482
481
  case ZTestMode.NotEqual:
483
482
  material.depthTest = true;
484
- material.depthFunc = THREE.NotEqualDepth;
483
+ material.depthFunc = NotEqualDepth;
485
484
  break;
486
485
  case ZTestMode.Less:
487
486
  material.depthTest = true;
488
- material.depthFunc = THREE.LessDepth;
487
+ material.depthFunc = LessDepth;
489
488
  break;
490
489
  case ZTestMode.LEqual:
491
490
  material.depthTest = true;
492
- material.depthFunc = THREE.LessEqualDepth;
491
+ material.depthFunc = LessEqualDepth;
493
492
  break;
494
493
  case ZTestMode.Greater:
495
494
  material.depthTest = true;
496
- material.depthFunc = THREE.GreaterDepth;
495
+ material.depthFunc = GreaterDepth;
497
496
  break;
498
497
  case ZTestMode.GEqual:
499
498
  material.depthTest = true;
500
- material.depthFunc = THREE.GreaterEqualDepth;
499
+ material.depthFunc = GreaterEqualDepth;
501
500
  break;
502
501
  case ZTestMode.Always:
503
502
  material.depthTest = false;
504
- material.depthFunc = THREE.AlwaysDepth;
503
+ material.depthFunc = AlwaysDepth;
505
504
  break;
506
505
  }
507
506
 
src/needle-engine.ts CHANGED
@@ -6,19 +6,16 @@
6
6
 
7
7
  import "./engine-components/CameraUtils"
8
8
 
9
-
10
- export { GameObject, Behaviour } from "./engine-components/Component";
11
- export { serializable, serializeable } from "./engine/engine_serialization_decorator";
12
- export { Collision } from "./engine/engine_types";
13
9
  export * from "./engine/api";
14
10
  export * from "./engine-components/api";
15
- export * from "./engine-components/codegen/components";
16
- export * from "./engine-components/js-extensions/Object3D";
11
+ export * from "./engine-components-experimental/api";
17
12
 
18
-
19
13
  // make accessible for external javascript
20
14
  import { Context } from "./engine/engine_setup";
21
15
  const Needle = { Context: Context };
16
+ if (globalThis["Needle"] !== undefined) {
17
+ console.warn("Needle Engine is already imported");
18
+ }
22
19
  globalThis["Needle"] = Needle;
23
20
  function registerGlobal(obj: object) {
24
21
  for (const key in obj) {
@@ -47,7 +44,7 @@
47
44
 
48
45
  // make three accessible
49
46
  import * as THREE from "three";
50
- if(!globalThis["THREE"]) {
47
+ if (!globalThis["THREE"]) {
51
48
  globalThis["THREE"] = THREE;
52
49
  }
53
50
  else console.warn("Threejs is already imported");
src/engine-components/ParticleSystemModules.ts CHANGED
@@ -1,1484 +1,1484 @@
1
- import { Color, Matrix4, Object3D, PointLightShadow, Quaternion, Vector3, Vector2, Euler, Vector4, DirectionalLightHelper } from "three";
2
- import { Mathf } from "../engine/engine_math";
3
- import { serializable } from "../engine/engine_serialization";
4
- import { RGBAColor } from "./js-extensions/RGBAColor";
5
- import { AnimationCurve } from "./AnimationCurve";
6
- import { Vec2, Vec3 } from "../engine/engine_types";
7
- import { Context } from "../engine/engine_setup";
8
- import { EmitterShape, FrameOverLife, Particle, ShapeJSON } from "three.quarks";
9
- import { createNoise4D, NoiseFunction4D } from 'simplex-noise';
10
- import { Gizmos } from "../engine/engine_gizmos";
11
- import { getParam } from "../engine/engine_utils";
12
-
13
- const debug = getParam("debugparticles");
14
-
15
- declare type Color4 = { r: number, g: number, b: number, a: number };
16
- declare type ColorKey = { time: number, color: Color4 };
17
- declare type AlphaKey = { time: number, alpha: number };
18
-
19
- export interface IParticleSystem {
20
- get currentParticles(): number;
21
- get maxParticles(): number;
22
- get time(): number;
23
- get deltaTime(): number;
24
- get duration(): number;
25
- readonly main: MainModule;
26
- get container(): Object3D;
27
- get worldspace(): boolean;
28
- get worldPos(): Vector3;
29
- get worldQuaternion(): Quaternion;
30
- get worldQuaternionInverted(): Quaternion;
31
- get worldScale(): Vector3;
32
- get matrixWorld(): Matrix4;
33
- }
34
-
35
-
36
- export enum ParticleSystemRenderMode {
37
- Billboard = 0,
38
- Stretch = 1,
39
- HorizontalBillboard = 2,
40
- VerticalBillboard = 3,
41
- Mesh = 4,
42
- // None = 5,
43
- }
44
-
45
-
46
- export class Gradient {
47
- @serializable()
48
- alphaKeys!: Array<AlphaKey>;
49
- @serializable()
50
- colorKeys!: Array<ColorKey>;
51
-
52
- get duration(): number {
53
- return 1;
54
- }
55
-
56
- evaluate(time: number, target: RGBAColor) {
57
-
58
- // target.r = this.colorKeys[0].color.r;
59
- // target.g = this.colorKeys[0].color.g;
60
- // target.b = this.colorKeys[0].color.b;
61
- // target.alpha = this.alphaKeys[0].alpha;
62
- // return;
63
-
64
- let closestAlpha: AlphaKey | undefined = undefined;
65
- let closestAlphaIndex = 0;
66
- let closestColor: ColorKey | null = null;
67
- let closestColorIndex = 0;
68
- for (let i = 0; i < this.alphaKeys.length; i++) {
69
- const key = this.alphaKeys[i];
70
- if (key.time < time || !closestAlpha) {
71
- closestAlpha = key;
72
- closestAlphaIndex = i;
73
- }
74
- }
75
- for (let i = 0; i < this.colorKeys.length; i++) {
76
- const key = this.colorKeys[i];
77
- if (key.time < time || !closestColor) {
78
- closestColor = key;
79
- closestColorIndex = i;
80
- }
81
- }
82
- if (closestColor) {
83
- const hasNextColor = closestColorIndex + 1 < this.colorKeys.length;
84
- if (hasNextColor) {
85
- const nextColor = this.colorKeys[closestColorIndex + 1];
86
- const t = Mathf.remap(time, closestColor.time, nextColor.time, 0, 1);
87
- target.r = Mathf.lerp(closestColor.color.r, nextColor.color.r, t);
88
- target.g = Mathf.lerp(closestColor.color.g, nextColor.color.g, t);
89
- target.b = Mathf.lerp(closestColor.color.b, nextColor.color.b, t);
90
- }
91
- else {
92
- target.r = closestColor.color.r;
93
- target.g = closestColor.color.g;
94
- target.b = closestColor.color.b;
95
- }
96
- }
97
- if (closestAlpha) {
98
- const hasNextAlpha = closestAlphaIndex + 1 < this.alphaKeys.length;
99
- if (hasNextAlpha) {
100
- const nextAlpha = this.alphaKeys[closestAlphaIndex + 1];
101
- const t = Mathf.remap(time, closestAlpha.time, nextAlpha.time, 0, 1);
102
- target.alpha = Mathf.lerp(closestAlpha.alpha, nextAlpha.alpha, t);
103
- }
104
- else {
105
- target.alpha = closestAlpha.alpha;
106
- }
107
- }
108
- return target;
109
- }
110
- }
111
-
112
- export enum ParticleSystemCurveMode {
113
- Constant = 0,
114
- Curve = 1,
115
- TwoCurves = 2,
116
- TwoConstants = 3
117
- }
118
-
119
- export enum ParticleSystemGradientMode {
120
- Color = 0,
121
- Gradient = 1,
122
- TwoColors = 2,
123
- TwoGradients = 3,
124
- RandomColor = 4,
125
- }
126
-
127
- export enum ParticleSystemSimulationSpace {
128
- Local = 0,
129
- World = 1,
130
- Custom = 2
131
- }
132
-
133
- export enum ParticleSystemShapeType {
134
- Sphere = 0,
135
- SphereShell = 1,
136
- Hemisphere = 2,
137
- HemisphereShell = 3,
138
- Cone = 4,
139
- Box = 5,
140
- Mesh = 6,
141
- ConeShell = 7,
142
- ConeVolume = 8,
143
- ConeVolumeShell = 9,
144
- Circle = 10,
145
- CircleEdge = 11,
146
- SingleSidedEdge = 12,
147
- MeshRenderer = 13,
148
- SkinnedMeshRenderer = 14,
149
- BoxShell = 15,
150
- BoxEdge = 16,
151
- Donut = 17,
152
- Rectangle = 18,
153
- Sprite = 19,
154
- SpriteRenderer = 20
155
- }
156
-
157
- export enum ParticleSystemShapeMultiModeValue {
158
- Random = 0,
159
- Loop = 1,
160
- PingPong = 2,
161
- BurstSpread = 3,
162
- }
163
-
164
- export class MinMaxCurve {
165
- @serializable()
166
- mode!: ParticleSystemCurveMode;
167
- @serializable()
168
- constant!: number;
169
- @serializable()
170
- constantMin!: number;
171
- @serializable()
172
- constantMax!: number;
173
- @serializable(AnimationCurve)
174
- curve?: AnimationCurve;
175
- @serializable(AnimationCurve)
176
- curveMin?: AnimationCurve;
177
- @serializable(AnimationCurve)
178
- curveMax?: AnimationCurve;
179
- @serializable()
180
- curveMultiplier?: number;
181
-
182
- evaluate(t01: number, lerpFactor?: number): number {
183
- const t = lerpFactor === undefined ? Math.random() : lerpFactor;
184
- switch (this.mode) {
185
- case ParticleSystemCurveMode.Constant:
186
- return this.constant;
187
- case ParticleSystemCurveMode.Curve:
188
- t01 = Mathf.clamp01(t01);
189
- return this.curve!.evaluate(t01) * this.curveMultiplier!;
190
- case ParticleSystemCurveMode.TwoCurves:
191
- const t1 = t01 * this.curveMin!.duration;
192
- const t2 = t01 * this.curveMax!.duration;
193
- return Mathf.lerp(this.curveMin!.evaluate(t1), this.curveMax!.evaluate(t2), t % 1) * this.curveMultiplier!;
194
- case ParticleSystemCurveMode.TwoConstants:
195
- return Mathf.lerp(this.constantMin, this.constantMax, t % 1)
196
- default:
197
- this.curveMax!.evaluate(t01) * this.curveMultiplier!;
198
- break;
199
- }
200
- return 0;
201
- }
202
-
203
- getMax(): number {
204
- switch (this.mode) {
205
- case ParticleSystemCurveMode.Constant:
206
- return this.constant;
207
- case ParticleSystemCurveMode.Curve:
208
- return this.getMaxFromCurve(this.curve!) * this.curveMultiplier!;
209
- case ParticleSystemCurveMode.TwoCurves:
210
- return Math.max(this.getMaxFromCurve(this.curveMin), this.getMaxFromCurve(this.curveMax)) * this.curveMultiplier!;
211
- case ParticleSystemCurveMode.TwoConstants:
212
- return Math.max(this.constantMin, this.constantMax);
213
- default:
214
- return 0;
215
- }
216
- }
217
-
218
- private getMaxFromCurve(curve?: AnimationCurve) {
219
- if (!curve) return 0;
220
- let maxNumber = Number.MIN_VALUE;
221
- for (let i = 0; i < curve!.keys.length; i++) {
222
- const key = curve!.keys[i];
223
- if (key.value > maxNumber) {
224
- maxNumber = key.value;
225
- }
226
- }
227
- return maxNumber;
228
- }
229
- }
230
-
231
- export class MinMaxGradient {
232
- mode!: ParticleSystemGradientMode;
233
- @serializable(RGBAColor)
234
- color!: RGBAColor;
235
- @serializable(RGBAColor)
236
- colorMin!: RGBAColor;
237
- @serializable(RGBAColor)
238
- colorMax!: RGBAColor;
239
- @serializable(Gradient)
240
- gradient!: Gradient;
241
- @serializable(Gradient)
242
- gradientMin!: Gradient;
243
- @serializable(Gradient)
244
- gradientMax!: Gradient;
245
-
246
- private static _temp: RGBAColor = new RGBAColor(0, 0, 0, 1);
247
- private static _temp2: RGBAColor = new RGBAColor(0, 0, 0, 1);
248
-
249
- evaluate(t01: number, lerpFactor?: number): RGBAColor {
250
- const t = lerpFactor === undefined ? Math.random() : lerpFactor;
251
- switch (this.mode) {
252
- case ParticleSystemGradientMode.Color:
253
- return this.color;
254
- case ParticleSystemGradientMode.Gradient:
255
- this.gradient.evaluate(t01, MinMaxGradient._temp);
256
- return MinMaxGradient._temp
257
- case ParticleSystemGradientMode.TwoColors:
258
- const col1 = MinMaxGradient._temp.lerpColors(this.colorMin, this.colorMax, t);
259
- return col1;
260
- case ParticleSystemGradientMode.TwoGradients:
261
- this.gradientMin.evaluate(t01, MinMaxGradient._temp);
262
- this.gradientMax.evaluate(t01, MinMaxGradient._temp2);
263
- return MinMaxGradient._temp.lerp(MinMaxGradient._temp2, t);
264
-
265
- }
266
- // console.warn("Not implemented", ParticleSystemGradientMode[this.mode]);
267
- MinMaxGradient._temp.set(0xff00ff)
268
- MinMaxGradient._temp.alpha = 1;
269
- return MinMaxGradient._temp;
270
- }
271
- }
272
-
273
- declare type ParticleSystemScalingMode = {
274
- Hierarchy: number;
275
- Local: number;
276
- Shape: number;
277
- }
278
-
279
- export class MainModule {
280
- cullingMode!: number;
281
- duration!: number;
282
- emitterVelocityMode!: number;
283
- flipRotation!: number;
284
- @serializable(MinMaxCurve)
285
- gravityModifier!: MinMaxCurve;
286
- gravityModifierMultiplier!: number;
287
- loop!: boolean;
288
- maxParticles!: number;
289
- playOnAwake!: boolean;
290
- prewarm!: boolean;
291
- ringBufferLoopRange!: { x: number, y: number };
292
- ringBufferMode!: boolean;
293
- scalingMode!: ParticleSystemScalingMode;
294
- simulationSpace!: ParticleSystemSimulationSpace;
295
- simulationSpeed!: number;
296
- @serializable(MinMaxGradient)
297
- startColor!: MinMaxGradient;
298
- @serializable(MinMaxCurve)
299
- startDelay!: MinMaxCurve;
300
- startDelayMultiplier!: number;
301
- @serializable(MinMaxCurve)
302
- startLifetime!: MinMaxCurve;
303
- startLifetimeMultiplier!: number;
304
- @serializable(MinMaxCurve)
305
- startRotation!: MinMaxCurve;
306
- startRotationMultiplier!: number;
307
- startRotation3D!: boolean;
308
- @serializable(MinMaxCurve)
309
- startRotationX!: MinMaxCurve;
310
- startRotationXMultiplier!: number;
311
- @serializable(MinMaxCurve)
312
- startRotationY!: MinMaxCurve;
313
- startRotationYMultiplier!: number;
314
- @serializable(MinMaxCurve)
315
- startRotationZ!: MinMaxCurve;
316
- startRotationZMultiplier!: number;
317
- @serializable(MinMaxCurve)
318
- startSize!: MinMaxCurve;
319
- startSize3D!: boolean;
320
- startSizeMultiplier!: number;
321
- @serializable(MinMaxCurve)
322
- startSizeX!: MinMaxCurve;
323
- startSizeXMultiplier!: number;
324
- @serializable(MinMaxCurve)
325
- startSizeY!: MinMaxCurve;
326
- startSizeYMultiplier!: number;
327
- @serializable(MinMaxCurve)
328
- startSizeZ!: MinMaxCurve;
329
- startSizeZMultiplier!: number;
330
- @serializable(MinMaxCurve)
331
- startSpeed!: MinMaxCurve;
332
- startSpeedMultiplier!: number;
333
- stopAction!: number;
334
- useUnscaledTime!: boolean;
335
- }
336
-
337
-
338
- export class ParticleBurst {
339
- cycleCount!: number;
340
- maxCount!: number;
341
- minCount!: number;
342
- probability!: number;
343
- repeatInterval!: number;
344
- time!: number;
345
- count!: {
346
- constant: number;
347
- constantMax: number;
348
- constantMin: number;
349
- curve?: AnimationCurve;
350
- curveMax?: AnimationCurve;
351
- curveMin?: AnimationCurve;
352
- curveMultiplier?: number;
353
- mode: ParticleSystemCurveMode;
354
- }
355
-
356
-
357
- private _performed: number = 0;
358
-
359
-
360
- reset() {
361
- this._performed = 0;
362
- }
363
- run(time: number): number {
364
- if (time <= this.time) {
365
- this.reset();
366
- return 0;
367
- }
368
- let amount = 0;
369
- if (this.cycleCount === 0 || this._performed < this.cycleCount) {
370
- const nextTime = this.time + this.repeatInterval * this._performed;
371
- if (time >= nextTime) {
372
- this._performed += 1;
373
- if (Math.random() < this.probability) {
374
- switch (this.count.mode) {
375
- case ParticleSystemCurveMode.Constant:
376
- amount = this.count.constant;
377
- break;
378
- case ParticleSystemCurveMode.TwoConstants:
379
- amount = Mathf.lerp(this.count.constantMin, this.count.constantMax, Math.random());
380
- break;
381
- case ParticleSystemCurveMode.Curve:
382
- amount = this.count.curve!.evaluate(Math.random());
383
- break;
384
- case ParticleSystemCurveMode.TwoCurves:
385
- const t = Math.random();
386
- amount = Mathf.lerp(this.count.curveMin!.evaluate(t), this.count.curveMax!.evaluate(t), Math.random());
387
- break;
388
- }
389
- }
390
- }
391
- }
392
- return amount;
393
- }
394
- }
395
-
396
- export class EmissionModule {
397
-
398
- @serializable()
399
- enabled!: boolean;
400
-
401
-
402
- get burstCount() {
403
- return this.bursts?.length ?? 0;
404
- }
405
-
406
- @serializable()
407
- bursts!: ParticleBurst[];
408
-
409
- @serializable(MinMaxCurve)
410
- rateOverTime!: MinMaxCurve;
411
- @serializable()
412
- rateOverTimeMultiplier!: number;
413
-
414
- @serializable(MinMaxCurve)
415
- rateOverDistance!: MinMaxCurve;
416
- @serializable()
417
- rateOverDistanceMultiplier!: number;
418
-
419
-
420
- /** set from system */
421
- system!: IParticleSystem;
422
-
423
- reset() {
424
- this.bursts?.forEach(b => b.reset());
425
- }
426
-
427
- getBurst() {
428
- let amount = 0;
429
- if (this.burstCount > 0) {
430
- for (let i = 0; i < this.burstCount; i++) {
431
- const burst = this.bursts[i];
432
- if (burst.time >= this.system.time) {
433
- burst.reset();
434
- }
435
- amount += Math.round(burst.run(this.system.time));
436
- }
437
- }
438
- return amount;
439
- }
440
- }
441
-
442
- export class ColorOverLifetimeModule {
443
- enabled!: boolean;
444
- @serializable(MinMaxGradient)
445
- color!: MinMaxGradient;
446
- }
447
-
448
- export class SizeOverLifetimeModule {
449
- enabled!: boolean;
450
- separateAxes!: boolean;
451
- @serializable(MinMaxCurve)
452
- size!: MinMaxCurve;
453
- sizeMultiplier!: number;
454
- @serializable(MinMaxCurve)
455
- x!: MinMaxCurve;
456
- xMultiplier!: number;
457
- @serializable(MinMaxCurve)
458
- y!: MinMaxCurve;
459
- yMultiplier!: number;
460
- @serializable(MinMaxCurve)
461
- z!: MinMaxCurve;
462
- zMultiplier!: number;
463
-
464
- private _time: number = 0;
465
- private _temp = new Vector3();
466
-
467
- evaluate(t01: number, target?: Vec3, lerpFactor?: number) {
468
- if (!target) target = this._temp;
469
-
470
- if (!this.enabled) {
471
- target.x = target.y = target.z = 1;
472
- return target;
473
- }
474
-
475
- if (!this.separateAxes) {
476
- const scale = this.size.evaluate(t01, lerpFactor) * this.sizeMultiplier;
477
- target.x = scale;
478
- // target.y = scale;
479
- // target.z = scale;
480
- }
481
- else {
482
- target.x = this.x.evaluate(t01, lerpFactor) * this.xMultiplier;
483
- target.y = this.y.evaluate(t01, lerpFactor) * this.yMultiplier;
484
- target.z = this.z.evaluate(t01, lerpFactor) * this.zMultiplier;
485
- }
486
- return target;
487
- }
488
- }
489
-
490
- export class ShapeModule implements EmitterShape {
491
-
492
- // Emittershape start
493
- get type(): string {
494
- return ParticleSystemShapeType[this.shapeType];
495
- }
496
- initialize(particle: Particle): void {
497
- this.getPosition();
498
- particle.position.copy(this._vector);
499
- }
500
- toJSON(): ShapeJSON {
501
- return this;
502
- }
503
- clone(): EmitterShape {
504
- return new ShapeModule();
505
- }
506
- // EmitterShape end
507
-
508
- @serializable()
509
- shapeType: ParticleSystemShapeType = ParticleSystemShapeType.Box;
510
- @serializable()
511
- enabled: boolean = true;
512
- @serializable()
513
- alignToDirection: boolean = false;
514
- @serializable()
515
- angle: number = 0;
516
- @serializable()
517
- arc: number = 360;
518
- @serializable()
519
- arcSpread!: number;
520
- @serializable()
521
- arcSpeedMultiplier!: number;
522
- @serializable()
523
- arcMode!: ParticleSystemShapeMultiModeValue;
524
-
525
-
526
- @serializable(Vector3)
527
- boxThickness!: Vector3;
528
- @serializable(Vector3)
529
- position!: Vector3;
530
- @serializable(Vector3)
531
- rotation!: Vector3;
532
- private _rotation: Euler = new Euler();
533
- @serializable(Vector3)
534
- scale!: Vector3;
535
-
536
- @serializable()
537
- radius!: number;
538
- @serializable()
539
- radiusThickness!: number;
540
- @serializable()
541
- sphericalDirectionAmount!: number;
542
- @serializable()
543
- randomDirectionAmount!: number;
544
- @serializable()
545
- randomPositionAmount!: number;
546
-
547
- private system!: IParticleSystem;
548
- private _space?: ParticleSystemSimulationSpace;
549
- private readonly _worldSpaceMatrix: Matrix4 = new Matrix4();
550
- private readonly _worldSpaceMatrixInverse: Matrix4 = new Matrix4();
551
-
552
- constructor() {
553
- if (debug)
554
- console.log(this);
555
- }
556
-
557
- update(system: IParticleSystem, _context: Context, simulationSpace: ParticleSystemSimulationSpace, obj: Object3D) {
558
- this.system = system;
559
- this._space = simulationSpace;
560
- if (simulationSpace === ParticleSystemSimulationSpace.World) {
561
- this._worldSpaceMatrix.copy(obj.matrixWorld);
562
- // set scale to 1
563
- this._worldSpaceMatrix.elements[0] = 1;
564
- this._worldSpaceMatrix.elements[5] = 1;
565
- this._worldSpaceMatrix.elements[10] = 1;
566
- this._worldSpaceMatrixInverse.copy(this._worldSpaceMatrix).invert();
567
- }
568
- }
569
-
570
- private applyRotation(vector: Vector3) {
571
- const isRotated = this.rotation.x !== 0 || this.rotation.y !== 0 || this.rotation.z !== 0;
572
- if (isRotated) {
573
- // console.log(this._rotation);
574
- // TODO: we need to convert this to threejs euler
575
- this._rotation.x = Mathf.toRadians(this.rotation.x);
576
- this._rotation.y = Mathf.toRadians(this.rotation.y);
577
- this._rotation.z = Mathf.toRadians(this.rotation.z);
578
- this._rotation.order = 'ZYX';
579
- vector.applyEuler(this._rotation);
580
- // this._quat.setFromEuler(this._rotation);
581
- // // this._quat.invert();
582
- // this._quat.x *= -1;
583
- // // this._quat.y *= -1;
584
- // // this._quat.z *= -1;
585
- // this._quat.w *= -1;
586
- // vector.applyQuaternion(this._quat);
587
-
588
- }
589
- return isRotated;
590
- }
591
-
592
- /** nebula implementations: */
593
-
594
- /** initializer implementation */
595
- private _vector: Vector3 = new Vector3(0, 0, 0);
596
- private _temp: Vector3 = new Vector3(0, 0, 0);
597
- /** called by nebula on initialize */
598
- get vector() {
599
- return this._vector;
600
- }
601
- getPosition(): void {
602
- this._vector.set(0, 0, 0);
603
- const pos = this._temp.copy(this.position);
604
- const isWorldSpace = this._space === ParticleSystemSimulationSpace.World;
605
- if (isWorldSpace) {
606
- pos.applyQuaternion(this.system.worldQuaternion);
607
- }
608
- let radius = this.radius;
609
- if (isWorldSpace) radius *= this.system.worldScale.x;
610
- if (this.enabled) {
611
- switch (this.shapeType) {
612
- case ParticleSystemShapeType.Box:
613
- if (debug) Gizmos.DrawBox(this.position, this.scale, 0xdddddd, 1);
614
- this._vector.x = Math.random() * this.scale.x - this.scale.x / 2;
615
- this._vector.y = Math.random() * this.scale.y - this.scale.y / 2;
616
- this._vector.z = Math.random() * this.scale.z - this.scale.z / 2;
617
- this._vector.add(pos);
618
- break;
619
- case ParticleSystemShapeType.Cone:
620
- this.randomConePoint(this.position, this.angle, radius, this.radiusThickness, this.arc, this.arcMode, this._vector);
621
- break;
622
- case ParticleSystemShapeType.Sphere:
623
- this.randomSpherePoint(this.position, radius, this.radiusThickness, this.arc, this._vector);
624
- break;
625
- case ParticleSystemShapeType.Circle:
626
- this.randomCirclePoint(this.position, radius, this.radiusThickness, this.arc, this._vector);
627
- break;
628
- default:
629
- this._vector.set(0, 0, 0);
630
- break;
631
- // case ParticleSystemShapeType.Hemisphere:
632
- // randomSpherePoint(this.position.x, this.position.y, this.position.z, this.radius, this.radiusThickness, 180, this._vector);
633
- // break;
634
- }
635
-
636
- this.randomizePosition(this._vector, this.randomPositionAmount);
637
- }
638
-
639
- this.applyRotation(this._vector);
640
-
641
- if (isWorldSpace) {
642
- this._vector.applyQuaternion(this.system.worldQuaternion);
643
- this._vector.add(this.system.worldPos);
644
- }
645
-
646
- if (debug) {
647
- Gizmos.DrawSphere(this._vector, .03, 0xff0000, .5, true);
648
- }
649
- }
650
-
651
-
652
-
653
- private _dir: Vector3 = new Vector3();
654
-
655
- getDirection(pos: Vec3): Vector3 {
656
- if (!this.enabled) {
657
- this._dir.set(0, 0, 1);
658
- return this._dir;
659
- }
660
- switch (this.shapeType) {
661
- case ParticleSystemShapeType.Box:
662
- this._dir.set(0, 0, 1);
663
- break;
664
- case ParticleSystemShapeType.Cone:
665
- this._dir.set(0, 0, 1);
666
- // apply cone angle
667
- // this._dir.applyAxisAngle(new Vector3(0, 1, 0), Mathf.toRadians(this.angle));
668
- break;
669
- case ParticleSystemShapeType.Circle:
670
- case ParticleSystemShapeType.Sphere:
671
- const rx = pos.x;
672
- const ry = pos.y;
673
- const rz = pos.z;
674
- this._dir.set(rx, ry, rz)
675
- if (this.system?.worldspace)
676
- this._dir.sub(this.system.worldPos)
677
- else
678
- this._dir.sub(this.position)
679
- break;
680
- default:
681
- this._dir.set(0, 0, 1);
682
- break;
683
- }
684
- if (this._space === ParticleSystemSimulationSpace.World) {
685
- this._dir.applyQuaternion(this.system.worldQuaternion);
686
- }
687
- this.applyRotation(this._dir);
688
- this._dir.normalize();
689
- this.spherizeDirection(this._dir, this.sphericalDirectionAmount);
690
- this.randomizeDirection(this._dir, this.randomDirectionAmount);
691
- if (debug) {
692
- Gizmos.DrawSphere(pos, .01, 0x883300, .5, true);
693
- Gizmos.DrawDirection(pos, this._dir, 0x883300, .5, true);
694
- }
695
- return this._dir;
696
- }
697
-
698
- private static _randomQuat = new Quaternion();
699
- private static _tempVec = new Vector3();
700
-
701
- private randomizePosition(pos: Vector3, amount: number) {
702
- if (amount <= 0) return;
703
- const rp = ShapeModule._tempVec;
704
- rp.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1);
705
- rp.x *= amount * this.scale.x;
706
- rp.y *= amount * this.scale.y;
707
- rp.z *= amount * this.scale.z;
708
- pos.add(rp);
709
- }
710
-
711
- private randomizeDirection(direction: Vector3, amount: number) {
712
- if (amount === 0) return;
713
- const randomQuat = ShapeModule._randomQuat;
714
- const tempVec = ShapeModule._tempVec;
715
- tempVec.set(Math.random() - .5, Math.random() - .5, Math.random() - .5).normalize();
716
- randomQuat.setFromAxisAngle(tempVec, amount * Math.random() * Math.PI);
717
- direction.applyQuaternion(randomQuat);
718
- }
719
-
720
- private spherizeDirection(dir: Vector3, amount: number) {
721
- if (amount === 0) return;
722
- const theta = Math.random() * Math.PI * 2;
723
- const phi = Math.acos(1 - Math.random() * 2);
724
- const x = Math.sin(phi) * Math.cos(theta);
725
- const y = Math.sin(phi) * Math.sin(theta);
726
- const z = Math.cos(phi);
727
- const v = new Vector3(x, y, z);
728
- dir.lerp(v, amount);
729
- }
730
-
731
- private randomSpherePoint(pos: Vec3, radius: number, thickness: number, arc: number, vec: Vec3) {
732
- const u = Math.random();
733
- const v = Math.random();
734
- const theta = 2 * Math.PI * u * (arc / 360);
735
- const phi = Math.acos(2 * v - 1);
736
- const r = Mathf.lerp(1, 1 - (Math.pow(1 - Math.random(), Math.PI)), thickness) * (radius);
737
- const x = pos.x + this.scale.x * (-r * Math.sin(phi) * Math.cos(theta));
738
- const y = pos.y + this.scale.y * (r * Math.sin(phi) * Math.sin(theta));
739
- const z = pos.z + this.scale.z * (r * Math.cos(phi));
740
- vec.x = x;
741
- vec.y = y;
742
- vec.z = z;
743
- }
744
-
745
- private randomCirclePoint(pos:Vec3, radius:number, thickness:number, arg:number, vec:Vec3){
746
- const u = Math.random();
747
- const theta = 2 * Math.PI * u * (arg / 360);
748
- const r = Mathf.lerp(1, 1 - (Math.pow(1 - Math.random(), Math.PI)), thickness) * (radius);
749
- const x = pos.x + this.scale.x * r * Math.cos(theta);
750
- const y = pos.y + this.scale.y * r * Math.sin(theta);
751
- const z = pos.z;
752
- vec.x = x;
753
- vec.y = y;
754
- vec.z = z;
755
- }
756
-
757
- private _loopTime: number = 0;
758
- private _loopDirection: number = 1;
759
-
760
- private randomConePoint(pos: Vec3, _angle: number, radius: number, thickness: number, arc: number, arcMode: ParticleSystemShapeMultiModeValue, vec: Vec3) {
761
- let u = 0;
762
- let v = 0;
763
- switch (arcMode) {
764
- case ParticleSystemShapeMultiModeValue.Random:
765
- u = Math.random();
766
- v = Math.random();
767
- break;
768
- case ParticleSystemShapeMultiModeValue.PingPong:
769
- if (this._loopTime > 1) this._loopDirection = -1;
770
- if (this._loopTime < 0) this._loopDirection = 1;
771
- // continue with loop
772
-
773
- case ParticleSystemShapeMultiModeValue.Loop:
774
- u = .5;
775
- v = Math.random()
776
- this._loopTime += this.system.deltaTime * this._loopDirection;
777
- break;
778
- }
779
-
780
- let theta = 2 * Math.PI * u * (arc / 360);
781
- switch (arcMode) {
782
- case ParticleSystemShapeMultiModeValue.PingPong:
783
- case ParticleSystemShapeMultiModeValue.Loop:
784
- theta += Math.PI + .5;
785
- theta += this._loopTime * Math.PI * 2;
786
- theta %= Mathf.toRadians(arc);
787
- break;
788
- }
789
-
790
- const phi = Math.acos(2 * v - 1);
791
- const r = Mathf.lerp(1, 1 - (Math.pow(1 - Math.random(), Math.PI)), thickness) * radius;
792
- const x = pos.x + (-r * Math.sin(phi) * Math.cos(theta));
793
- const y = pos.y + (r * Math.sin(phi) * Math.sin(theta));
794
- const z = pos.z;
795
- vec.x = x * this.scale.x;
796
- vec.y = y * this.scale.y;
797
- vec.z = z * this.scale.z;
798
- }
799
- }
800
-
801
-
802
-
803
-
804
-
805
- export class NoiseModule {
806
- @serializable()
807
- damping!: boolean;
808
- @serializable()
809
- enabled!: boolean;
810
- @serializable()
811
- frequency!: number;
812
- @serializable()
813
- octaveCount!: number;
814
- @serializable()
815
- octaveMultiplier!: number;
816
- @serializable()
817
- octaveScale!: number;
818
- @serializable(MinMaxCurve)
819
- positionAmount!: MinMaxCurve;
820
- @serializable()
821
- quality!: number;
822
-
823
- @serializable(MinMaxCurve)
824
- remap!: MinMaxCurve;
825
- @serializable()
826
- remapEnabled!: boolean;
827
- @serializable()
828
- remapMultiplier!: number;
829
- @serializable(MinMaxCurve)
830
- remapX!: MinMaxCurve;
831
- @serializable()
832
- remapXMultiplier!: number;
833
- @serializable(MinMaxCurve)
834
- remapY!: MinMaxCurve;
835
- @serializable()
836
- remapYMultiplier!: number;
837
- @serializable(MinMaxCurve)
838
- remapZ!: MinMaxCurve;
839
- @serializable()
840
- remapZMultiplier!: number;
841
-
842
- @serializable()
843
- scrollSpeedMultiplier!: number;
844
- @serializable()
845
- separateAxes!: boolean;
846
- @serializable()
847
- strengthMultiplier!: number;
848
- @serializable(MinMaxCurve)
849
- strengthX!: MinMaxCurve;
850
- @serializable()
851
- strengthXMultiplier!: number;
852
- @serializable(MinMaxCurve)
853
- strengthY!: MinMaxCurve;
854
- @serializable()
855
- strengthYMultiplier!: number;
856
- @serializable(MinMaxCurve)
857
- strengthZ!: MinMaxCurve;
858
- @serializable()
859
- strengthZMultiplier!: number;
860
-
861
-
862
- private _noise?: NoiseFunction4D;
863
- private _time: number = 0;
864
-
865
- update(context: Context) {
866
- this._time += context.time.deltaTime * this.scrollSpeedMultiplier;
867
- }
868
-
869
- /** nebula implementations: */
870
- private _temp: Vector3 = new Vector3();
871
- apply(_index: number, pos: Vec3, vel: Vec3, _deltaTime: number, age: number, life: number) {
872
- if (!this.enabled) return;
873
- if (!this._noise) {
874
- this._noise = createNoise4D(() => 0);
875
- }
876
- const temp = this._temp.set(pos.x, pos.y, pos.z).multiplyScalar(this.frequency);
877
- const nx = this._noise(temp.x, temp.y, temp.z, this._time);
878
- const ny = this._noise(temp.x, temp.y, temp.z, this._time + 1000 * this.frequency);
879
- const nz = this._noise(temp.x, temp.y, temp.z, this._time + 2000 * this.frequency);
880
- this._temp.set(nx, ny, nz).normalize()
881
-
882
- const t = age / life;
883
- let strengthFactor = this.positionAmount.evaluate(t);
884
- if (!this.separateAxes) {
885
- if (this.strengthX) {
886
- strengthFactor *= this.strengthX.evaluate(t) * 1.5;
887
- }
888
- // strengthFactor *= this.strengthMultiplier;
889
- // strengthFactor *= deltaTime;
890
- this._temp.multiplyScalar(strengthFactor);
891
- }
892
- else {
893
- this._temp.x *= strengthFactor * this.strengthXMultiplier
894
- this._temp.y *= strengthFactor * this.strengthYMultiplier;
895
- this._temp.z *= strengthFactor * this.strengthZMultiplier;
896
- }
897
- // this._temp.setLength(strengthFactor * deltaTime);
898
- vel.x += this._temp.x;
899
- vel.y += this._temp.y;
900
- vel.z += this._temp.z;
901
- }
902
- }
903
-
904
- export enum ParticleSystemTrailMode {
905
- PerParticle,
906
- Ribbon,
907
- }
908
-
909
- export enum ParticleSystemTrailTextureMode {
910
- Stretch = 0,
911
- Tile = 1,
912
- DistributePerSegment = 2,
913
- RepeatPerSegment = 3,
914
- }
915
-
916
- export class TrailModule {
917
-
918
- @serializable()
919
- enabled!: boolean;
920
-
921
- @serializable()
922
- attachRibbonToTransform = false;
923
-
924
- @serializable(MinMaxGradient)
925
- colorOverLifetime!: MinMaxGradient;
926
-
927
- @serializable(MinMaxGradient)
928
- colorOverTrail!: MinMaxGradient;
929
-
930
- @serializable()
931
- dieWithParticles: boolean = true;
932
-
933
- @serializable()
934
- inheritParticleColor: boolean = true;
935
-
936
- @serializable(MinMaxCurve)
937
- lifetime!: MinMaxCurve;
938
- @serializable()
939
- lifetimeMultiplier!: number;
940
-
941
- @serializable()
942
- minVertexDistance: number = .2;
943
-
944
- @serializable()
945
- mode: ParticleSystemTrailMode = ParticleSystemTrailMode.PerParticle;
946
-
947
- @serializable()
948
- ratio: number = 1;
949
-
950
- @serializable()
951
- ribbonCount: number = 1;
952
-
953
- @serializable()
954
- shadowBias: number = 0;
955
-
956
- @serializable()
957
- sizeAffectsLifetime: boolean = false;
958
-
959
- @serializable()
960
- sizeAffectsWidth: boolean = false;
961
-
962
- @serializable()
963
- splitSubEmitterRibbons: boolean = false;
964
-
965
- @serializable()
966
- textureMode: ParticleSystemTrailTextureMode = ParticleSystemTrailTextureMode.Stretch;
967
-
968
- @serializable(MinMaxCurve)
969
- widthOverTrail!: MinMaxCurve;
970
- @serializable()
971
- widthOverTrailMultiplier!: number;
972
-
973
- @serializable()
974
- worldSpace: boolean = false;
975
-
976
- getWidth(size: number, _life01: number, pos01: number) {
977
- let res = this.widthOverTrail.evaluate(pos01);
978
- if (pos01 === 0) res = size;
979
- size *= res;
980
- return size;
981
- }
982
-
983
- getColor(color: Vector4, life01: number, pos01: number) {
984
- const overTrail = this.colorOverTrail.evaluate(pos01);
985
- const overLife = this.colorOverLifetime.evaluate(life01);
986
- color.x *= overTrail.r * overLife.r;
987
- color.y *= overTrail.g * overLife.g;
988
- color.z *= overTrail.b * overLife.b;
989
- color.w *= overTrail.alpha * overLife.alpha;
990
- }
991
- }
992
-
993
- export class VelocityOverLifetimeModule {
994
- @serializable()
995
- enabled!: boolean;
996
-
997
- @serializable()
998
- space: ParticleSystemSimulationSpace = ParticleSystemSimulationSpace.Local;
999
-
1000
- @serializable(MinMaxCurve)
1001
- orbitalX!: MinMaxCurve;
1002
- @serializable(MinMaxCurve)
1003
- orbitalY!: MinMaxCurve;
1004
- @serializable(MinMaxCurve)
1005
- orbitalZ!: MinMaxCurve;
1006
-
1007
- @serializable()
1008
- orbitalXMultiplier!: number;
1009
- @serializable()
1010
- orbitalYMultiplier!: number;
1011
- @serializable()
1012
- orbitalZMultiplier!: number;
1013
-
1014
- @serializable()
1015
- orbitalOffsetX!: number;
1016
- @serializable()
1017
- orbitalOffsetY!: number;
1018
- @serializable()
1019
- orbitalOffsetZ!: number;
1020
-
1021
- @serializable(MinMaxCurve)
1022
- speedModifier!: MinMaxCurve;
1023
- @serializable()
1024
- speedModifierMultiplier!: number;
1025
- @serializable(MinMaxCurve)
1026
- x!: MinMaxCurve;
1027
- @serializable()
1028
- xMultiplier!: number;
1029
- @serializable(MinMaxCurve)
1030
- y!: MinMaxCurve;
1031
- @serializable()
1032
- yMultiplier!: number;
1033
- @serializable(MinMaxCurve)
1034
- z!: MinMaxCurve;
1035
- @serializable()
1036
- zMultiplier!: number;
1037
-
1038
- private _system?: IParticleSystem;
1039
- // private _worldRotation: Quaternion = new Quaternion();
1040
-
1041
- update(system: IParticleSystem) {
1042
- this._system = system;
1043
- }
1044
-
1045
- private _temp: Vector3 = new Vector3();
1046
- private _temp2: Vector3 = new Vector3();
1047
- private _temp3: Vector3 = new Vector3();
1048
- private _hasOrbital = false;
1049
- private _index = 0;
1050
- private _orbitalMatrix: Matrix4 = new Matrix4();
1051
-
1052
- init(particle: object) {
1053
- if (this._index == 0) particle["debug"] = true;
1054
- this._index += 1;
1055
- particle["orbitx"] = this.orbitalX.evaluate(Math.random());
1056
- particle["orbity"] = this.orbitalY.evaluate(Math.random());
1057
- particle["orbitz"] = this.orbitalZ.evaluate(Math.random());
1058
- // console.log(particle["orbitx"], particle["orbity"], particle["orbitz"])
1059
- this._hasOrbital = particle["orbitx"] != 0 || particle["orbity"] != 0 || particle["orbitz"] != 0;
1060
- }
1061
-
1062
- apply(_particle: object, _index: number, _pos: Vec3, vel: Vec3, _dt: number, age: number, life: number) {
1063
- if (!this.enabled) return;
1064
- const t = age / life;
1065
-
1066
- const speed = this.speedModifier.evaluate(t) * this.speedModifierMultiplier;
1067
- const x = this.x.evaluate(t);
1068
- const y = this.y.evaluate(t);
1069
- const z = this.z.evaluate(t);
1070
- this._temp.set(-x, y, z);
1071
- if (this._system) {
1072
- // if (this.space === ParticleSystemSimulationSpace.World) {
1073
- // this._temp.applyQuaternion(this._system.worldQuaternionInverted);
1074
- // }
1075
- // if (this._system.main.simulationSpace === ParticleSystemSimulationSpace.World) {
1076
- // this._temp.applyQuaternion(this._system.worldQuaternion);
1077
- // }
1078
- }
1079
-
1080
- if (this._hasOrbital) {
1081
- const position = this._system?.worldPos;
1082
- if (position) {
1083
-
1084
- // TODO: we absolutely need to fix this, this is a hack for a specific usecase and doesnt work yet correctly
1085
- // https://github.com/needle-tools/needle-tiny/issues/710
1086
-
1087
- const pos = this._temp2.set(_pos.x, _pos.y, _pos.z);
1088
-
1089
- const ox = this.orbitalXMultiplier;// particle["orbitx"];
1090
- const oy = this.orbitalYMultiplier;// particle["orbity"];
1091
- const oz = this.orbitalZMultiplier;// particle["orbitz"];
1092
- const angle = speed * Math.PI * 2 * 10; // < Oh god
1093
-
1094
- const cosX = Math.cos(angle * ox);
1095
- const sinX = Math.sin(angle * ox);
1096
- const cosY = Math.cos(angle * oy);
1097
- const sinY = Math.sin(angle * oy);
1098
- const cosZ = Math.cos(angle * oz);
1099
- const sinZ = Math.sin(angle * oz);
1100
-
1101
- const newX = pos.x * (cosY * cosZ) + pos.y * (cosY * sinZ) + pos.z * (-sinY);
1102
- const newY = pos.x * (sinX * sinY * cosZ - cosX * sinZ) + pos.y * (sinX * sinY * sinZ + cosX * cosZ) + pos.z * (sinX * cosY);
1103
- const newZ = pos.x * (cosX * sinY * cosZ + sinX * sinZ) + pos.y * (cosX * sinY * sinZ - sinX * cosZ) + pos.z * (cosX * cosY);
1104
-
1105
- // pos.x += this.orbitalOffsetX;
1106
- // pos.y += this.orbitalOffsetY;
1107
- // pos.z += this.orbitalOffsetZ;
1108
- const v = this._temp3.set(pos.x - newX, pos.y - newY, pos.z - newZ);
1109
- v.normalize();
1110
- v.multiplyScalar(.2 / _dt * (Math.max(this.orbitalXMultiplier, this.orbitalYMultiplier, this.orbitalZMultiplier)));
1111
- vel.x += v.x;
1112
- vel.y += v.y;
1113
- vel.z += v.z;
1114
- }
1115
- }
1116
-
1117
- vel.x += this._temp.x;
1118
- vel.y += this._temp.y;
1119
- vel.z += this._temp.z;
1120
- vel.x *= speed;
1121
- vel.y *= speed;
1122
- vel.z *= speed;
1123
- }
1124
- }
1125
-
1126
-
1127
-
1128
- enum ParticleSystemAnimationTimeMode {
1129
- Lifetime,
1130
- Speed,
1131
- FPS,
1132
- }
1133
-
1134
- enum ParticleSystemAnimationMode {
1135
- Grid,
1136
- Sprites,
1137
- }
1138
-
1139
- enum ParticleSystemAnimationRowMode {
1140
- Custom,
1141
- Random,
1142
- MeshIndex,
1143
- }
1144
-
1145
- enum ParticleSystemAnimationType {
1146
- WholeSheet,
1147
- SingleRow,
1148
- }
1149
-
1150
- export class TextureSheetAnimationModule {
1151
-
1152
- @serializable()
1153
- animation!: ParticleSystemAnimationType;
1154
-
1155
- @serializable()
1156
- enabled!: boolean;
1157
-
1158
- @serializable()
1159
- cycleCount!: number;
1160
-
1161
- @serializable(MinMaxCurve)
1162
- frameOverTime!: MinMaxCurve;
1163
- @serializable()
1164
- frameOverTimeMultiplier!: number;
1165
-
1166
- @serializable()
1167
- numTilesX!: number;
1168
- @serializable()
1169
- numTilesY!: number;
1170
-
1171
- @serializable(MinMaxCurve)
1172
- startFrame!: MinMaxCurve;
1173
- @serializable()
1174
- startFrameMultiplier!: number;
1175
-
1176
- @serializable()
1177
- rowMode!: ParticleSystemAnimationRowMode;
1178
- @serializable()
1179
- rowIndex!: number;
1180
-
1181
- @serializable()
1182
- spriteCount!: number;
1183
-
1184
- @serializable()
1185
- timeMode!: ParticleSystemAnimationTimeMode;
1186
-
1187
- private sampleOnceAtStart(): boolean {
1188
- if (this.timeMode === ParticleSystemAnimationTimeMode.Lifetime) {
1189
- switch (this.frameOverTime.mode) {
1190
- case ParticleSystemCurveMode.Constant:
1191
- case ParticleSystemCurveMode.TwoConstants:
1192
- case ParticleSystemCurveMode.TwoCurves:
1193
- case ParticleSystemCurveMode.Curve:
1194
- return true;
1195
- }
1196
- }
1197
- return false;
1198
- }
1199
-
1200
- getStartIndex(): number {
1201
- if (this.sampleOnceAtStart()) {
1202
- const start = Math.random();
1203
- return start * (this.numTilesX * this.numTilesY);
1204
- }
1205
- return 0;
1206
- }
1207
-
1208
- evaluate(t01: number): number | undefined {
1209
- if (this.sampleOnceAtStart()) {
1210
- return undefined;
1211
- }
1212
- return this.getIndex(t01);
1213
- }
1214
-
1215
- private getIndex(t01: number): number {
1216
- const tiles = this.numTilesX * this.numTilesY;
1217
- t01 = t01 * this.cycleCount;
1218
- let index = this.frameOverTime.evaluate(t01 % 1);
1219
- index *= this.frameOverTimeMultiplier;
1220
- index *= tiles;
1221
- index = index % tiles;
1222
- index = Math.floor(index);
1223
- return index;
1224
- }
1225
- }
1226
-
1227
-
1228
- export class RotationOverLifetimeModule {
1229
- @serializable()
1230
- enabled!: boolean;
1231
-
1232
- @serializable()
1233
- separateAxes!: boolean;
1234
-
1235
- @serializable(MinMaxCurve)
1236
- x!: MinMaxCurve;
1237
- @serializable()
1238
- xMultiplier!: number;
1239
- @serializable(MinMaxCurve)
1240
- y!: MinMaxCurve;
1241
- @serializable()
1242
- yMultiplier!: number;
1243
- @serializable(MinMaxCurve)
1244
- z!: MinMaxCurve;
1245
- @serializable()
1246
- zMultiplier!: number;
1247
-
1248
- evaluate(t01: number, t: number): number {
1249
- if (!this.enabled) return 0;
1250
- if (!this.separateAxes) {
1251
- const rot = this.z.evaluate(t01, t) * -1;
1252
- return rot;
1253
- }
1254
- return 0;
1255
- }
1256
- }
1257
-
1258
- export class RotationBySpeedModule {
1259
- @serializable()
1260
- enabled!: boolean;
1261
-
1262
- @serializable()
1263
- range!: Vec2;
1264
-
1265
- @serializable()
1266
- separateAxes!: boolean;
1267
-
1268
- @serializable(MinMaxCurve)
1269
- x!: MinMaxCurve;
1270
- @serializable()
1271
- xMultiplier!: number;
1272
- @serializable(MinMaxCurve)
1273
- y!: MinMaxCurve;
1274
- @serializable()
1275
- yMultiplier!: number;
1276
- @serializable(MinMaxCurve)
1277
- z!: MinMaxCurve;
1278
- @serializable()
1279
- zMultiplier!: number;
1280
-
1281
- evaluate(_t01: number, speed: number): number {
1282
- if (!this.enabled) return 0;
1283
- if (!this.separateAxes) {
1284
- const t = Mathf.lerp(this.range.x, this.range.y, speed);
1285
- const rot = this.z.evaluate(t) * -1;
1286
- return rot;
1287
- }
1288
- return 0;
1289
- }
1290
- }
1291
-
1292
-
1293
- export class LimitVelocityOverLifetimeModule {
1294
- @serializable()
1295
- enabled!: boolean;
1296
-
1297
- @serializable()
1298
- dampen!: number;
1299
-
1300
- @serializable(MinMaxCurve)
1301
- drag!: MinMaxCurve;
1302
- @serializable()
1303
- dragMultiplier!: number;
1304
-
1305
- @serializable(MinMaxCurve)
1306
- limit!: MinMaxCurve;
1307
- @serializable()
1308
- limitMultiplier!: number;
1309
-
1310
- @serializable()
1311
- separateAxes!: boolean;
1312
-
1313
- @serializable(MinMaxCurve)
1314
- limitX!: MinMaxCurve;
1315
- @serializable()
1316
- limitXMultiplier!: number;
1317
- @serializable(MinMaxCurve)
1318
- limitY!: MinMaxCurve;
1319
- @serializable()
1320
- limitYMultiplier!: number;
1321
- @serializable(MinMaxCurve)
1322
- limitZ!: MinMaxCurve;
1323
- @serializable()
1324
- limitZMultiplier!: number;
1325
-
1326
- @serializable()
1327
- multiplyDragByParticleSize: boolean = false;
1328
- @serializable()
1329
- multiplyDragByParticleVelocity: boolean = false;
1330
-
1331
- @serializable()
1332
- space!: ParticleSystemSimulationSpace;
1333
-
1334
- private _temp: Vector3 = new Vector3();
1335
- private _temp2: Vector3 = new Vector3();
1336
-
1337
- apply(_position: Vec3, baseVelocity: Vector3, currentVelocity: Vector3, _size: number, t01: number, _dt: number, _scale: number) {
1338
- if (!this.enabled) return;
1339
- // if (this.separateAxes) {
1340
- // // const maxX = this.limitX.evaluate(t01) * this.limitXMultiplier;
1341
- // // const maxY = this.limitY.evaluate(t01) * this.limitYMultiplier;
1342
- // // const maxZ = this.limitZ.evaluate(t01) * this.limitZMultiplier;
1343
-
1344
- // }
1345
- // else
1346
- {
1347
- const max = this.limit.evaluate(t01) * this.limitMultiplier;
1348
- const speed = baseVelocity.length();
1349
- if (speed > max) {
1350
- this._temp.copy(baseVelocity).normalize().multiplyScalar(max);
1351
- let t = this.dampen * .5;
1352
- // t *= scale;
1353
- baseVelocity.x = Mathf.lerp(baseVelocity.x, this._temp.x, t);
1354
- baseVelocity.y = Mathf.lerp(baseVelocity.y, this._temp.y, t);
1355
- baseVelocity.z = Mathf.lerp(baseVelocity.z, this._temp.z, t);
1356
-
1357
- // this._temp2.set(0, 0, 0);
1358
- currentVelocity.x = Mathf.lerp(currentVelocity.x, this._temp.x, t);
1359
- currentVelocity.y = Mathf.lerp(currentVelocity.y, this._temp.y, t);
1360
- currentVelocity.z = Mathf.lerp(currentVelocity.z, this._temp.z, t);
1361
- }
1362
- // vel.multiplyScalar(dragFactor);
1363
- }
1364
- // vel.x *= 0.3;
1365
- // vel.y *= 0.3;
1366
- // vel.z *= 0.3;
1367
- }
1368
- }
1369
-
1370
-
1371
- export enum ParticleSystemInheritVelocityMode {
1372
- Initial,
1373
- Current,
1374
- }
1375
-
1376
- export class InheritVelocityModule {
1377
-
1378
- @serializable()
1379
- enabled!: boolean;
1380
-
1381
- @serializable(MinMaxCurve)
1382
- curve!: MinMaxCurve;
1383
- @serializable()
1384
- curveMultiplier!: number;
1385
-
1386
- @serializable()
1387
- mode!: ParticleSystemInheritVelocityMode;
1388
-
1389
- system!: IParticleSystem;
1390
- private _lastWorldPosition!: Vector3;
1391
- private _velocity: Vector3 = new Vector3();
1392
- private _temp: Vector3 = new Vector3();
1393
-
1394
- update(_context: Context) {
1395
- if (!this.enabled) return;
1396
- if (this.system.worldspace === false) return;
1397
- if (this._lastWorldPosition) {
1398
- this._velocity.copy(this.system.worldPos).sub(this._lastWorldPosition).multiplyScalar(1 / this.system.deltaTime);
1399
- this._lastWorldPosition.copy(this.system.worldPos);
1400
- }
1401
- else {
1402
- this._velocity.set(0, 0, 0);
1403
- this._lastWorldPosition = this.system.worldPos.clone();
1404
- }
1405
- }
1406
-
1407
- // TODO: make work for subsystems
1408
- applyInitial(vel: Vector3) {
1409
- if (!this.enabled) return;
1410
- if (this.system.worldspace === false) return;
1411
- if (this.mode === ParticleSystemInheritVelocityMode.Initial) {
1412
- const factor = this.curve.evaluate(Math.random(), Math.random());
1413
- this._temp.copy(this._velocity).multiplyScalar(factor);
1414
- vel.add(this._temp);
1415
- }
1416
- }
1417
-
1418
- applyCurrent(vel: Vector3, t01: number, lerpFactor: number) {
1419
- if (!this.enabled) return;
1420
- if (this.system.worldspace === false) return;
1421
- if (this.mode === ParticleSystemInheritVelocityMode.Current) {
1422
- const factor = this.curve.evaluate(t01, lerpFactor);
1423
- this._temp.copy(this._velocity).multiplyScalar(factor);
1424
- vel.add(this._temp);
1425
- }
1426
- }
1427
- }
1428
-
1429
-
1430
- export class SizeBySpeedModule {
1431
- @serializable()
1432
- enabled!: boolean;
1433
-
1434
- @serializable(Vector2)
1435
- range!: Vector2;
1436
- @serializable()
1437
- separateAxes!: boolean;
1438
-
1439
- @serializable(MinMaxCurve)
1440
- size!: MinMaxCurve;
1441
- @serializable()
1442
- sizeMultiplier!: number;
1443
-
1444
- @serializable(MinMaxCurve)
1445
- x!: MinMaxCurve;
1446
- @serializable()
1447
- xMultiplier!: number;
1448
- @serializable(MinMaxCurve)
1449
- y!: MinMaxCurve;
1450
- @serializable()
1451
- yMultiplier!: number;
1452
- @serializable(MinMaxCurve)
1453
- z!: MinMaxCurve;
1454
- @serializable()
1455
- zMultiplier!: number;
1456
-
1457
- evaluate(vel: Vector3, _t01: number, lerpFactor: number, size: number): number {
1458
-
1459
- const speed = vel.length();
1460
- const x = Mathf.remap(speed, this.range.x, this.range.y, 0, 1);
1461
- const factor = this.size.evaluate(x, lerpFactor);
1462
- // return size;
1463
- return size * factor;
1464
- }
1465
- }
1466
-
1467
- export class ColorBySpeedModule {
1468
- @serializable()
1469
- enabled!: boolean;
1470
- @serializable(Vector2)
1471
- range!: Vector2;
1472
- @serializable(MinMaxGradient)
1473
- color!: MinMaxGradient;
1474
-
1475
- evaluate(vel: Vector3, lerpFactor: number, color: Vector4) {
1476
- const speed = vel.length();
1477
- const x = Mathf.remap(speed, this.range.x, this.range.y, 0, 1);
1478
- const res = this.color.evaluate(x, lerpFactor);
1479
- color.x *= res.r;
1480
- color.y *= res.g;
1481
- color.z *= res.b;
1482
- color.w *= res.alpha;
1483
- }
1
+ import { Color, Matrix4, Object3D, PointLightShadow, Quaternion, Vector3, Vector2, Euler, Vector4, DirectionalLightHelper } from "three";
2
+ import { Mathf } from "../engine/engine_math";
3
+ import { serializable } from "../engine/engine_serialization";
4
+ import { RGBAColor } from "./js-extensions/RGBAColor";
5
+ import { AnimationCurve } from "./AnimationCurve";
6
+ import { Vec2, Vec3 } from "../engine/engine_types";
7
+ import { Context } from "../engine/engine_setup";
8
+ import { EmitterShape, FrameOverLife, Particle, ShapeJSON } from "three.quarks";
9
+ import { createNoise4D, NoiseFunction4D } from 'simplex-noise';
10
+ import { Gizmos } from "../engine/engine_gizmos";
11
+ import { getParam } from "../engine/engine_utils";
12
+
13
+ const debug = getParam("debugparticles");
14
+
15
+ declare type Color4 = { r: number, g: number, b: number, a: number };
16
+ declare type ColorKey = { time: number, color: Color4 };
17
+ declare type AlphaKey = { time: number, alpha: number };
18
+
19
+ export interface IParticleSystem {
20
+ get currentParticles(): number;
21
+ get maxParticles(): number;
22
+ get time(): number;
23
+ get deltaTime(): number;
24
+ get duration(): number;
25
+ readonly main: MainModule;
26
+ get container(): Object3D;
27
+ get worldspace(): boolean;
28
+ get worldPos(): Vector3;
29
+ get worldQuaternion(): Quaternion;
30
+ get worldQuaternionInverted(): Quaternion;
31
+ get worldScale(): Vector3;
32
+ get matrixWorld(): Matrix4;
33
+ }
34
+
35
+
36
+ export enum ParticleSystemRenderMode {
37
+ Billboard = 0,
38
+ Stretch = 1,
39
+ HorizontalBillboard = 2,
40
+ VerticalBillboard = 3,
41
+ Mesh = 4,
42
+ // None = 5,
43
+ }
44
+
45
+
46
+ export class Gradient {
47
+ @serializable()
48
+ alphaKeys!: Array<AlphaKey>;
49
+ @serializable()
50
+ colorKeys!: Array<ColorKey>;
51
+
52
+ get duration(): number {
53
+ return 1;
54
+ }
55
+
56
+ evaluate(time: number, target: RGBAColor) {
57
+
58
+ // target.r = this.colorKeys[0].color.r;
59
+ // target.g = this.colorKeys[0].color.g;
60
+ // target.b = this.colorKeys[0].color.b;
61
+ // target.alpha = this.alphaKeys[0].alpha;
62
+ // return;
63
+
64
+ let closestAlpha: AlphaKey | undefined = undefined;
65
+ let closestAlphaIndex = 0;
66
+ let closestColor: ColorKey | null = null;
67
+ let closestColorIndex = 0;
68
+ for (let i = 0; i < this.alphaKeys.length; i++) {
69
+ const key = this.alphaKeys[i];
70
+ if (key.time < time || !closestAlpha) {
71
+ closestAlpha = key;
72
+ closestAlphaIndex = i;
73
+ }
74
+ }
75
+ for (let i = 0; i < this.colorKeys.length; i++) {
76
+ const key = this.colorKeys[i];
77
+ if (key.time < time || !closestColor) {
78
+ closestColor = key;
79
+ closestColorIndex = i;
80
+ }
81
+ }
82
+ if (closestColor) {
83
+ const hasNextColor = closestColorIndex + 1 < this.colorKeys.length;
84
+ if (hasNextColor) {
85
+ const nextColor = this.colorKeys[closestColorIndex + 1];
86
+ const t = Mathf.remap(time, closestColor.time, nextColor.time, 0, 1);
87
+ target.r = Mathf.lerp(closestColor.color.r, nextColor.color.r, t);
88
+ target.g = Mathf.lerp(closestColor.color.g, nextColor.color.g, t);
89
+ target.b = Mathf.lerp(closestColor.color.b, nextColor.color.b, t);
90
+ }
91
+ else {
92
+ target.r = closestColor.color.r;
93
+ target.g = closestColor.color.g;
94
+ target.b = closestColor.color.b;
95
+ }
96
+ }
97
+ if (closestAlpha) {
98
+ const hasNextAlpha = closestAlphaIndex + 1 < this.alphaKeys.length;
99
+ if (hasNextAlpha) {
100
+ const nextAlpha = this.alphaKeys[closestAlphaIndex + 1];
101
+ const t = Mathf.remap(time, closestAlpha.time, nextAlpha.time, 0, 1);
102
+ target.alpha = Mathf.lerp(closestAlpha.alpha, nextAlpha.alpha, t);
103
+ }
104
+ else {
105
+ target.alpha = closestAlpha.alpha;
106
+ }
107
+ }
108
+ return target;
109
+ }
110
+ }
111
+
112
+ export enum ParticleSystemCurveMode {
113
+ Constant = 0,
114
+ Curve = 1,
115
+ TwoCurves = 2,
116
+ TwoConstants = 3
117
+ }
118
+
119
+ export enum ParticleSystemGradientMode {
120
+ Color = 0,
121
+ Gradient = 1,
122
+ TwoColors = 2,
123
+ TwoGradients = 3,
124
+ RandomColor = 4,
125
+ }
126
+
127
+ export enum ParticleSystemSimulationSpace {
128
+ Local = 0,
129
+ World = 1,
130
+ Custom = 2
131
+ }
132
+
133
+ export enum ParticleSystemShapeType {
134
+ Sphere = 0,
135
+ SphereShell = 1,
136
+ Hemisphere = 2,
137
+ HemisphereShell = 3,
138
+ Cone = 4,
139
+ Box = 5,
140
+ Mesh = 6,
141
+ ConeShell = 7,
142
+ ConeVolume = 8,
143
+ ConeVolumeShell = 9,
144
+ Circle = 10,
145
+ CircleEdge = 11,
146
+ SingleSidedEdge = 12,
147
+ MeshRenderer = 13,
148
+ SkinnedMeshRenderer = 14,
149
+ BoxShell = 15,
150
+ BoxEdge = 16,
151
+ Donut = 17,
152
+ Rectangle = 18,
153
+ Sprite = 19,
154
+ SpriteRenderer = 20
155
+ }
156
+
157
+ export enum ParticleSystemShapeMultiModeValue {
158
+ Random = 0,
159
+ Loop = 1,
160
+ PingPong = 2,
161
+ BurstSpread = 3,
162
+ }
163
+
164
+ export class MinMaxCurve {
165
+ @serializable()
166
+ mode!: ParticleSystemCurveMode;
167
+ @serializable()
168
+ constant!: number;
169
+ @serializable()
170
+ constantMin!: number;
171
+ @serializable()
172
+ constantMax!: number;
173
+ @serializable(AnimationCurve)
174
+ curve?: AnimationCurve;
175
+ @serializable(AnimationCurve)
176
+ curveMin?: AnimationCurve;
177
+ @serializable(AnimationCurve)
178
+ curveMax?: AnimationCurve;
179
+ @serializable()
180
+ curveMultiplier?: number;
181
+
182
+ evaluate(t01: number, lerpFactor?: number): number {
183
+ const t = lerpFactor === undefined ? Math.random() : lerpFactor;
184
+ switch (this.mode) {
185
+ case ParticleSystemCurveMode.Constant:
186
+ return this.constant;
187
+ case ParticleSystemCurveMode.Curve:
188
+ t01 = Mathf.clamp01(t01);
189
+ return this.curve!.evaluate(t01) * this.curveMultiplier!;
190
+ case ParticleSystemCurveMode.TwoCurves:
191
+ const t1 = t01 * this.curveMin!.duration;
192
+ const t2 = t01 * this.curveMax!.duration;
193
+ return Mathf.lerp(this.curveMin!.evaluate(t1), this.curveMax!.evaluate(t2), t % 1) * this.curveMultiplier!;
194
+ case ParticleSystemCurveMode.TwoConstants:
195
+ return Mathf.lerp(this.constantMin, this.constantMax, t % 1)
196
+ default:
197
+ this.curveMax!.evaluate(t01) * this.curveMultiplier!;
198
+ break;
199
+ }
200
+ return 0;
201
+ }
202
+
203
+ getMax(): number {
204
+ switch (this.mode) {
205
+ case ParticleSystemCurveMode.Constant:
206
+ return this.constant;
207
+ case ParticleSystemCurveMode.Curve:
208
+ return this.getMaxFromCurve(this.curve!) * this.curveMultiplier!;
209
+ case ParticleSystemCurveMode.TwoCurves:
210
+ return Math.max(this.getMaxFromCurve(this.curveMin), this.getMaxFromCurve(this.curveMax)) * this.curveMultiplier!;
211
+ case ParticleSystemCurveMode.TwoConstants:
212
+ return Math.max(this.constantMin, this.constantMax);
213
+ default:
214
+ return 0;
215
+ }
216
+ }
217
+
218
+ private getMaxFromCurve(curve?: AnimationCurve) {
219
+ if (!curve) return 0;
220
+ let maxNumber = Number.MIN_VALUE;
221
+ for (let i = 0; i < curve!.keys.length; i++) {
222
+ const key = curve!.keys[i];
223
+ if (key.value > maxNumber) {
224
+ maxNumber = key.value;
225
+ }
226
+ }
227
+ return maxNumber;
228
+ }
229
+ }
230
+
231
+ export class MinMaxGradient {
232
+ mode!: ParticleSystemGradientMode;
233
+ @serializable(RGBAColor)
234
+ color!: RGBAColor;
235
+ @serializable(RGBAColor)
236
+ colorMin!: RGBAColor;
237
+ @serializable(RGBAColor)
238
+ colorMax!: RGBAColor;
239
+ @serializable(Gradient)
240
+ gradient!: Gradient;
241
+ @serializable(Gradient)
242
+ gradientMin!: Gradient;
243
+ @serializable(Gradient)
244
+ gradientMax!: Gradient;
245
+
246
+ private static _temp: RGBAColor = new RGBAColor(0, 0, 0, 1);
247
+ private static _temp2: RGBAColor = new RGBAColor(0, 0, 0, 1);
248
+
249
+ evaluate(t01: number, lerpFactor?: number): RGBAColor {
250
+ const t = lerpFactor === undefined ? Math.random() : lerpFactor;
251
+ switch (this.mode) {
252
+ case ParticleSystemGradientMode.Color:
253
+ return this.color;
254
+ case ParticleSystemGradientMode.Gradient:
255
+ this.gradient.evaluate(t01, MinMaxGradient._temp);
256
+ return MinMaxGradient._temp
257
+ case ParticleSystemGradientMode.TwoColors:
258
+ const col1 = MinMaxGradient._temp.lerpColors(this.colorMin, this.colorMax, t);
259
+ return col1;
260
+ case ParticleSystemGradientMode.TwoGradients:
261
+ this.gradientMin.evaluate(t01, MinMaxGradient._temp);
262
+ this.gradientMax.evaluate(t01, MinMaxGradient._temp2);
263
+ return MinMaxGradient._temp.lerp(MinMaxGradient._temp2, t);
264
+
265
+ }
266
+ // console.warn("Not implemented", ParticleSystemGradientMode[this.mode]);
267
+ MinMaxGradient._temp.set(0xff00ff)
268
+ MinMaxGradient._temp.alpha = 1;
269
+ return MinMaxGradient._temp;
270
+ }
271
+ }
272
+
273
+ declare type ParticleSystemScalingMode = {
274
+ Hierarchy: number;
275
+ Local: number;
276
+ Shape: number;
277
+ }
278
+
279
+ export class MainModule {
280
+ cullingMode!: number;
281
+ duration!: number;
282
+ emitterVelocityMode!: number;
283
+ flipRotation!: number;
284
+ @serializable(MinMaxCurve)
285
+ gravityModifier!: MinMaxCurve;
286
+ gravityModifierMultiplier!: number;
287
+ loop!: boolean;
288
+ maxParticles!: number;
289
+ playOnAwake!: boolean;
290
+ prewarm!: boolean;
291
+ ringBufferLoopRange!: { x: number, y: number };
292
+ ringBufferMode!: boolean;
293
+ scalingMode!: ParticleSystemScalingMode;
294
+ simulationSpace!: ParticleSystemSimulationSpace;
295
+ simulationSpeed!: number;
296
+ @serializable(MinMaxGradient)
297
+ startColor!: MinMaxGradient;
298
+ @serializable(MinMaxCurve)
299
+ startDelay!: MinMaxCurve;
300
+ startDelayMultiplier!: number;
301
+ @serializable(MinMaxCurve)
302
+ startLifetime!: MinMaxCurve;
303
+ startLifetimeMultiplier!: number;
304
+ @serializable(MinMaxCurve)
305
+ startRotation!: MinMaxCurve;
306
+ startRotationMultiplier!: number;
307
+ startRotation3D!: boolean;
308
+ @serializable(MinMaxCurve)
309
+ startRotationX!: MinMaxCurve;
310
+ startRotationXMultiplier!: number;
311
+ @serializable(MinMaxCurve)
312
+ startRotationY!: MinMaxCurve;
313
+ startRotationYMultiplier!: number;
314
+ @serializable(MinMaxCurve)
315
+ startRotationZ!: MinMaxCurve;
316
+ startRotationZMultiplier!: number;
317
+ @serializable(MinMaxCurve)
318
+ startSize!: MinMaxCurve;
319
+ startSize3D!: boolean;
320
+ startSizeMultiplier!: number;
321
+ @serializable(MinMaxCurve)
322
+ startSizeX!: MinMaxCurve;
323
+ startSizeXMultiplier!: number;
324
+ @serializable(MinMaxCurve)
325
+ startSizeY!: MinMaxCurve;
326
+ startSizeYMultiplier!: number;
327
+ @serializable(MinMaxCurve)
328
+ startSizeZ!: MinMaxCurve;
329
+ startSizeZMultiplier!: number;
330
+ @serializable(MinMaxCurve)
331
+ startSpeed!: MinMaxCurve;
332
+ startSpeedMultiplier!: number;
333
+ stopAction!: number;
334
+ useUnscaledTime!: boolean;
335
+ }
336
+
337
+
338
+ export class ParticleBurst {
339
+ cycleCount!: number;
340
+ maxCount!: number;
341
+ minCount!: number;
342
+ probability!: number;
343
+ repeatInterval!: number;
344
+ time!: number;
345
+ count!: {
346
+ constant: number;
347
+ constantMax: number;
348
+ constantMin: number;
349
+ curve?: AnimationCurve;
350
+ curveMax?: AnimationCurve;
351
+ curveMin?: AnimationCurve;
352
+ curveMultiplier?: number;
353
+ mode: ParticleSystemCurveMode;
354
+ }
355
+
356
+
357
+ private _performed: number = 0;
358
+
359
+
360
+ reset() {
361
+ this._performed = 0;
362
+ }
363
+ run(time: number): number {
364
+ if (time <= this.time) {
365
+ this.reset();
366
+ return 0;
367
+ }
368
+ let amount = 0;
369
+ if (this.cycleCount === 0 || this._performed < this.cycleCount) {
370
+ const nextTime = this.time + this.repeatInterval * this._performed;
371
+ if (time >= nextTime) {
372
+ this._performed += 1;
373
+ if (Math.random() < this.probability) {
374
+ switch (this.count.mode) {
375
+ case ParticleSystemCurveMode.Constant:
376
+ amount = this.count.constant;
377
+ break;
378
+ case ParticleSystemCurveMode.TwoConstants:
379
+ amount = Mathf.lerp(this.count.constantMin, this.count.constantMax, Math.random());
380
+ break;
381
+ case ParticleSystemCurveMode.Curve:
382
+ amount = this.count.curve!.evaluate(Math.random());
383
+ break;
384
+ case ParticleSystemCurveMode.TwoCurves:
385
+ const t = Math.random();
386
+ amount = Mathf.lerp(this.count.curveMin!.evaluate(t), this.count.curveMax!.evaluate(t), Math.random());
387
+ break;
388
+ }
389
+ }
390
+ }
391
+ }
392
+ return amount;
393
+ }
394
+ }
395
+
396
+ export class EmissionModule {
397
+
398
+ @serializable()
399
+ enabled!: boolean;
400
+
401
+
402
+ get burstCount() {
403
+ return this.bursts?.length ?? 0;
404
+ }
405
+
406
+ @serializable()
407
+ bursts!: ParticleBurst[];
408
+
409
+ @serializable(MinMaxCurve)
410
+ rateOverTime!: MinMaxCurve;
411
+ @serializable()
412
+ rateOverTimeMultiplier!: number;
413
+
414
+ @serializable(MinMaxCurve)
415
+ rateOverDistance!: MinMaxCurve;
416
+ @serializable()
417
+ rateOverDistanceMultiplier!: number;
418
+
419
+
420
+ /** set from system */
421
+ system!: IParticleSystem;
422
+
423
+ reset() {
424
+ this.bursts?.forEach(b => b.reset());
425
+ }
426
+
427
+ getBurst() {
428
+ let amount = 0;
429
+ if (this.burstCount > 0) {
430
+ for (let i = 0; i < this.burstCount; i++) {
431
+ const burst = this.bursts[i];
432
+ if (burst.time >= this.system.time) {
433
+ burst.reset();
434
+ }
435
+ amount += Math.round(burst.run(this.system.time));
436
+ }
437
+ }
438
+ return amount;
439
+ }
440
+ }
441
+
442
+ export class ColorOverLifetimeModule {
443
+ enabled!: boolean;
444
+ @serializable(MinMaxGradient)
445
+ color!: MinMaxGradient;
446
+ }
447
+
448
+ export class SizeOverLifetimeModule {
449
+ enabled!: boolean;
450
+ separateAxes!: boolean;
451
+ @serializable(MinMaxCurve)
452
+ size!: MinMaxCurve;
453
+ sizeMultiplier!: number;
454
+ @serializable(MinMaxCurve)
455
+ x!: MinMaxCurve;
456
+ xMultiplier!: number;
457
+ @serializable(MinMaxCurve)
458
+ y!: MinMaxCurve;
459
+ yMultiplier!: number;
460
+ @serializable(MinMaxCurve)
461
+ z!: MinMaxCurve;
462
+ zMultiplier!: number;
463
+
464
+ private _time: number = 0;
465
+ private _temp = new Vector3();
466
+
467
+ evaluate(t01: number, target?: Vec3, lerpFactor?: number) {
468
+ if (!target) target = this._temp;
469
+
470
+ if (!this.enabled) {
471
+ target.x = target.y = target.z = 1;
472
+ return target;
473
+ }
474
+
475
+ if (!this.separateAxes) {
476
+ const scale = this.size.evaluate(t01, lerpFactor) * this.sizeMultiplier;
477
+ target.x = scale;
478
+ // target.y = scale;
479
+ // target.z = scale;
480
+ }
481
+ else {
482
+ target.x = this.x.evaluate(t01, lerpFactor) * this.xMultiplier;
483
+ target.y = this.y.evaluate(t01, lerpFactor) * this.yMultiplier;
484
+ target.z = this.z.evaluate(t01, lerpFactor) * this.zMultiplier;
485
+ }
486
+ return target;
487
+ }
488
+ }
489
+
490
+ export class ShapeModule implements EmitterShape {
491
+
492
+ // Emittershape start
493
+ get type(): string {
494
+ return ParticleSystemShapeType[this.shapeType];
495
+ }
496
+ initialize(particle: Particle): void {
497
+ this.getPosition();
498
+ particle.position.copy(this._vector);
499
+ }
500
+ toJSON(): ShapeJSON {
501
+ return this;
502
+ }
503
+ clone(): EmitterShape {
504
+ return new ShapeModule();
505
+ }
506
+ // EmitterShape end
507
+
508
+ @serializable()
509
+ shapeType: ParticleSystemShapeType = ParticleSystemShapeType.Box;
510
+ @serializable()
511
+ enabled: boolean = true;
512
+ @serializable()
513
+ alignToDirection: boolean = false;
514
+ @serializable()
515
+ angle: number = 0;
516
+ @serializable()
517
+ arc: number = 360;
518
+ @serializable()
519
+ arcSpread!: number;
520
+ @serializable()
521
+ arcSpeedMultiplier!: number;
522
+ @serializable()
523
+ arcMode!: ParticleSystemShapeMultiModeValue;
524
+
525
+
526
+ @serializable(Vector3)
527
+ boxThickness!: Vector3;
528
+ @serializable(Vector3)
529
+ position!: Vector3;
530
+ @serializable(Vector3)
531
+ rotation!: Vector3;
532
+ private _rotation: Euler = new Euler();
533
+ @serializable(Vector3)
534
+ scale!: Vector3;
535
+
536
+ @serializable()
537
+ radius!: number;
538
+ @serializable()
539
+ radiusThickness!: number;
540
+ @serializable()
541
+ sphericalDirectionAmount!: number;
542
+ @serializable()
543
+ randomDirectionAmount!: number;
544
+ @serializable()
545
+ randomPositionAmount!: number;
546
+
547
+ private system!: IParticleSystem;
548
+ private _space?: ParticleSystemSimulationSpace;
549
+ private readonly _worldSpaceMatrix: Matrix4 = new Matrix4();
550
+ private readonly _worldSpaceMatrixInverse: Matrix4 = new Matrix4();
551
+
552
+ constructor() {
553
+ if (debug)
554
+ console.log(this);
555
+ }
556
+
557
+ update(system: IParticleSystem, _context: Context, simulationSpace: ParticleSystemSimulationSpace, obj: Object3D) {
558
+ this.system = system;
559
+ this._space = simulationSpace;
560
+ if (simulationSpace === ParticleSystemSimulationSpace.World) {
561
+ this._worldSpaceMatrix.copy(obj.matrixWorld);
562
+ // set scale to 1
563
+ this._worldSpaceMatrix.elements[0] = 1;
564
+ this._worldSpaceMatrix.elements[5] = 1;
565
+ this._worldSpaceMatrix.elements[10] = 1;
566
+ this._worldSpaceMatrixInverse.copy(this._worldSpaceMatrix).invert();
567
+ }
568
+ }
569
+
570
+ private applyRotation(vector: Vector3) {
571
+ const isRotated = this.rotation.x !== 0 || this.rotation.y !== 0 || this.rotation.z !== 0;
572
+ if (isRotated) {
573
+ // console.log(this._rotation);
574
+ // TODO: we need to convert this to threejs euler
575
+ this._rotation.x = Mathf.toRadians(this.rotation.x);
576
+ this._rotation.y = Mathf.toRadians(this.rotation.y);
577
+ this._rotation.z = Mathf.toRadians(this.rotation.z);
578
+ this._rotation.order = 'ZYX';
579
+ vector.applyEuler(this._rotation);
580
+ // this._quat.setFromEuler(this._rotation);
581
+ // // this._quat.invert();
582
+ // this._quat.x *= -1;
583
+ // // this._quat.y *= -1;
584
+ // // this._quat.z *= -1;
585
+ // this._quat.w *= -1;
586
+ // vector.applyQuaternion(this._quat);
587
+
588
+ }
589
+ return isRotated;
590
+ }
591
+
592
+ /** nebula implementations: */
593
+
594
+ /** initializer implementation */
595
+ private _vector: Vector3 = new Vector3(0, 0, 0);
596
+ private _temp: Vector3 = new Vector3(0, 0, 0);
597
+ /** called by nebula on initialize */
598
+ get vector() {
599
+ return this._vector;
600
+ }
601
+ getPosition(): void {
602
+ this._vector.set(0, 0, 0);
603
+ const pos = this._temp.copy(this.position);
604
+ const isWorldSpace = this._space === ParticleSystemSimulationSpace.World;
605
+ if (isWorldSpace) {
606
+ pos.applyQuaternion(this.system.worldQuaternion);
607
+ }
608
+ let radius = this.radius;
609
+ if (isWorldSpace) radius *= this.system.worldScale.x;
610
+ if (this.enabled) {
611
+ switch (this.shapeType) {
612
+ case ParticleSystemShapeType.Box:
613
+ if (debug) Gizmos.DrawBox(this.position, this.scale, 0xdddddd, 1);
614
+ this._vector.x = Math.random() * this.scale.x - this.scale.x / 2;
615
+ this._vector.y = Math.random() * this.scale.y - this.scale.y / 2;
616
+ this._vector.z = Math.random() * this.scale.z - this.scale.z / 2;
617
+ this._vector.add(pos);
618
+ break;
619
+ case ParticleSystemShapeType.Cone:
620
+ this.randomConePoint(this.position, this.angle, radius, this.radiusThickness, this.arc, this.arcMode, this._vector);
621
+ break;
622
+ case ParticleSystemShapeType.Sphere:
623
+ this.randomSpherePoint(this.position, radius, this.radiusThickness, this.arc, this._vector);
624
+ break;
625
+ case ParticleSystemShapeType.Circle:
626
+ this.randomCirclePoint(this.position, radius, this.radiusThickness, this.arc, this._vector);
627
+ break;
628
+ default:
629
+ this._vector.set(0, 0, 0);
630
+ break;
631
+ // case ParticleSystemShapeType.Hemisphere:
632
+ // randomSpherePoint(this.position.x, this.position.y, this.position.z, this.radius, this.radiusThickness, 180, this._vector);
633
+ // break;
634
+ }
635
+
636
+ this.randomizePosition(this._vector, this.randomPositionAmount);
637
+ }
638
+
639
+ this.applyRotation(this._vector);
640
+
641
+ if (isWorldSpace) {
642
+ this._vector.applyQuaternion(this.system.worldQuaternion);
643
+ this._vector.add(this.system.worldPos);
644
+ }
645
+
646
+ if (debug) {
647
+ Gizmos.DrawSphere(this._vector, .03, 0xff0000, .5, true);
648
+ }
649
+ }
650
+
651
+
652
+
653
+ private _dir: Vector3 = new Vector3();
654
+
655
+ getDirection(pos: Vec3): Vector3 {
656
+ if (!this.enabled) {
657
+ this._dir.set(0, 0, 1);
658
+ return this._dir;
659
+ }
660
+ switch (this.shapeType) {
661
+ case ParticleSystemShapeType.Box:
662
+ this._dir.set(0, 0, 1);
663
+ break;
664
+ case ParticleSystemShapeType.Cone:
665
+ this._dir.set(0, 0, 1);
666
+ // apply cone angle
667
+ // this._dir.applyAxisAngle(new Vector3(0, 1, 0), Mathf.toRadians(this.angle));
668
+ break;
669
+ case ParticleSystemShapeType.Circle:
670
+ case ParticleSystemShapeType.Sphere:
671
+ const rx = pos.x;
672
+ const ry = pos.y;
673
+ const rz = pos.z;
674
+ this._dir.set(rx, ry, rz)
675
+ if (this.system?.worldspace)
676
+ this._dir.sub(this.system.worldPos)
677
+ else
678
+ this._dir.sub(this.position)
679
+ break;
680
+ default:
681
+ this._dir.set(0, 0, 1);
682
+ break;
683
+ }
684
+ if (this._space === ParticleSystemSimulationSpace.World) {
685
+ this._dir.applyQuaternion(this.system.worldQuaternion);
686
+ }
687
+ this.applyRotation(this._dir);
688
+ this._dir.normalize();
689
+ this.spherizeDirection(this._dir, this.sphericalDirectionAmount);
690
+ this.randomizeDirection(this._dir, this.randomDirectionAmount);
691
+ if (debug) {
692
+ Gizmos.DrawSphere(pos, .01, 0x883300, .5, true);
693
+ Gizmos.DrawDirection(pos, this._dir, 0x883300, .5, true);
694
+ }
695
+ return this._dir;
696
+ }
697
+
698
+ private static _randomQuat = new Quaternion();
699
+ private static _tempVec = new Vector3();
700
+
701
+ private randomizePosition(pos: Vector3, amount: number) {
702
+ if (amount <= 0) return;
703
+ const rp = ShapeModule._tempVec;
704
+ rp.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1);
705
+ rp.x *= amount * this.scale.x;
706
+ rp.y *= amount * this.scale.y;
707
+ rp.z *= amount * this.scale.z;
708
+ pos.add(rp);
709
+ }
710
+
711
+ private randomizeDirection(direction: Vector3, amount: number) {
712
+ if (amount === 0) return;
713
+ const randomQuat = ShapeModule._randomQuat;
714
+ const tempVec = ShapeModule._tempVec;
715
+ tempVec.set(Math.random() - .5, Math.random() - .5, Math.random() - .5).normalize();
716
+ randomQuat.setFromAxisAngle(tempVec, amount * Math.random() * Math.PI);
717
+ direction.applyQuaternion(randomQuat);
718
+ }
719
+
720
+ private spherizeDirection(dir: Vector3, amount: number) {
721
+ if (amount === 0) return;
722
+ const theta = Math.random() * Math.PI * 2;
723
+ const phi = Math.acos(1 - Math.random() * 2);
724
+ const x = Math.sin(phi) * Math.cos(theta);
725
+ const y = Math.sin(phi) * Math.sin(theta);
726
+ const z = Math.cos(phi);
727
+ const v = new Vector3(x, y, z);
728
+ dir.lerp(v, amount);
729
+ }
730
+
731
+ private randomSpherePoint(pos: Vec3, radius: number, thickness: number, arc: number, vec: Vec3) {
732
+ const u = Math.random();
733
+ const v = Math.random();
734
+ const theta = 2 * Math.PI * u * (arc / 360);
735
+ const phi = Math.acos(2 * v - 1);
736
+ const r = Mathf.lerp(1, 1 - (Math.pow(1 - Math.random(), Math.PI)), thickness) * (radius);
737
+ const x = pos.x + this.scale.x * (-r * Math.sin(phi) * Math.cos(theta));
738
+ const y = pos.y + this.scale.y * (r * Math.sin(phi) * Math.sin(theta));
739
+ const z = pos.z + this.scale.z * (r * Math.cos(phi));
740
+ vec.x = x;
741
+ vec.y = y;
742
+ vec.z = z;
743
+ }
744
+
745
+ private randomCirclePoint(pos:Vec3, radius:number, thickness:number, arg:number, vec:Vec3){
746
+ const u = Math.random();
747
+ const theta = 2 * Math.PI * u * (arg / 360);
748
+ const r = Mathf.lerp(1, 1 - (Math.pow(1 - Math.random(), Math.PI)), thickness) * (radius);
749
+ const x = pos.x + this.scale.x * r * Math.cos(theta);
750
+ const y = pos.y + this.scale.y * r * Math.sin(theta);
751
+ const z = pos.z;
752
+ vec.x = x;
753
+ vec.y = y;
754
+ vec.z = z;
755
+ }
756
+
757
+ private _loopTime: number = 0;
758
+ private _loopDirection: number = 1;
759
+
760
+ private randomConePoint(pos: Vec3, _angle: number, radius: number, thickness: number, arc: number, arcMode: ParticleSystemShapeMultiModeValue, vec: Vec3) {
761
+ let u = 0;
762
+ let v = 0;
763
+ switch (arcMode) {
764
+ case ParticleSystemShapeMultiModeValue.Random:
765
+ u = Math.random();
766
+ v = Math.random();
767
+ break;
768
+ case ParticleSystemShapeMultiModeValue.PingPong:
769
+ if (this._loopTime > 1) this._loopDirection = -1;
770
+ if (this._loopTime < 0) this._loopDirection = 1;
771
+ // continue with loop
772
+
773
+ case ParticleSystemShapeMultiModeValue.Loop:
774
+ u = .5;
775
+ v = Math.random()
776
+ this._loopTime += this.system.deltaTime * this._loopDirection;
777
+ break;
778
+ }
779
+
780
+ let theta = 2 * Math.PI * u * (arc / 360);
781
+ switch (arcMode) {
782
+ case ParticleSystemShapeMultiModeValue.PingPong:
783
+ case ParticleSystemShapeMultiModeValue.Loop:
784
+ theta += Math.PI + .5;
785
+ theta += this._loopTime * Math.PI * 2;
786
+ theta %= Mathf.toRadians(arc);
787
+ break;
788
+ }
789
+
790
+ const phi = Math.acos(2 * v - 1);
791
+ const r = Mathf.lerp(1, 1 - (Math.pow(1 - Math.random(), Math.PI)), thickness) * radius;
792
+ const x = pos.x + (-r * Math.sin(phi) * Math.cos(theta));
793
+ const y = pos.y + (r * Math.sin(phi) * Math.sin(theta));
794
+ const z = pos.z;
795
+ vec.x = x * this.scale.x;
796
+ vec.y = y * this.scale.y;
797
+ vec.z = z * this.scale.z;
798
+ }
799
+ }
800
+
801
+
802
+
803
+
804
+
805
+ export class NoiseModule {
806
+ @serializable()
807
+ damping!: boolean;
808
+ @serializable()
809
+ enabled!: boolean;
810
+ @serializable()
811
+ frequency!: number;
812
+ @serializable()
813
+ octaveCount!: number;
814
+ @serializable()
815
+ octaveMultiplier!: number;
816
+ @serializable()
817
+ octaveScale!: number;
818
+ @serializable(MinMaxCurve)
819
+ positionAmount!: MinMaxCurve;
820
+ @serializable()
821
+ quality!: number;
822
+
823
+ @serializable(MinMaxCurve)
824
+ remap!: MinMaxCurve;
825
+ @serializable()
826
+ remapEnabled!: boolean;
827
+ @serializable()
828
+ remapMultiplier!: number;
829
+ @serializable(MinMaxCurve)
830
+ remapX!: MinMaxCurve;
831
+ @serializable()
832
+ remapXMultiplier!: number;
833
+ @serializable(MinMaxCurve)
834
+ remapY!: MinMaxCurve;
835
+ @serializable()
836
+ remapYMultiplier!: number;
837
+ @serializable(MinMaxCurve)
838
+ remapZ!: MinMaxCurve;
839
+ @serializable()
840
+ remapZMultiplier!: number;
841
+
842
+ @serializable()
843
+ scrollSpeedMultiplier!: number;
844
+ @serializable()
845
+ separateAxes!: boolean;
846
+ @serializable()
847
+ strengthMultiplier!: number;
848
+ @serializable(MinMaxCurve)
849
+ strengthX!: MinMaxCurve;
850
+ @serializable()
851
+ strengthXMultiplier!: number;
852
+ @serializable(MinMaxCurve)
853
+ strengthY!: MinMaxCurve;
854
+ @serializable()
855
+ strengthYMultiplier!: number;
856
+ @serializable(MinMaxCurve)
857
+ strengthZ!: MinMaxCurve;
858
+ @serializable()
859
+ strengthZMultiplier!: number;
860
+
861
+
862
+ private _noise?: NoiseFunction4D;
863
+ private _time: number = 0;
864
+
865
+ update(context: Context) {
866
+ this._time += context.time.deltaTime * this.scrollSpeedMultiplier;
867
+ }
868
+
869
+ /** nebula implementations: */
870
+ private _temp: Vector3 = new Vector3();
871
+ apply(_index: number, pos: Vec3, vel: Vec3, _deltaTime: number, age: number, life: number) {
872
+ if (!this.enabled) return;
873
+ if (!this._noise) {
874
+ this._noise = createNoise4D(() => 0);
875
+ }
876
+ const temp = this._temp.set(pos.x, pos.y, pos.z).multiplyScalar(this.frequency);
877
+ const nx = this._noise(temp.x, temp.y, temp.z, this._time);
878
+ const ny = this._noise(temp.x, temp.y, temp.z, this._time + 1000 * this.frequency);
879
+ const nz = this._noise(temp.x, temp.y, temp.z, this._time + 2000 * this.frequency);
880
+ this._temp.set(nx, ny, nz).normalize()
881
+
882
+ const t = age / life;
883
+ let strengthFactor = this.positionAmount.evaluate(t);
884
+ if (!this.separateAxes) {
885
+ if (this.strengthX) {
886
+ strengthFactor *= this.strengthX.evaluate(t) * 1.5;
887
+ }
888
+ // strengthFactor *= this.strengthMultiplier;
889
+ // strengthFactor *= deltaTime;
890
+ this._temp.multiplyScalar(strengthFactor);
891
+ }
892
+ else {
893
+ this._temp.x *= strengthFactor * this.strengthXMultiplier
894
+ this._temp.y *= strengthFactor * this.strengthYMultiplier;
895
+ this._temp.z *= strengthFactor * this.strengthZMultiplier;
896
+ }
897
+ // this._temp.setLength(strengthFactor * deltaTime);
898
+ vel.x += this._temp.x;
899
+ vel.y += this._temp.y;
900
+ vel.z += this._temp.z;
901
+ }
902
+ }
903
+
904
+ export enum ParticleSystemTrailMode {
905
+ PerParticle,
906
+ Ribbon,
907
+ }
908
+
909
+ export enum ParticleSystemTrailTextureMode {
910
+ Stretch = 0,
911
+ Tile = 1,
912
+ DistributePerSegment = 2,
913
+ RepeatPerSegment = 3,
914
+ }
915
+
916
+ export class TrailModule {
917
+
918
+ @serializable()
919
+ enabled!: boolean;
920
+
921
+ @serializable()
922
+ attachRibbonToTransform = false;
923
+
924
+ @serializable(MinMaxGradient)
925
+ colorOverLifetime!: MinMaxGradient;
926
+
927
+ @serializable(MinMaxGradient)
928
+ colorOverTrail!: MinMaxGradient;
929
+
930
+ @serializable()
931
+ dieWithParticles: boolean = true;
932
+
933
+ @serializable()
934
+ inheritParticleColor: boolean = true;
935
+
936
+ @serializable(MinMaxCurve)
937
+ lifetime!: MinMaxCurve;
938
+ @serializable()
939
+ lifetimeMultiplier!: number;
940
+
941
+ @serializable()
942
+ minVertexDistance: number = .2;
943
+
944
+ @serializable()
945
+ mode: ParticleSystemTrailMode = ParticleSystemTrailMode.PerParticle;
946
+
947
+ @serializable()
948
+ ratio: number = 1;
949
+
950
+ @serializable()
951
+ ribbonCount: number = 1;
952
+
953
+ @serializable()
954
+ shadowBias: number = 0;
955
+
956
+ @serializable()
957
+ sizeAffectsLifetime: boolean = false;
958
+
959
+ @serializable()
960
+ sizeAffectsWidth: boolean = false;
961
+
962
+ @serializable()
963
+ splitSubEmitterRibbons: boolean = false;
964
+
965
+ @serializable()
966
+ textureMode: ParticleSystemTrailTextureMode = ParticleSystemTrailTextureMode.Stretch;
967
+
968
+ @serializable(MinMaxCurve)
969
+ widthOverTrail!: MinMaxCurve;
970
+ @serializable()
971
+ widthOverTrailMultiplier!: number;
972
+
973
+ @serializable()
974
+ worldSpace: boolean = false;
975
+
976
+ getWidth(size: number, _life01: number, pos01: number) {
977
+ let res = this.widthOverTrail.evaluate(pos01);
978
+ if (pos01 === 0) res = size;
979
+ size *= res;
980
+ return size;
981
+ }
982
+
983
+ getColor(color: Vector4, life01: number, pos01: number) {
984
+ const overTrail = this.colorOverTrail.evaluate(pos01);
985
+ const overLife = this.colorOverLifetime.evaluate(life01);
986
+ color.x *= overTrail.r * overLife.r;
987
+ color.y *= overTrail.g * overLife.g;
988
+ color.z *= overTrail.b * overLife.b;
989
+ color.w *= overTrail.alpha * overLife.alpha;
990
+ }
991
+ }
992
+
993
+ export class VelocityOverLifetimeModule {
994
+ @serializable()
995
+ enabled!: boolean;
996
+
997
+ @serializable()
998
+ space: ParticleSystemSimulationSpace = ParticleSystemSimulationSpace.Local;
999
+
1000
+ @serializable(MinMaxCurve)
1001
+ orbitalX!: MinMaxCurve;
1002
+ @serializable(MinMaxCurve)
1003
+ orbitalY!: MinMaxCurve;
1004
+ @serializable(MinMaxCurve)
1005
+ orbitalZ!: MinMaxCurve;
1006
+
1007
+ @serializable()
1008
+ orbitalXMultiplier!: number;
1009
+ @serializable()
1010
+ orbitalYMultiplier!: number;
1011
+ @serializable()
1012
+ orbitalZMultiplier!: number;
1013
+
1014
+ @serializable()
1015
+ orbitalOffsetX!: number;
1016
+ @serializable()
1017
+ orbitalOffsetY!: number;
1018
+ @serializable()
1019
+ orbitalOffsetZ!: number;
1020
+
1021
+ @serializable(MinMaxCurve)
1022
+ speedModifier!: MinMaxCurve;
1023
+ @serializable()
1024
+ speedModifierMultiplier!: number;
1025
+ @serializable(MinMaxCurve)
1026
+ x!: MinMaxCurve;
1027
+ @serializable()
1028
+ xMultiplier!: number;
1029
+ @serializable(MinMaxCurve)
1030
+ y!: MinMaxCurve;
1031
+ @serializable()
1032
+ yMultiplier!: number;
1033
+ @serializable(MinMaxCurve)
1034
+ z!: MinMaxCurve;
1035
+ @serializable()
1036
+ zMultiplier!: number;
1037
+
1038
+ private _system?: IParticleSystem;
1039
+ // private _worldRotation: Quaternion = new Quaternion();
1040
+
1041
+ update(system: IParticleSystem) {
1042
+ this._system = system;
1043
+ }
1044
+
1045
+ private _temp: Vector3 = new Vector3();
1046
+ private _temp2: Vector3 = new Vector3();
1047
+ private _temp3: Vector3 = new Vector3();
1048
+ private _hasOrbital = false;
1049
+ private _index = 0;
1050
+ private _orbitalMatrix: Matrix4 = new Matrix4();
1051
+
1052
+ init(particle: object) {
1053
+ if (this._index == 0) particle["debug"] = true;
1054
+ this._index += 1;
1055
+ particle["orbitx"] = this.orbitalX.evaluate(Math.random());
1056
+ particle["orbity"] = this.orbitalY.evaluate(Math.random());
1057
+ particle["orbitz"] = this.orbitalZ.evaluate(Math.random());
1058
+ // console.log(particle["orbitx"], particle["orbity"], particle["orbitz"])
1059
+ this._hasOrbital = particle["orbitx"] != 0 || particle["orbity"] != 0 || particle["orbitz"] != 0;
1060
+ }
1061
+
1062
+ apply(_particle: object, _index: number, _pos: Vec3, vel: Vec3, _dt: number, age: number, life: number) {
1063
+ if (!this.enabled) return;
1064
+ const t = age / life;
1065
+
1066
+ const speed = this.speedModifier.evaluate(t) * this.speedModifierMultiplier;
1067
+ const x = this.x.evaluate(t);
1068
+ const y = this.y.evaluate(t);
1069
+ const z = this.z.evaluate(t);
1070
+ this._temp.set(-x, y, z);
1071
+ if (this._system) {
1072
+ // if (this.space === ParticleSystemSimulationSpace.World) {
1073
+ // this._temp.applyQuaternion(this._system.worldQuaternionInverted);
1074
+ // }
1075
+ // if (this._system.main.simulationSpace === ParticleSystemSimulationSpace.World) {
1076
+ // this._temp.applyQuaternion(this._system.worldQuaternion);
1077
+ // }
1078
+ }
1079
+
1080
+ if (this._hasOrbital) {
1081
+ const position = this._system?.worldPos;
1082
+ if (position) {
1083
+
1084
+ // TODO: we absolutely need to fix this, this is a hack for a specific usecase and doesnt work yet correctly
1085
+ // https://github.com/needle-tools/needle-tiny/issues/710
1086
+
1087
+ const pos = this._temp2.set(_pos.x, _pos.y, _pos.z);
1088
+
1089
+ const ox = this.orbitalXMultiplier;// particle["orbitx"];
1090
+ const oy = this.orbitalYMultiplier;// particle["orbity"];
1091
+ const oz = this.orbitalZMultiplier;// particle["orbitz"];
1092
+ const angle = speed * Math.PI * 2 * 10; // < Oh god
1093
+
1094
+ const cosX = Math.cos(angle * ox);
1095
+ const sinX = Math.sin(angle * ox);
1096
+ const cosY = Math.cos(angle * oy);
1097
+ const sinY = Math.sin(angle * oy);
1098
+ const cosZ = Math.cos(angle * oz);
1099
+ const sinZ = Math.sin(angle * oz);
1100
+
1101
+ const newX = pos.x * (cosY * cosZ) + pos.y * (cosY * sinZ) + pos.z * (-sinY);
1102
+ const newY = pos.x * (sinX * sinY * cosZ - cosX * sinZ) + pos.y * (sinX * sinY * sinZ + cosX * cosZ) + pos.z * (sinX * cosY);
1103
+ const newZ = pos.x * (cosX * sinY * cosZ + sinX * sinZ) + pos.y * (cosX * sinY * sinZ - sinX * cosZ) + pos.z * (cosX * cosY);
1104
+
1105
+ // pos.x += this.orbitalOffsetX;
1106
+ // pos.y += this.orbitalOffsetY;
1107
+ // pos.z += this.orbitalOffsetZ;
1108
+ const v = this._temp3.set(pos.x - newX, pos.y - newY, pos.z - newZ);
1109
+ v.normalize();
1110
+ v.multiplyScalar(.2 / _dt * (Math.max(this.orbitalXMultiplier, this.orbitalYMultiplier, this.orbitalZMultiplier)));
1111
+ vel.x += v.x;
1112
+ vel.y += v.y;
1113
+ vel.z += v.z;
1114
+ }
1115
+ }
1116
+
1117
+ vel.x += this._temp.x;
1118
+ vel.y += this._temp.y;
1119
+ vel.z += this._temp.z;
1120
+ vel.x *= speed;
1121
+ vel.y *= speed;
1122
+ vel.z *= speed;
1123
+ }
1124
+ }
1125
+
1126
+
1127
+
1128
+ enum ParticleSystemAnimationTimeMode {
1129
+ Lifetime,
1130
+ Speed,
1131
+ FPS,
1132
+ }
1133
+
1134
+ enum ParticleSystemAnimationMode {
1135
+ Grid,
1136
+ Sprites,
1137
+ }
1138
+
1139
+ enum ParticleSystemAnimationRowMode {
1140
+ Custom,
1141
+ Random,
1142
+ MeshIndex,
1143
+ }
1144
+
1145
+ enum ParticleSystemAnimationType {
1146
+ WholeSheet,
1147
+ SingleRow,
1148
+ }
1149
+
1150
+ export class TextureSheetAnimationModule {
1151
+
1152
+ @serializable()
1153
+ animation!: ParticleSystemAnimationType;
1154
+
1155
+ @serializable()
1156
+ enabled!: boolean;
1157
+
1158
+ @serializable()
1159
+ cycleCount!: number;
1160
+
1161
+ @serializable(MinMaxCurve)
1162
+ frameOverTime!: MinMaxCurve;
1163
+ @serializable()
1164
+ frameOverTimeMultiplier!: number;
1165
+
1166
+ @serializable()
1167
+ numTilesX!: number;
1168
+ @serializable()
1169
+ numTilesY!: number;
1170
+
1171
+ @serializable(MinMaxCurve)
1172
+ startFrame!: MinMaxCurve;
1173
+ @serializable()
1174
+ startFrameMultiplier!: number;
1175
+
1176
+ @serializable()
1177
+ rowMode!: ParticleSystemAnimationRowMode;
1178
+ @serializable()
1179
+ rowIndex!: number;
1180
+
1181
+ @serializable()
1182
+ spriteCount!: number;
1183
+
1184
+ @serializable()
1185
+ timeMode!: ParticleSystemAnimationTimeMode;
1186
+
1187
+ private sampleOnceAtStart(): boolean {
1188
+ if (this.timeMode === ParticleSystemAnimationTimeMode.Lifetime) {
1189
+ switch (this.frameOverTime.mode) {
1190
+ case ParticleSystemCurveMode.Constant:
1191
+ case ParticleSystemCurveMode.TwoConstants:
1192
+ case ParticleSystemCurveMode.TwoCurves:
1193
+ case ParticleSystemCurveMode.Curve:
1194
+ return true;
1195
+ }
1196
+ }
1197
+ return false;
1198
+ }
1199
+
1200
+ getStartIndex(): number {
1201
+ if (this.sampleOnceAtStart()) {
1202
+ const start = Math.random();
1203
+ return start * (this.numTilesX * this.numTilesY);
1204
+ }
1205
+ return 0;
1206
+ }
1207
+
1208
+ evaluate(t01: number): number | undefined {
1209
+ if (this.sampleOnceAtStart()) {
1210
+ return undefined;
1211
+ }
1212
+ return this.getIndex(t01);
1213
+ }
1214
+
1215
+ private getIndex(t01: number): number {
1216
+ const tiles = this.numTilesX * this.numTilesY;
1217
+ t01 = t01 * this.cycleCount;
1218
+ let index = this.frameOverTime.evaluate(t01 % 1);
1219
+ index *= this.frameOverTimeMultiplier;
1220
+ index *= tiles;
1221
+ index = index % tiles;
1222
+ index = Math.floor(index);
1223
+ return index;
1224
+ }
1225
+ }
1226
+
1227
+
1228
+ export class RotationOverLifetimeModule {
1229
+ @serializable()
1230
+ enabled!: boolean;
1231
+
1232
+ @serializable()
1233
+ separateAxes!: boolean;
1234
+
1235
+ @serializable(MinMaxCurve)
1236
+ x!: MinMaxCurve;
1237
+ @serializable()
1238
+ xMultiplier!: number;
1239
+ @serializable(MinMaxCurve)
1240
+ y!: MinMaxCurve;
1241
+ @serializable()
1242
+ yMultiplier!: number;
1243
+ @serializable(MinMaxCurve)
1244
+ z!: MinMaxCurve;
1245
+ @serializable()
1246
+ zMultiplier!: number;
1247
+
1248
+ evaluate(t01: number, t: number): number {
1249
+ if (!this.enabled) return 0;
1250
+ if (!this.separateAxes) {
1251
+ const rot = this.z.evaluate(t01, t) * -1;
1252
+ return rot;
1253
+ }
1254
+ return 0;
1255
+ }
1256
+ }
1257
+
1258
+ export class RotationBySpeedModule {
1259
+ @serializable()
1260
+ enabled!: boolean;
1261
+
1262
+ @serializable()
1263
+ range!: Vec2;
1264
+
1265
+ @serializable()
1266
+ separateAxes!: boolean;
1267
+
1268
+ @serializable(MinMaxCurve)
1269
+ x!: MinMaxCurve;
1270
+ @serializable()
1271
+ xMultiplier!: number;
1272
+ @serializable(MinMaxCurve)
1273
+ y!: MinMaxCurve;
1274
+ @serializable()
1275
+ yMultiplier!: number;
1276
+ @serializable(MinMaxCurve)
1277
+ z!: MinMaxCurve;
1278
+ @serializable()
1279
+ zMultiplier!: number;
1280
+
1281
+ evaluate(_t01: number, speed: number): number {
1282
+ if (!this.enabled) return 0;
1283
+ if (!this.separateAxes) {
1284
+ const t = Mathf.lerp(this.range.x, this.range.y, speed);
1285
+ const rot = this.z.evaluate(t) * -1;
1286
+ return rot;
1287
+ }
1288
+ return 0;
1289
+ }
1290
+ }
1291
+
1292
+
1293
+ export class LimitVelocityOverLifetimeModule {
1294
+ @serializable()
1295
+ enabled!: boolean;
1296
+
1297
+ @serializable()
1298
+ dampen!: number;
1299
+
1300
+ @serializable(MinMaxCurve)
1301
+ drag!: MinMaxCurve;
1302
+ @serializable()
1303
+ dragMultiplier!: number;
1304
+
1305
+ @serializable(MinMaxCurve)
1306
+ limit!: MinMaxCurve;
1307
+ @serializable()
1308
+ limitMultiplier!: number;
1309
+
1310
+ @serializable()
1311
+ separateAxes!: boolean;
1312
+
1313
+ @serializable(MinMaxCurve)
1314
+ limitX!: MinMaxCurve;
1315
+ @serializable()
1316
+ limitXMultiplier!: number;
1317
+ @serializable(MinMaxCurve)
1318
+ limitY!: MinMaxCurve;
1319
+ @serializable()
1320
+ limitYMultiplier!: number;
1321
+ @serializable(MinMaxCurve)
1322
+ limitZ!: MinMaxCurve;
1323
+ @serializable()
1324
+ limitZMultiplier!: number;
1325
+
1326
+ @serializable()
1327
+ multiplyDragByParticleSize: boolean = false;
1328
+ @serializable()
1329
+ multiplyDragByParticleVelocity: boolean = false;
1330
+
1331
+ @serializable()
1332
+ space!: ParticleSystemSimulationSpace;
1333
+
1334
+ private _temp: Vector3 = new Vector3();
1335
+ private _temp2: Vector3 = new Vector3();
1336
+
1337
+ apply(_position: Vec3, baseVelocity: Vector3, currentVelocity: Vector3, _size: number, t01: number, _dt: number, _scale: number) {
1338
+ if (!this.enabled) return;
1339
+ // if (this.separateAxes) {
1340
+ // // const maxX = this.limitX.evaluate(t01) * this.limitXMultiplier;
1341
+ // // const maxY = this.limitY.evaluate(t01) * this.limitYMultiplier;
1342
+ // // const maxZ = this.limitZ.evaluate(t01) * this.limitZMultiplier;
1343
+
1344
+ // }
1345
+ // else
1346
+ {
1347
+ const max = this.limit.evaluate(t01) * this.limitMultiplier;
1348
+ const speed = baseVelocity.length();
1349
+ if (speed > max) {
1350
+ this._temp.copy(baseVelocity).normalize().multiplyScalar(max);
1351
+ let t = this.dampen * .5;
1352
+ // t *= scale;
1353
+ baseVelocity.x = Mathf.lerp(baseVelocity.x, this._temp.x, t);
1354
+ baseVelocity.y = Mathf.lerp(baseVelocity.y, this._temp.y, t);
1355
+ baseVelocity.z = Mathf.lerp(baseVelocity.z, this._temp.z, t);
1356
+
1357
+ // this._temp2.set(0, 0, 0);
1358
+ currentVelocity.x = Mathf.lerp(currentVelocity.x, this._temp.x, t);
1359
+ currentVelocity.y = Mathf.lerp(currentVelocity.y, this._temp.y, t);
1360
+ currentVelocity.z = Mathf.lerp(currentVelocity.z, this._temp.z, t);
1361
+ }
1362
+ // vel.multiplyScalar(dragFactor);
1363
+ }
1364
+ // vel.x *= 0.3;
1365
+ // vel.y *= 0.3;
1366
+ // vel.z *= 0.3;
1367
+ }
1368
+ }
1369
+
1370
+
1371
+ export enum ParticleSystemInheritVelocityMode {
1372
+ Initial,
1373
+ Current,
1374
+ }
1375
+
1376
+ export class InheritVelocityModule {
1377
+
1378
+ @serializable()
1379
+ enabled!: boolean;
1380
+
1381
+ @serializable(MinMaxCurve)
1382
+ curve!: MinMaxCurve;
1383
+ @serializable()
1384
+ curveMultiplier!: number;
1385
+
1386
+ @serializable()
1387
+ mode!: ParticleSystemInheritVelocityMode;
1388
+
1389
+ system!: IParticleSystem;
1390
+ private _lastWorldPosition!: Vector3;
1391
+ private _velocity: Vector3 = new Vector3();
1392
+ private _temp: Vector3 = new Vector3();
1393
+
1394
+ update(_context: Context) {
1395
+ if (!this.enabled) return;
1396
+ if (this.system.worldspace === false) return;
1397
+ if (this._lastWorldPosition) {
1398
+ this._velocity.copy(this.system.worldPos).sub(this._lastWorldPosition).multiplyScalar(1 / this.system.deltaTime);
1399
+ this._lastWorldPosition.copy(this.system.worldPos);
1400
+ }
1401
+ else {
1402
+ this._velocity.set(0, 0, 0);
1403
+ this._lastWorldPosition = this.system.worldPos.clone();
1404
+ }
1405
+ }
1406
+
1407
+ // TODO: make work for subsystems
1408
+ applyInitial(vel: Vector3) {
1409
+ if (!this.enabled) return;
1410
+ if (this.system.worldspace === false) return;
1411
+ if (this.mode === ParticleSystemInheritVelocityMode.Initial) {
1412
+ const factor = this.curve.evaluate(Math.random(), Math.random());
1413
+ this._temp.copy(this._velocity).multiplyScalar(factor);
1414
+ vel.add(this._temp);
1415
+ }
1416
+ }
1417
+
1418
+ applyCurrent(vel: Vector3, t01: number, lerpFactor: number) {
1419
+ if (!this.enabled) return;
1420
+ if (this.system.worldspace === false) return;
1421
+ if (this.mode === ParticleSystemInheritVelocityMode.Current) {
1422
+ const factor = this.curve.evaluate(t01, lerpFactor);
1423
+ this._temp.copy(this._velocity).multiplyScalar(factor);
1424
+ vel.add(this._temp);
1425
+ }
1426
+ }
1427
+ }
1428
+
1429
+
1430
+ export class SizeBySpeedModule {
1431
+ @serializable()
1432
+ enabled!: boolean;
1433
+
1434
+ @serializable(Vector2)
1435
+ range!: Vector2;
1436
+ @serializable()
1437
+ separateAxes!: boolean;
1438
+
1439
+ @serializable(MinMaxCurve)
1440
+ size!: MinMaxCurve;
1441
+ @serializable()
1442
+ sizeMultiplier!: number;
1443
+
1444
+ @serializable(MinMaxCurve)
1445
+ x!: MinMaxCurve;
1446
+ @serializable()
1447
+ xMultiplier!: number;
1448
+ @serializable(MinMaxCurve)
1449
+ y!: MinMaxCurve;
1450
+ @serializable()
1451
+ yMultiplier!: number;
1452
+ @serializable(MinMaxCurve)
1453
+ z!: MinMaxCurve;
1454
+ @serializable()
1455
+ zMultiplier!: number;
1456
+
1457
+ evaluate(vel: Vector3, _t01: number, lerpFactor: number, size: number): number {
1458
+
1459
+ const speed = vel.length();
1460
+ const x = Mathf.remap(speed, this.range.x, this.range.y, 0, 1);
1461
+ const factor = this.size.evaluate(x, lerpFactor);
1462
+ // return size;
1463
+ return size * factor;
1464
+ }
1465
+ }
1466
+
1467
+ export class ColorBySpeedModule {
1468
+ @serializable()
1469
+ enabled!: boolean;
1470
+ @serializable(Vector2)
1471
+ range!: Vector2;
1472
+ @serializable(MinMaxGradient)
1473
+ color!: MinMaxGradient;
1474
+
1475
+ evaluate(vel: Vector3, lerpFactor: number, color: Vector4) {
1476
+ const speed = vel.length();
1477
+ const x = Mathf.remap(speed, this.range.x, this.range.y, 0, 1);
1478
+ const res = this.color.evaluate(x, lerpFactor);
1479
+ color.x *= res.r;
1480
+ color.y *= res.g;
1481
+ color.z *= res.b;
1482
+ color.w *= res.alpha;
1483
+ }
1484
1484
  }
src/engine-components-experimental/networking/PlayerSync.ts CHANGED
@@ -3,11 +3,12 @@
3
3
  import { serializable } from "../../engine/engine_serialization_decorator";
4
4
  import { syncField } from "../../engine/engine_networking_auto"
5
5
  import { RoomEvents } from "../../engine/engine_networking";
6
- import { Object3D } from "three";
7
6
  import { syncDestroy } from "../../engine/engine_networking_instantiate";
8
- import { Vector3 } from "three";
7
+ import { getParam } from "../../engine/engine_utils";
9
8
 
9
+ import { Object3D } from "three";
10
10
 
11
+ const debug = getParam("debugplayersync");
11
12
 
12
13
  export class PlayerSync extends Behaviour {
13
14
  @serializable(AssetReference)
@@ -28,13 +29,21 @@
28
29
  }
29
30
 
30
31
  private async onUserJoined(_model) {
32
+ if (debug) console.log("PlayerSync.onUserJoined", _model);
31
33
  const instance = await this.asset?.instantiateSynced({ parent: this.gameObject }, true);
32
34
  if (instance) {
33
- let pl = GameObject.getComponent(instance as Object3D, PlayerState);
35
+ let pl = GameObject.getComponent(instance, PlayerState);
34
36
  if (pl) {
35
37
  pl.owner = this.context.connection.connectionId!;
36
38
  }
39
+ else {
40
+ console.error("<strong>Failed finding PlayerState on " + this.asset?.uri + "</strong>: please make sure the asset has a PlayerState component!");
41
+ GameObject.destroySynced(instance);
42
+ }
37
43
  }
44
+ else{
45
+ console.warn("PlayerSync: failed instantiating asset!")
46
+ }
38
47
 
39
48
  // TODO: previously created instances are not re-created when re-joining room
40
49
  // const inRoom = this.context.connection.usersInRoom();
@@ -87,7 +96,7 @@
87
96
  }
88
97
 
89
98
  //** use to check if a component or gameobject is part of a instance owned by the local player */
90
- static isLocalPlayer(obj: THREE.Object3D | Component): boolean {
99
+ static isLocalPlayer(obj: Object3D | Component): boolean {
91
100
  if (obj instanceof Object3D) {
92
101
  const state = GameObject.getComponentInParent(obj, PlayerState);
93
102
  return state?.isLocalPlayer ?? false;
@@ -129,6 +138,8 @@
129
138
  }
130
139
 
131
140
  private onOwnerChange(newOwner: string, oldOwner: string) {
141
+ if (debug) console.log("PlayerSync.onOwnerChange", this, "newOwner", newOwner, "oldOwner", oldOwner);
142
+
132
143
  // Remove from local owner array if it was local before
133
144
  const index = PlayerState._local.indexOf(this);
134
145
  if (index >= 0) PlayerState._local.splice(index, 1);
@@ -154,6 +165,7 @@
154
165
 
155
166
  awake(): void {
156
167
  PlayerState.all.push(this);
168
+ if(debug) console.log("Registered new PlayerState", this, PlayerState.all.length-1, PlayerState.all)
157
169
 
158
170
  this.context.connection.beginListen(RoomEvents.UserLeftRoom, (model: { userId: string }) => {
159
171
  // console.log("USER LEFT", model.userId)
@@ -177,6 +189,7 @@
177
189
 
178
190
  /** this tells the server that this client has been destroyed and the networking message for the instantiate will be removed */
179
191
  doDestroy() {
192
+ if (debug) console.log("PlayerSync.doDestroy → syncDestroy", this);
180
193
  syncDestroy(this.gameObject, this.context.connection);
181
194
  }
182
195
 
src/engine-components/ui/Utils.ts CHANGED
@@ -1,10 +1,10 @@
1
1
 
2
- import { FrontSide, DoubleSide, BackSide, TetrahedronGeometry } from "three"
2
+ import { FrontSide, DoubleSide, Object3D } from "three"
3
3
  import { FrameEvent } from "../../engine/engine_setup";
4
4
  import { Behaviour } from "../Component";
5
5
  import { $shadowDomOwner, BaseUIComponent } from "./BaseUIComponent";
6
6
 
7
- export function tryGetUIComponent(obj: THREE.Object3D): BaseUIComponent | null {
7
+ export function tryGetUIComponent(obj: Object3D): BaseUIComponent | null {
8
8
  const owner = obj[$shadowDomOwner];
9
9
  if (owner) {
10
10
  return owner;
@@ -15,7 +15,7 @@
15
15
  return null;
16
16
  }
17
17
 
18
- export function isUIObject(obj: THREE.Object3D) {
18
+ export function isUIObject(obj: Object3D) {
19
19
  return obj["isUI"] === true || typeof obj[$shadowDomOwner] === "object";
20
20
  }
21
21
 
@@ -27,7 +27,7 @@
27
27
  receiveShadows?: boolean;
28
28
  }
29
29
 
30
- export function updateRenderSettings(shadowComponent: THREE.Object3D, settings: RenderSettings) {
30
+ export function updateRenderSettings(shadowComponent: Object3D, settings: RenderSettings) {
31
31
  if (!shadowComponent) return;
32
32
  // const owner = shadowComponent[$shadowDomOwner];
33
33
  // if (!owner)
src/engine-components-experimental/api.ts ADDED
@@ -0,0 +1,1 @@
1
+ export * from "./networking/PlayerSync";