@@ -1,6 +1,6 @@
|
|
1
|
-
import
|
1
|
+
import { FileLoader } from "three";
|
2
2
|
|
3
|
-
export const loader = new
|
3
|
+
export const loader = new FileLoader();
|
4
4
|
|
5
5
|
export async function loadFileAsync(url) {
|
6
6
|
return new Promise((resolve, reject) => {
|
@@ -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.
|
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("
|
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
|
}
|
@@ -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);
|
@@ -1,9 +1,7 @@
|
|
1
1
|
import { Behaviour } from "./Component";
|
2
|
-
import
|
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:
|
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():
|
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():
|
82
|
+
get currentActions(): AnimationAction[] {
|
85
83
|
return this._currentActions;
|
86
84
|
}
|
87
85
|
|
88
|
-
private mixer:
|
89
|
-
get actions(): Array<
|
86
|
+
private mixer: AnimationMixer | undefined = undefined;
|
87
|
+
get actions(): Array<AnimationAction> {
|
90
88
|
return this._actions;
|
91
89
|
}
|
92
|
-
set actions(val: Array<
|
90
|
+
set actions(val: Array<AnimationAction>) {
|
93
91
|
this._actions = val;
|
94
92
|
}
|
95
|
-
private _actions: Array<
|
93
|
+
private _actions: Array<AnimationAction> = [];
|
96
94
|
|
97
|
-
// private _currentAction:
|
95
|
+
// private _currentAction: AnimationAction | null = null;
|
98
96
|
|
99
|
-
private _currentActions:
|
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):
|
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 ?
|
214
|
-
else action.loop =
|
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
|
240
|
+
this.mixer = new AnimationMixer(this.gameObject);
|
243
241
|
}
|
244
242
|
}
|
245
243
|
|
246
244
|
|
247
245
|
class AnimationHandle {
|
248
|
-
mixer:
|
249
|
-
action:
|
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:
|
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;
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import { Behaviour } from "./Component";
|
2
|
-
import
|
3
|
-
import {
|
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:
|
14
|
+
action: AnimationAction;
|
16
15
|
loopDelta: number;
|
17
|
-
target:
|
16
|
+
target: AnimationMixer;
|
18
17
|
}
|
19
18
|
|
20
19
|
export declare class PlayOptions {
|
@@ -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,
|
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:
|
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!:
|
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:
|
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
|
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]:
|
605
|
-
static lastObjRotation: { [key: string]:
|
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]:
|
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]:
|
611
|
-
private static effectiveSpaceRotation: { [key: string]:
|
609
|
+
private static spaceRotation: { [key: string]: Quaternion } = {};
|
610
|
+
private static effectiveSpaceRotation: { [key: string]: Quaternion } = {};
|
612
611
|
|
613
|
-
private static clipOffsetRotation: { [key: string]:
|
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:
|
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
|
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
|
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!:
|
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:
|
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) {
|
@@ -1,1 +1,3 @@
|
|
1
|
+
export * from "./codegen/components";
|
2
|
+
export * from "./js-extensions/Object3D";
|
1
3
|
export * from "./ui/PointerEvents"
|
@@ -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
|
@@ -1,18 +1,18 @@
|
|
1
1
|
import { Behaviour, GameObject } from "./Component";
|
2
|
-
import
|
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():
|
9
|
+
get listener(): ThreeAudioListener {
|
10
10
|
if (this._listener == null)
|
11
|
-
this._listener = new
|
11
|
+
this._listener = new ThreeAudioListener();
|
12
12
|
return this._listener;
|
13
13
|
}
|
14
14
|
|
15
|
-
private _listener:
|
15
|
+
private _listener: ThreeAudioListener | null = null;
|
16
16
|
|
17
17
|
awake() {
|
18
18
|
AudioSource.registerWaitForAllowAudio(() => {
|
@@ -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 {
|
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:
|
149
|
+
private sound: PositionalAudio | null = null;
|
150
150
|
private helper: PositionalAudioHelper | null = null;
|
151
151
|
private wasPlaying = false;
|
152
|
-
private audioLoader:
|
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():
|
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
|
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
|
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
|
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)
|
@@ -1,28 +1,27 @@
|
|
1
|
-
import {
|
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:
|
16
|
-
head:
|
17
|
-
leftHand:
|
18
|
-
rigthHand:
|
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:
|
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 |
|
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:
|
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<
|
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:
|
148
|
+
private findAvatar(obj: Object3D): AvatarModel {
|
150
149
|
|
151
|
-
const root:
|
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
|
167
|
-
new
|
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:
|
179
|
+
private findAvatarPart(obj: Object3D, searchString: string[]): Object3D | null {
|
181
180
|
|
182
181
|
const name = obj.name.toLowerCase();
|
183
182
|
let matchesAll = true;
|
@@ -11,7 +11,7 @@
|
|
11
11
|
@serializable()
|
12
12
|
public isGizmo:boolean = true;
|
13
13
|
|
14
|
-
private _axes:
|
14
|
+
private _axes: _AxesHelper | null = null;
|
15
15
|
|
16
16
|
onEnable(): void {
|
17
17
|
if (this.isGizmo && !params.showGizmos) return;
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { Behaviour, GameObject } from "./Component";
|
2
2
|
import * as utils from "./../engine/engine_three_utils";
|
3
|
-
import
|
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
|
36
|
+
let offsetDir = new Vector3();
|
37
37
|
offsetDir.crossVectors(hintDir, dir0);
|
38
38
|
offsetDir.crossVectors(dir0, offsetDir);
|
39
39
|
offsetDir.normalize();
|
@@ -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:
|
13
|
-
private static testBox:
|
12
|
+
private box: Box3 | null = null;
|
13
|
+
private static testBox: Box3 = new Box3();
|
14
14
|
private _lastMatrixUpdateFrame: number = -1;
|
15
|
-
private static _position:
|
16
|
-
private static _size:
|
15
|
+
private static _position: Vector3 = new Vector3();
|
16
|
+
private static _size: Vector3 = new Vector3(.01, .01, .01);
|
17
17
|
|
18
|
-
public isInBox(obj:
|
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
|
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:
|
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):
|
64
|
+
public updateBox(force: boolean = false): Box3 {
|
65
65
|
if (!this.box) {
|
66
|
-
this.box = new
|
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:
|
81
|
-
private _color:
|
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:
|
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)
|
@@ -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,
|
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
|
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():
|
29
|
+
abstract get transform(): Object3D;
|
31
30
|
|
32
|
-
public static isDestroyed(go:
|
31
|
+
public static isDestroyed(go: Object3D): boolean {
|
33
32
|
return isDestroyed(go);
|
34
33
|
}
|
35
34
|
|
36
|
-
public static setActive(go:
|
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:
|
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:
|
53
|
+
public static isActiveInHierarchy(go: Object3D): boolean {
|
55
54
|
return isActiveInHierarchy(go);
|
56
55
|
}
|
57
56
|
|
58
|
-
public static markAsInstancedRendered(go:
|
57
|
+
public static markAsInstancedRendered(go: Object3D, instanced: boolean) {
|
59
58
|
markAsInstancedRendered(go, instanced);
|
60
59
|
}
|
61
60
|
|
62
|
-
public static 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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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 |
|
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 |
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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():
|
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:
|
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:
|
525
|
-
private static _worldQuaternionBuffer:
|
526
|
-
private static _worldEulerBuffer:
|
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:
|
529
|
-
private _worldQuaternion:
|
530
|
-
private static _tempQuaternionBuffer2:
|
531
|
-
private _worldEuler:
|
532
|
-
private _worldRotation:
|
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():
|
535
|
-
if (!this._worldPosition) this._worldPosition = new
|
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:
|
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():
|
552
|
-
if (!this._worldQuaternion) this._worldQuaternion = new
|
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:
|
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():
|
566
|
-
if (!this._worldEuler) this._worldEuler = new
|
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:
|
573
|
-
if (!this._worldQuaternion) this._worldQuaternion = new
|
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():
|
578
|
+
get worldRotation(): Vector3 {
|
580
579
|
const rot = this.worldEuler;
|
581
|
-
if (!this._worldRotation) this._worldRotation = new
|
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:
|
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:
|
606
|
-
public get forward():
|
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:
|
610
|
-
public get right():
|
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:
|
614
|
-
public get up():
|
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
|
|
@@ -1,19 +1,18 @@
|
|
1
|
-
import {
|
2
|
-
// import { DragControls as Control } from "../include/three/DragControls";
|
1
|
+
import { GameObject } from "./Component";
|
3
2
|
import { SyncedTransform } from "./SyncedTransform";
|
4
|
-
import
|
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,
|
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:
|
27
|
-
attached:
|
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:
|
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
|
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:
|
76
|
+
private static lastHovered: Object3D;
|
78
77
|
private _draggingRigidbodies: Rigidbody[] = [];
|
79
78
|
|
80
|
-
private allowEdit(_obj:
|
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!:
|
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:
|
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:
|
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():
|
287
|
+
public get selected(): Object3D | null {
|
289
288
|
return this._selected;
|
290
289
|
}
|
291
290
|
|
292
|
-
private _selected:
|
291
|
+
private _selected: Object3D | null = null;
|
293
292
|
private _context: Context | null = null;
|
294
|
-
private _camera:
|
295
|
-
private _cameraPlane:
|
293
|
+
private _camera: Camera;
|
294
|
+
private _cameraPlane: Plane = new Plane();
|
296
295
|
|
297
296
|
private _hasGroundPlane: boolean = false;
|
298
|
-
private _groundPlane:
|
299
|
-
private _groundOffset:
|
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:
|
301
|
+
private _groundPlanePoint: Vector3 = new Vector3();
|
303
302
|
|
304
|
-
private _raycaster = new
|
305
|
-
private _cameraPlaneOffset = new
|
306
|
-
private _intersection = new
|
307
|
-
private _worldPosition = new
|
308
|
-
private _inverseMatrix = new
|
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:
|
312
|
-
private _groundMarker:
|
313
|
-
private static geometry = new
|
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:
|
314
|
+
constructor(camera: Camera) {
|
316
315
|
this._camera = camera;
|
317
316
|
|
318
|
-
const line = new
|
319
|
-
const mat = line.material as
|
320
|
-
mat.color = new
|
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
|
328
|
-
const material = new
|
329
|
-
const sphere = new
|
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:
|
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
|
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
|
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:
|
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
|
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
|
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:
|
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) {
|
@@ -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,
|
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
|
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?:
|
187
|
+
async instantiate(parent?: Object3D | InstantiateOptions) {
|
189
188
|
return this.onInstantiate(parent, false);
|
190
189
|
}
|
191
190
|
|
192
|
-
async instantiateSynced(parent?:
|
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?:
|
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):
|
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;
|
@@ -1,5 +1,8 @@
|
|
1
|
-
import { Camera, Clock,
|
2
|
-
|
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?:
|
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:
|
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:
|
179
|
-
renderer:
|
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():
|
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 =
|
257
|
+
this.renderer.toneMapping = NoToneMapping; // could also set to LinearToneMapping, ACESFilmicToneMapping
|
255
258
|
|
256
|
-
this.renderer.setClearColor(new
|
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 =
|
265
|
+
this.renderer.shadowMap.type = PCFSoftShadowMap;
|
263
266
|
this.renderer.setSize(this.domWidth, this.domHeight);
|
264
|
-
this.renderer.outputEncoding =
|
267
|
+
this.renderer.outputEncoding = sRGBEncoding;
|
265
268
|
this.renderer.physicallyCorrectLights = true;
|
266
269
|
}
|
267
270
|
|
268
|
-
this.scene = new
|
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:
|
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
|
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:
|
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:
|
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():
|
479
|
+
get depthTexture(): DepthTexture | null {
|
477
480
|
return this._renderTarget?.depthTexture || null;
|
478
481
|
}
|
479
482
|
|
480
|
-
get opaqueColorTexture():
|
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
|
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
|
834
|
+
this._renderTarget.texture = new Texture();
|
832
835
|
this._renderTarget.texture.generateMipmaps = false;
|
833
|
-
this._renderTarget.texture.minFilter =
|
834
|
-
this._renderTarget.texture.magFilter =
|
835
|
-
this._renderTarget.texture.format =
|
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
|
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
|
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 =
|
924
|
+
// renderer.shadowMap.type = PCFSoftShadowMap;
|
922
925
|
// renderer.setSize(window.innerWidth, window.innerHeight);
|
923
|
-
// renderer.outputEncoding =
|
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:
|
932
|
+
// mainCamera: Camera | undefined
|
930
933
|
// } = {
|
931
934
|
// preparing: [],
|
932
935
|
// resolving: [],
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import {
|
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?:
|
40
|
-
rotation?:
|
41
|
-
scale?:
|
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:
|
91
|
+
export function markAsInstancedRendered(go: Object3D, instanced: boolean) {
|
92
92
|
go[$isUsingInstancing] = instanced;
|
93
93
|
}
|
94
94
|
|
95
|
-
export function isUsingInstancing(instance:
|
95
|
+
export function isUsingInstancing(instance: Object3D): boolean { return InstancingUtil.isUsingInstancing(instance); }
|
96
96
|
|
97
97
|
|
98
|
-
export function findByGuid(guid: string, hierarchy:
|
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:
|
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:
|
202
|
-
clone:
|
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
|
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 |
|
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 |
|
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:
|
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
|
411
|
+
const original = val.original as SkinnedMesh;
|
412
412
|
const originalSkeleton = original.skeleton;
|
413
|
-
const clone = val.clone as
|
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<
|
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
|
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
|
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
|
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:
|
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"];
|
@@ -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 {
|
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:
|
111
|
+
export function CreateWireCube(col: ColorRepresentation | null = null): LineSegments {
|
113
112
|
const color = new Color(col ?? 0xdddddd);
|
114
|
-
// const material = new
|
115
|
-
// material.color = new
|
113
|
+
// const material = new MeshBasicMaterial();
|
114
|
+
// material.color = new Color(col ?? 0xdddddd);
|
116
115
|
// material.wireframe = true;
|
117
|
-
// const box = new
|
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 }));
|
@@ -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:
|
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:
|
147
|
+
obj: Object3D;
|
149
148
|
}
|
150
149
|
|
151
|
-
declare type LateResolveCallback = (gltf:
|
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:
|
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
|
326
|
+
// return new Vector2(entry.x, entry.y);
|
328
327
|
// case "Vector3":
|
329
|
-
// return new
|
328
|
+
// return new Vector3(entry.x, entry.y, entry.z);
|
330
329
|
// case "Vector4":
|
331
|
-
// return new
|
330
|
+
// return new Vector4(entry.x, entry.y, entry.z, entry.w);
|
332
331
|
// case "Quaternion":
|
333
|
-
// return new
|
332
|
+
// return new Quaternion(entry.x, entry.y, entry.z, entry.w);
|
334
333
|
// }
|
335
334
|
// return undefined;
|
336
335
|
// }
|
@@ -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():
|
62
|
-
get mousePositionRC():
|
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):
|
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):
|
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):
|
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):
|
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:
|
217
|
-
private _pointerPositionsLastFrame:
|
218
|
-
private _pointerPositionsDelta:
|
219
|
-
private _pointerPositionsRC:
|
220
|
-
private _pointerPositionDown:
|
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
|
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
|
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
|
581
|
-
while (evt.button >= this._pointerPositionsLastFrame.length) this._pointerPositionsLastFrame.push(new
|
582
|
-
while (evt.button >= this._pointerPositionsDelta.length) this._pointerPositionsDelta.push(new
|
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
|
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);
|
@@ -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
|
10
|
+
const NEEDLE_ENGINE_COMMERCIAL_USE_LICENSE = false;
|
11
11
|
|
12
12
|
export function hasProLicense() {
|
13
|
-
return
|
13
|
+
return NEEDLE_ENGINE_COMMERCIAL_USE_LICENSE;
|
14
14
|
}
|
15
15
|
|
16
16
|
|
@@ -230,7 +230,7 @@
|
|
230
230
|
}
|
231
231
|
}
|
232
232
|
|
233
|
-
function updateIsActiveInHierarchyRecursiveRuntime(go:
|
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<
|
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:
|
325
|
+
export function updateActiveInHierarchyWithoutEventCall(go: Object3D) {
|
326
326
|
let activeInHierarchy = true;
|
327
|
-
let current:
|
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:
|
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);
|
@@ -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
|
213
|
+
export const syncField = function(onFieldChanged?: string | FieldChangedCallbackFn) {
|
214
214
|
|
215
215
|
return function (target: any, propertyKey: string) {
|
216
216
|
|
@@ -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:
|
23
|
+
position: Vector3 | null;
|
25
24
|
seed: number;
|
26
25
|
sender: string;
|
27
26
|
serverUrl: string;
|
28
27
|
parentGuid?: string;
|
29
28
|
|
30
|
-
boundsSize?:
|
29
|
+
boundsSize?: Vector3;
|
31
30
|
|
32
|
-
constructor(connectionId: string, seed: number, guid: string, name: string, hash: string, size: number, position:
|
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
|
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]:
|
184
|
+
const previews: { [key: string]: Object3D } = {};
|
186
185
|
|
187
186
|
function addPreview(evt: FileSpawnModel, context: Context) {
|
188
|
-
const sphere = new
|
189
|
-
const object = new
|
190
|
-
const box = new
|
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
|
193
|
+
const parent = findByGuid(evt.parentGuid, context.scene) as Object3D;
|
195
194
|
if (parent)
|
196
195
|
parent.add(box);
|
197
196
|
}
|
@@ -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:
|
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
|
22
|
+
const _worldPositions = new CircularBuffer(() => new Vector3(), 100);
|
23
23
|
|
24
|
-
export function getWorldPosition(obj:
|
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:
|
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:
|
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:
|
54
|
-
const _worldQuaternion:
|
55
|
-
const _tempQuaternionBuffer2:
|
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:
|
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:
|
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:
|
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:
|
83
|
-
const _worldScale2:
|
82
|
+
const _worldScale: Vector3 = new Vector3();
|
83
|
+
const _worldScale2: Vector3 = new Vector3();
|
84
84
|
|
85
|
-
export function getWorldScale(obj:
|
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:
|
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
|
106
|
-
const _forwardQuat = new
|
107
|
-
export function forward(obj:
|
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:
|
115
|
-
const _worldEuler:
|
116
|
-
const _worldRotation:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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/
|
163
|
-
export function logHierarchy(root:
|
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:
|
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:
|
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
|
201
|
+
private static planeGeometry = new PlaneGeometry(2, 2, 1, 1);
|
202
202
|
private static renderer = new WebGLRenderer({ antialias: false });
|
203
|
-
private static perspectiveCam = new
|
204
|
-
private static scene = new
|
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
|
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:
|
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:
|
300
|
+
export function textureToCanvas(texture: Texture, force: boolean = false): HTMLCanvasElement | null {
|
301
301
|
return Graphics.textureToCanvas(texture, force);
|
302
302
|
}
|
303
303
|
|
@@ -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:
|
21
|
+
node: Object3D;
|
21
22
|
nodeIndex: number;
|
22
23
|
nodeDef: any;
|
23
24
|
|
24
|
-
constructor(node:
|
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:
|
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:
|
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:
|
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:
|
202
|
+
private async createComponents(obj: Object3D, data: ExtensionData) {
|
202
203
|
if (!data) return;
|
203
204
|
const componentData = data[builtinComponentKeyName];
|
204
205
|
if (componentData) {
|
@@ -43,11 +43,20 @@
|
|
43
43
|
if (ext) {
|
44
44
|
if (debug)
|
45
45
|
console.log("Apply \"" + this.name + "\", src: \"" + this.sourceId + "\"", ext);
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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,
|
121
|
+
this._ambientLightObj = new AmbientLight(this.ambientLight, this.ambientIntensity);
|
113
122
|
}
|
114
123
|
if (this._ambientLightObj) {
|
115
124
|
this.gameObject.add(this._ambientLightObj)
|
@@ -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:
|
41
|
-
worldToObjectMatrix:
|
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:
|
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
|
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:
|
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:
|
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:
|
154
|
+
private static _worldSpaceCameraPos: Vector3 = new Vector3();
|
156
155
|
|
157
|
-
private static _mainLightColor:
|
158
|
-
private static _mainLightPosition:
|
159
|
-
private static _lightData:
|
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?:
|
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<
|
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<
|
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
|
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
|
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 =
|
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
|
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 =
|
462
|
+
material.side = DoubleSide;
|
464
463
|
break;
|
465
464
|
case CullMode.Front:
|
466
|
-
material.side =
|
465
|
+
material.side = BackSide;
|
467
466
|
break;
|
468
467
|
case CullMode.Back:
|
469
|
-
material.side =
|
468
|
+
material.side = FrontSide;
|
470
469
|
break;
|
471
470
|
default:
|
472
|
-
material.side =
|
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 =
|
479
|
+
material.depthFunc = EqualDepth;
|
481
480
|
break;
|
482
481
|
case ZTestMode.NotEqual:
|
483
482
|
material.depthTest = true;
|
484
|
-
material.depthFunc =
|
483
|
+
material.depthFunc = NotEqualDepth;
|
485
484
|
break;
|
486
485
|
case ZTestMode.Less:
|
487
486
|
material.depthTest = true;
|
488
|
-
material.depthFunc =
|
487
|
+
material.depthFunc = LessDepth;
|
489
488
|
break;
|
490
489
|
case ZTestMode.LEqual:
|
491
490
|
material.depthTest = true;
|
492
|
-
material.depthFunc =
|
491
|
+
material.depthFunc = LessEqualDepth;
|
493
492
|
break;
|
494
493
|
case ZTestMode.Greater:
|
495
494
|
material.depthTest = true;
|
496
|
-
material.depthFunc =
|
495
|
+
material.depthFunc = GreaterDepth;
|
497
496
|
break;
|
498
497
|
case ZTestMode.GEqual:
|
499
498
|
material.depthTest = true;
|
500
|
-
material.depthFunc =
|
499
|
+
material.depthFunc = GreaterEqualDepth;
|
501
500
|
break;
|
502
501
|
case ZTestMode.Always:
|
503
502
|
material.depthTest = false;
|
504
|
-
material.depthFunc =
|
503
|
+
material.depthFunc = AlwaysDepth;
|
505
504
|
break;
|
506
505
|
}
|
507
506
|
|
@@ -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/
|
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");
|
@@ -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
|
}
|
@@ -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 {
|
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
|
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:
|
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
|
|
@@ -1,10 +1,10 @@
|
|
1
1
|
|
2
|
-
import { FrontSide, DoubleSide,
|
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:
|
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:
|
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:
|
30
|
+
export function updateRenderSettings(shadowComponent: Object3D, settings: RenderSettings) {
|
31
31
|
if (!shadowComponent) return;
|
32
32
|
// const owner = shadowComponent[$shadowDomOwner];
|
33
33
|
// if (!owner)
|
@@ -0,0 +1,1 @@
|
|
1
|
+
export * from "./networking/PlayerSync";
|