@woosh/meep-engine 2.43.3 → 2.43.4

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 (48) hide show
  1. package/core/binary/BinaryBuffer.js +13 -1
  2. package/core/binary/BitSet.js +2 -2
  3. package/core/collection/array/array_range_equal_strict.js +22 -0
  4. package/core/color/sRGB_to_linear.js +9 -4
  5. package/core/geom/3d/plane/orient3d_fast.js +3 -0
  6. package/core/geom/3d/plane/orient3d_robust.js +41 -0
  7. package/core/geom/3d/sphere/harmonics/README.md +15 -0
  8. package/core/geom/3d/sphere/harmonics/sh3_add.js +21 -0
  9. package/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.js +618 -0
  10. package/core/geom/3d/sphere/harmonics/sh3_sample_by_direction.js +49 -0
  11. package/core/geom/3d/sphere/harmonics/sh3_sample_irradiance_by_direction.js +53 -0
  12. package/core/geom/3d/tetrahedra/TetrahedralMesh.js +251 -68
  13. package/core/geom/3d/tetrahedra/TetrahedralMesh.spec.js +80 -3
  14. package/core/geom/3d/tetrahedra/build_tetrahedral_mesh_buffer_geometry.js +75 -0
  15. package/core/geom/3d/tetrahedra/delaunay/Cavity.js +5 -1
  16. package/core/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.js +30 -31
  17. package/core/geom/3d/tetrahedra/delaunay/fill_in_a_cavity.js +54 -18
  18. package/core/geom/3d/tetrahedra/delaunay/push_boundary_with_validation.js +27 -0
  19. package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_cavity.js +89 -0
  20. package/core/geom/3d/tetrahedra/delaunay/{tetrahedral_mesh_walk_toward_cavity.js → tetrahedral_mesh_walk_towards_containing_tetrahedron.js} +15 -12
  21. package/core/geom/3d/tetrahedra/delaunay/validate_cavity_boundary.js +60 -0
  22. package/core/geom/3d/tetrahedra/{point_in_tetrahedron_circumsphere.js → in_sphere_fast.js} +2 -4
  23. package/core/geom/3d/tetrahedra/in_sphere_robust.js +53 -0
  24. package/core/geom/3d/tetrahedra/prototypeTetrahedraBuilder.js +44 -35
  25. package/core/geom/3d/tetrahedra/validate_tetrahedral_mesh.js +85 -38
  26. package/core/geom/3d/util/make_justified_point_grid.js +31 -0
  27. package/core/process/delay.js +5 -0
  28. package/editor/Editor.js +3 -0
  29. package/editor/ecs/component/editors/ecs/ParameterLookupTableEditor.js +195 -11
  30. package/editor/ecs/component/editors/ecs/ParameterTrackSetEditor.js +16 -0
  31. package/editor/ecs/component/editors/ecs/ParticleEmitterLayerEditor.js +4 -0
  32. package/engine/EngineHarness.js +11 -5
  33. package/engine/ecs/terrain/ecs/TerrainSystem.js +7 -1
  34. package/engine/ecs/transform/copy_three_transform.js +15 -0
  35. package/engine/graphics/ecs/light/Light.js +6 -1
  36. package/engine/graphics/ecs/light/LightSystem.d.ts +1 -1
  37. package/engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.js +2 -17
  38. package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +2 -2
  39. package/engine/graphics/sh3/LightProbeVolume.js +595 -0
  40. package/engine/graphics/sh3/SH3VisualisationMaterial.js +79 -0
  41. package/engine/graphics/sh3/prototypeSH3Probe.js +427 -0
  42. package/engine/graphics/sh3/visualise_probe.js +40 -0
  43. package/engine/graphics/texture/atlas/TextureAtlas.js +15 -3
  44. package/engine/intelligence/blackboard/AbstractBlackboard.d.ts +1 -1
  45. package/package.json +2 -1
  46. package/samples/terrain/from_image_2.js +127 -82
  47. package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_cavity2.js +0 -224
  48. package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_insert_point.js +0 -98
@@ -1,8 +1,6 @@
1
1
  import { EngineHarness } from "../../../../engine/EngineHarness.js";
