@woosh/meep-engine 2.33.52 → 2.33.56
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/core/geom/3d/shape/AbstractShape3D.js +17 -0
- package/core/geom/3d/shape/TransformedShape3D.d.ts +10 -0
- package/core/geom/3d/shape/TransformedShape3D.js +28 -3
- package/core/geom/3d/shape/TransformedShape3D.spec.js +22 -0
- package/core/geom/3d/shape/UnionShape3D.d.ts +5 -0
- package/core/geom/3d/shape/UnionShape3D.js +29 -0
- package/core/geom/3d/shape/UnitCubeShape3D.d.ts +5 -0
- package/core/geom/3d/shape/UnitCubeShape3D.js +7 -1
- package/core/geom/3d/shape/UnitSphereShape3D.d.ts +5 -0
- package/core/geom/3d/shape/UnitSphereShape3D.js +7 -1
- package/core/geom/3d/shape/json/shape_from_json.js +19 -0
- package/core/geom/3d/shape/json/shape_to_json.js +21 -0
- package/core/geom/3d/shape/json/shape_to_type.js +23 -0
- package/core/geom/3d/shape/json/type_adapters.js +58 -0
- package/core/math/MathUtils.spec.js +0 -11
- package/core/math/hash/computeHashFloatArray.js +4 -15
- package/core/math/hash/computeHashFloatArray.spec.js +11 -0
- package/core/model/object/read_property.js +1 -1
- package/core/model/object/write_property.js +6 -6
- package/engine/ecs/transform/Transform.d.ts +4 -0
- package/engine/graphics/composit/CompositLayer.js +22 -2
- package/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.js +2 -2
- package/engine/graphics/particles/particular/engine/renderers/billboard/ParticleBillboardMaterial.js +38 -66
- package/engine/graphics/particles/particular/engine/utils/volume/AttributeValue.js +54 -0
- package/engine/graphics/particles/particular/engine/utils/volume/ParticleVolume.js +73 -28
- package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +2 -3
- package/engine/graphics/render/buffer/simple-fx/DepthLimitedBlur.js +7 -3
- package/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js +25 -18
- package/engine/graphics/render/buffer/simple-fx/ao/SAOShader.js +18 -16
- package/engine/graphics/shaders/ScreenSpaceQuadShader.js +23 -3
- package/package.json +1 -1
|
@@ -62,4 +62,21 @@ export class AbstractShape3D {
|
|
|
62
62
|
get surface_area() {
|
|
63
63
|
throw new Error('Not Implemented');
|
|
64
64
|
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @template {AbstractShape3D} T
|
|
68
|
+
* @param {T} other
|
|
69
|
+
* @returns {boolean}
|
|
70
|
+
*/
|
|
71
|
+
equals(other) {
|
|
72
|
+
return this.constructor === other.constructor;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
*
|
|
77
|
+
* @returns {number}
|
|
78
|
+
*/
|
|
79
|
+
hash() {
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
65
82
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {AbstractShape3D} from "./AbstractShape3D";
|
|
2
|
+
|
|
3
|
+
export class TransformedShape3D extends AbstractShape3D {
|
|
4
|
+
|
|
5
|
+
static from_m4(subject: AbstractShape3D, matrix: ArrayLike<number>): TransformedShape3D
|
|
6
|
+
|
|
7
|
+
static from_translation_scale(subject: AbstractShape3D, translation: ArrayLike<number>, scale: ArrayLike<number>): TransformedShape3D
|
|
8
|
+
|
|
9
|
+
static from_scale(subject: AbstractShape3D, scale: ArrayLike<number>): TransformedShape3D
|
|
10
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { mat4, vec3 } from "gl-matrix";
|
|
2
2
|
import { AbstractShape3D } from "./AbstractShape3D.js";
|
|
3
3
|
import { sign } from "../../../math/sign.js";
|
|
4
|
+
import { isArrayEqualStrict } from "../../../collection/array/isArrayEqualStrict.js";
|
|
5
|
+
import { computeHashFloatArray } from "../../../math/hash/computeHashFloatArray.js";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
*
|
|
@@ -140,9 +142,9 @@ export class TransformedShape3D extends AbstractShape3D {
|
|
|
140
142
|
// normalize gradient
|
|
141
143
|
const gd = distance / Math.hypot(scratch_v3_0[0], scratch_v3_0[1], scratch_v3_0[2]);
|
|
142
144
|
|
|
143
|
-
const scaled_dx = scaled_gx * gd
|
|
144
|
-
const scaled_dy = scaled_gy * gd
|
|
145
|
-
const scaled_dz = scaled_gz * gd
|
|
145
|
+
const scaled_dx = scaled_gx * gd;
|
|
146
|
+
const scaled_dy = scaled_gy * gd;
|
|
147
|
+
const scaled_dz = scaled_gz * gd;
|
|
146
148
|
|
|
147
149
|
// determine sign
|
|
148
150
|
const result_sign = sign(distance);
|
|
@@ -178,4 +180,27 @@ export class TransformedShape3D extends AbstractShape3D {
|
|
|
178
180
|
result[1] = scratch_v3_0[1];
|
|
179
181
|
result[2] = scratch_v3_0[2];
|
|
180
182
|
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
*
|
|
186
|
+
* @param {TransformedShape3D} other
|
|
187
|
+
* @returns {boolean}
|
|
188
|
+
*/
|
|
189
|
+
equals(other) {
|
|
190
|
+
return super.equals(other)
|
|
191
|
+
&& this.__subject.equals(other.subject)
|
|
192
|
+
&& isArrayEqualStrict(this.__matrix, other.__matrix)
|
|
193
|
+
;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
hash() {
|
|
197
|
+
// to avoid recursion, only use matrix for hash
|
|
198
|
+
return computeHashFloatArray(this.__matrix);
|
|
199
|
+
}
|
|
181
200
|
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* @readonly
|
|
204
|
+
* @type {boolean}
|
|
205
|
+
*/
|
|
206
|
+
TransformedShape3D.isTransformedShape3D = true;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { UnitCubeShape3D } from "./UnitCubeShape3D.js";
|
|
2
2
|
import { TransformedShape3D } from "./TransformedShape3D.js";
|
|
3
|
+
import { UnitSphereShape3D } from "./UnitSphereShape3D.js";
|
|
3
4
|
|
|
4
5
|
test('signed distance identity transform', () => {
|
|
5
6
|
|
|
@@ -31,3 +32,24 @@ test('signed distance scale', () => {
|
|
|
31
32
|
|
|
32
33
|
expect(s.signed_distance_at_point([2, 0, 0])).toBeCloseTo(1);
|
|
33
34
|
});
|
|
35
|
+
|
|
36
|
+
test('equals', () => {
|
|
37
|
+
|
|
38
|
+
const a = TransformedShape3D.from_translation_scale(new UnitCubeShape3D(), [0, 0, 0], [2, 2, 2]);
|
|
39
|
+
const b = TransformedShape3D.from_translation_scale(new UnitCubeShape3D(), [0, 0, 0], [2, 2, 2]);
|
|
40
|
+
|
|
41
|
+
expect(a.equals(b)).toBe(true);
|
|
42
|
+
|
|
43
|
+
const c = TransformedShape3D.from_translation_scale(new UnitCubeShape3D(), [0, 1, 0], [2, 2, 2]);
|
|
44
|
+
|
|
45
|
+
expect(a.equals(c)).toBe(false);
|
|
46
|
+
|
|
47
|
+
const d = TransformedShape3D.from_translation_scale(new UnitCubeShape3D(), [0, 0, 0], [2, 1, 2]);
|
|
48
|
+
|
|
49
|
+
expect(a.equals(d)).toBe(false);
|
|
50
|
+
|
|
51
|
+
const e = TransformedShape3D.from_translation_scale(new UnitSphereShape3D(), [0, 0, 0], [2, 2, 2]);
|
|
52
|
+
|
|
53
|
+
expect(a.equals(e)).toBe(false);
|
|
54
|
+
|
|
55
|
+
});
|
|
@@ -4,6 +4,7 @@ import { binarySearchHighIndex } from "../../../collection/array/binarySearchHig
|
|
|
4
4
|
import { compareNumbers } from "../../../primitives/numbers/compareNumbers.js";
|
|
5
5
|
import { min2 } from "../../../math/min2.js";
|
|
6
6
|
import { compute_signed_distance_gradient_by_sampling } from "./util/compute_signed_distance_gradient_by_sampling.js";
|
|
7
|
+
import { isArrayEqual } from "../../../collection/ArrayUtils.js";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* To avoid severe performance overhead, we limit number of possible rejections
|
|
@@ -119,6 +120,11 @@ export class UnionShape3D extends AbstractShape3D {
|
|
|
119
120
|
this.__volume_overlap_fraction = tap_overlaps / (VOLUME_OVERLAP_ESTIMATION_TAPS);
|
|
120
121
|
}
|
|
121
122
|
|
|
123
|
+
/**
|
|
124
|
+
*
|
|
125
|
+
* @param {AbstractShape3D[]} children
|
|
126
|
+
* @returns {UnionShape3D}
|
|
127
|
+
*/
|
|
122
128
|
static from(children) {
|
|
123
129
|
const r = new UnionShape3D();
|
|
124
130
|
|
|
@@ -252,4 +258,27 @@ export class UnionShape3D extends AbstractShape3D {
|
|
|
252
258
|
break;
|
|
253
259
|
}
|
|
254
260
|
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
*
|
|
264
|
+
* @param {UnionShape3D} other
|
|
265
|
+
* @returns {boolean}
|
|
266
|
+
*/
|
|
267
|
+
equals(other) {
|
|
268
|
+
return super.equals(other)
|
|
269
|
+
&& isArrayEqual(this.children, other.children);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
hash() {
|
|
273
|
+
const children = this.children;
|
|
274
|
+
const child_count = children.length;
|
|
275
|
+
|
|
276
|
+
let hash = child_count;
|
|
277
|
+
|
|
278
|
+
if (child_count > 0) {
|
|
279
|
+
hash ^= children[0].hash();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return hash;
|
|
283
|
+
}
|
|
255
284
|
}
|
|
@@ -58,7 +58,7 @@ export class UnitCubeShape3D extends AbstractShape3D {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
signed_distance_gradient_at_point(result, point) {
|
|
61
|
-
return compute_signed_distance_gradient_by_sampling(result,this, point);
|
|
61
|
+
return compute_signed_distance_gradient_by_sampling(result, this, point);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
signed_distance_at_point(point) {
|
|
@@ -98,4 +98,10 @@ export class UnitCubeShape3D extends AbstractShape3D {
|
|
|
98
98
|
sample_random_point_in_volume(result, result_offset, random) {
|
|
99
99
|
randomPointInBox(random, result, result_offset);
|
|
100
100
|
}
|
|
101
|
+
|
|
102
|
+
hash() {
|
|
103
|
+
return 1;
|
|
104
|
+
}
|
|
101
105
|
}
|
|
106
|
+
|
|
107
|
+
UnitCubeShape3D.INSTANCE = new UnitCubeShape3D();
|
|
@@ -30,7 +30,7 @@ export class UnitSphereShape3D extends AbstractShape3D {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
signed_distance_gradient_at_point(result, point) {
|
|
33
|
-
return compute_signed_distance_gradient_by_sampling(result,this, point);
|
|
33
|
+
return compute_signed_distance_gradient_by_sampling(result, this, point);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
signed_distance_at_point(point) {
|
|
@@ -52,4 +52,10 @@ export class UnitSphereShape3D extends AbstractShape3D {
|
|
|
52
52
|
sample_random_point_in_volume(result, result_offset, random) {
|
|
53
53
|
randomPointInSphere(random, result, result_offset);
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
hash() {
|
|
57
|
+
return 13;
|
|
58
|
+
}
|
|
55
59
|
}
|
|
60
|
+
|
|
61
|
+
UnitSphereShape3D.INSTANCE = new UnitSphereShape3D();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type_adapters } from "./type_adapters.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {Object} j
|
|
6
|
+
* @returns {AbstractShape3D}
|
|
7
|
+
*/
|
|
8
|
+
export function shape_from_json(j) {
|
|
9
|
+
const type = j.type;
|
|
10
|
+
|
|
11
|
+
const adapter = type_adapters[type];
|
|
12
|
+
|
|
13
|
+
if (adapter === undefined) {
|
|
14
|
+
throw new Error(`Unsupported shape type '${type}'`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return adapter.read(j);
|
|
18
|
+
}
|
|
19
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { shape_to_type } from "./shape_to_type.js";
|
|
2
|
+
import { type_adapters } from "./type_adapters.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {AbstractShape3D} shape
|
|
7
|
+
* @returns {Object}
|
|
8
|
+
*/
|
|
9
|
+
export function shape_to_json(shape) {
|
|
10
|
+
const type = shape_to_type(shape);
|
|
11
|
+
|
|
12
|
+
const adapter = type_adapters[type];
|
|
13
|
+
|
|
14
|
+
if (adapter === undefined) {
|
|
15
|
+
throw new Error(`Unsupported shape type '${type}'`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const shape_json = adapter.write(shape);
|
|
19
|
+
|
|
20
|
+
return Object.assign({ type }, shape_json);
|
|
21
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { UnionShape3D } from "../UnionShape3D.js";
|
|
2
|
+
import { UnitCubeShape3D } from "../UnitCubeShape3D.js";
|
|
3
|
+
import { UnitSphereShape3D } from "../UnitSphereShape3D.js";
|
|
4
|
+
import { TransformedShape3D } from "../TransformedShape3D.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param {AbstractShape3D} shape
|
|
9
|
+
* @returns {string}
|
|
10
|
+
*/
|
|
11
|
+
export function shape_to_type(shape) {
|
|
12
|
+
if (shape instanceof UnionShape3D) {
|
|
13
|
+
return 'union';
|
|
14
|
+
} else if (shape instanceof UnitCubeShape3D) {
|
|
15
|
+
return 'cube';
|
|
16
|
+
} else if (shape instanceof UnitSphereShape3D) {
|
|
17
|
+
return 'sphere';
|
|
18
|
+
} else if (shape instanceof TransformedShape3D) {
|
|
19
|
+
return 'transform';
|
|
20
|
+
} else {
|
|
21
|
+
throw new Error('Unsupported shape');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { UnitCubeShape3D } from "../UnitCubeShape3D.js";
|
|
2
|
+
import { UnitSphereShape3D } from "../UnitSphereShape3D.js";
|
|
3
|
+
import { TransformedShape3D } from "../TransformedShape3D.js";
|
|
4
|
+
import { UnionShape3D } from "../UnionShape3D.js";
|
|
5
|
+
import { shape_from_json } from "./shape_from_json.js";
|
|
6
|
+
import { shape_to_json } from "./shape_to_json.js";
|
|
7
|
+
|
|
8
|
+
export const type_adapters = {
|
|
9
|
+
'cube': {
|
|
10
|
+
read() {
|
|
11
|
+
return UnitCubeShape3D.INSTANCE;
|
|
12
|
+
},
|
|
13
|
+
write() {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
'sphere': {
|
|
18
|
+
read() {
|
|
19
|
+
return UnitSphereShape3D.INSTANCE;
|
|
20
|
+
},
|
|
21
|
+
write() {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
'transform': {
|
|
26
|
+
read({ transform, subject }) {
|
|
27
|
+
const subject_shape = shape_from_json(subject);
|
|
28
|
+
|
|
29
|
+
return TransformedShape3D.from_m4(subject_shape, transform);
|
|
30
|
+
},
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @param {TransformedShape3D} object
|
|
34
|
+
*/
|
|
35
|
+
write(object) {
|
|
36
|
+
return {
|
|
37
|
+
subject: shape_to_json(object.subject),
|
|
38
|
+
transform: Array.from(object.transform)
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
'union': {
|
|
43
|
+
read({ members }) {
|
|
44
|
+
const member_shapes = members.map(shape_from_json);
|
|
45
|
+
|
|
46
|
+
return UnionShape3D.from(member_shapes);
|
|
47
|
+
},
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
* @param {UnionShape3D} object
|
|
51
|
+
*/
|
|
52
|
+
write(object) {
|
|
53
|
+
return {
|
|
54
|
+
members: object.children.map(shape_to_json)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
@@ -13,7 +13,6 @@ import { isValueBetweenInclusive } from "./isValueBetweenInclusive.js";
|
|
|
13
13
|
import { solveQuadratic } from "./solveQuadratic.js";
|
|
14
14
|
import { overlap1D } from "./overlap1D.js";
|
|
15
15
|
import { computeHashFloat } from "./hash/computeHashFloat.js";
|
|
16
|
-
import { computeHashFloatArray } from "./hash/computeHashFloatArray.js";
|
|
17
16
|
|
|
18
17
|
test('min2', () => {
|
|
19
18
|
expect(min2(0, 0)).toBe(0);
|
|
@@ -128,16 +127,6 @@ test("computeHashFloat", () => {
|
|
|
128
127
|
expect(computeHashFloat(500)).not.toEqual(computeHashFloat(400))
|
|
129
128
|
});
|
|
130
129
|
|
|
131
|
-
test("computeHashFloatArray", () => {
|
|
132
|
-
expect(computeHashFloatArray([1, 2, 1.5], 1, 2)).toEqual(computeHashFloatArray([1, 2, 1.5], 1, 2));
|
|
133
|
-
|
|
134
|
-
expect(computeHashFloatArray([1, 2, 1.5], 1, 3)).not.toEqual(computeHashFloatArray([1, 2, 1.5], 1, 2));
|
|
135
|
-
|
|
136
|
-
expect(computeHashFloatArray([1, 2, 1.5], 2, 3)).not.toEqual(computeHashFloatArray([1, 2, 1.5], 1, 3));
|
|
137
|
-
|
|
138
|
-
expect(computeHashFloatArray([1, 2, 3], 1, 3)).not.toEqual(computeHashFloatArray([1, 2, 1.5], 1, 3));
|
|
139
|
-
});
|
|
140
|
-
|
|
141
130
|
|
|
142
131
|
test('sign', () => {
|
|
143
132
|
expect(sign(1)).toBe(1);
|
|
@@ -3,28 +3,17 @@ import { computeHashFloat } from "./computeHashFloat.js";
|
|
|
3
3
|
/**
|
|
4
4
|
*
|
|
5
5
|
* @param {number[]|Float32Array|Float64Array} values
|
|
6
|
-
* @param {number} min
|
|
7
|
-
* @param {number} max
|
|
8
6
|
*/
|
|
9
|
-
export function computeHashFloatArray(values
|
|
7
|
+
export function computeHashFloatArray(values) {
|
|
10
8
|
const dataLength = values.length;
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
/*
|
|
15
|
-
hash min and max
|
|
16
|
-
*/
|
|
17
|
-
const minHash = computeHashFloat(min);
|
|
18
|
-
const maxHash = computeHashFloat(min);
|
|
19
|
-
|
|
20
|
-
let hash = ((minHash << 5) - minHash) + maxHash;
|
|
10
|
+
let hash = dataLength;
|
|
21
11
|
|
|
22
12
|
for (let i = 0; i < dataLength; i++) {
|
|
23
13
|
const singleValue = values[i];
|
|
24
|
-
|
|
25
|
-
const valueNormalized = (singleValue - min) / dataRange;
|
|
14
|
+
|
|
26
15
|
//scale value to integer range
|
|
27
|
-
const integerValue =
|
|
16
|
+
const integerValue = computeHashFloat(singleValue);
|
|
28
17
|
|
|
29
18
|
hash = ((hash << 5) - hash) + integerValue;
|
|
30
19
|
hash |= 0; // Convert to 32bit integer
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { computeHashFloatArray } from "./computeHashFloatArray.js";
|
|
2
|
+
|
|
3
|
+
test("computeHashFloatArray", () => {
|
|
4
|
+
expect(computeHashFloatArray([1, 2, 1.5])).toEqual(computeHashFloatArray([1, 2, 1.5]));
|
|
5
|
+
|
|
6
|
+
expect(computeHashFloatArray([1, 2, 1.5])).not.toEqual(computeHashFloatArray([1, 3, 1.5]));
|
|
7
|
+
|
|
8
|
+
expect(computeHashFloatArray([1, 2, 1.5])).not.toEqual(computeHashFloatArray([1, 2, 2.5]));
|
|
9
|
+
|
|
10
|
+
expect(computeHashFloatArray([1, 2, 3])).not.toEqual(computeHashFloatArray([1, 2, 1.5]));
|
|
11
|
+
});
|
|
@@ -16,7 +16,7 @@ export function read_property(root, parts, part_offset, part_count) {
|
|
|
16
16
|
|
|
17
17
|
const part = parts[i + part_offset];
|
|
18
18
|
|
|
19
|
-
if (thing
|
|
19
|
+
if (typeof thing[part] !== 'undefined') {
|
|
20
20
|
thing = thing[part];
|
|
21
21
|
} else {
|
|
22
22
|
const getter_name = `get${capitalize(part)}`;
|
|
@@ -17,8 +17,10 @@ export function write_property(root, parts, part_offset, part_count, value) {
|
|
|
17
17
|
|
|
18
18
|
const part = parts[i + part_offset];
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const thing_part = thing[part];
|
|
21
|
+
|
|
22
|
+
if (typeof thing_part !== 'undefined') {
|
|
23
|
+
thing = thing_part;
|
|
22
24
|
} else {
|
|
23
25
|
|
|
24
26
|
const getter_name = `get${capitalize(part)}`;
|
|
@@ -42,11 +44,9 @@ export function write_property(root, parts, part_offset, part_count, value) {
|
|
|
42
44
|
if (typeof setter === "function") {
|
|
43
45
|
// resolve via setter
|
|
44
46
|
setter.call(thing, value);
|
|
45
|
-
} else if (thing.hasOwnProperty(last_part)) {
|
|
46
|
-
thing[last_part] = value;
|
|
47
47
|
} else {
|
|
48
|
-
//
|
|
49
|
-
|
|
48
|
+
// write directly to the field
|
|
49
|
+
thing[last_part] = value;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AddEquation,
|
|
3
|
+
CustomBlending,
|
|
4
|
+
Mesh,
|
|
5
|
+
OneFactor,
|
|
6
|
+
OneMinusSrcAlphaFactor,
|
|
7
|
+
RedFormat,
|
|
8
|
+
RGBAFormat,
|
|
9
|
+
ShaderMaterial
|
|
10
|
+
} from "three";
|
|
2
11
|
import Vector2 from "../../../core/geom/Vector2.js";
|
|
3
12
|
import { assert } from "../../../core/assert.js";
|
|
4
13
|
import { blendingType2three } from "../texture/sampler/blendingType2Three.js";
|
|
@@ -201,10 +210,21 @@ export class CompositLayer {
|
|
|
201
210
|
}
|
|
202
211
|
};
|
|
203
212
|
|
|
213
|
+
// determine swizzle for the material
|
|
214
|
+
let swizzle;
|
|
215
|
+
const texture_format = this.renderTarget.texture.format;
|
|
216
|
+
if (texture_format === RedFormat) {
|
|
217
|
+
swizzle = ['r', 'r', 'r', 1];
|
|
218
|
+
} else if (texture_format === RGBAFormat) {
|
|
219
|
+
swizzle = ['r', 'g', 'b', 'a'];
|
|
220
|
+
} else {
|
|
221
|
+
throw new Error('Unsupported format');
|
|
222
|
+
}
|
|
223
|
+
|
|
204
224
|
const material = new ShaderMaterial({
|
|
205
225
|
uniforms,
|
|
206
226
|
vertexShader: ScreenSpaceQuadShader.vertexShader(),
|
|
207
|
-
fragmentShader: ScreenSpaceQuadShader.fragmentShader(),
|
|
227
|
+
fragmentShader: ScreenSpaceQuadShader.fragmentShader(swizzle),
|
|
208
228
|
lights: false,
|
|
209
229
|
fog: false,
|
|
210
230
|
depthTest: false,
|
|
@@ -296,8 +296,8 @@ export class ParameterLookupTable {
|
|
|
296
296
|
//special case, all values are the same
|
|
297
297
|
return computeHashIntegerArray(valueMin);
|
|
298
298
|
} else {
|
|
299
|
-
const dataHash = computeHashFloatArray(this.data
|
|
300
|
-
const positionHash = computeHashFloatArray(this.positions
|
|
299
|
+
const dataHash = computeHashFloatArray(this.data);
|
|
300
|
+
const positionHash = computeHashFloatArray(this.positions);
|
|
301
301
|
|
|
302
302
|
return computeHashIntegerArray(dataHash, positionHash, this.itemSize);
|
|
303
303
|
}
|
package/engine/graphics/particles/particular/engine/renderers/billboard/ParticleBillboardMaterial.js
CHANGED
|
@@ -28,27 +28,11 @@ backGeometry.viewDir = geometry.viewDir;
|
|
|
28
28
|
|
|
29
29
|
vLightFront = vec3( 0.0 );
|
|
30
30
|
vIndirectFront = vec3( 0.0 );
|
|
31
|
-
#ifdef DOUBLE_SIDED
|
|
32
|
-
vLightBack = vec3( 0.0 );
|
|
33
|
-
vIndirectBack = vec3( 0.0 );
|
|
34
|
-
#endif
|
|
35
31
|
|
|
36
32
|
IncidentLight directLight;
|
|
37
33
|
float dotNL;
|
|
38
34
|
vec3 directLightColor_Diffuse;
|
|
39
35
|
|
|
40
|
-
vIndirectFront += getAmbientLightIrradiance( ambientLightColor );
|
|
41
|
-
|
|
42
|
-
vIndirectFront += getLightProbeIrradiance( lightProbe, geometry.normal );
|
|
43
|
-
|
|
44
|
-
#ifdef DOUBLE_SIDED
|
|
45
|
-
|
|
46
|
-
vIndirectBack += getAmbientLightIrradiance( ambientLightColor );
|
|
47
|
-
|
|
48
|
-
vIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry.normal );
|
|
49
|
-
|
|
50
|
-
#endif
|
|
51
|
-
|
|
52
36
|
#if NUM_POINT_LIGHTS > 0
|
|
53
37
|
|
|
54
38
|
#pragma unroll_loop_start
|
|
@@ -56,17 +40,8 @@ vIndirectFront += getLightProbeIrradiance( lightProbe, geometry.normal );
|
|
|
56
40
|
|
|
57
41
|
getPointLightInfo( pointLights[ i ], geometry, directLight );
|
|
58
42
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
vLightFront += saturate( dotNL ) * directLightColor_Diffuse;
|
|
63
|
-
|
|
64
|
-
#ifdef DOUBLE_SIDED
|
|
65
|
-
|
|
66
|
-
vLightBack += saturate( - dotNL ) * directLightColor_Diffuse;
|
|
67
|
-
|
|
68
|
-
#endif
|
|
69
|
-
|
|
43
|
+
RE_Direct( directLight, geometry, material, reflectedLight );
|
|
44
|
+
|
|
70
45
|
}
|
|
71
46
|
#pragma unroll_loop_end
|
|
72
47
|
|
|
@@ -79,16 +54,7 @@ vIndirectFront += getLightProbeIrradiance( lightProbe, geometry.normal );
|
|
|
79
54
|
|
|
80
55
|
getSpotLightInfo( spotLights[ i ], geometry, directLight );
|
|
81
56
|
|
|
82
|
-
|
|
83
|
-
directLightColor_Diffuse = directLight.color;
|
|
84
|
-
|
|
85
|
-
vLightFront += saturate( dotNL ) * directLightColor_Diffuse;
|
|
86
|
-
|
|
87
|
-
#ifdef DOUBLE_SIDED
|
|
88
|
-
|
|
89
|
-
vLightBack += saturate( - dotNL ) * directLightColor_Diffuse;
|
|
90
|
-
|
|
91
|
-
#endif
|
|
57
|
+
RE_Direct( directLight, geometry, material, reflectedLight );
|
|
92
58
|
}
|
|
93
59
|
#pragma unroll_loop_end
|
|
94
60
|
|
|
@@ -101,38 +67,50 @@ vIndirectFront += getLightProbeIrradiance( lightProbe, geometry.normal );
|
|
|
101
67
|
|
|
102
68
|
getDirectionalLightInfo( directionalLights[ i ], geometry, directLight );
|
|
103
69
|
|
|
104
|
-
|
|
105
|
-
directLightColor_Diffuse = directLight.color;
|
|
106
|
-
|
|
107
|
-
vLightFront += saturate( dotNL ) * directLightColor_Diffuse;
|
|
108
|
-
|
|
109
|
-
#ifdef DOUBLE_SIDED
|
|
110
|
-
|
|
111
|
-
vLightBack += saturate( - dotNL ) * directLightColor_Diffuse;
|
|
112
|
-
|
|
113
|
-
#endif
|
|
70
|
+
RE_Direct( directLight, geometry, material, reflectedLight );
|
|
114
71
|
|
|
115
72
|
}
|
|
116
73
|
#pragma unroll_loop_end
|
|
117
74
|
|
|
118
75
|
#endif
|
|
119
76
|
|
|
120
|
-
#if NUM_HEMI_LIGHTS > 0
|
|
121
77
|
|
|
122
|
-
#pragma unroll_loop_start
|
|
123
|
-
for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {
|
|
124
78
|
|
|
125
|
-
|
|
79
|
+
#if defined( RE_IndirectDiffuse )
|
|
80
|
+
vec3 iblIrradiance = vec3( 0.0 );
|
|
81
|
+
vec3 irradiance = getAmbientLightIrradiance( ambientLightColor );
|
|
82
|
+
irradiance += getLightProbeIrradiance( lightProbe, geometry.normal );
|
|
83
|
+
#if NUM_HEMI_LIGHTS > 0
|
|
126
84
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
85
|
+
#pragma unroll_loop_start
|
|
86
|
+
for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {
|
|
87
|
+
|
|
88
|
+
vIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal );
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
#pragma unroll_loop_end
|
|
92
|
+
|
|
93
|
+
#endif
|
|
94
|
+
|
|
95
|
+
#if defined( USE_ENVMAP ) && defined( ENVMAP_TYPE_CUBE_UV )
|
|
96
|
+
iblIrradiance += getIBLIrradiance( geometry.normal );
|
|
97
|
+
#endif
|
|
98
|
+
#endif
|
|
130
99
|
|
|
131
|
-
|
|
100
|
+
#if defined( RE_IndirectSpecular )
|
|
101
|
+
vec3 radiance = vec3( 0.0 );
|
|
102
|
+
vec3 clearcoatRadiance = vec3( 0.0 );
|
|
103
|
+
#endif
|
|
132
104
|
|
|
133
|
-
|
|
134
|
-
|
|
105
|
+
#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )
|
|
106
|
+
radiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness );
|
|
107
|
+
#endif
|
|
135
108
|
|
|
109
|
+
#if defined( RE_IndirectDiffuse )
|
|
110
|
+
RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );
|
|
111
|
+
#endif
|
|
112
|
+
#if defined( RE_IndirectSpecular )
|
|
113
|
+
RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight );
|
|
136
114
|
#endif
|
|
137
115
|
`;
|
|
138
116
|
|
|
@@ -389,18 +367,12 @@ function make_fragment_shader() {
|
|
|
389
367
|
|
|
390
368
|
vec3 transformedNormal = construct_spherical_normal_from_point_coord(gl_PointCoord.xy);
|
|
391
369
|
|
|
392
|
-
${CHUNK_LAMBERT_LIGHTING}
|
|
393
|
-
|
|
394
370
|
LambertMaterial material = LambertMaterial(texel.rgb);
|
|
395
|
-
|
|
396
371
|
ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
|
|
397
|
-
vec3 totalEmissiveRadiance = vec3(0.0);
|
|
398
|
-
|
|
399
|
-
reflectedLight.indirectDiffuse += vIndirectFront;
|
|
400
|
-
reflectedLight.indirectDiffuse *= BRDF_Lambert( texel.rgb );
|
|
401
372
|
|
|
402
|
-
|
|
403
|
-
|
|
373
|
+
${CHUNK_LAMBERT_LIGHTING}
|
|
374
|
+
|
|
375
|
+
vec3 totalEmissiveRadiance = vec3(0.0);
|
|
404
376
|
|
|
405
377
|
//@injection_point FORWARD_PLUS:accumulation
|
|
406
378
|
|
|
@@ -3,6 +3,7 @@ import { DataType2TypedArrayConstructorMapping } from "../../../../../../../core
|
|
|
3
3
|
import { array_copy } from "../../../../../../../core/collection/array/copyArray.js";
|
|
4
4
|
import { min2 } from "../../../../../../../core/math/min2.js";
|
|
5
5
|
import { assert } from "../../../../../../../core/assert.js";
|
|
6
|
+
import { ParameterLookupTable } from "../../parameter/ParameterLookupTable.js";
|
|
6
7
|
|
|
7
8
|
export class AttributeValue {
|
|
8
9
|
constructor() {
|
|
@@ -142,4 +143,57 @@ export class AttributeValue {
|
|
|
142
143
|
read_value(destination) {
|
|
143
144
|
array_copy(this.__value, 0, destination, 0, this.spec.itemSize);
|
|
144
145
|
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
*
|
|
149
|
+
* @param {number[]} [value]
|
|
150
|
+
* @param {Object} [lookup]
|
|
151
|
+
* @param {number} [lookup_value]
|
|
152
|
+
*/
|
|
153
|
+
fromJSON({
|
|
154
|
+
value,
|
|
155
|
+
lookup,
|
|
156
|
+
lookup_value
|
|
157
|
+
}) {
|
|
158
|
+
|
|
159
|
+
if (value !== undefined) {
|
|
160
|
+
if (Array.isArray(value)) {
|
|
161
|
+
|
|
162
|
+
if (lookup !== undefined) {
|
|
163
|
+
console.warn("Both 'lookup' and 'value' were specified, expected one or the other. Ignoring the value attribute.");
|
|
164
|
+
} else {
|
|
165
|
+
this.write_value(value);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
} else {
|
|
169
|
+
throw new Error(`Expected 'value' to be an array, instead got something else (=${value})`);
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
// retain current value
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (lookup !== undefined) {
|
|
176
|
+
const lut = new ParameterLookupTable();
|
|
177
|
+
|
|
178
|
+
lut.fromJSON(lookup);
|
|
179
|
+
|
|
180
|
+
this.lookup = lut;
|
|
181
|
+
} else {
|
|
182
|
+
this.lookup = null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (lookup_value !== undefined) {
|
|
186
|
+
assert.isNumber(lookup_value, 'lookup_value');
|
|
187
|
+
assert.isFiniteNumber(lookup_value, 'lookup_value');
|
|
188
|
+
|
|
189
|
+
if (lookup === undefined) {
|
|
190
|
+
console.warn("found 'lookup_value' attribute, but no 'lookup' table. Ignoring lookup value");
|
|
191
|
+
} else {
|
|
192
|
+
this.lookup_value = lookup_value;
|
|
193
|
+
}
|
|
194
|
+
} else if (lookup !== undefined) {
|
|
195
|
+
// supply a default lookup value
|
|
196
|
+
this.lookup_value = 0;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
145
199
|
}
|
|
@@ -12,6 +12,7 @@ import { AttributeValue } from "./AttributeValue.js";
|
|
|
12
12
|
import { PARTICULAR_PARTICLE_SPECIFICATION } from "../../emitter/PARTICULAR_PARTICLE_SPECIFICATION.js";
|
|
13
13
|
import { SerializationMetadata } from "../../../../../../ecs/components/SerializationMetadata.js";
|
|
14
14
|
import { UnitCubeShape3D } from "../../../../../../../core/geom/3d/shape/UnitCubeShape3D.js";
|
|
15
|
+
import { shape_from_json } from "../../../../../../../core/geom/3d/shape/json/shape_from_json.js";
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
*
|
|
@@ -154,28 +155,13 @@ export class ParticleVolume {
|
|
|
154
155
|
"track": {
|
|
155
156
|
"itemSize": 4,
|
|
156
157
|
"data": [
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
0.803921568627451,
|
|
162
|
-
0.803921568627451,
|
|
163
|
-
0.803921568627451,
|
|
164
|
-
0.28160919540229884,
|
|
165
|
-
0.803921568627451,
|
|
166
|
-
0.803921568627451,
|
|
167
|
-
0.803921568627451,
|
|
168
|
-
0.21264367816091953,
|
|
169
|
-
0.803921568627451,
|
|
170
|
-
0.803921568627451,
|
|
171
|
-
0.803921568627451,
|
|
172
|
-
0
|
|
158
|
+
1,
|
|
159
|
+
1,
|
|
160
|
+
1,
|
|
161
|
+
1,
|
|
173
162
|
],
|
|
174
163
|
"positions": [
|
|
175
|
-
0
|
|
176
|
-
0.18085106382978725,
|
|
177
|
-
0.7074468085106383,
|
|
178
|
-
1
|
|
164
|
+
0
|
|
179
165
|
]
|
|
180
166
|
}
|
|
181
167
|
},
|
|
@@ -271,8 +257,10 @@ export class ParticleVolume {
|
|
|
271
257
|
init_attributes() {
|
|
272
258
|
const spec = PARTICULAR_PARTICLE_SPECIFICATION;
|
|
273
259
|
|
|
274
|
-
|
|
275
|
-
|
|
260
|
+
const attributeSpecs = spec.attributes;
|
|
261
|
+
const attribute_count = attributeSpecs.length;
|
|
262
|
+
for (let i = 0; i < attribute_count; i++) {
|
|
263
|
+
const attributeSpec = attributeSpecs[i];
|
|
276
264
|
|
|
277
265
|
const av = new AttributeValue();
|
|
278
266
|
av.spec = attributeSpec;
|
|
@@ -314,6 +302,7 @@ export class ParticleVolume {
|
|
|
314
302
|
* @param {number} [particle_size]
|
|
315
303
|
* @param {AbstractShape3D} [shape]
|
|
316
304
|
* @param {boolean} sort
|
|
305
|
+
* @param {boolean} lighting
|
|
317
306
|
* @param {string} [sprite]
|
|
318
307
|
*/
|
|
319
308
|
from({
|
|
@@ -321,20 +310,73 @@ export class ParticleVolume {
|
|
|
321
310
|
particle_size = 1,
|
|
322
311
|
shape = new UnitCubeShape3D(),
|
|
323
312
|
sort = false,
|
|
313
|
+
lighting = true,
|
|
324
314
|
sprite = "data/textures/particle/smokeparticle.png"
|
|
325
315
|
} = {}) {
|
|
326
316
|
|
|
327
317
|
this.__density = density;
|
|
328
318
|
this.__shape = shape;
|
|
329
319
|
|
|
330
|
-
this.__emitter
|
|
331
|
-
|
|
320
|
+
const emitter = this.__emitter;
|
|
321
|
+
|
|
322
|
+
emitter.writeFlag(ParticleEmitterFlag.DepthSorting, sort);
|
|
323
|
+
emitter.writeFlag(ParticleEmitterFlag.Lit, lighting);
|
|
324
|
+
|
|
325
|
+
emitter.layers.get(0).imageURL = sprite;
|
|
332
326
|
|
|
333
327
|
const attributes = this.attributes;
|
|
334
328
|
|
|
335
329
|
attributes.size.write_value_silent([particle_size]);
|
|
336
330
|
}
|
|
337
331
|
|
|
332
|
+
/**
|
|
333
|
+
*
|
|
334
|
+
* @param {number} density
|
|
335
|
+
* @param {boolean} [is_density_size_adjusted]
|
|
336
|
+
* @param {number} [particle_size]
|
|
337
|
+
* @param {Object} shape
|
|
338
|
+
* @param {boolean} [is_sorted]
|
|
339
|
+
* @param {boolean} [is_lit]
|
|
340
|
+
* @param {string} sprite
|
|
341
|
+
* @param {Object<Object>} [attributes]
|
|
342
|
+
*/
|
|
343
|
+
fromJSON({
|
|
344
|
+
density,
|
|
345
|
+
is_density_size_adjusted = true,
|
|
346
|
+
particle_size = 1,
|
|
347
|
+
shape,
|
|
348
|
+
is_sorted = true,
|
|
349
|
+
is_lit = true,
|
|
350
|
+
sprite,
|
|
351
|
+
attributes = {}
|
|
352
|
+
}) {
|
|
353
|
+
|
|
354
|
+
const shape_object = shape_from_json(shape);
|
|
355
|
+
|
|
356
|
+
const actual_density = is_density_size_adjusted ? density / particle_size : density;
|
|
357
|
+
|
|
358
|
+
this.from({
|
|
359
|
+
density: actual_density,
|
|
360
|
+
shape: shape_object,
|
|
361
|
+
particle_size,
|
|
362
|
+
sort: is_sorted,
|
|
363
|
+
lighting: is_lit,
|
|
364
|
+
sprite
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// parse attributes
|
|
368
|
+
for (const attributesKey in attributes) {
|
|
369
|
+
const attribute_object = this.attributes[attributesKey];
|
|
370
|
+
|
|
371
|
+
if (attribute_object === undefined) {
|
|
372
|
+
console.warn(`No '${attributesKey}' attribute, skipping`);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const attribute_json = attributes[attributesKey];
|
|
376
|
+
attribute_object.fromJSON(attribute_json);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
338
380
|
/**
|
|
339
381
|
*
|
|
340
382
|
* @private
|
|
@@ -367,13 +409,13 @@ export class ParticleVolume {
|
|
|
367
409
|
|
|
368
410
|
const layer = emitter.layers.get(0);
|
|
369
411
|
|
|
412
|
+
// build transform matrix
|
|
370
413
|
const m4_transform = new Matrix4();
|
|
371
414
|
|
|
372
415
|
const t = this.__transform;
|
|
373
416
|
|
|
374
417
|
composeMatrix4(m4_transform, t.position, t.rotation, t.scale);
|
|
375
418
|
|
|
376
|
-
|
|
377
419
|
const m4_layer = new Matrix4();
|
|
378
420
|
|
|
379
421
|
composeMatrix4(m4_layer, layer.position, new Quaternion(), layer.scale);
|
|
@@ -384,11 +426,14 @@ export class ParticleVolume {
|
|
|
384
426
|
|
|
385
427
|
m4_final.multiplyMatrices(m4_transform, m4_layer);
|
|
386
428
|
|
|
387
|
-
|
|
429
|
+
// cache variables to save a bit on de-referencing
|
|
430
|
+
const matrix_elements = m4_final.elements;
|
|
431
|
+
const shape = this.__shape;
|
|
388
432
|
|
|
389
|
-
|
|
433
|
+
for (let i = 0; i < particle_count; i++) {
|
|
434
|
+
shape.sample_random_point_in_volume(v3_position, 0, random);
|
|
390
435
|
|
|
391
|
-
vec3.transformMat4(v3_position, v3_position,
|
|
436
|
+
vec3.transformMat4(v3_position, v3_position, matrix_elements);
|
|
392
437
|
|
|
393
438
|
particles.writeAttributeVector3(i, PARTICLE_ATTRIBUTE_POSITION, v3_position[0], v3_position[1], v3_position[2]);
|
|
394
439
|
}
|
|
@@ -328,9 +328,8 @@ async function sample_scene_0(engine) {
|
|
|
328
328
|
ecd: ecd,
|
|
329
329
|
lut: heatmap_lut_0,
|
|
330
330
|
shape: UnionShape3D.from([
|
|
331
|
-
TransformedShape3D.from_m4(new UnitCubeShape3D(),
|
|
332
|
-
TransformedShape3D.from_m4(new UnitCubeShape3D(),
|
|
333
|
-
TransformedShape3D.from_m4(new UnitCubeShape3D(), [23.762890107594952, 0, 0, 0, 0, 2.812495250139616, 0, 0, 0, 0, 34.57956316503163, 0, 55.436098243647756, 1.568241402881399, 113.46988143189215, 1])
|
|
331
|
+
TransformedShape3D.from_m4(new UnitCubeShape3D(), mat_from_aabb3(new AABB3(67.73, 0, 89, 115.84, 3, 118.34))),
|
|
332
|
+
TransformedShape3D.from_m4(new UnitCubeShape3D(), mat_from_aabb3(new AABB3(98, 0, 76.5, 109.5, 3, 89)))
|
|
334
333
|
]),
|
|
335
334
|
particle_size: 2,
|
|
336
335
|
density: 1,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { NoBlending, ShaderMaterial, UniformsUtils, Vector2, WebGLMultisampleRenderTarget } from "three";
|
|
1
|
+
import { NoBlending, RGBAFormat, ShaderMaterial, UniformsUtils, Vector2, WebGLMultisampleRenderTarget } from "three";
|
|
2
2
|
import { BlurShaderUtils, DepthLimitedBlurShader } from "three/examples/jsm/shaders/DepthLimitedBlurShader.js";
|
|
3
3
|
import { renderScreenSpace } from "../../utils/renderScreenSpace.js";
|
|
4
4
|
|
|
5
5
|
export class DepthLimitedBlur {
|
|
6
|
-
constructor() {
|
|
6
|
+
constructor({ format = RGBAFormat }) {
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
*
|
|
@@ -19,7 +19,7 @@ export class DepthLimitedBlur {
|
|
|
19
19
|
*/
|
|
20
20
|
this.__rt_output = null;
|
|
21
21
|
|
|
22
|
-
this.__intermediate = new WebGLMultisampleRenderTarget({});
|
|
22
|
+
this.__intermediate = new WebGLMultisampleRenderTarget({ format });
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
this.__material_blur_x = new ShaderMaterial({
|
|
@@ -161,4 +161,8 @@ export class DepthLimitedBlur {
|
|
|
161
161
|
this.__rt_output = render_target;
|
|
162
162
|
|
|
163
163
|
}
|
|
164
|
+
|
|
165
|
+
dispose() {
|
|
166
|
+
this.__intermediate.dispose();
|
|
167
|
+
}
|
|
164
168
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { EnginePlugin } from "../../../../../plugin/EnginePlugin.js";
|
|
2
2
|
import {
|
|
3
|
+
GLSL3,
|
|
3
4
|
LinearFilter,
|
|
4
5
|
NoBlending,
|
|
5
|
-
|
|
6
|
+
RedFormat,
|
|
6
7
|
ShaderMaterial,
|
|
7
8
|
UniformsUtils,
|
|
8
9
|
WebGLMultisampleRenderTarget
|
|
@@ -25,9 +26,18 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
|
|
|
25
26
|
defines: defines,
|
|
26
27
|
fragmentShader: SAOShader.fragmentShader,
|
|
27
28
|
vertexShader: SAOShader.vertexShader,
|
|
28
|
-
uniforms: UniformsUtils.clone(SAOShader.uniforms)
|
|
29
|
+
uniforms: UniformsUtils.clone(SAOShader.uniforms),
|
|
30
|
+
glslVersion: GLSL3
|
|
29
31
|
});
|
|
30
32
|
|
|
33
|
+
// AO parameters
|
|
34
|
+
const uniforms = this.__material.uniforms;
|
|
35
|
+
uniforms.bias.value = 0.5;
|
|
36
|
+
uniforms.intensity.value = 1;
|
|
37
|
+
|
|
38
|
+
uniforms.kernelRadius.value = 30;
|
|
39
|
+
uniforms.minResolution.value = 0;
|
|
40
|
+
|
|
31
41
|
this.__material.blending = NoBlending;
|
|
32
42
|
|
|
33
43
|
this.__material.extensions.derivatives = true;
|
|
@@ -40,10 +50,10 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
|
|
|
40
50
|
defines.PERSPECTIVE_CAMERA = 1;
|
|
41
51
|
|
|
42
52
|
// More samples = better quality
|
|
43
|
-
defines.NUM_SAMPLES =
|
|
53
|
+
defines.NUM_SAMPLES = 13;
|
|
44
54
|
|
|
45
55
|
// More rings = better quality
|
|
46
|
-
defines.NUM_RINGS =
|
|
56
|
+
defines.NUM_RINGS = 5;
|
|
47
57
|
|
|
48
58
|
/**
|
|
49
59
|
* Should normal buffer be used or not? Using normal buffer might be significantly more expensive, as this would require normal buffer to be built every frame. If there are other users of the buffer - this is not an issue
|
|
@@ -78,10 +88,10 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
|
|
|
78
88
|
minFilter: LinearFilter,
|
|
79
89
|
magFilter: LinearFilter,
|
|
80
90
|
|
|
81
|
-
format:
|
|
91
|
+
format: RedFormat,
|
|
82
92
|
depthBuffer: false,
|
|
83
93
|
stencilBuffer: false,
|
|
84
|
-
generateMipmaps: false
|
|
94
|
+
generateMipmaps: false,
|
|
85
95
|
});
|
|
86
96
|
|
|
87
97
|
/**
|
|
@@ -96,7 +106,7 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
|
|
|
96
106
|
* @type {DepthLimitedBlur}
|
|
97
107
|
* @private
|
|
98
108
|
*/
|
|
99
|
-
this.__blur = new DepthLimitedBlur();
|
|
109
|
+
this.__blur = new DepthLimitedBlur({ format: RedFormat });
|
|
100
110
|
}
|
|
101
111
|
|
|
102
112
|
/**
|
|
@@ -156,16 +166,6 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
|
|
|
156
166
|
uniforms.cameraInverseProjectionMatrix.value.copy(camera.projectionMatrixInverse);
|
|
157
167
|
uniforms.cameraProjectionMatrix.value.copy(camera.projectionMatrix);
|
|
158
168
|
|
|
159
|
-
// AO parameters
|
|
160
|
-
uniforms.bias.value = 0.5;
|
|
161
|
-
uniforms.intensity.value = 0.18;
|
|
162
|
-
|
|
163
|
-
// scale is divided by camera far in the shader, we unbias it here
|
|
164
|
-
// TODO remove this term from the shader
|
|
165
|
-
uniforms.scale.value = far * 0.25;
|
|
166
|
-
|
|
167
|
-
uniforms.kernelRadius.value = 30;
|
|
168
|
-
uniforms.minResolution.value = 0;
|
|
169
169
|
|
|
170
170
|
// trigger uniform update
|
|
171
171
|
material.uniformsNeedUpdate = true;
|
|
@@ -300,7 +300,12 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
|
|
|
300
300
|
const graphics = engine.graphics;
|
|
301
301
|
|
|
302
302
|
// bind compositing layer
|
|
303
|
-
this.__composit_layer = graphics.layerComposer.createLayer(
|
|
303
|
+
this.__composit_layer = graphics.layerComposer.createLayer(
|
|
304
|
+
{
|
|
305
|
+
format: RedFormat
|
|
306
|
+
},
|
|
307
|
+
BlendingType.Multiply
|
|
308
|
+
);
|
|
304
309
|
this.__composit_layer.name = 'Ambient Occlusion';
|
|
305
310
|
|
|
306
311
|
// apply ambient occlusion on opaque pass, otherwise it would occlude transparent geometry
|
|
@@ -344,6 +349,8 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
|
|
|
344
349
|
this.__composit_layer.on.preRender.remove(this.__render, this);
|
|
345
350
|
graphics.viewport.size.onChanged.add(this.__update_render_target_size, this);
|
|
346
351
|
|
|
352
|
+
this.__blur.dispose();
|
|
353
|
+
|
|
347
354
|
return super.shutdown();
|
|
348
355
|
}
|
|
349
356
|
|
|
@@ -25,7 +25,6 @@ const SAOShader = {
|
|
|
25
25
|
'cameraProjectionMatrix': { value: new Matrix4() },
|
|
26
26
|
'cameraInverseProjectionMatrix': { value: new Matrix4() },
|
|
27
27
|
|
|
28
|
-
'scale': { value: 1.0 },
|
|
29
28
|
'intensity': { value: 0.1 },
|
|
30
29
|
'bias': { value: 0.5 },
|
|
31
30
|
|
|
@@ -63,7 +62,6 @@ const SAOShader = {
|
|
|
63
62
|
uniform mat4 cameraProjectionMatrix;
|
|
64
63
|
uniform mat4 cameraInverseProjectionMatrix;
|
|
65
64
|
|
|
66
|
-
uniform float scale;
|
|
67
65
|
uniform float intensity;
|
|
68
66
|
uniform float bias;
|
|
69
67
|
uniform float kernelRadius;
|
|
@@ -74,6 +72,8 @@ const SAOShader = {
|
|
|
74
72
|
// RGBA depth
|
|
75
73
|
|
|
76
74
|
#include <packing>
|
|
75
|
+
|
|
76
|
+
out float out_occlusion;
|
|
77
77
|
|
|
78
78
|
vec4 getDefaultColor( const in vec2 screenPosition ) {
|
|
79
79
|
#if DIFFUSE_TEXTURE == 1
|
|
@@ -115,15 +115,18 @@ const SAOShader = {
|
|
|
115
115
|
#endif
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
float scaleDividedByCameraFar;
|
|
119
118
|
float minResolutionMultipliedByCameraFar;
|
|
120
119
|
|
|
121
120
|
float getOcclusion( const in vec3 centerViewPosition, const in vec3 centerViewNormal, const in vec3 sampleViewPosition ) {
|
|
122
121
|
vec3 viewDelta = sampleViewPosition - centerViewPosition;
|
|
122
|
+
|
|
123
123
|
float viewDistance = length( viewDelta );
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
|
|
125
|
+
float vn = dot( centerViewNormal, viewDelta );
|
|
126
|
+
float a2 = (vn - minResolutionMultipliedByCameraFar) / viewDistance - bias;
|
|
127
|
+
float a1 = (1.0 + pow2( viewDistance ) );
|
|
128
|
+
|
|
129
|
+
return max(0.0, a2) / a1;
|
|
127
130
|
}
|
|
128
131
|
|
|
129
132
|
// moving costly divides into consts
|
|
@@ -132,31 +135,34 @@ const SAOShader = {
|
|
|
132
135
|
|
|
133
136
|
float getAmbientOcclusion( const in vec3 centerViewPosition ) {
|
|
134
137
|
// precompute some variables require in getOcclusion.
|
|
135
|
-
scaleDividedByCameraFar = scale / cameraFar;
|
|
136
138
|
minResolutionMultipliedByCameraFar = minResolution * cameraFar;
|
|
137
139
|
vec3 centerViewNormal = getViewNormal( centerViewPosition, vUv );
|
|
138
140
|
|
|
139
141
|
// jsfiddle that shows sample pattern: https://jsfiddle.net/a16ff1p7/
|
|
140
142
|
float angle = rand( vUv + randomSeed ) * PI2;
|
|
141
143
|
vec2 radius = vec2( kernelRadius * INV_NUM_SAMPLES ) / size;
|
|
142
|
-
vec2 radiusStep = radius;
|
|
143
144
|
|
|
144
145
|
float occlusionSum = 0.0;
|
|
145
146
|
float weightSum = 0.0;
|
|
146
147
|
|
|
147
148
|
for( int i = 0; i < NUM_SAMPLES; i ++ ) {
|
|
148
|
-
vec2 sampleUv = vUv + vec2( cos( angle ), sin( angle ) ) * radius;
|
|
149
|
-
|
|
149
|
+
vec2 sampleUv = vUv + vec2( cos( angle ), sin( angle ) ) * radius * float(i+1);
|
|
150
|
+
|
|
150
151
|
angle += ANGLE_STEP;
|
|
151
152
|
|
|
152
153
|
float sampleDepth = getDepth( sampleUv );
|
|
154
|
+
|
|
153
155
|
if( sampleDepth >= ( 1.0 - EPSILON ) ) {
|
|
156
|
+
// skip, too far
|
|
154
157
|
continue;
|
|
155
158
|
}
|
|
156
159
|
|
|
157
160
|
float sampleViewZ = getViewZ( sampleDepth );
|
|
161
|
+
|
|
158
162
|
vec3 sampleViewPosition = getViewPosition( sampleUv, sampleDepth, sampleViewZ );
|
|
163
|
+
|
|
159
164
|
occlusionSum += getOcclusion( centerViewPosition, centerViewNormal, sampleViewPosition );
|
|
165
|
+
|
|
160
166
|
weightSum += 1.0;
|
|
161
167
|
}
|
|
162
168
|
|
|
@@ -177,12 +183,8 @@ const SAOShader = {
|
|
|
177
183
|
float ambientOcclusion = getAmbientOcclusion( viewPosition );
|
|
178
184
|
|
|
179
185
|
float invOcclusion = 1.0 - ambientOcclusion;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
gl_FragColor.xyz *= invOcclusion;
|
|
183
|
-
#else
|
|
184
|
-
gl_FragColor = vec4(invOcclusion,invOcclusion,invOcclusion,1.0);
|
|
185
|
-
#endif
|
|
186
|
+
|
|
187
|
+
out_occlusion = invOcclusion;
|
|
186
188
|
}`
|
|
187
189
|
|
|
188
190
|
};
|
|
@@ -11,13 +11,33 @@ function vertexShader() {
|
|
|
11
11
|
`;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @param {string[]} [swizzle] what to read from the texture
|
|
17
|
+
* @returns {string}
|
|
18
|
+
*/
|
|
19
|
+
function fragmentShader(swizzle = ['r', 'g', 'b', 'a']) {
|
|
20
|
+
const swizzle_read = [];
|
|
21
|
+
const n = swizzle.length;
|
|
22
|
+
for (let i = 0; i < n; i++) {
|
|
23
|
+
const token = swizzle[i];
|
|
24
|
+
|
|
25
|
+
if (typeof token === "string") {
|
|
26
|
+
swizzle_read[i] = `texel.${token}`;
|
|
27
|
+
} else if (typeof token === "number") {
|
|
28
|
+
swizzle_read[i] = Number(token).toFixed(4);
|
|
29
|
+
} else {
|
|
30
|
+
throw new Error(`Unsupported swizzle token '${token}'`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
15
34
|
return `
|
|
16
35
|
uniform sampler2D tTexture;
|
|
17
36
|
varying vec2 vUv;
|
|
18
37
|
|
|
19
38
|
void main(){
|
|
20
|
-
|
|
39
|
+
vec4 texel = texture2D( tTexture, vUv );
|
|
40
|
+
gl_FragColor = vec4(${swizzle_read.join(',')});
|
|
21
41
|
}
|
|
22
42
|
`;
|
|
23
43
|
}
|
|
@@ -25,4 +45,4 @@ function fragmentShader() {
|
|
|
25
45
|
export const ScreenSpaceQuadShader = {
|
|
26
46
|
vertexShader,
|
|
27
47
|
fragmentShader
|
|
28
|
-
};
|
|
48
|
+
};
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"productName": "Meep",
|
|
6
6
|
"description": "production-ready JavaScript game engine based on Entity Component System Architecture",
|
|
7
7
|
"author": "Alexander Goldring",
|
|
8
|
-
"version": "2.33.
|
|
8
|
+
"version": "2.33.56",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"gl-matrix": "3.4.3",
|
|
11
11
|
"fast-levenshtein": "2.0.6",
|