@woosh/meep-engine 2.50.0 → 2.50.1
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/package.json +1 -1
- package/src/core/geom/3d/aabb/aabb3_detailed_volume_intersection.js +4 -4
- package/src/core/geom/3d/frustum/frustum3_computeNearestPointToPoint.js +5 -5
- package/src/core/geom/3d/plane/is_point_within_planes.js +1 -1
- package/src/core/geom/3d/plane/lerp_planes_to_array.js +2 -0
- package/src/core/geom/3d/plane/{plane_computeConvex3PlaneIntersection.js → plane3_compute_convex_3_plane_intersection.js} +1 -1
- package/src/core/geom/3d/plane/{plane3_computeLineSegmentIntersection.js → plane3_compute_line_segment_intersection.js} +1 -1
- package/src/core/geom/3d/plane/{computePlanePlaneIntersection.js → plane3_compute_plane_intersection.js} +15 -11
- package/src/core/geom/3d/plane/{computePlaneLineIntersection.js → plane3_compute_ray_intersection.js} +5 -1
- package/src/core/geom/3d/plane/plane3_intersect_plane.js +14 -0
- package/src/core/geom/3d/plane/{plane_three_compute_convex3_plane_intersection.js → plane3_three_compute_convex_3_plane_intersection.js} +7 -4
- package/src/core/geom/3d/plane/planeRayIntersection.js +2 -2
- package/src/core/geom/3d/tetrahedra/compute_bounding_simplex_3d.js +5 -5
- package/src/core/geom/Vector3.d.ts +4 -2
- package/src/engine/ecs/EntityBlueprint.d.ts +14 -0
- package/src/engine/ecs/EntityBlueprint.js +2 -2
- package/src/engine/ecs/EntityBlueprint.spec.js +52 -0
- package/src/engine/ecs/EntityBuilder.js +8 -0
- package/src/engine/ecs/EntityManager.d.ts +1 -0
- package/src/engine/ecs/EntityManager.js +16 -8
- package/src/engine/ecs/EntityManager.spec.js +62 -1
- package/src/engine/ecs/System.js +8 -2
- package/src/engine/graphics/ecs/camera/Camera.js +2 -2
- package/src/engine/graphics/ecs/path/entity/EntityPath.js +1 -1
- package/src/engine/graphics/render/forward_plus/computeFrustumCorners.js +10 -10
- package/src/engine/graphics/render/layers/RenderLayerUtils.js +3 -3
- package/src/engine/intelligence/behavior/primitive/SucceedingBehavior.js +1 -1
- package/src/engine/scene/SceneManager.spec.js +24 -0
- package/src/generation/grid/GridData.js +8 -2
- package/src/generation/grid/GridData.spec.js +5 -0
- package/src/generation/markers/actions/MarkerNodeActionEntityPlacement.js +1 -1
- package/src/view/minimap/dom/MinimapCameraView.js +3 -3
- package/src/core/geom/3d/plane/computePlaneRayIntersection.js +0 -55
- package/src/generation/grid/MarkerMatchCounter.js +0 -25
package/package.json
CHANGED
|
@@ -2,11 +2,11 @@ import { aabb3_build_corners } from "./aabb3_build_corners.js";
|
|
|
2
2
|
import { aabb_build_frustum } from "./aabb3_build_frustum.js";
|
|
3
3
|
import { aabb3_corner_edge_mapping } from "./aabb3_corner_edge_mapping.js";
|
|
4
4
|
import { aabb3_edge_corner_mapping } from "./aabb3_edge_corner_mapping.js";
|
|
5
|
-
import {
|
|
5
|
+
import { plane3_compute_convex_3_plane_intersection } from "../plane/plane3_compute_convex_3_plane_intersection.js";
|
|
6
6
|
import { is_point_within_planes } from "../plane/is_point_within_planes.js";
|
|
7
7
|
import { v3_distance_above_plane } from "../../v3_distance_above_plane.js";
|
|
8
8
|
import { EPSILON } from "../../../math/EPSILON.js";
|
|
9
|
-
import {
|
|
9
|
+
import { plane3_compute_line_segment_intersection } from "../plane/plane3_compute_line_segment_intersection.js";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Common piece of continuous memory for better cache coherence
|
|
@@ -205,7 +205,7 @@ export function aabb3_detailed_volume_intersection(
|
|
|
205
205
|
const nz = planes[plane_address + 2];
|
|
206
206
|
const c = planes[plane_address + 3];
|
|
207
207
|
|
|
208
|
-
const edge_intersects =
|
|
208
|
+
const edge_intersects = plane3_compute_line_segment_intersection(
|
|
209
209
|
scratch_v3_array, 0,
|
|
210
210
|
corner_0_x, corner_0_y, corner_0_z,
|
|
211
211
|
corner_1_x, corner_1_y, corner_1_z,
|
|
@@ -256,7 +256,7 @@ export function aabb3_detailed_volume_intersection(
|
|
|
256
256
|
const next_orthogonal_plane_index = (frustum_plane_index + 2) % 4;
|
|
257
257
|
const cp_1 = next_orthogonal_plane_index * 4;
|
|
258
258
|
|
|
259
|
-
const intersection_exists =
|
|
259
|
+
const intersection_exists = plane3_compute_convex_3_plane_intersection(
|
|
260
260
|
scratch_v3_array, 0,
|
|
261
261
|
aabb_plane_nx, aabb_plane_ny, aabb_plane_nz, aabb_plane_constant,
|
|
262
262
|
planes[cp_0], planes[cp_0 + 1], planes[cp_0 + 2], planes[cp_0 + 3],
|
|
@@ -3,9 +3,9 @@ import { plane3_projectPoint } from "../plane/plane3_projectPoint.js";
|
|
|
3
3
|
import { ray_computeNearestPointToPoint } from "../ray/ray_computeNearestPointToPoint.js";
|
|
4
4
|
import { v3_distance_above_plane } from "../../v3_distance_above_plane.js";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
} from "../plane/
|
|
8
|
-
import {
|
|
6
|
+
plane3_three_compute_convex_3_plane_intersection
|
|
7
|
+
} from "../plane/plane3_three_compute_convex_3_plane_intersection.js";
|
|
8
|
+
import { plane3_compute_plane_intersection } from "../plane/plane3_compute_plane_intersection.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
*
|
|
@@ -82,7 +82,7 @@ export function frustum3_computeNearestPointToPoint(result, planes, target_x, ta
|
|
|
82
82
|
const plane_0 = planes[plane_0_index];
|
|
83
83
|
const plane_1 = planes[plane_1_index];
|
|
84
84
|
|
|
85
|
-
const intersection_exists =
|
|
85
|
+
const intersection_exists = plane3_compute_plane_intersection(plane_0, plane_1, scratch_v3_0, scratch_v3_1);
|
|
86
86
|
|
|
87
87
|
if (!intersection_exists) {
|
|
88
88
|
throw new Error('Planes are parallel and do not intersect, likely an invalid frustum');
|
|
@@ -103,7 +103,7 @@ export function frustum3_computeNearestPointToPoint(result, planes, target_x, ta
|
|
|
103
103
|
const plane_1 = planes[plane_1_index];
|
|
104
104
|
const plane_2 = planes[plane_2_index];
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
plane3_three_compute_convex_3_plane_intersection(scratch_v3_array, 0, plane_0, plane_1, plane_2);
|
|
107
107
|
|
|
108
108
|
result.readFromArray(scratch_v3_array, 0)
|
|
109
109
|
}
|
|
@@ -2,7 +2,7 @@ import { v3_distance_above_plane } from "../../v3_distance_above_plane.js";
|
|
|
2
2
|
import { EPSILON } from "../../../math/EPSILON.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Checks if point lies in a volume bound by a number of planes, such a camera frustum
|
|
6
6
|
* @param {number} x
|
|
7
7
|
* @param {number} y
|
|
8
8
|
* @param {number} z
|
|
@@ -23,7 +23,7 @@ import { v3_dot } from "../../v3_dot.js";
|
|
|
23
23
|
* @param {number} c_constant
|
|
24
24
|
* @returns {boolean} true if planes intersect, false otherwise
|
|
25
25
|
*/
|
|
26
|
-
export function
|
|
26
|
+
export function plane3_compute_convex_3_plane_intersection(
|
|
27
27
|
result, result_offset,
|
|
28
28
|
a_normal_x, a_normal_y, a_normal_z, a_constant,
|
|
29
29
|
b_normal_x, b_normal_y, b_normal_z, b_constant,
|
|
@@ -16,7 +16,7 @@ import { v3_dot } from "../../v3_dot.js";
|
|
|
16
16
|
* @param {number} dist
|
|
17
17
|
* @returns {boolean}
|
|
18
18
|
*/
|
|
19
|
-
export function
|
|
19
|
+
export function plane3_compute_line_segment_intersection(
|
|
20
20
|
result, result_offset,
|
|
21
21
|
x0, y0, z0,
|
|
22
22
|
x1, y1, z1,
|
|
@@ -3,21 +3,25 @@
|
|
|
3
3
|
Algorithm taken from http://geomalgorithms.com/a05-_intersect-1.html. See the
|
|
4
4
|
section 'Intersection of 2 Planes' and specifically the subsection
|
|
5
5
|
(A) Direct Linear Equation
|
|
6
|
+
TODO consolidate with {@link plane3_intersect_plane} as they seem to do the same thing
|
|
6
7
|
@param {Plane} p1
|
|
7
8
|
@param {Plane} p2
|
|
8
|
-
@param {Vector3}
|
|
9
|
-
@param {Vector3}
|
|
9
|
+
@param {Vector3} out_point
|
|
10
|
+
@param {Vector3} out_direction
|
|
10
11
|
@returns {boolean}
|
|
11
12
|
*/
|
|
12
|
-
export function
|
|
13
|
+
export function plane3_compute_plane_intersection(
|
|
14
|
+
p1, p2,
|
|
15
|
+
out_point, out_direction
|
|
16
|
+
) {
|
|
13
17
|
|
|
14
18
|
// the cross product gives us the direction of the line at the intersection
|
|
15
19
|
// of the two planes, and gives us an easy way to check if the two planes
|
|
16
20
|
// are parallel - the cross product will have zero magnitude
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
out_direction.crossVectors(p1.normal, p2.normal);
|
|
19
23
|
|
|
20
|
-
const magnitude =
|
|
24
|
+
const magnitude = out_direction.lengthSqr();
|
|
21
25
|
|
|
22
26
|
if (magnitude === 0) {
|
|
23
27
|
return false;
|
|
@@ -28,16 +32,16 @@ export function computePlanePlaneIntersection(p1, p2, point, direction) {
|
|
|
28
32
|
// to set as zero by seeing which has the largest absolute value in the
|
|
29
33
|
// directional vector
|
|
30
34
|
|
|
31
|
-
const X = Math.abs(
|
|
32
|
-
const Y = Math.abs(
|
|
33
|
-
const Z = Math.abs(
|
|
35
|
+
const X = Math.abs(out_direction.x);
|
|
36
|
+
const Y = Math.abs(out_direction.y);
|
|
37
|
+
const Z = Math.abs(out_direction.z);
|
|
34
38
|
|
|
35
39
|
if (Z >= X && Z >= Y) {
|
|
36
|
-
solveIntersectingPoint('z', 'x', 'y', p1, p2,
|
|
40
|
+
solveIntersectingPoint('z', 'x', 'y', p1, p2, out_point);
|
|
37
41
|
} else if (Y >= Z && Y >= X) {
|
|
38
|
-
solveIntersectingPoint('y', 'z', 'x', p1, p2,
|
|
42
|
+
solveIntersectingPoint('y', 'z', 'x', p1, p2, out_point);
|
|
39
43
|
} else {
|
|
40
|
-
solveIntersectingPoint('x', 'y', 'z', p1, p2,
|
|
44
|
+
solveIntersectingPoint('x', 'y', 'z', p1, p2, out_point);
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
return true;
|
|
@@ -15,7 +15,7 @@ import { v3_dot } from "../../v3_dot.js";
|
|
|
15
15
|
* @param {number} dist Plane distance
|
|
16
16
|
* @returns {boolean} true if intersection is found, false otherwise
|
|
17
17
|
*/
|
|
18
|
-
export function
|
|
18
|
+
export function plane3_compute_ray_intersection(
|
|
19
19
|
out,
|
|
20
20
|
originX, originY, originZ,
|
|
21
21
|
directionX, directionY, directionZ,
|
|
@@ -29,6 +29,10 @@ export function computePlaneLineIntersection(
|
|
|
29
29
|
|
|
30
30
|
const t = -p / denom;
|
|
31
31
|
|
|
32
|
+
if (t < 0) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
32
36
|
out.set(
|
|
33
37
|
directionX * t + originX,
|
|
34
38
|
directionY * t + originY,
|
|
@@ -3,6 +3,20 @@ import Vector3 from "../../Vector3.js";
|
|
|
3
3
|
const v0 = new Vector3();
|
|
4
4
|
const v1 = new Vector3();
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param {Vector3} result_point
|
|
9
|
+
* @param {Vector3} result_direction
|
|
10
|
+
* @param {number} a_normal_x
|
|
11
|
+
* @param {number} a_normal_y
|
|
12
|
+
* @param {number} a_normal_z
|
|
13
|
+
* @param {number} a_constant
|
|
14
|
+
* @param {number} b_normal_x
|
|
15
|
+
* @param {number} b_normal_y
|
|
16
|
+
* @param {number} b_normal_z
|
|
17
|
+
* @param {number} b_constant
|
|
18
|
+
* @return {boolean}
|
|
19
|
+
*/
|
|
6
20
|
export function plane3_intersect_plane(
|
|
7
21
|
result_point,
|
|
8
22
|
result_direction,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { plane3_compute_convex_3_plane_intersection } from "./plane3_compute_convex_3_plane_intersection.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Works with THREE.js Plane representations as inputs
|
|
5
5
|
* @param {number[]} result
|
|
6
6
|
* @param {number} result_offset
|
|
7
7
|
* @param {Plane} a
|
|
@@ -9,12 +9,15 @@ import { plane_computeConvex3PlaneIntersection } from "./plane_computeConvex3Pla
|
|
|
9
9
|
* @param {Plane} c
|
|
10
10
|
* @returns {boolean}
|
|
11
11
|
*/
|
|
12
|
-
export function
|
|
12
|
+
export function plane3_three_compute_convex_3_plane_intersection(
|
|
13
|
+
result, result_offset,
|
|
14
|
+
a, b, c
|
|
15
|
+
) {
|
|
13
16
|
const a_normal = a.normal;
|
|
14
17
|
const b_normal = b.normal;
|
|
15
18
|
const c_normal = c.normal;
|
|
16
19
|
|
|
17
|
-
return
|
|
20
|
+
return plane3_compute_convex_3_plane_intersection(
|
|
18
21
|
result,
|
|
19
22
|
result_offset,
|
|
20
23
|
a_normal.x, a_normal.y, a_normal.z, a.constant,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { plane3_compute_ray_intersection } from "./plane3_compute_ray_intersection.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
*
|
|
@@ -10,5 +10,5 @@ import { computePlaneRayIntersection } from "./computePlaneRayIntersection.js";
|
|
|
10
10
|
* @returns {boolean} true if intersection is found, false otherwise
|
|
11
11
|
*/
|
|
12
12
|
export function planeRayIntersection(out, origin, direction, normal, dist) {
|
|
13
|
-
return
|
|
13
|
+
return plane3_compute_ray_intersection(out, origin.x, origin.y, origin.z, direction.x, direction.y, direction.z, normal.x, normal.y, normal.z, dist)
|
|
14
14
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { v3_dot } from "../../v3_dot.js";
|
|
2
2
|
import { min2 } from "../../../math/min2.js";
|
|
3
3
|
import { max2 } from "../../../math/max2.js";
|
|
4
|
-
import {
|
|
4
|
+
import { plane3_compute_convex_3_plane_intersection } from "../plane/plane3_compute_convex_3_plane_intersection.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Planes used to bound the simplex
|
|
@@ -152,25 +152,25 @@ export function compute_bounding_simplex_3d(
|
|
|
152
152
|
min_d -= padding;
|
|
153
153
|
|
|
154
154
|
// let's compute intersection points for each tri-plane combination to find simplex corners
|
|
155
|
-
|
|
155
|
+
plane3_compute_convex_3_plane_intersection(
|
|
156
156
|
result, result_offset,
|
|
157
157
|
SIMPLEX_PROJECTION_AXES[0], SIMPLEX_PROJECTION_AXES[1], SIMPLEX_PROJECTION_AXES[2], -min_a,
|
|
158
158
|
SIMPLEX_PROJECTION_AXES[3], SIMPLEX_PROJECTION_AXES[4], SIMPLEX_PROJECTION_AXES[5], -min_b,
|
|
159
159
|
SIMPLEX_PROJECTION_AXES[9], SIMPLEX_PROJECTION_AXES[10], SIMPLEX_PROJECTION_AXES[11], -min_d
|
|
160
160
|
);
|
|
161
|
-
|
|
161
|
+
plane3_compute_convex_3_plane_intersection(
|
|
162
162
|
result, result_offset + 3,
|
|
163
163
|
SIMPLEX_PROJECTION_AXES[3], SIMPLEX_PROJECTION_AXES[4], SIMPLEX_PROJECTION_AXES[5], -min_b,
|
|
164
164
|
SIMPLEX_PROJECTION_AXES[6], SIMPLEX_PROJECTION_AXES[7], SIMPLEX_PROJECTION_AXES[8], -min_c,
|
|
165
165
|
SIMPLEX_PROJECTION_AXES[9], SIMPLEX_PROJECTION_AXES[10], SIMPLEX_PROJECTION_AXES[11], -min_d
|
|
166
166
|
);
|
|
167
|
-
|
|
167
|
+
plane3_compute_convex_3_plane_intersection(
|
|
168
168
|
result, result_offset + 6,
|
|
169
169
|
SIMPLEX_PROJECTION_AXES[0], SIMPLEX_PROJECTION_AXES[1], SIMPLEX_PROJECTION_AXES[2], -min_a,
|
|
170
170
|
SIMPLEX_PROJECTION_AXES[3], SIMPLEX_PROJECTION_AXES[4], SIMPLEX_PROJECTION_AXES[5], -min_b,
|
|
171
171
|
SIMPLEX_PROJECTION_AXES[6], SIMPLEX_PROJECTION_AXES[7], SIMPLEX_PROJECTION_AXES[8], -min_c
|
|
172
172
|
);
|
|
173
|
-
|
|
173
|
+
plane3_compute_convex_3_plane_intersection(
|
|
174
174
|
result, result_offset + 9,
|
|
175
175
|
SIMPLEX_PROJECTION_AXES[6], SIMPLEX_PROJECTION_AXES[7], SIMPLEX_PROJECTION_AXES[8], -min_c,
|
|
176
176
|
SIMPLEX_PROJECTION_AXES[0], SIMPLEX_PROJECTION_AXES[1], SIMPLEX_PROJECTION_AXES[2], -min_a,
|
|
@@ -69,14 +69,16 @@ export default class Vector3 implements Vector3Like {
|
|
|
69
69
|
|
|
70
70
|
clone(): Vector3
|
|
71
71
|
|
|
72
|
-
equals(other:Vector3Like):boolean
|
|
72
|
+
equals(other: Vector3Like): boolean
|
|
73
73
|
|
|
74
|
-
roughlyEquals(other:Vector3Like,tolerance?:number):boolean
|
|
74
|
+
roughlyEquals(other: Vector3Like, tolerance?: number): boolean
|
|
75
75
|
|
|
76
76
|
normalize(): void
|
|
77
77
|
|
|
78
78
|
length(): number
|
|
79
79
|
|
|
80
|
+
lengthSqr(): number
|
|
81
|
+
|
|
80
82
|
lerpVectors(a: Vector3Like, b: Vector3Like, fraction: number): void
|
|
81
83
|
|
|
82
84
|
distanceTo(other: Vector3Like): number
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
import EntityBuilder from "./EntityBuilder";
|
|
2
|
+
|
|
3
|
+
interface Type<T> extends Function {
|
|
4
|
+
new(...args: any[]): T;
|
|
5
|
+
}
|
|
6
|
+
|
|
1
7
|
export class EntityBlueprint {
|
|
2
8
|
static from(components: any[]): EntityBlueprint
|
|
9
|
+
|
|
10
|
+
add<T>(component: T): void
|
|
11
|
+
|
|
12
|
+
addJSON<T>(klass: Type<T>, json: any): void
|
|
13
|
+
|
|
14
|
+
clear(): void
|
|
15
|
+
|
|
16
|
+
build(seed?: object): EntityBuilder
|
|
3
17
|
}
|
|
@@ -140,10 +140,10 @@ export class EntityBlueprint {
|
|
|
140
140
|
|
|
141
141
|
|
|
142
142
|
/**
|
|
143
|
-
*
|
|
143
|
+
* @param {object} [templateSeed]
|
|
144
144
|
* @return {EntityBuilder}
|
|
145
145
|
*/
|
|
146
|
-
|
|
146
|
+
build(templateSeed) {
|
|
147
147
|
const eb = new EntityBuilder();
|
|
148
148
|
|
|
149
149
|
this.componentnts.forEach((template, ComponentClass) => {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { EntityBlueprint } from "./EntityBlueprint.js";
|
|
2
|
+
import EntityBuilder from "./EntityBuilder.js";
|
|
3
|
+
|
|
4
|
+
test("constructor does not throw", () => {
|
|
5
|
+
|
|
6
|
+
expect(() => new EntityBlueprint()).not.toThrow();
|
|
7
|
+
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("build empty", () => {
|
|
11
|
+
const blueprint = new EntityBlueprint();
|
|
12
|
+
|
|
13
|
+
const entity = blueprint.build();
|
|
14
|
+
|
|
15
|
+
expect(entity).toBeDefined();
|
|
16
|
+
expect(entity).toBeInstanceOf(EntityBuilder);
|
|
17
|
+
expect(entity.count).toEqual(0);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
class DummyComponent {
|
|
21
|
+
a = 0
|
|
22
|
+
|
|
23
|
+
fromJSON({ a }) {
|
|
24
|
+
this.a = a;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
test("a copy of the added component ends up on built entity", () => {
|
|
29
|
+
|
|
30
|
+
const blueprint = new EntityBlueprint();
|
|
31
|
+
|
|
32
|
+
blueprint.addJSON(DummyComponent, { a: 7 });
|
|
33
|
+
|
|
34
|
+
const entity = blueprint.build();
|
|
35
|
+
|
|
36
|
+
const retrieved = entity.getComponent(DummyComponent);
|
|
37
|
+
|
|
38
|
+
expect(retrieved).toBeInstanceOf(DummyComponent);
|
|
39
|
+
expect(retrieved.a).toEqual(7);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("seed template", () => {
|
|
43
|
+
const blueprint = new EntityBlueprint();
|
|
44
|
+
|
|
45
|
+
blueprint.addJSON(DummyComponent, { a: '$x' });
|
|
46
|
+
|
|
47
|
+
const entity = blueprint.build({ x: 13 });
|
|
48
|
+
|
|
49
|
+
const component = entity.getComponent(DummyComponent);
|
|
50
|
+
|
|
51
|
+
expect(component.a).toEqual(13);
|
|
52
|
+
});
|
|
@@ -329,6 +329,9 @@ EntityManager.prototype.simulate = function (timeDelta) {
|
|
|
329
329
|
const fixed_step = this.fixedUpdateStepSize;
|
|
330
330
|
const accumulatedTime = this.systemAccumulatedFixedStepTime;
|
|
331
331
|
|
|
332
|
+
assert.notNaN(fixed_step, 'fixed_step');
|
|
333
|
+
assert.greaterThan(fixed_step, 0, 'fixed_step must be greater than 0');
|
|
334
|
+
|
|
332
335
|
for (let i = 0; i < system_count; i++) {
|
|
333
336
|
|
|
334
337
|
const system = systems[i];
|
|
@@ -337,7 +340,7 @@ EntityManager.prototype.simulate = function (timeDelta) {
|
|
|
337
340
|
let accumulated_time = accumulatedTime.get(system) + timeDelta;
|
|
338
341
|
|
|
339
342
|
|
|
340
|
-
while (accumulated_time
|
|
343
|
+
while (accumulated_time >= fixed_step) {
|
|
341
344
|
|
|
342
345
|
try {
|
|
343
346
|
system.fixedUpdate(fixed_step)
|
|
@@ -458,9 +461,10 @@ function validateSystem(system) {
|
|
|
458
461
|
/**
|
|
459
462
|
* If the {@link EntityManager} is already started, the system will be started automatically before being added
|
|
460
463
|
* @param {System} system
|
|
464
|
+
* @returns {Promise}
|
|
461
465
|
* @throws {IllegalStateException}
|
|
462
466
|
*/
|
|
463
|
-
EntityManager.prototype.addSystem =
|
|
467
|
+
EntityManager.prototype.addSystem = function (system) {
|
|
464
468
|
assert.defined(system, "system");
|
|
465
469
|
assert.isInstanceOf(system, System, 'system', 'System');
|
|
466
470
|
|
|
@@ -469,7 +473,7 @@ EntityManager.prototype.addSystem = async function (system) {
|
|
|
469
473
|
if (existing !== null) {
|
|
470
474
|
if (existing === system) {
|
|
471
475
|
//system already added, do nothing
|
|
472
|
-
return;
|
|
476
|
+
return Promise.resolve();
|
|
473
477
|
} else {
|
|
474
478
|
throw new IllegalStateException(`Another instance of system '${computeSystemName(system)}' is already registered`);
|
|
475
479
|
}
|
|
@@ -508,7 +512,7 @@ EntityManager.prototype.addSystem = async function (system) {
|
|
|
508
512
|
if (this.state === EntityManagerState.Running) {
|
|
509
513
|
//initialize the system
|
|
510
514
|
startup_promise = new Promise((resolve, reject) => {
|
|
511
|
-
|
|
515
|
+
this.startSystem(system, resolve, reject);
|
|
512
516
|
});
|
|
513
517
|
} else {
|
|
514
518
|
startup_promise = Promise.resolve();
|
|
@@ -526,13 +530,13 @@ EntityManager.prototype.addSystem = async function (system) {
|
|
|
526
530
|
|
|
527
531
|
this.on.systemAdded.send1(system);
|
|
528
532
|
|
|
529
|
-
|
|
533
|
+
return startup_promise;
|
|
530
534
|
};
|
|
531
535
|
|
|
532
536
|
/**
|
|
533
537
|
*
|
|
534
538
|
* @param {System} system
|
|
535
|
-
* @returns {boolean}
|
|
539
|
+
* @returns {Promise<boolean>}
|
|
536
540
|
*/
|
|
537
541
|
EntityManager.prototype.removeSystem = async function (system) {
|
|
538
542
|
assert.defined(system, "system");
|
|
@@ -573,11 +577,13 @@ EntityManager.prototype.removeSystem = async function (system) {
|
|
|
573
577
|
this.systemAccumulatedFixedStepTime.delete(system);
|
|
574
578
|
|
|
575
579
|
this.on.systemRemoved.send1(system);
|
|
580
|
+
|
|
581
|
+
return true;
|
|
576
582
|
};
|
|
577
583
|
|
|
578
584
|
|
|
579
585
|
/**
|
|
580
|
-
*
|
|
586
|
+
* @private
|
|
581
587
|
* @param {System} system
|
|
582
588
|
* @param {function(system: System)} successCallback
|
|
583
589
|
* @param {function(reason:*)} errorCallback
|
|
@@ -614,13 +620,15 @@ EntityManager.prototype.stopSystem = function (system, successCallback, errorCal
|
|
|
614
620
|
};
|
|
615
621
|
|
|
616
622
|
/**
|
|
617
|
-
*
|
|
623
|
+
* @private
|
|
618
624
|
* @param {System} system
|
|
619
625
|
* @param {function(system: System)} successCallback
|
|
620
626
|
* @param {function(reason:*)} errorCallback
|
|
621
627
|
*/
|
|
622
628
|
EntityManager.prototype.startSystem = function (system, successCallback, errorCallback) {
|
|
623
629
|
assert.defined(system, "system");
|
|
630
|
+
assert.isFunction(successCallback, "successCallback");
|
|
631
|
+
assert.isFunction(errorCallback, "errorCallback");
|
|
624
632
|
|
|
625
633
|
if (system.state.getValue() === System.State.RUNNING) {
|
|
626
634
|
//system is already running
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EntityManager } from "./EntityManager.js";
|
|
2
|
-
import { System } from "./System.js";
|
|
2
|
+
import { System, SystemState } from "./System.js";
|
|
3
3
|
import { jest } from '@jest/globals';
|
|
4
|
+
import { IllegalStateException } from "../../core/fsm/exceptions/IllegalStateException.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
*
|
|
@@ -97,3 +98,63 @@ test("call to 'simulate' propagate to registered system", () => {
|
|
|
97
98
|
expect(update).toHaveBeenLastCalledWith(7);
|
|
98
99
|
});
|
|
99
100
|
});
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
test("call to 'simulate' ticks fixed update correctly", async () => {
|
|
104
|
+
|
|
105
|
+
const dummySystem = new DummySystem();
|
|
106
|
+
const fixedUpdate = jest.spyOn(dummySystem, 'fixedUpdate');
|
|
107
|
+
|
|
108
|
+
const em = await makeEm([dummySystem]);
|
|
109
|
+
|
|
110
|
+
em.fixedUpdateStepSize = 1.1;
|
|
111
|
+
|
|
112
|
+
em.simulate(3.3001);
|
|
113
|
+
|
|
114
|
+
expect(fixedUpdate).toHaveBeenCalledTimes(3);
|
|
115
|
+
expect(fixedUpdate).toHaveBeenLastCalledWith(1.1);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("hasSystem", async () => {
|
|
119
|
+
|
|
120
|
+
const dummySystem = new DummySystem();
|
|
121
|
+
const em = await makeEm([]);
|
|
122
|
+
|
|
123
|
+
expect(em.hasSystem(DummySystem)).toBe(false);
|
|
124
|
+
|
|
125
|
+
await em.addSystem(dummySystem);
|
|
126
|
+
|
|
127
|
+
expect(em.hasSystem(DummySystem)).toBe(true);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("addSystem", async () => {
|
|
131
|
+
const em = await makeEm([]);
|
|
132
|
+
|
|
133
|
+
const s1 = new DummySystem();
|
|
134
|
+
|
|
135
|
+
await em.addSystem(s1);
|
|
136
|
+
|
|
137
|
+
expect(s1.state.get()).toEqual(SystemState.RUNNING)
|
|
138
|
+
|
|
139
|
+
// system is already added, expect silent return
|
|
140
|
+
expect(() => em.addSystem(s1)).not.toThrow();
|
|
141
|
+
|
|
142
|
+
// 2 systems of the same class are not allowed
|
|
143
|
+
expect(() => em.addSystem(new DummySystem())).toThrow(IllegalStateException);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("removeSystem", async () => {
|
|
147
|
+
|
|
148
|
+
const system = new DummySystem();
|
|
149
|
+
|
|
150
|
+
const em = await makeEm([system]);
|
|
151
|
+
|
|
152
|
+
expect(await em.removeSystem(system)).toBe(true);
|
|
153
|
+
|
|
154
|
+
expect(em.hasSystem(DummySystem)).toBe(false);
|
|
155
|
+
|
|
156
|
+
expect(system.state.get()).toEqual(SystemState.STOPPED);
|
|
157
|
+
|
|
158
|
+
// Try again, this time we expect nothing to happen
|
|
159
|
+
expect(await em.removeSystem(system)).toEqual(false);
|
|
160
|
+
});
|
package/src/engine/ecs/System.js
CHANGED
|
@@ -155,13 +155,19 @@ Object.defineProperties(System.prototype, {
|
|
|
155
155
|
* @readonly
|
|
156
156
|
* @enum {number}
|
|
157
157
|
*/
|
|
158
|
-
|
|
158
|
+
export const SystemState = {
|
|
159
159
|
INITIAL: 0,
|
|
160
160
|
STARTING: 1,
|
|
161
161
|
RUNNING: 2,
|
|
162
162
|
STOPPING: 3,
|
|
163
163
|
STOPPED: 4
|
|
164
|
-
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @readonly
|
|
168
|
+
* @deprecated use {@link SystemState} directly instead
|
|
169
|
+
*/
|
|
170
|
+
System.State = SystemState;
|
|
165
171
|
|
|
166
172
|
/**
|
|
167
173
|
*
|
|
@@ -14,7 +14,7 @@ import { frustum_from_camera } from "./frustum_from_camera.js";
|
|
|
14
14
|
import { invertQuaternionOrientation } from "./InvertQuaternionOrientation.js";
|
|
15
15
|
import { v3_distance_above_plane } from "../../../../core/geom/v3_distance_above_plane.js";
|
|
16
16
|
import { ProjectionType } from "./ProjectionType.js";
|
|
17
|
-
import {
|
|
17
|
+
import { plane3_compute_ray_intersection } from "../../../../core/geom/3d/plane/plane3_compute_ray_intersection.js";
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* @class
|
|
@@ -250,7 +250,7 @@ export class Camera {
|
|
|
250
250
|
|
|
251
251
|
assert.defined(plane, 'plane');
|
|
252
252
|
|
|
253
|
-
|
|
253
|
+
plane3_compute_ray_intersection(
|
|
254
254
|
out,
|
|
255
255
|
origin_x, origin_y, origin_z,
|
|
256
256
|
direction_x, direction_y, direction_z,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "../../../../core/geom/3d/plane/
|
|
2
|
+
plane3_three_compute_convex_3_plane_intersection
|
|
3
|
+
} from "../../../../core/geom/3d/plane/plane3_three_compute_convex_3_plane_intersection.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Get frustum corners from a set of planes
|
|
@@ -13,7 +13,7 @@ export function computeFrustumCorners(result, planes) {
|
|
|
13
13
|
const plane_y0 = planes[2];
|
|
14
14
|
const plane_z0 = planes[4];
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
plane3_three_compute_convex_3_plane_intersection(result, 0,
|
|
17
17
|
plane_x0,
|
|
18
18
|
plane_y0,
|
|
19
19
|
plane_z0
|
|
@@ -21,7 +21,7 @@ export function computeFrustumCorners(result, planes) {
|
|
|
21
21
|
|
|
22
22
|
const plane_z1 = planes[5];
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
plane3_three_compute_convex_3_plane_intersection(result, 3,
|
|
25
25
|
plane_x0,
|
|
26
26
|
plane_y0,
|
|
27
27
|
plane_z1
|
|
@@ -29,13 +29,13 @@ export function computeFrustumCorners(result, planes) {
|
|
|
29
29
|
|
|
30
30
|
const plane_y1 = planes[3];
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
plane3_three_compute_convex_3_plane_intersection(result, 6,
|
|
33
33
|
plane_x0,
|
|
34
34
|
plane_y1,
|
|
35
35
|
plane_z0
|
|
36
36
|
);
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
plane3_three_compute_convex_3_plane_intersection(result, 9,
|
|
39
39
|
plane_x0,
|
|
40
40
|
plane_y1,
|
|
41
41
|
plane_z1
|
|
@@ -43,25 +43,25 @@ export function computeFrustumCorners(result, planes) {
|
|
|
43
43
|
|
|
44
44
|
const plane_x1 = planes[1];
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
plane3_three_compute_convex_3_plane_intersection(result, 12,
|
|
47
47
|
plane_x1,
|
|
48
48
|
plane_y0,
|
|
49
49
|
plane_z0
|
|
50
50
|
);
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
plane3_three_compute_convex_3_plane_intersection(result, 15,
|
|
53
53
|
plane_x1,
|
|
54
54
|
plane_y0,
|
|
55
55
|
plane_z1
|
|
56
56
|
);
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
plane3_three_compute_convex_3_plane_intersection(result, 18,
|
|
59
59
|
plane_x1,
|
|
60
60
|
plane_y1,
|
|
61
61
|
plane_z0
|
|
62
62
|
);
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
plane3_three_compute_convex_3_plane_intersection(result, 21,
|
|
65
65
|
plane_x1,
|
|
66
66
|
plane_y1,
|
|
67
67
|
plane_z1
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { v3_distance_above_plane } from "../../../../core/geom/v3_distance_above_plane.js";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
} from "../../../../core/geom/3d/plane/
|
|
3
|
+
plane3_three_compute_convex_3_plane_intersection
|
|
4
|
+
} from "../../../../core/geom/3d/plane/plane3_three_compute_convex_3_plane_intersection.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
*
|
|
@@ -43,7 +43,7 @@ export function buildPlanarRenderLayerClipPlaneComputationMethod(traversePlanes,
|
|
|
43
43
|
const p0 = pair[0];
|
|
44
44
|
const p1 = pair[1];
|
|
45
45
|
|
|
46
|
-
const planeIntersection =
|
|
46
|
+
const planeIntersection = plane3_three_compute_convex_3_plane_intersection(p, 0, p0, p1, objectPlane);
|
|
47
47
|
|
|
48
48
|
if (!planeIntersection) {
|
|
49
49
|
//no intersection
|
|
@@ -85,6 +85,7 @@ test("set active scene", () => {
|
|
|
85
85
|
manager.set('x');
|
|
86
86
|
|
|
87
87
|
expect(manager.current_scene).toBe(scene);
|
|
88
|
+
expect(scene.active.getValue()).toBe(true);
|
|
88
89
|
expect(entityManager.dataset).toBe(scene.dataset);
|
|
89
90
|
});
|
|
90
91
|
|
|
@@ -105,3 +106,26 @@ test("stack push/pop", () => {
|
|
|
105
106
|
|
|
106
107
|
expect(manager.current_scene).toBe(scene_x);
|
|
107
108
|
});
|
|
109
|
+
|
|
110
|
+
test("clear on empty does nothing", () => {
|
|
111
|
+
const { manager } = makeSM();
|
|
112
|
+
|
|
113
|
+
manager.clear();
|
|
114
|
+
|
|
115
|
+
expect(manager.current_scene).toBeNull();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("clear correctly clears active scene", () => {
|
|
119
|
+
|
|
120
|
+
const { manager, entityManager } = makeSM();
|
|
121
|
+
|
|
122
|
+
const scene = manager.create('x');
|
|
123
|
+
|
|
124
|
+
manager.set('x');
|
|
125
|
+
|
|
126
|
+
manager.clear();
|
|
127
|
+
|
|
128
|
+
expect(manager.current_scene).toBeNull();
|
|
129
|
+
expect(scene.active.getValue()).toBe(false);
|
|
130
|
+
expect(entityManager.dataset).toBe(null);
|
|
131
|
+
});
|
|
@@ -3,7 +3,6 @@ import { QuadTreeNode } from "../../core/geom/2d/quad-tree/QuadTreeNode.js";
|
|
|
3
3
|
import { OffsetScaleTransform2D } from "../../engine/ecs/terrain/ecs/OffsetScaleTransform2D.js";
|
|
4
4
|
import { max2 } from "../../core/math/max2.js";
|
|
5
5
|
import { qt_collect_by_circle } from "../../core/geom/2d/quad-tree/qt_collect_by_circle.js";
|
|
6
|
-
import { v2_length_sqr } from "../../core/geom/Vector2.js";
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
*
|
|
@@ -122,7 +121,14 @@ export class GridData {
|
|
|
122
121
|
const distance_limit_sqr = distance_limit * distance_limit;
|
|
123
122
|
|
|
124
123
|
// check refined containment
|
|
125
|
-
|
|
124
|
+
const position = marker.position;
|
|
125
|
+
|
|
126
|
+
const dx = x - position.x;
|
|
127
|
+
const dy = y - position.y;
|
|
128
|
+
|
|
129
|
+
const distance_sqr = dx * dx + dy * dy;
|
|
130
|
+
|
|
131
|
+
if (distance_sqr > distance_limit_sqr) {
|
|
126
132
|
continue;
|
|
127
133
|
}
|
|
128
134
|
|
|
@@ -64,7 +64,7 @@ export class MarkerNodeActionEntityPlacement extends MarkerNodeAction {
|
|
|
64
64
|
execute(grid, ecd, node) {
|
|
65
65
|
const blueprint = this.entity;
|
|
66
66
|
|
|
67
|
-
const entityBuilder = blueprint.
|
|
67
|
+
const entityBuilder = blueprint.build(node.properties);
|
|
68
68
|
|
|
69
69
|
// execute post-process step
|
|
70
70
|
if (this.processor !== null) {
|
|
@@ -3,7 +3,7 @@ import Vector3 from "../../../core/geom/Vector3.js";
|
|
|
3
3
|
import ObservedValue from "../../../core/model/ObservedValue.js";
|
|
4
4
|
import { SurfacePoint3 } from "../../../core/geom/3d/SurfacePoint3.js";
|
|
5
5
|
import SVG from "../../SVG.js";
|
|
6
|
-
import {
|
|
6
|
+
import { plane3_compute_ray_intersection } from "../../../core/geom/3d/plane/plane3_compute_ray_intersection.js";
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
const rayContact = new SurfacePoint3();
|
|
@@ -93,7 +93,7 @@ export class MinimapCameraView extends View {
|
|
|
93
93
|
const JITTER = 0.00001;
|
|
94
94
|
|
|
95
95
|
if (terrain === null) {
|
|
96
|
-
|
|
96
|
+
plane3_compute_ray_intersection(result, originX, originY, originZ, directionX, directionY, directionZ, 0, 1, 0, 0);
|
|
97
97
|
} else {
|
|
98
98
|
|
|
99
99
|
let oX = originX;
|
|
@@ -118,7 +118,7 @@ export class MinimapCameraView extends View {
|
|
|
118
118
|
|
|
119
119
|
if (!foundHit) {
|
|
120
120
|
//no hit found, fall back to planar test
|
|
121
|
-
|
|
121
|
+
plane3_compute_ray_intersection(result, originX, originY, originZ, directionX, directionY, directionZ, 0, 1, 0, 0);
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { v3_dot } from "../../v3_dot.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
* @param {Vector3} out Result will be written here
|
|
6
|
-
* @param {number} originX Ray origin
|
|
7
|
-
* @param {number} originY Ray origin
|
|
8
|
-
* @param {number} originZ Ray origin
|
|
9
|
-
* @param {number} directionX Ray direction
|
|
10
|
-
* @param {number} directionY Ray direction
|
|
11
|
-
* @param {number} directionZ Ray direction
|
|
12
|
-
* @param {number} normalX Plane normal
|
|
13
|
-
* @param {number} normalY Plane normal
|
|
14
|
-
* @param {number} normalZ Plane normal
|
|
15
|
-
* @param {number} dist Plane distance
|
|
16
|
-
* @returns {boolean} true if intersection is found, false otherwise
|
|
17
|
-
*/
|
|
18
|
-
export function computePlaneRayIntersection(out, originX, originY, originZ, directionX, directionY, directionZ, normalX, normalY, normalZ, dist) {
|
|
19
|
-
const denom = v3_dot(directionX, directionY, directionZ, normalX, normalY, normalZ);
|
|
20
|
-
|
|
21
|
-
const p = v3_dot(normalX, normalY, normalZ, originX, originY, originZ) + dist;
|
|
22
|
-
|
|
23
|
-
if (denom !== 0) {
|
|
24
|
-
|
|
25
|
-
const t = -p / denom;
|
|
26
|
-
|
|
27
|
-
if (t < 0) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
out.set(
|
|
32
|
-
directionX * t + originX,
|
|
33
|
-
directionY * t + originY,
|
|
34
|
-
directionZ * t + originZ
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
return true;
|
|
38
|
-
|
|
39
|
-
} else {
|
|
40
|
-
|
|
41
|
-
if (p === 0) {
|
|
42
|
-
|
|
43
|
-
out.set(originX, originY, originZ);
|
|
44
|
-
|
|
45
|
-
return true;
|
|
46
|
-
|
|
47
|
-
} else {
|
|
48
|
-
|
|
49
|
-
//no intersection
|
|
50
|
-
return false;
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export class MarkerMatchCounter {
|
|
2
|
-
/**
|
|
3
|
-
* @template T
|
|
4
|
-
*/
|
|
5
|
-
constructor() {
|
|
6
|
-
/**
|
|
7
|
-
* Predicate
|
|
8
|
-
* @type {MarkerNodeMatcher|null}
|
|
9
|
-
*/
|
|
10
|
-
this.matcher = null;
|
|
11
|
-
this.count = 0;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
*
|
|
16
|
-
* @param {QuadTreeDatum<MarkerNode>} node
|
|
17
|
-
*/
|
|
18
|
-
exec(node) {
|
|
19
|
-
const marker = node.data;
|
|
20
|
-
|
|
21
|
-
if (this.matcher.match(marker)) {
|
|
22
|
-
this.count++;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|