@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.
Files changed (31) hide show
  1. package/core/geom/3d/shape/AbstractShape3D.js +17 -0
  2. package/core/geom/3d/shape/TransformedShape3D.d.ts +10 -0
  3. package/core/geom/3d/shape/TransformedShape3D.js +28 -3
  4. package/core/geom/3d/shape/TransformedShape3D.spec.js +22 -0
  5. package/core/geom/3d/shape/UnionShape3D.d.ts +5 -0
  6. package/core/geom/3d/shape/UnionShape3D.js +29 -0
  7. package/core/geom/3d/shape/UnitCubeShape3D.d.ts +5 -0
  8. package/core/geom/3d/shape/UnitCubeShape3D.js +7 -1
  9. package/core/geom/3d/shape/UnitSphereShape3D.d.ts +5 -0
  10. package/core/geom/3d/shape/UnitSphereShape3D.js +7 -1
  11. package/core/geom/3d/shape/json/shape_from_json.js +19 -0
  12. package/core/geom/3d/shape/json/shape_to_json.js +21 -0
  13. package/core/geom/3d/shape/json/shape_to_type.js +23 -0
  14. package/core/geom/3d/shape/json/type_adapters.js +58 -0
  15. package/core/math/MathUtils.spec.js +0 -11
  16. package/core/math/hash/computeHashFloatArray.js +4 -15
  17. package/core/math/hash/computeHashFloatArray.spec.js +11 -0
  18. package/core/model/object/read_property.js +1 -1
  19. package/core/model/object/write_property.js +6 -6
  20. package/engine/ecs/transform/Transform.d.ts +4 -0
  21. package/engine/graphics/composit/CompositLayer.js +22 -2
  22. package/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.js +2 -2
  23. package/engine/graphics/particles/particular/engine/renderers/billboard/ParticleBillboardMaterial.js +38 -66
  24. package/engine/graphics/particles/particular/engine/utils/volume/AttributeValue.js +54 -0
  25. package/engine/graphics/particles/particular/engine/utils/volume/ParticleVolume.js +73 -28
  26. package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +2 -3
  27. package/engine/graphics/render/buffer/simple-fx/DepthLimitedBlur.js +7 -3
  28. package/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js +25 -18
  29. package/engine/graphics/render/buffer/simple-fx/ao/SAOShader.js +18 -16
  30. package/engine/graphics/shaders/ScreenSpaceQuadShader.js +23 -3
  31. 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
+ });
@@ -0,0 +1,5 @@
1
+ import {AbstractShape3D} from "./AbstractShape3D";
2
+
3
+ export class UnionShape3D extends AbstractShape3D {
4
+ static from(children: AbstractShape3D[]): UnionShape3D
5
+ }
@@ -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
  }
@@ -0,0 +1,5 @@
1
+ import {AbstractShape3D} from "./AbstractShape3D";
2
+
3
+ export class UnitCubeShape3D extends AbstractShape3D {
4
+ static INSTANCE: UnitCubeShape3D
5
+ }
@@ -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();
@@ -0,0 +1,5 @@
1
+ import {AbstractShape3D} from "./AbstractShape3D";
2
+
3
+ export class UnitSphereShape3D extends AbstractShape3D {
4
+ static INSTANCE: UnitSphereShape3D
5
+ }
@@ -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, min, max) {
7
+ export function computeHashFloatArray(values) {
10
8
  const dataLength = values.length;
11
9
 
12
- const dataRange = max - min;
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
- //normalize value
25
- const valueNormalized = (singleValue - min) / dataRange;
14
+
26
15
  //scale value to integer range
27
- const integerValue = valueNormalized * 4294967295;
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.hasOwnProperty(part)) {
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
- if (thing.hasOwnProperty(part)) {
21
- thing = thing[part];
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
- // no setter
49
- throw new Error(`No no property or getter found for '${last_part}' on [${part_count - 1}]'${parts.slice(part_offset, part_offset + part_count).join('/')}'`);
48
+ // write directly to the field
49
+ thing[last_part] = value;
50
50
  }
51
51
  }
52
52
 
@@ -25,4 +25,8 @@ export class Transform {
25
25
  clone(): Transform
26
26
 
27
27
  equals(other: Transform): void
28
+
29
+ toMatrix4(result: ArrayLike<number>): void
30
+
31
+ fromMatrix4(matrix: ArrayLike<number>): void
28
32
  }
@@ -1,4 +1,13 @@
1
- import { AddEquation, CustomBlending, Mesh, OneFactor, OneMinusSrcAlphaFactor, ShaderMaterial } from "three";
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, valueMin, valueMax);
300
- const positionHash = computeHashFloatArray(this.positions, 0, 1);
299
+ const dataHash = computeHashFloatArray(this.data);
300
+ const positionHash = computeHashFloatArray(this.positions);
301
301
 
