@needle-tools/engine 2.41.0-pre → 2.42.0-pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/needle-engine.d.ts +100 -40
- package/dist/needle-engine.js +367 -367
- package/dist/needle-engine.js.map +3 -3
- package/dist/needle-engine.min.js +37 -37
- package/dist/needle-engine.min.js.map +3 -3
- package/lib/engine/engine_element.js +1 -1
- package/lib/engine/engine_element.js.map +1 -1
- package/lib/engine/engine_element_loading.js +6 -1
- package/lib/engine/engine_element_loading.js.map +1 -1
- package/lib/engine/engine_gizmos.d.ts +8 -22
- package/lib/engine/engine_gizmos.js +37 -3
- package/lib/engine/engine_gizmos.js.map +1 -1
- package/lib/engine/engine_setup.d.ts +3 -0
- package/lib/engine/engine_setup.js +15 -0
- package/lib/engine/engine_setup.js.map +1 -1
- package/lib/engine/engine_three_utils.d.ts +16 -1
- package/lib/engine/engine_three_utils.js +94 -54
- package/lib/engine/engine_three_utils.js.map +1 -1
- package/lib/engine/engine_types.d.ts +6 -0
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine-components/AnimationCurve.js +2 -2
- package/lib/engine-components/AnimationCurve.js.map +1 -1
- package/lib/engine-components/BoxHelperComponent.js +9 -10
- package/lib/engine-components/BoxHelperComponent.js.map +1 -1
- package/lib/engine-components/ParticleSystem.d.ts +24 -10
- package/lib/engine-components/ParticleSystem.js +136 -41
- package/lib/engine-components/ParticleSystem.js.map +1 -1
- package/lib/engine-components/ParticleSystemModules.d.ts +40 -6
- package/lib/engine-components/ParticleSystemModules.js +103 -21
- package/lib/engine-components/ParticleSystemModules.js.map +1 -1
- package/lib/engine-components/ReflectionProbe.js +29 -11
- package/lib/engine-components/ReflectionProbe.js.map +1 -1
- package/lib/engine-components/Renderer.d.ts +1 -0
- package/lib/engine-components/Renderer.js +4 -2
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +1 -0
- package/lib/engine-components/codegen/components.js +1 -0
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/package.json +2 -2
- package/src/engine/codegen/register_types.js +4 -0
- package/src/engine/dist/engine_three_utils.js +279 -0
- package/src/engine/engine_element.ts +1 -1
- package/src/engine/engine_element_loading.ts +5 -1
- package/src/engine/engine_gizmos.ts +45 -8
- package/src/engine/engine_setup.ts +25 -2
- package/src/engine/engine_three_utils.ts +103 -62
- package/src/engine/engine_types.ts +8 -1
- package/src/engine-components/AnimationCurve.ts +2 -2
- package/src/engine-components/BoxHelperComponent.ts +12 -15
- package/src/engine-components/ParticleSystem.ts +151 -75
- package/src/engine-components/ParticleSystemModules.ts +127 -24
- package/src/engine-components/ReflectionProbe.ts +37 -13
- package/src/engine-components/Renderer.ts +4 -2
- package/src/engine-components/codegen/components.ts +1 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as THREE from "three";
|
|
2
2
|
import { Mathf } from "./engine_math"
|
|
3
|
-
import { Vector3, Quaternion, Uniform } from "three";
|
|
3
|
+
import { WebGLRenderTarget, WebGLRenderer, Vector3, Quaternion, Uniform, Texture, Material, ShaderMaterial, CanvasTexture } from "three";
|
|
4
4
|
import { CircularBuffer } from "./engine_utils";
|
|
5
5
|
|
|
6
6
|
|
|
@@ -78,7 +78,7 @@ const _worldScale2: THREE.Vector3 = new THREE.Vector3();
|
|
|
78
78
|
|
|
79
79
|
export function getWorldScale(obj: THREE.Object3D, vec: THREE.Vector3 | null = null): THREE.Vector3 {
|
|
80
80
|
if (!obj) return _worldScale.set(0, 0, 0);
|
|
81
|
-
if (!obj.parent) return _worldScale.copy(obj.
|
|
81
|
+
if (!obj.parent) return _worldScale.copy(obj.scale);
|
|
82
82
|
obj.getWorldScale(vec ?? _worldScale);
|
|
83
83
|
return vec ?? _worldScale;
|
|
84
84
|
}
|
|
@@ -180,39 +180,109 @@ export function logHierarchy(root: THREE.Object3D | null | undefined, collapsibl
|
|
|
180
180
|
|
|
181
181
|
|
|
182
182
|
|
|
183
|
-
const planeGeometry = new THREE.PlaneGeometry(2, 2, 1, 1);
|
|
184
|
-
const renderer = new THREE.WebGLRenderer({ antialias: false });
|
|
185
|
-
const perspectiveCam = new THREE.PerspectiveCamera();
|
|
186
|
-
const scene = new THREE.Scene();
|
|
187
|
-
const shaderMaterial = new THREE.ShaderMaterial({
|
|
188
|
-
uniforms: { blitTexture: new Uniform(null) },
|
|
189
|
-
vertexShader: `
|
|
190
|
-
varying vec2 vUv;
|
|
191
|
-
void main(){
|
|
192
|
-
vUv = uv;
|
|
193
|
-
gl_Position = vec4(position.xy * 1.0,0.,.999999);
|
|
194
|
-
}`,
|
|
195
|
-
fragmentShader: `
|
|
196
|
-
uniform sampler2D blitTexture;
|
|
197
|
-
varying vec2 vUv;
|
|
198
|
-
void main(){
|
|
199
|
-
vec2 uv = vUv;
|
|
200
|
-
uv.y = 1.0 - uv.y;
|
|
201
|
-
gl_FragColor = vec4(uv.xy, 0, 1);
|
|
202
|
-
gl_FragColor = texture2D( blitTexture, uv);
|
|
203
|
-
}`
|
|
204
|
-
});
|
|
205
183
|
|
|
184
|
+
export class Graphics {
|
|
185
|
+
private static planeGeometry = new THREE.PlaneGeometry(2, 2, 1, 1);
|
|
186
|
+
private static renderer = new WebGLRenderer({ antialias: false });
|
|
187
|
+
private static perspectiveCam = new THREE.PerspectiveCamera();
|
|
188
|
+
private static scene = new THREE.Scene();
|
|
189
|
+
private static readonly vertex = `
|
|
190
|
+
varying vec2 vUv;
|
|
191
|
+
void main(){
|
|
192
|
+
vUv = uv;
|
|
193
|
+
gl_Position = vec4(position.xy * 1.0,0.,.999999);
|
|
194
|
+
}`;
|
|
195
|
+
private static readonly fragment = `
|
|
196
|
+
uniform sampler2D map;
|
|
197
|
+
varying vec2 vUv;
|
|
198
|
+
void main(){
|
|
199
|
+
vec2 uv = vUv;
|
|
200
|
+
uv.y = 1.0 - uv.y;
|
|
201
|
+
gl_FragColor = texture2D( map, uv);
|
|
202
|
+
// gl_FragColor = vec4(uv.xy, 0, 1);
|
|
203
|
+
}`;
|
|
204
|
+
private static readonly blipMaterial = new ShaderMaterial({
|
|
205
|
+
uniforms: { map: new Uniform(null) },
|
|
206
|
+
vertexShader: this.vertex,
|
|
207
|
+
fragmentShader: this.fragment
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
static createBlitMaterial(fragment: string): ShaderMaterial {
|
|
211
|
+
return new ShaderMaterial({
|
|
212
|
+
uniforms: { map: new Uniform(null) },
|
|
213
|
+
vertexShader: this.vertex,
|
|
214
|
+
fragmentShader: fragment
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
private static readonly mesh = new THREE.Mesh(this.planeGeometry, this.blipMaterial);
|
|
218
|
+
|
|
219
|
+
static copyTexture(texture: Texture, blitMaterial?: ShaderMaterial) {
|
|
220
|
+
const material = blitMaterial ?? this.blipMaterial;
|
|
221
|
+
material.uniforms.map.value = texture;
|
|
222
|
+
material.needsUpdate = true;
|
|
223
|
+
material.uniformsNeedUpdate = true;
|
|
224
|
+
const mesh = this.mesh;
|
|
225
|
+
mesh.material = material;
|
|
226
|
+
mesh.frustumCulled = false;
|
|
227
|
+
this.scene.children.length = 0;
|
|
228
|
+
this.scene.add(mesh);
|
|
229
|
+
this.renderer.setSize(texture.image.width, texture.image.height);
|
|
230
|
+
this.renderer.clear();
|
|
231
|
+
this.renderer.render(this.scene, this.perspectiveCam);
|
|
232
|
+
const tex = new Texture(this.renderer.domElement);
|
|
233
|
+
tex.name = "Copy";
|
|
234
|
+
tex.needsUpdate = true; // < important!
|
|
235
|
+
return tex;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// static blit(src: Texture, target: Texture, blitMaterial?: ShaderMaterial) {
|
|
239
|
+
// let material = blitMaterial ?? this.blipMaterial;
|
|
240
|
+
// material.uniforms.map.value = src;
|
|
241
|
+
// this.mesh.material = material;
|
|
242
|
+
// this.mesh.frustumCulled = false;
|
|
243
|
+
// this.mesh.matrix.identity();
|
|
244
|
+
// this.scene.children.length = 0;
|
|
245
|
+
// this.scene.add(this.mesh);
|
|
246
|
+
// this.renderer.setSize(src.image.width, src.image.height);
|
|
247
|
+
// this.renderer.clear();
|
|
248
|
+
// this.renderer.render(this.scene, this.perspectiveCam);
|
|
249
|
+
// return new Texture(this.renderer.domElement);
|
|
250
|
+
// }
|
|
251
|
+
|
|
252
|
+
static textureToCanvas(texture: Texture, force: boolean) {
|
|
253
|
+
if (!texture) return null;
|
|
254
|
+
|
|
255
|
+
if (force === true || texture["isCompressedTexture"] === true) {
|
|
256
|
+
texture = copyTexture(texture);
|
|
257
|
+
}
|
|
258
|
+
const image = texture.image;
|
|
259
|
+
if (isImageBitmap(image)) {
|
|
260
|
+
|
|
261
|
+
const canvas = document.createElement('canvas');
|
|
262
|
+
canvas.width = image.width;
|
|
263
|
+
canvas.height = image.height;
|
|
264
|
+
|
|
265
|
+
const context = canvas.getContext('2d');
|
|
266
|
+
if (!context) {
|
|
267
|
+
console.error("Failed getting canvas 2d context");
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
context.drawImage(image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height);
|
|
271
|
+
return canvas;
|
|
272
|
+
|
|
273
|
+
}
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**@obsolete use Graphics.copyTexture */
|
|
206
279
|
export function copyTexture(texture: THREE.Texture): THREE.Texture {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
renderer.clear();
|
|
214
|
-
renderer.render(scene, perspectiveCam);
|
|
215
|
-
return new THREE.Texture(renderer.domElement);
|
|
280
|
+
return Graphics.copyTexture(texture);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**@obsolete use Graphics.textureToCanvas */
|
|
284
|
+
export function textureToCanvas(texture: THREE.Texture, force: boolean = false): HTMLCanvasElement | null {
|
|
285
|
+
return Graphics.textureToCanvas(texture, force);
|
|
216
286
|
}
|
|
217
287
|
|
|
218
288
|
declare class OffscreenCanvas { };
|
|
@@ -222,33 +292,4 @@ function isImageBitmap(image) {
|
|
|
222
292
|
(typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement) ||
|
|
223
293
|
(typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas) ||
|
|
224
294
|
(typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
export function textureToCanvas(texture: THREE.Texture, force : boolean = false): HTMLCanvasElement | null {
|
|
229
|
-
|
|
230
|
-
if(!texture) return null;
|
|
231
|
-
|
|
232
|
-
if (force === true || texture["isCompressedTexture"] === true) {
|
|
233
|
-
texture = copyTexture(texture);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const image = texture.image;
|
|
237
|
-
|
|
238
|
-
if (isImageBitmap(image)) {
|
|
239
|
-
|
|
240
|
-
const canvas = document.createElement('canvas');
|
|
241
|
-
canvas.width = image.width;
|
|
242
|
-
canvas.height = image.height;
|
|
243
|
-
|
|
244
|
-
const context = canvas.getContext('2d');
|
|
245
|
-
if (!context) {
|
|
246
|
-
console.error("Failed getting canvas 2d context");
|
|
247
|
-
return null;
|
|
248
|
-
}
|
|
249
|
-
context.drawImage(image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height);
|
|
250
|
-
return canvas;
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
return null;
|
|
254
295
|
}
|
|
@@ -157,7 +157,14 @@ export type Vec2 = {
|
|
|
157
157
|
export type Vec3 = {
|
|
158
158
|
x: number,
|
|
159
159
|
y: number,
|
|
160
|
-
z: number
|
|
160
|
+
z: number,
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export type Vec4 = {
|
|
164
|
+
x: number,
|
|
165
|
+
y: number,
|
|
166
|
+
z: number,
|
|
167
|
+
w: number,
|
|
161
168
|
}
|
|
162
169
|
|
|
163
170
|
const contactsVectorBuffer = new CircularBuffer(() => new Vector3(), 20);
|
|
@@ -32,12 +32,12 @@ export class AnimationCurve {
|
|
|
32
32
|
if (!this.keys || this.keys.length == 0) return 0;
|
|
33
33
|
// if the first keyframe time is already greater than the time we want to evaluate
|
|
34
34
|
// then we dont need to iterate
|
|
35
|
-
if (this.keys[0].time
|
|
35
|
+
if (this.keys[0].time >= time) {
|
|
36
36
|
return this.keys[0].value;
|
|
37
37
|
}
|
|
38
38
|
for (let i = 0; i < this.keys.length; i++) {
|
|
39
39
|
const kf = this.keys[i];
|
|
40
|
-
if (kf.time
|
|
40
|
+
if (kf.time <= time) {
|
|
41
41
|
const hasNextKeyframe = i+1 < this.keys.length;
|
|
42
42
|
if (hasNextKeyframe) {
|
|
43
43
|
const nextKf = this.keys[i+1];
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Behaviour } from "./Component";
|
|
2
2
|
import * as THREE from "three";
|
|
3
3
|
import { getParam } from "../engine/engine_utils";
|
|
4
|
-
import { CreateWireCube } from "../engine/engine_gizmos";
|
|
4
|
+
import { CreateWireCube, Gizmos } from "../engine/engine_gizmos";
|
|
5
5
|
import { getWorldPosition, getWorldScale } from "../engine/engine_three_utils";
|
|
6
6
|
|
|
7
7
|
const gizmos = getParam("gizmos");
|
|
8
|
+
const debug = getParam("debugboxhelper");
|
|
8
9
|
|
|
9
10
|
export class BoxHelperComponent extends Behaviour {
|
|
10
11
|
|
|
@@ -14,7 +15,7 @@ export class BoxHelperComponent extends Behaviour {
|
|
|
14
15
|
private static _position: THREE.Vector3 = new THREE.Vector3();
|
|
15
16
|
private static _size: THREE.Vector3 = new THREE.Vector3(.01, .01, .01);
|
|
16
17
|
|
|
17
|
-
public isInBox(obj: THREE.Object3D, scaleFactor
|
|
18
|
+
public isInBox(obj: THREE.Object3D, scaleFactor?: number): boolean | undefined {
|
|
18
19
|
if (!obj) return undefined;
|
|
19
20
|
|
|
20
21
|
// if (!obj.geometry.boundingBox) obj.geometry.computeBoundingBox();
|
|
@@ -24,15 +25,13 @@ export class BoxHelperComponent extends Behaviour {
|
|
|
24
25
|
this.box = new THREE.Box3();
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
|
|
27
29
|
if (obj.type === "Mesh") {
|
|
28
30
|
BoxHelperComponent.testBox.setFromObject(obj);
|
|
29
31
|
}
|
|
30
32
|
else if (obj.type === "Group") {
|
|
33
|
+
BoxHelperComponent.testBox.makeEmpty();
|
|
31
34
|
if (obj.children.length > 0) {
|
|
32
|
-
BoxHelperComponent.testBox.setFromCenterAndSize(
|
|
33
|
-
BoxHelperComponent._position.set(0, 0, 0),
|
|
34
|
-
BoxHelperComponent._size.set(0, 0, 0)
|
|
35
|
-
);
|
|
36
35
|
for (let i = 0; i < obj.children.length; i++) {
|
|
37
36
|
const ch = obj.children[i];
|
|
38
37
|
if (ch.type === "Mesh") {
|
|
@@ -44,19 +43,17 @@ export class BoxHelperComponent extends Behaviour {
|
|
|
44
43
|
else {
|
|
45
44
|
const wp = getWorldPosition(obj, BoxHelperComponent._position);
|
|
46
45
|
const size = getWorldScale(obj, BoxHelperComponent._size);
|
|
47
|
-
if(scaleFactor !== undefined) size.multiplyScalar(scaleFactor);
|
|
46
|
+
if (scaleFactor !== undefined) size.multiplyScalar(scaleFactor);
|
|
48
47
|
BoxHelperComponent.testBox.setFromCenterAndSize(wp, size);
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
// console.log("Obj box:", obj.name, obj.uuid, BoxHelperComponent.testBox.getCenter(new THREE.Vector3()), BoxHelperComponent.testBox.getSize(new THREE.Vector3()),
|
|
52
|
-
// "\nTest box:", this.box.getCenter(new THREE.Vector3()), this.box.getSize(new THREE.Vector3()), this.context.time.frameCount);
|
|
53
|
-
// this.tp.copy(obj.geometry.boundingBox);
|
|
54
|
-
// // this.tp.setFromCenterAndSize(obj.position, obj.scale);
|
|
55
|
-
// this.tp.applyMatrix4(obj.matrixWorld);
|
|
56
|
-
// console.log(this.tp);
|
|
57
|
-
// console.log(this.box?.min, this.box?.max, this.tp.min, this.tp.max);
|
|
58
50
|
this.updateBox();
|
|
59
|
-
|
|
51
|
+
const intersects = this.box?.intersectsBox(BoxHelperComponent.testBox);
|
|
52
|
+
if (intersects) {
|
|
53
|
+
if (debug)
|
|
54
|
+
Gizmos.DrawBox3(BoxHelperComponent.testBox, 0xff0000, 5);
|
|
55
|
+
}
|
|
56
|
+
return intersects;
|
|
60
57
|
}
|
|
61
58
|
|
|
62
59
|
public intersects(box: THREE.Box3): boolean {
|
|
@@ -1,107 +1,160 @@
|
|
|
1
1
|
import { Behaviour, GameObject } from "./Component";
|
|
2
2
|
import * as THREE from "three";
|
|
3
|
-
import { MainModule, EmissionModule, ShapeModule, ParticleSystemShapeType, MinMaxCurve, MinMaxGradient, ColorOverLifetimeModule, SizeOverLifetimeModule, NoiseModule, ParticleSystemSimulationSpace } from "./ParticleSystemModules"
|
|
3
|
+
import { MainModule, EmissionModule, ShapeModule, ParticleSystemShapeType, MinMaxCurve, MinMaxGradient, ColorOverLifetimeModule, SizeOverLifetimeModule, NoiseModule, ParticleSystemSimulationSpace, ParticleBurst, IParticleSystem, ParticleSystemRenderMode } from "./ParticleSystemModules"
|
|
4
4
|
import { getParam } from "../engine/engine_utils";
|
|
5
|
-
|
|
6
|
-
Emitter,
|
|
7
|
-
Rate,
|
|
8
|
-
Span,
|
|
9
|
-
Position,
|
|
10
|
-
Mass,
|
|
11
|
-
Radius,
|
|
12
|
-
Life,
|
|
13
|
-
PointZone,
|
|
14
|
-
Vector3D,
|
|
15
|
-
Alpha,
|
|
16
|
-
Scale,
|
|
17
|
-
Color,
|
|
18
|
-
SpriteRenderer,
|
|
19
|
-
RadialVelocity,
|
|
20
|
-
BoxZone,
|
|
21
|
-
Particle,
|
|
22
|
-
Gravity,
|
|
23
|
-
SphereZone,
|
|
24
|
-
} from 'three-nebula';
|
|
5
|
+
|
|
25
6
|
// https://github.dev/creativelifeform/three-nebula
|
|
7
|
+
import System, { Emitter, Position, Life, SpriteRenderer, Particle, Body, MeshRenderer, } from 'three-nebula';
|
|
26
8
|
|
|
27
9
|
import { serializeable } from "../engine/engine_serialization";
|
|
28
10
|
import { Time } from "../engine/engine_time";
|
|
29
11
|
import { Context } from "../engine/engine_setup";
|
|
30
12
|
import { RGBAColor } from "./js-extensions/RGBAColor";
|
|
31
|
-
import {
|
|
32
|
-
import { Vec3 } from "../engine/engine_types";
|
|
33
|
-
import { AxesHelper, Material, Object3D, Sprite, SpriteMaterial, Vector3 } from "three";
|
|
34
|
-
import { Renderer } from "./Renderer";
|
|
13
|
+
import { AxesHelper, BufferGeometry, Material, Mesh, Object3D, Sprite, SpriteMaterial, Vector3 } from "three";
|
|
35
14
|
import { getWorldQuaternion, getWorldScale } from "../engine/engine_three_utils";
|
|
15
|
+
import { assign } from "../engine/engine_serialization_core";
|
|
36
16
|
|
|
37
17
|
const debug = getParam("debugparticles");
|
|
38
18
|
|
|
39
19
|
|
|
40
|
-
|
|
41
20
|
export class ParticleSystemRenderer extends Behaviour {
|
|
42
21
|
|
|
22
|
+
@serializeable()
|
|
23
|
+
renderMode?: ParticleSystemRenderMode;
|
|
24
|
+
|
|
43
25
|
@serializeable(Material)
|
|
44
26
|
particleMaterial?: SpriteMaterial;
|
|
27
|
+
|
|
28
|
+
// @serializeable(Mesh)
|
|
29
|
+
particleMesh?: Mesh | string;
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
getMesh() {
|
|
33
|
+
let geo: BufferGeometry | null = null;
|
|
34
|
+
if (this.particleMesh instanceof Mesh) {
|
|
35
|
+
geo = this.particleMesh.geometry;
|
|
36
|
+
}
|
|
37
|
+
if (geo === null) geo = new THREE.BoxGeometry(1, 1, 1);
|
|
38
|
+
const res = new Mesh(geo, this.particleMaterial);
|
|
39
|
+
return res;
|
|
40
|
+
}
|
|
45
41
|
}
|
|
46
42
|
|
|
47
|
-
export class ParticleSystem extends Behaviour {
|
|
43
|
+
export class ParticleSystem extends Behaviour implements IParticleSystem {
|
|
48
44
|
|
|
49
45
|
@serializeable(ColorOverLifetimeModule)
|
|
50
|
-
colorOverLifetime!: ColorOverLifetimeModule;
|
|
46
|
+
readonly colorOverLifetime!: ColorOverLifetimeModule;
|
|
51
47
|
|
|
52
48
|
@serializeable(MainModule)
|
|
53
|
-
main!: MainModule;
|
|
49
|
+
readonly main!: MainModule;
|
|
54
50
|
|
|
55
51
|
@serializeable(EmissionModule)
|
|
56
|
-
emission!: EmissionModule;
|
|
52
|
+
readonly emission!: EmissionModule;
|
|
57
53
|
|
|
58
54
|
@serializeable(SizeOverLifetimeModule)
|
|
59
|
-
sizeOverLifetime!: SizeOverLifetimeModule;
|
|
55
|
+
readonly sizeOverLifetime!: SizeOverLifetimeModule;
|
|
60
56
|
|
|
61
57
|
@serializeable(ShapeModule)
|
|
62
|
-
shape!: ShapeModule;
|
|
58
|
+
readonly shape!: ShapeModule;
|
|
63
59
|
|
|
64
60
|
@serializeable(NoiseModule)
|
|
65
|
-
noise!: NoiseModule;
|
|
61
|
+
readonly noise!: NoiseModule;
|
|
62
|
+
|
|
63
|
+
get renderer(): ParticleSystemRenderer {
|
|
64
|
+
return this._renderer;
|
|
65
|
+
}
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
get currentParticles() {
|
|
68
|
+
return this._system?.getCount() ?? 0;
|
|
69
|
+
}
|
|
70
|
+
get maxParticles() {
|
|
71
|
+
return this.main.maxParticles;
|
|
72
|
+
}
|
|
73
|
+
get time() {
|
|
74
|
+
return this._time;
|
|
75
|
+
}
|
|
76
|
+
get duration() {
|
|
77
|
+
return this.main.duration;
|
|
78
|
+
}
|
|
68
79
|
|
|
80
|
+
private _renderer!: ParticleSystemRenderer;
|
|
69
81
|
private _system!: System;
|
|
70
82
|
private _emitter: Emitter;
|
|
71
83
|
private _size!: SizeBehaviour;
|
|
84
|
+
private _container?: Object3D;
|
|
85
|
+
private _time: number = 0;
|
|
86
|
+
|
|
87
|
+
/** called from deserialization */
|
|
88
|
+
private set bursts(arr: ParticleBurst[]) {
|
|
89
|
+
for (let i = 0; i < arr.length; i++) {
|
|
90
|
+
const burst = arr[i];
|
|
91
|
+
if ((burst instanceof ParticleBurst) === false) {
|
|
92
|
+
const instance = new ParticleBurst();
|
|
93
|
+
assign(instance, burst);
|
|
94
|
+
arr[i] = instance;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
this._bursts = arr;
|
|
98
|
+
}
|
|
99
|
+
private _bursts?: ParticleBurst[];
|
|
72
100
|
|
|
73
101
|
awake(): void {
|
|
102
|
+
this._renderer = this.gameObject.getComponent(ParticleSystemRenderer) as ParticleSystemRenderer;
|
|
103
|
+
this._system = new System();
|
|
104
|
+
|
|
105
|
+
if (this.main.simulationSpace == ParticleSystemSimulationSpace.Local) {
|
|
106
|
+
this._container = new Object3D();
|
|
107
|
+
this._container.matrixAutoUpdate = false;
|
|
108
|
+
this.gameObject.add(this._container);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const container = this.main.simulationSpace == ParticleSystemSimulationSpace.Local ? this._container : this.context.scene;
|
|
112
|
+
|
|
74
113
|
if (debug) {
|
|
75
114
|
console.log(this);
|
|
76
115
|
this.gameObject.add(new AxesHelper(1))
|
|
77
116
|
}
|
|
117
|
+
const renderMode = this._renderer.renderMode;
|
|
118
|
+
let renderer: any = undefined;
|
|
119
|
+
|
|
120
|
+
switch (renderMode) {
|
|
121
|
+
case ParticleSystemRenderMode.Mesh:
|
|
122
|
+
// https://three-nebula-docs.netlify.app/class/src/renderer/meshrenderer.js~meshrenderer
|
|
123
|
+
// mesh renderer example: https://github.com/creativelifeform/three-nebula/blob/master/website/components/Examples/MeshRenderer/init.js
|
|
124
|
+
renderer = new MeshRenderer(container, THREE);
|
|
125
|
+
break;
|
|
126
|
+
case ParticleSystemRenderMode.Billboard:
|
|
127
|
+
renderer = new SpriteRenderer(container, THREE);
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
78
130
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
131
|
+
if (!renderer) {
|
|
132
|
+
console.error("No renderer for particle system");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
83
135
|
|
|
84
|
-
const renderer = new SpriteRenderer(container, THREE);
|
|
85
136
|
renderer.logRendererType = () => { };
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
137
|
+
if (renderMode === ParticleSystemRenderMode.Billboard) {
|
|
138
|
+
if (this.renderer.particleMaterial) {
|
|
139
|
+
const sprite = renderer._body as Sprite;
|
|
140
|
+
sprite.layers.disableAll();
|
|
141
|
+
sprite.layers.set(2); // ignore raycasting particles
|
|
142
|
+
sprite.renderOrder = 1;
|
|
143
|
+
sprite.material.map = this.renderer.particleMaterial.map;
|
|
144
|
+
sprite.material.transparent = true;
|
|
145
|
+
// sprite.material.sizeAttenuation = false;
|
|
146
|
+
sprite.material.blending = this.renderer.particleMaterial.blending;
|
|
147
|
+
sprite.material.blendDst = this.renderer.particleMaterial.blendDst;
|
|
148
|
+
sprite.material.blendDstAlpha = this.renderer.particleMaterial.blendDstAlpha;
|
|
149
|
+
sprite.material.blendEquation = this.renderer.particleMaterial.blendEquation;
|
|
150
|
+
sprite.material.blendEquationAlpha = this.renderer.particleMaterial.blendEquationAlpha;
|
|
151
|
+
sprite.material.blendSrc = this.renderer.particleMaterial.blendSrc;
|
|
152
|
+
sprite.material.blendSrcAlpha = this.renderer.particleMaterial.blendSrcAlpha;
|
|
153
|
+
sprite.material.premultipliedAlpha = this.renderer.particleMaterial.premultipliedAlpha;
|
|
154
|
+
sprite.material.depthWrite = false;
|
|
155
|
+
sprite.material.depthTest = true;
|
|
156
|
+
if (debug) console.log(sprite);
|
|
157
|
+
}
|
|
105
158
|
}
|
|
106
159
|
|
|
107
160
|
this._system.addRenderer(renderer);
|
|
@@ -109,8 +162,6 @@ export class ParticleSystem extends Behaviour {
|
|
|
109
162
|
const initializers: Array<any> = [];
|
|
110
163
|
const behaviours: Array<any> = [];
|
|
111
164
|
|
|
112
|
-
|
|
113
|
-
|
|
114
165
|
const life = new Life();
|
|
115
166
|
life.lifePan = new MinMaxCurveSpan(this.main.startLifetime, 1);
|
|
116
167
|
initializers.push(life);
|
|
@@ -130,6 +181,9 @@ export class ParticleSystem extends Behaviour {
|
|
|
130
181
|
velocity.gravityModifierMultiplier = this.main.gravityModifierMultiplier;
|
|
131
182
|
behaviours.push(velocity);
|
|
132
183
|
|
|
184
|
+
if (renderMode === ParticleSystemRenderMode.Mesh) {
|
|
185
|
+
initializers.push(new Body(this._renderer.getMesh()))
|
|
186
|
+
}
|
|
133
187
|
|
|
134
188
|
const emitter = this._emitter = new Emitter({});
|
|
135
189
|
emitter.setInitializers(initializers);
|
|
@@ -142,6 +196,7 @@ export class ParticleSystem extends Behaviour {
|
|
|
142
196
|
this._emitter.emit();
|
|
143
197
|
this._system.addEmitter(this._emitter);
|
|
144
198
|
this._system.emit({ onStart: () => { }, onUpdate: () => { }, onEnd: () => { }, });
|
|
199
|
+
this._time = 0;
|
|
145
200
|
}
|
|
146
201
|
|
|
147
202
|
onDisable() {
|
|
@@ -149,11 +204,9 @@ export class ParticleSystem extends Behaviour {
|
|
|
149
204
|
}
|
|
150
205
|
|
|
151
206
|
onBeforeRender() {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
this.noise.update(this.context);
|
|
156
|
-
this._system.update(this.context.time.deltaTime * this.main.simulationSpeed);
|
|
207
|
+
if (this._bursts) {
|
|
208
|
+
this.emission.bursts = this._bursts;
|
|
209
|
+
}
|
|
157
210
|
|
|
158
211
|
// sprite materials must be scaled in AR
|
|
159
212
|
const cam = this.context.mainCamera;
|
|
@@ -161,13 +214,37 @@ export class ParticleSystem extends Behaviour {
|
|
|
161
214
|
const scale = getWorldScale(cam);
|
|
162
215
|
this._size.cameraScale = scale.x;
|
|
163
216
|
}
|
|
217
|
+
|
|
218
|
+
if (this._container && this.gameObject?.parent) {
|
|
219
|
+
// this._container.matrix.copy(this.gameObject.matrixWorld);
|
|
220
|
+
// this._container.matrixWorld.copy(this.gameObject.matrixWorld);
|
|
221
|
+
|
|
222
|
+
const scale = getWorldScale(this.gameObject.parent);
|
|
223
|
+
scale.x = 1 / scale.x;
|
|
224
|
+
scale.y = 1 / scale.y;
|
|
225
|
+
scale.z = 1 / scale.z;
|
|
226
|
+
// console.log(scale);
|
|
227
|
+
// this._container.scale.copy(scale);
|
|
228
|
+
// // this._container.updateMatrix();
|
|
229
|
+
// // this._container.updateMatrixWorld();
|
|
230
|
+
// // // set scale to 1
|
|
231
|
+
this._container.matrix.identity();
|
|
232
|
+
this._container.matrix.scale(scale);
|
|
233
|
+
// this._container.matrixWorld.copy(this._container.matrix);
|
|
234
|
+
}
|
|
235
|
+
// this._system.time
|
|
236
|
+
// this._system.duration = this.main.duration;
|
|
237
|
+
this.emission.system = this;
|
|
238
|
+
this.shape.update(this.context, this.main.simulationSpace, this.gameObject);
|
|
239
|
+
this.noise.update(this.context);
|
|
240
|
+
this._system.update(this.context.time.deltaTime * this.main.simulationSpeed);
|
|
241
|
+
this._time += this.context.time.deltaTime;
|
|
242
|
+
if (this._time > this.duration) this._time = 0;
|
|
164
243
|
}
|
|
165
244
|
|
|
166
245
|
}
|
|
167
246
|
|
|
168
247
|
|
|
169
|
-
|
|
170
|
-
|
|
171
248
|
class MinMaxCurveSpan {
|
|
172
249
|
|
|
173
250
|
readonly time: Time;
|
|
@@ -308,7 +385,7 @@ class VelocityBehaviour extends CustomBehaviour {
|
|
|
308
385
|
gravity?: MinMaxCurve;
|
|
309
386
|
gravityModifierMultiplier: number = 1;
|
|
310
387
|
|
|
311
|
-
downDirection = new Vector3(
|
|
388
|
+
downDirection = new Vector3();
|
|
312
389
|
private _temp: Vector3 = new Vector3();
|
|
313
390
|
|
|
314
391
|
constructor(container: Object3D, shape: ShapeModule, noise: NoiseModule, startCurve: MinMaxCurve, startMultiplier: number) {
|
|
@@ -319,8 +396,6 @@ class VelocityBehaviour extends CustomBehaviour {
|
|
|
319
396
|
this.startSpeed = startCurve;
|
|
320
397
|
this.startMultiplier = startMultiplier;
|
|
321
398
|
|
|
322
|
-
const quat = getWorldQuaternion(container);
|
|
323
|
-
this.downDirection.applyQuaternion(quat);
|
|
324
399
|
}
|
|
325
400
|
|
|
326
401
|
applyBehaviour(target: ParticleWithVelocity | Emitter, deltaTime: number, index: number) {
|
|
@@ -329,11 +404,11 @@ class VelocityBehaviour extends CustomBehaviour {
|
|
|
329
404
|
// target.velocity.x = target.velocity_start.x;
|
|
330
405
|
// target.velocity.y = target.velocity_start.y;
|
|
331
406
|
// target.velocity.z = target.velocity_start.z;
|
|
332
|
-
if (target.gravity) {
|
|
407
|
+
if (target.gravity !== 0 && this.gravity) {
|
|
333
408
|
// get world down vector
|
|
334
409
|
this._temp.copy(this.downDirection);
|
|
335
410
|
this._temp.multiplyScalar(target.gravity * deltaTime);
|
|
336
|
-
target.velocity.
|
|
411
|
+
target.velocity.add(this._temp);
|
|
337
412
|
}
|
|
338
413
|
|
|
339
414
|
if (this.noise) {
|
|
@@ -344,6 +419,7 @@ class VelocityBehaviour extends CustomBehaviour {
|
|
|
344
419
|
|
|
345
420
|
initialize(particle: ParticleWithVelocity) {
|
|
346
421
|
const dir = this.shape.getDirection(particle.position);
|
|
422
|
+
// dir.applyQuaternion(quat);
|
|
347
423
|
const speed = this.startSpeed.evaluate(0, Math.random()) * this.startMultiplier;
|
|
348
424
|
particle.velocity.x = dir.x * speed;
|
|
349
425
|
particle.velocity.y = dir.y * speed;
|
|
@@ -355,14 +431,14 @@ class VelocityBehaviour extends CustomBehaviour {
|
|
|
355
431
|
particle.velocity_start.z = particle.velocity.z;
|
|
356
432
|
|
|
357
433
|
if (this.gravity) {
|
|
358
|
-
particle.gravity = this.gravity.evaluate(
|
|
434
|
+
particle.gravity = this.gravity.evaluate(Math.random(), Math.random()) * 9.81;
|
|
359
435
|
}
|
|
360
436
|
else {
|
|
361
437
|
particle.gravity = 0;
|
|
362
438
|
}
|
|
363
439
|
|
|
364
|
-
|
|
365
|
-
|
|
440
|
+
const quat = getWorldQuaternion(this.container);
|
|
441
|
+
this.downDirection.set(0, -1, 0).applyQuaternion(quat);
|
|
366
442
|
}
|
|
367
443
|
|
|
368
444
|
}
|