2
2
  import { compute_delaunay_tetrahedral_mesh } from "./delaunay/compute_delaunay_tetrahedral_mesh.js";
3
- import { LineBasicMaterial, Uint32BufferAttribute } from "three";
4
- import { BufferGeometry } from "three/src/core/BufferGeometry.js";
5
- import { Float32BufferAttribute } from "three/src/core/BufferAttribute.js";
3
+ import { LineBasicMaterial } from "three";
6
4
  import { ShadedGeometry } from "../../../../engine/graphics/ecs/mesh-v2/ShadedGeometry.js";
7
5
  import { DrawMode } from "../../../../engine/graphics/ecs/mesh-v2/DrawMode.js";
8
6
  import EntityBuilder from "../../../../engine/ecs/EntityBuilder.js";
@@ -10,7 +8,11 @@ import { Transform } from "../../../../engine/ecs/transform/Transform.js";
10
8
  import { ShadedGeometrySystem } from "../../../../engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js";
11
9
  import { GizmoRenderingPlugin } from "../../../../engine/graphics/render/gizmo/GizmoRenderingPlugin.js";
12
10
  import { Gizmo } from "../../../../engine/graphics/render/gizmo/Gizmo.js";
13
- import { LAYOUT_TETRA_BYTE_SIZE } from "./TetrahedralMesh.js";
11
+ import { build_tetrahedral_mesh_buffer_geometry } from "./build_tetrahedral_mesh_buffer_geometry.js";
12
+ import { seededRandom } from "../../../math/random/seededRandom.js";
13
+ import { randomFloatBetween } from "../../../math/random/randomFloatBetween.js";
14
+ import { delay } from "../../../process/delay.js";
15
+ import { TetrahedralMesh } from "./TetrahedralMesh.js";
14
16
 
15
17
  /**
16
18
  *
@@ -26,45 +28,52 @@ async function main(engine) {
26
28
  const offset = [7.5, 1, 7.5];
27
29
  const scale = [.5, .5, .5];
28
30
 
29
- const points = [
30
- 0, 0, 0,
31
- 10, 0, 0,
32
- 10, 0, 10,
33
- 0, 0, 10,
34
-
35
- 0, 10, 0,
36
- 10, 10, 0,
37
- 10, 10, 10,
38
- 0, 10, 10,
39
- ];
40
-
41
- const tetrahedra = compute_delaunay_tetrahedral_mesh(points, points.length / 3);
42
-
43
- const lines = [];
31
+ const points = [];
32
+
33
+ // points.push(
34
+ // 0, 0, 0,
35
+ // 10, 0, 0,
36
+ // 10, 0, 10,
37
+ // 0, 0, 10,
38
+ //
39
+ // 0, 10, 0,
40
+ // 10, 10, 0,
41
+ // 10, 10, 10,
42
+ // 0, 10, 10,
43
+ // );
44
+
45
+ const random = seededRandom();
46
+
47
+ for (let i = 0; i < 600; i++) {
48
+ points.push(
49
+ randomFloatBetween(random, -100, 100),
50
+ randomFloatBetween(random, 0, 150),
51
+ randomFloatBetween(random, -100, 100),
52
+ );
53
+ }
44
54
 
45
- for (let i = tetrahedra.__occupancy.nextSetBit(0); i !== -1; i = tetrahedra.__occupancy.nextSetBit(i + 1)) {
55
+ // make_justified_point_grid(new AABB3(-100, 0, -100, 100, 150, 100), 40, (x, y, z) => {
56
+ // points.push(x, y, z);
57
+ // });
46
58
 
47
- const tet_address = i * LAYOUT_TETRA_BYTE_SIZE;
59
+ await delay(1000);
48
60
 
49
- const a = tetrahedra.__view.getUint32(tet_address);
50
- const b = tetrahedra.__view.getUint32(tet_address + 4);
51
- const c = tetrahedra.__view.getUint32(tet_address + 8);
52
- const d = tetrahedra.__view.getUint32(tet_address + 12);
61
+ const tetrahedra = new TetrahedralMesh();
62
+ // console.time('mesh build');
63
+ console.profile('mesh build');
64
+ compute_delaunay_tetrahedral_mesh(tetrahedra, points, points.length / 3);
65
+ console.profileEnd('mesh build');
66
+ // console.timeEnd('mesh build');
53
67
 
54
- lines.push(a, b);
55
- lines.push(a, c);
56
- lines.push(a, d);
68
+ console.log(tetrahedra);
57
69
 
58
- lines.push(b, c);
59
- lines.push(c, d);
60
- lines.push(b, d);
70
+ await delay(500);
61
71
 
62
- }
72
+ const relocated_elements = tetrahedra.compact();
73
+ console.log(`Compaction relocated ${relocated_elements} elements`);
63
74
 
64
- const geometry = new BufferGeometry();
75
+ const geometry = build_tetrahedral_mesh_buffer_geometry(tetrahedra, points);
65
76
 
66
- geometry.setIndex(new Uint32BufferAttribute(lines, 1, false));
67
- geometry.setAttribute('position', new Float32BufferAttribute(points, 3, false));
68
77
 
69
78
 
70
79
  new EntityBuilder()
@@ -9,7 +9,7 @@ import { INVALID_NEIGHBOUR } from "./TetrahedralMesh.js";
9
9
  * @param {number} tet
10
10
  * @return {number}
11
11
  */