302
302
  return computeHashIntegerArray(dataHash, positionHash, this.itemSize);
303
303
  }
@@ -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
- dotNL = dot( geometry.normal, directLight.direction );
60
- directLightColor_Diffuse = directLight.color;
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
- dotNL = dot( geometry.normal, directLight.direction );
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
- dotNL = dot( geometry.normal, directLight.direction );
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
- vIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal );
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
- #ifdef DOUBLE_SIDED
128
-
129
- vIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry.normal );
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
- #endif
100
+ #if defined( RE_IndirectSpecular )
101
+ vec3 radiance = vec3( 0.0 );
102
+ vec3 clearcoatRadiance = vec3( 0.0 );
103
+ #endif
132
104
 
133
- }
134
- #pragma unroll_loop_end
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
- reflectedLight.directDiffuse = vLightFront;
403
- reflectedLight.directDiffuse *= BRDF_Lambert( texel.rgb ); // shadowmask goes here
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
- 0.5764705882352941,
158
- 0.5764705882352941,
159
- 0.5764705882352941,
160
- 0,
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
- for (let i = 0; i < spec.attributes.length; i++) {
275
- const attributeSpec = spec.attributes[i];
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.writeFlag(ParticleEmitterFlag.DepthSorting, sort);
331
- this.__emitter.layers.get(0).imageURL = sprite;
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
- for (let i = 0; i < particle_count; i++) {
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
- this.__shape.sample_random_point_in_volume(v3_position, 0, random);
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, m4_final.elements);
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(), [14.980091485108495, 0, 0, 0, 0, 2.812495250139616, 0, 0, 0, 0, 7.469446746375293, 0, 54.83080514238756, 1.568241402881399, 92.51937192481576, 1]),
332
- TransformedShape3D.from_m4(new UnitCubeShape3D(), [6.644515553810907, 0, 0, 0, 0, 2.812495250139616, 0, 0, 0, 0, 5.507507223409315, 0, 46.95032009188344, 1.568241402881399, 95.54148649329517, 1]),
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
- RGBAFormat,
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 = 7;
53
+ defines.NUM_SAMPLES = 13;
44
54
 
45
55
  // More rings = better quality
46
- defines.NUM_RINGS = 4;
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: RGBAFormat,
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({}, BlendingType.Multiply);
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
- float scaledScreenDistance = scaleDividedByCameraFar * viewDistance;
125
-
126
- return max(0.0, (dot(centerViewNormal, viewDelta) - minResolutionMultipliedByCameraFar) / scaledScreenDistance - bias) / (1.0 + pow2( scaledScreenDistance ) );
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
- radius += radiusStep;
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
- #if DIFFUSE_TEXTURE == 1
181
- gl_FragColor = getDefaultColor( vUv );
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
- function fragmentShader() {
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
- gl_FragColor = texture2D( tTexture, vUv );
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.52",
8
+ "version": "2.33.56",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",