@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.
- package/core/binary/BinaryBuffer.js +13 -1
- package/core/binary/BitSet.js +2 -2
- package/core/collection/array/array_range_equal_strict.js +22 -0
- package/core/color/sRGB_to_linear.js +9 -4
- package/core/geom/3d/plane/orient3d_fast.js +3 -0
- package/core/geom/3d/plane/orient3d_robust.js +41 -0
- package/core/geom/3d/sphere/harmonics/README.md +15 -0
- package/core/geom/3d/sphere/harmonics/sh3_add.js +21 -0
- package/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.js +618 -0
- package/core/geom/3d/sphere/harmonics/sh3_sample_by_direction.js +49 -0
- package/core/geom/3d/sphere/harmonics/sh3_sample_irradiance_by_direction.js +53 -0
- package/core/geom/3d/tetrahedra/TetrahedralMesh.js +251 -68
- package/core/geom/3d/tetrahedra/TetrahedralMesh.spec.js +80 -3
- package/core/geom/3d/tetrahedra/build_tetrahedral_mesh_buffer_geometry.js +75 -0
- package/core/geom/3d/tetrahedra/delaunay/Cavity.js +5 -1
- package/core/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.js +30 -31
- package/core/geom/3d/tetrahedra/delaunay/fill_in_a_cavity.js +54 -18
- package/core/geom/3d/tetrahedra/delaunay/push_boundary_with_validation.js +27 -0
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_cavity.js +89 -0
- package/core/geom/3d/tetrahedra/delaunay/{tetrahedral_mesh_walk_toward_cavity.js → tetrahedral_mesh_walk_towards_containing_tetrahedron.js} +15 -12
- package/core/geom/3d/tetrahedra/delaunay/validate_cavity_boundary.js +60 -0
- package/core/geom/3d/tetrahedra/{point_in_tetrahedron_circumsphere.js → in_sphere_fast.js} +2 -4
- package/core/geom/3d/tetrahedra/in_sphere_robust.js +53 -0
- package/core/geom/3d/tetrahedra/prototypeTetrahedraBuilder.js +44 -35
- package/core/geom/3d/tetrahedra/validate_tetrahedral_mesh.js +85 -38
- package/core/geom/3d/util/make_justified_point_grid.js +31 -0
- package/core/process/delay.js +5 -0
- package/editor/Editor.js +3 -0
- package/editor/ecs/component/editors/ecs/ParameterLookupTableEditor.js +195 -11
- package/editor/ecs/component/editors/ecs/ParameterTrackSetEditor.js +16 -0
- package/editor/ecs/component/editors/ecs/ParticleEmitterLayerEditor.js +4 -0
- package/engine/EngineHarness.js +11 -5
- package/engine/ecs/terrain/ecs/TerrainSystem.js +7 -1
- package/engine/ecs/transform/copy_three_transform.js +15 -0
- package/engine/graphics/ecs/light/Light.js +6 -1
- package/engine/graphics/ecs/light/LightSystem.d.ts +1 -1
- package/engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.js +2 -17
- package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +2 -2
- package/engine/graphics/sh3/LightProbeVolume.js +595 -0
- package/engine/graphics/sh3/SH3VisualisationMaterial.js +79 -0
- package/engine/graphics/sh3/prototypeSH3Probe.js +427 -0
- package/engine/graphics/sh3/visualise_probe.js +40 -0
- package/engine/graphics/texture/atlas/TextureAtlas.js +15 -3
- package/engine/intelligence/blackboard/AbstractBlackboard.d.ts +1 -1
- package/package.json +2 -1
- package/samples/terrain/from_image_2.js +127 -82
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_cavity2.js +0 -224
- 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
|
|
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 {
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
59
|
+
await delay(1000);
|
|
48
60
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
55
|
-
lines.push(a, c);
|
|
56
|
-
lines.push(a, d);
|
|
68
|
+
console.log(tetrahedra);
|
|
57
69
|
|
|
58
|
-
|
|
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 =
|
|
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 {
|
|
26
|
+
* @param {number} neighbour_id
|
|
27
|
+
* @param {function(string):*} consumer
|
|
28
|
+
* @return {boolean}
|
|
27
29
|
*/
|
|
28
|
-
function
|
|
30
|
+
export function validate_neighbour(mesh, tet, neighbour_id, consumer) {
|
|
29
31
|
let valid = true;
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
const neighbour = mesh.getNeighbour(tet, i);
|
|
33
|
+
const neighbour = mesh.getNeighbour(tet, neighbour_id);
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
if (neighbour === INVALID_NEIGHBOUR) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
// decode neighbour
|
|
40
|
+
const neighbour_tet = neighbour >> 2;
|
|
41
|
+
const neighbour_vertex_id = neighbour & 3;
|
|
41
42
|
|
|
42
|
-
|
|
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
|
-
|
|
48
|
-
}
|
|
46
|
+
consumer(`Tetrahedron ${tet} Neighbour[${neighbour_id}] ${neighbour_tet} does not exist in the mesh`);
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
51
50
|
|
|
52
|
-
|
|
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
|
-
|
|
59
|
-
const back_link_vertex_index = back_link & 3;
|
|
53
|
+
if (back_link === INVALID_NEIGHBOUR) {
|
|
60
54
|
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
65
|
-
|
|
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[${
|
|
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
|
-
|
|
73
|
-
|
|
88
|
+
return valid;
|
|
89
|
+
}
|
|
74
90
|
|
|
75
|
-
if (j === i) {
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
91
|
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
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
|
+
}
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
},
|
package/engine/EngineHarness.js
CHANGED
|
@@ -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:
|
|
315
|
+
shadowResolution: shadowmapResolution
|
|
310
316
|
}));
|
|
311
317
|
}
|
|
312
318
|
|