12
- function get_tetrahedron_volume(mesh, points, tet) {
12
+ export function get_tetrahedron_volume(mesh, points, tet) {
13
13
 
14
14
  const a = mesh.getVertexIndex(tet, 0);
15
15
  const b = mesh.getVertexIndex(tet, 1);
@@ -23,65 +23,85 @@ function get_tetrahedron_volume(mesh, points, tet) {
23
23
  *
24
24
  * @param {TetrahedralMesh} mesh
25
25
  * @param {number} tet
26
- * @param {function(problem:string):*} consumer
26
+ * @param {number} neighbour_id
27
+ * @param {function(string):*} consumer
28
+ * @return {boolean}
27
29
  */
28
- function validate_tetrahedron_neighbourhood(mesh, tet, consumer = noop) {
30
+ export function validate_neighbour(mesh, tet, neighbour_id, consumer) {
29
31
  let valid = true;
30
32
 
31
- for (let i = 0; i < 4; i++) {
32
- const neighbour = mesh.getNeighbour(tet, i);
33
+ const neighbour = mesh.getNeighbour(tet, neighbour_id);
33
34
 
34
- if (neighbour === INVALID_NEIGHBOUR) {
35
- continue;
36
- }
35
+ if (neighbour === INVALID_NEIGHBOUR) {
36
+ return true;
37
+ }
37
38
 
38
- // decode neighbour
39
- const neighbour_tet = neighbour >> 2;
40
- const neighbour_vertex_id = neighbour & 3;
39
+ // decode neighbour
40
+ const neighbour_tet = neighbour >> 2;
41
+ const neighbour_vertex_id = neighbour & 3;
41
42
 
42
- if (!mesh.exists(neighbour_tet)) {
43
- valid = false;
43
+ if (!mesh.exists(neighbour_tet)) {
44
44
 
45
- consumer(`Tetrahedron ${tet} Neighbour[${i}] ${neighbour_tet} does not exist in the mesh`);
46
45
 
47
- continue;
48
- }
46
+ consumer(`Tetrahedron ${tet} Neighbour[${neighbour_id}] ${neighbour_tet} does not exist in the mesh`);
49
47
 
50
- const back_link = mesh.getNeighbour(neighbour_tet, neighbour_vertex_id);
48
+ return false;
49
+ }
51
50
 
52
- if (back_link === INVALID_NEIGHBOUR) {
53
- valid = false;
54
- consumer(`Tetrahedron ${tet} Neighbour[${i}] ${neighbour_tet} links back to invalid neighbour (no neighbour), expected ${tet} instead`);
55
- continue;
56
- }
51
+ const back_link = mesh.getNeighbour(neighbour_tet, neighbour_vertex_id);
57
52
 
58
- const back_link_tet = back_link >> 2;
59
- const back_link_vertex_index = back_link & 3;
53
+ if (back_link === INVALID_NEIGHBOUR) {
60
54
 
61
- if (back_link_tet !== tet) {
62
- valid = false;
55
+ consumer(`Tetrahedron ${tet} Neighbour[${neighbour_id}] ${neighbour_tet} links back to invalid neighbour (no neighbour), expected ${tet} instead`);
56
+ return false;
57
+ }
63
58
 
64
- consumer(`Tetrahedron ${tet} Neighbour[${i}] ${neighbour_tet} links back to a different tet, expected ${tet}, got ${back_link_tet}`)
65
- } else if (back_link_vertex_index !== i) {
59
+ const back_link_tet = back_link >> 2;
60
+ const back_link_vertex_index = back_link & 3;
61
+
62
+ if (back_link_tet !== tet) {
63
+ valid = false;
64
+
65
+ consumer(`Tetrahedron ${tet} Neighbour[${neighbour_id}] ${neighbour_tet} links back to a different tet, expected ${tet}, got ${back_link_tet}`)
66
+ } else if (back_link_vertex_index !== neighbour_id) {
67
+ valid = false;
68
+
69
+ consumer(`Tetrahedron ${tet} Neighbour[${neighbour_id}] ${neighbour_tet} links back to a different vertex, expected ${neighbour_id}, got ${back_link_vertex_index}`);
70
+
71
+ }
72
+
73
+ for (let j = 0; j < 4; j++) {
74
+ const v = mesh.getVertexIndex(tet, j);
75
+
76
+ if (j === neighbour_id) {
77
+ continue;
78
+ }
79
+
80
+ if (!mesh.tetContainsVertex(neighbour_tet, v)) {
66
81
  valid = false;
67
82
 
68
- consumer(`Tetrahedron ${tet} Neighbour[${i}] ${neighbour_tet} links back to a different vertex, expected ${i}, got ${back_link_vertex_index}`);
83
+ consumer(`Tetrahedron ${tet} Neighbour[${neighbour_id}] ${neighbour_tet} does not share vertex ${v} with the tet`);
69
84
 
70
85
  }
86
+ }
71
87
 
72
- for (let j = 0; j < 4; j++) {
73
- const v = mesh.getVertexIndex(tet, j);
88
+ return valid;
89
+ }
74
90
 
75
- if (j === i) {
76
- continue;
77
- }
78
91
 
79
- if (!mesh.tetContainsVertex(neighbour_tet, v)) {
80
- valid = false;
92
+ /**
93
+ *
94
+ * @param {TetrahedralMesh} mesh
95
+ * @param {number} tet
96
+ * @param {function(problem:string):*} consumer
97
+ */
98
+ export function validate_tetrahedron_neighbourhood(mesh, tet, consumer = noop) {
99
+ let valid = true;
81
100
 
82
- consumer(`Tetrahedron ${tet} Neighbour[${i}] ${neighbour_tet} does not share vertex ${v} with the tet`);
101
+ for (let i = 0; i < 4; i++) {
83
102
 
84
- }
103
+ if (!validate_neighbour(mesh, tet, i, consumer)) {
104
+ valid = false;
85
105
  }
86
106
 
87
107
  }
@@ -89,6 +109,27 @@ function validate_tetrahedron_neighbourhood(mesh, tet, consumer = noop) {
89
109
  return valid;
90
110
  }
91
111
 
112
+ /**
113
+ *
114
+ * @param {TetrahedralMesh} mesh
115
+ * @param {number} tet
116
+ * @returns {boolean}
117
+ */
118
+ function is_tetrahedron_degenerate(mesh, tet) {
119
+
120
+ for (let i0 = 0; i0 < 4; i0++) {
121
+ const v0 = mesh.getVertexIndex(tet, i0);
122
+ for (let i1 = i0 + 1; i1 < 4; i1++) {
123
+ if (v0 === mesh.getVertexIndex(tet, i1)) {
124
+ // identical vertices
125
+ return true;
126
+ }
127
+ }
128
+ }
129
+
130
+ return false;
131
+ }
132
+
92
133
  /**
93
134
  *
94
135
  * @param {TetrahedralMesh} mesh
@@ -108,6 +149,12 @@ export function validate_tetrahedral_mesh(mesh, points, consumer = noop) {
108
149
  consumer(`Tetrahedron ${tet} has negative volume ${volume}`);
109
150
  }
110
151
 
152
+ // check for degeneracy
153
+ if (is_tetrahedron_degenerate(mesh, tet)) {
154
+ is_valid = false;
155
+ consumer(`Tetrahedron ${tet} is degenerate (multiple vertices have the same index)`);
156
+ }
157
+
111
158
  if (!validate_tetrahedron_neighbourhood(mesh, tet, consumer)) {
112
159
  is_valid = false;
113
160
  }
@@ -0,0 +1,31 @@
1
+ import { max2 } from "../../../math/max2.js";
2
+
3
+ /**
4
+ * Created a grid of points to fill given bounds, spacing is used as upper-bound,
5
+ * actual spacing will be justified to provide even distanced along each dimension separately
6
+ * @param {AABB3} aabb3
7
+ * @param {number} spacing
8
+ * @param {function(x:number,y:number,z:number)} callback
9
+ */
10
+ export function make_justified_point_grid(aabb3, spacing, callback) {
11
+
12
+ const steps_x = Math.ceil(aabb3.getExtentsX() / spacing);
13
+ const steps_y = Math.ceil(aabb3.getExtentsY() / spacing);
14
+ const steps_z = Math.ceil(aabb3.getExtentsZ() / spacing);
15
+
16
+ const spacing_x = aabb3.getExtentsX() / max2(1, steps_x - 1);
17
+ const spacing_y = aabb3.getExtentsY() / max2(1, steps_y - 1);
18
+ const spacing_z = aabb3.getExtentsZ() / max2(1, steps_z - 1);
19
+
20
+ for (let i = 0; i < steps_x; i++) {
21
+ for (let j = 0; j < steps_y; j++) {
22
+ for (let k = 0; k < steps_z; k++) {
23
+ const x = i * spacing_x + aabb3.x0;
24
+ const y = j * spacing_y + aabb3.y0;
25
+ const z = k * spacing_z + aabb3.z0;
26
+
27
+ callback(x, y, z);
28
+ }
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,5 @@
1
+ export function delay(timeout_ms) {
2
+ return new Promise((resolve, reject) => {
3
+ setTimeout(resolve, timeout_ms);
4
+ });
5
+ }
package/editor/Editor.js CHANGED
@@ -71,6 +71,8 @@ import { ObservedIntegerEditor } from "./ecs/component/editors/ObservedIntegerEd
71
71
 
72
72
  import '../../../../css/editor/EntityEditorView.scss';
73
73
  import '../../../../css/editor/EditorView.scss';
74
+ import {ParameterTrackSet} from "../engine/graphics/particles/particular/engine/parameter/ParameterTrackSet.js";
75
+ import {ParameterTrackSetEditor} from "./ecs/component/editors/ecs/ParameterTrackSetEditor.js";
74
76
 
75
77
  /**
76
78
  * @template T
@@ -369,6 +371,7 @@ function initialize_basic_registry(registry) {
369
371
  registry.set(Quaternion, new QuaternionEditor());
370
372
 
371
373
  registry.set(Sampler2D, new Sampler2DEditor());
374
+ registry.set(ParameterTrackSet, new ParameterTrackSetEditor());
372
375
 
373
376
  registry.set(HTMLElement, new HTMLElementEditor());
374
377
  registry.set(HTMLCanvasElement, new HTMLElementEditor());
@@ -2,6 +2,11 @@ import { TypeEditor } from "../../TypeEditor.js";
2
2
  import { CanvasView } from "../../../../../view/elements/CanvasView.js";
3
3
  import { plot_data } from "../../../../../engine/animation/curve/draw/plot_data.js";
4
4
  import Vector2 from "../../../../../core/geom/Vector2.js";
5
+ import {ColorEditor} from "../ColorEditor.js";
6
+ import {ColorPickerView} from "../../../../../view/elements/ColorPickerView.js";
7
+ import {AutoCanvasView} from "../../../../view/ecs/components/common/AutoCanvasView.js";
8
+ import EmptyView from "../../../../../view/elements/EmptyView.js";
9
+ import {MouseEvents} from "../../../../../engine/input/devices/events/MouseEvents.js";
5
10
 
6
11
  export class ParameterLookupTableEditor extends TypeEditor {
7
12
  get schema() {
@@ -16,29 +21,208 @@ export class ParameterLookupTableEditor extends TypeEditor {
16
21
 
17
22
  build(parent, field,registry) {
18
23
 
19
- const canvasView = new CanvasView();
20
- canvasView.size.set(200, 100);
21
- const ctx = canvasView.context2d;
22
-
23
- const data = [];
24
-
25
24
  /**
26
25
  * @type {ParameterLookupTable}
27
26
  */
28
27
  const lut = field.adapter.read(parent, field.name);
28
+ const trackName = field.adapter.read(parent, "name");
29
29
 
30
- const sample = [];
30
+ // if it is a track scale
31
+ if (trackName === "scale"){
32
+ const canvasView = new CanvasView();
33
+ canvasView.size.set(200, 100);
34
+ const ctx = canvasView.context2d;
35
+
36
+ const data = [];
37
+
38
+ const sample = [];
39
+ for (let i = 0; i < canvasView.size.x; i++) {
40
+ const f = i / (canvasView.size.x - 1);
41
+
42
+ lut.sample(f, sample);
43
+
44
+ data[i] = sample[0];
45
+ }
46
+
47
+ plot_data({ ctx, data, width: canvasView.size.x, height: canvasView.size.y, margin: new Vector2(4, 4) });
48
+
49
+ return canvasView;
50
+ } else if (trackName === "color"){
51
+ // if it is color track
52
+ const resultView = new EmptyView();
53
+ const canvasView = new CanvasView();
54
+ canvasView.css({borderColor: 'white', borderStyle: 'solid', borderWidth: '1px',});
55
+ canvasView.size.set(200, 70);
56
+
57
+ const ctx = canvasView.context2d;
58
+
59
+ const colorView = new EmptyView({tag: "input", attr: {type: "color"}, css: {padding: "0px", border: "0px"}});
60
+
61
+ resultView.addChild(canvasView);
62
+ resultView.addChild(colorView);
63
+
64
+ //canvas internal configuration, such as background
65
+ let config = {
66
+ tileSize: new Vector2(10, 10),
67
+ tileColor1: "rgb(255, 255, 255)",
68
+ tileColor2: "rgb(119,119,119)",
69
+ gradientBoxYRange: new Vector2(30, canvasView.size.y),
70
+ };
71
+
72
+ this.DrawBackground(canvasView, ctx, config);
73
+ this.DrawGradient(canvasView, lut, ctx, config);
74
+ let stops = this.DrawStops(canvasView, lut, ctx, colorView, config, 0);
75
+
76
+ canvasView.el.addEventListener(MouseEvents.Click, (e) => {
77
+ const rect = canvasView.el.getBoundingClientRect();
78
+ let x = e.clientX - rect.left,
79
+ y = e.clientY - rect.top;
80
+ for (let i = 0; i < stops.length; i++) {
81
+ if (stops[i].x <= x && stops[i].x + stops[i].width >= x &&
82
+ stops[i].y <= y && stops[i].y + stops[i].height >= y){
83
+ let s = [];
84
+ lut.sample(lut.positions[i], s);
85
+
86
+ this.convertRGB(s);
87
+
88
+ colorView.el.value = this.RGBToHex(s[0], s[1], s[2]);
89
+ canvasView.clear();
90
+
91
+ this.DrawBackground(canvasView, ctx, config);
92
+ this.DrawGradient(canvasView, lut, ctx, config);
93
+ stops = this.DrawStops(canvasView, lut, ctx, colorView, config, i);
94
+
95
+ break;
96
+ }
97
+ }
98
+ });
99
+
100
+ return resultView;
101
+ }
102
+ }
103
+ /**
104
+ * converts rgb to hex string
105
+ * @param {Float} r
106
+ * @param {Float} g
107
+ * @param {Float} b
108
+ */
109
+ RGBToHex(r,g,b) {
110
+ r = r.toString(16);
111
+ g = g.toString(16);
112
+ b = b.toString(16);
113
+
114
+ if (r.length == 1)
115
+ r = "0" + r;
116
+ if (g.length == 1)
117
+ g = "0" + g;
118
+ if (b.length == 1)
119
+ b = "0" + b;
120
+
121
+ return "#" + r + g + b;
122
+ }
123
+
124
+ /**
125
+ * converts lut color sample to RGB
126
+ * @param {[]} sample
127
+ */
128
+ convertRGB(sample){
129
+ sample[0] = Math.floor(sample[0] * 255);
130
+ sample[1] = Math.floor(sample[1] * 255);
131
+ sample[2] = Math.floor(sample[2] * 255);
132
+ }
133
+ /**
134
+ * draws tiled background
135
+ * @param {CanvasView} canvasView
136
+ * @param {CanvasRenderingContext2D} ctx
137
+ * @param {{}} config
138
+ */
139
+ DrawBackground(canvasView, ctx, config){
140
+ for (let i = 0; i < canvasView.size.x; i += config.tileSize.x) {
141
+ for (let j = config.gradientBoxYRange.x; j < config.gradientBoxYRange.y; j += config.tileSize.y){
142
+ ctx.fillStyle = (((i / config.tileSize.x) + (j / config.tileSize.y)) % 2 === 0 ? config.tileColor1 : config.tileColor2);
143
+ ctx.fillRect(i, j, config.tileSize.x, config.tileSize.y);
144
+ }
145
+ }
146
+ }
31
147
 
148
+ /**
149
+ * draws gradient
150
+ * @param {CanvasView} canvasView
151
+ * @param {ParameterLookupTable} lut
152
+ * @param {CanvasRenderingContext2D} ctx
153
+ * @param {{}} config
154
+ */
155
+ DrawGradient(canvasView, lut, ctx, config){
156
+ const sample = [];
32
157
  for (let i = 0; i < canvasView.size.x; i++) {
33
158
  const f = i / (canvasView.size.x - 1);
34
-
35
159
  lut.sample(f, sample);
36
160
 
37
- data[i] = sample[0];
161
+ this.convertRGB(sample);
162
+
163
+ ctx.fillStyle = `rgba(${sample[0]},${sample[1]},${sample[2]}, ${sample[3]})`;
164
+ ctx.fillRect(i, config.gradientBoxYRange.x, 1, config.gradientBoxYRange.y - config.gradientBoxYRange.x);
38
165
  }
166
+ }
167
+
168
+ /**
169
+ * draws gradient stops
170
+ * @param {CanvasView} canvasView
171
+ * @param {ParameterLookupTable} lut
172
+ * @param {CanvasRenderingContext2D} ctx
173
+ * @param {EmptyView} colorView
174
+ * @param {{}} config
175
+ * @param {Integer} selectedInd
176
+ * @returns {*[]}
177
+ */
178
+ DrawStops(canvasView, lut, ctx, colorView, config, selectedInd){
179
+ const sample = [];
39
180
 
40
- plot_data({ ctx, data, width: canvasView.size.x, height: canvasView.size.y, margin: new Vector2(4, 4) });
181
+ let stops = [];
182
+ for (let i = 0; i < lut.positions.length; i++){
183
+ const posX = lut.positions[i] * canvasView.size.x;
184
+ if (i === selectedInd){
185
+ lut.sample(lut.positions[i], sample);
41
186
 
42
- return canvasView;
187
+ this.convertRGB(sample);
188
+
189
+ colorView.el.value = this.RGBToHex(sample[0], sample[1], sample[2]);
190
+ }
191
+ stops.push(this.DrawStop(ctx, config, posX, i=== selectedInd));
192
+ }
193
+ return stops;
194
+ }
195
+ /**
196
+ * draws gradient stop
197
+ * @param {CanvasRenderingContext2D} ctx
198
+ * @param {Object} config
199
+ * @param {Integer} posX
200
+ * @param {Boolean} isSelected
201
+ */
202
+ DrawStop(ctx, config, posX, isSelected){
203
+ ctx.beginPath();
204
+ ctx.moveTo(posX, config.gradientBoxYRange.x + 7);
205
+ ctx.lineTo(posX - 5, config.gradientBoxYRange.x);
206
+ ctx.lineTo(posX - 5, config.gradientBoxYRange.x - 12);
207
+ ctx.lineTo(posX + 5, config.gradientBoxYRange.x - 12);
208
+ ctx.lineTo(posX + 5, config.gradientBoxYRange.x);
209
+ ctx.closePath();
210
+
211
+ ctx.lineWidth = (isSelected ? 4 : 2);
212
+ if (!isSelected)
213
+ ctx.strokeStyle = "rgb(114,114,114)";
214
+ else
215
+ ctx.strokeStyle = "rgb(0,23,255)";
216
+ ctx.stroke();
217
+ ctx.fillStyle = "rgb(255, 255, 255)";
218
+ ctx.fill();
219
+
220
+ return {
221
+ x: posX - 5,
222
+ y: config.gradientBoxYRange.x - 12,
223
+ width: 10,
224
+ height: 12,
225
+ posX: posX,
226
+ };
43
227
  }
44
228
  }
@@ -0,0 +1,16 @@
1
+ import List from "../../../../../core/collection/list/List.js";
2
+ import { ObjectEditor } from "../primitive/ObjectEditor.js";
3
+ import {ParameterTrack} from "../../../../../engine/graphics/particles/particular/engine/parameter/ParameterTrack.js";
4
+
5
+ export class ParameterTrackSetEditor extends ObjectEditor {
6
+ get schema() {
7
+ return {
8
+ properties: {
9
+ tracks: {
10
+ type: List,
11
+ type_parameters: [ParameterTrack],
12
+ },
13
+ }
14
+ };
15
+ }
16
+ }
@@ -12,6 +12,7 @@ import {
12
12
  EmissionFromType
13
13
  } from "../../../../../engine/graphics/particles/particular/engine/emitter/EmissionFromType.js";
14
14
  import { DataType } from "../../../../../core/collection/table/DataType.js";
15
+ import {ParameterTrackSetEditor} from "./ParameterTrackSetEditor.js";
15
16
 
16
17
  export class ParticleEmitterLayerEditor extends ObjectEditor {
17
18
  get schema() {
@@ -35,6 +36,9 @@ export class ParticleEmitterLayerEditor extends ObjectEditor {
35
36
  type: Number,
36
37
  numeric_type: DataType.Uint32
37
38
  },
39
+ parameterTracks: {
40
+ editor: new ParameterTrackSetEditor()
41
+ },
38
42
  scaledSpriteHalfSize: {
39
43
  transient: true
40
44
  },
@@ -236,6 +236,8 @@ export class EngineHarness {
236
236
  * @param {number} [cameraFieldOfView]
237
237
  * @param {number} [cameraFarDistance]
238
238
  * @param {boolean} [cameraController=true]
239
+ * @param {boolean} [cameraAutoClip]
240
+ * @param shadowmapResolution
239
241
  */
240
242
  static async buildBasics({
241
243
  engine,
@@ -252,11 +254,13 @@ export class EngineHarness {
252
254
  enableLights = true,
253
255
  cameraFieldOfView,
254
256
  cameraFarDistance,
255
- cameraController = true
257
+ cameraController = true,
258
+ cameraAutoClip = false,
259
+ shadowmapResolution
256
260
  }) {
257
261
 
258
262
  if (enableLights) {
259
- EngineHarness.buildLights({ engine: engine });
263
+ EngineHarness.buildLights({ engine: engine, shadowmapResolution });
260
264
  }
261
265
 
262
266
  const camera = EngineHarness.buildCamera({
@@ -266,7 +270,8 @@ export class EngineHarness {
266
270
  yaw,
267
271
  distance,
268
272
  fieldOfView: cameraFieldOfView,
269
- distanceMax: cameraFarDistance
273
+ distanceMax: cameraFarDistance,
274
+ autoClip: cameraAutoClip
270
275
  });
271
276
 
272
277
  const cameraEntity = camera.entity;
@@ -300,13 +305,14 @@ export class EngineHarness {
300
305
  *
301
306
  * @param {Engine} engine
302
307
  * @param {EntityComponentDataset} ecd
308
+ * @param shadowmapResolution
303
309
  */
304
- static buildLights({ engine, ecd = engine.entityManager.dataset }) {
310
+ static buildLights({ engine, ecd = engine.entityManager.dataset, shadowmapResolution = 1024 }) {
305
311
  const em = engine.entityManager;
306
312
 
307
313
  if (em.getSystem(LightSystem) === null) {
308
314
  em.addSystem(new LightSystem(engine, {
309
- shadowResolution: 1024
315
+ shadowResolution: shadowmapResolution
310
316
  }));
311
317
  }
312
318