@woosh/meep-engine 2.33.69 → 2.33.73

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.
@@ -0,0 +1,115 @@
1
+ //
2
+
3
+ import { v2_magnitude } from "../Vector2.js";
4
+
5
+ function atan2(a, b) {
6
+ return Math.atan2(a, b);
7
+ }
8
+
9
+ /**
10
+ *
11
+ * @param {number[]} mat4
12
+ * @param {number} row_index
13
+ * @param {number} column_index
14
+ * @return {number}
15
+ */
16
+ function coeff(mat4, row_index, column_index) {
17
+ return mat4[row_index + column_index * 4];
18
+ }
19
+
20
+ function sin(a) {
21
+ return Math.sin(a);
22
+ }
23
+
24
+ function cos(a) {
25
+ return Math.cos(a);
26
+ }
27
+
28
+ const EIGEN_PI = Math.PI;
29
+
30
+ /**
31
+ *
32
+ * \returns the Euler-angles of the rotation matrix \c *this using the convention defined by the triplet (\a a0,\a a1,\a a2)
33
+ *
34
+ * Each of the three parameters \a a0,\a a1,\a a2 represents the respective rotation axis as an integer in {0,1,2}.
35
+ * For instance, in:
36
+ * \code Vector3f ea = mat.eulerAngles(2, 0, 2); \endcode
37
+ * "2" represents the z axis and "0" the x axis, etc. The returned angles are such that
38
+ * we have the following equality:
39
+ * \code
40
+ * mat == AngleAxisf(ea[0], Vector3f::UnitZ())
41
+ * * AngleAxisf(ea[1], Vector3f::UnitX())
42
+ * * AngleAxisf(ea[2], Vector3f::UnitZ()); \endcode
43
+ * This corresponds to the right-multiply conventions (with right hand side frames).
44
+ *
45
+ * The returned angles are in the ranges [0:pi]x[-pi:pi]x[-pi:pi].
46
+
47
+ * NOTE: ported from Eigen C++ library
48
+ * @see https://gitlab.com/libeigen/eigen/-/blob/master/Eigen/src/Geometry/EulerAngles.h
49
+ * @see https://stackoverflow.com/questions/11514063/extract-yaw-pitch-and-roll-from-a-rotationmatrix
50
+ * @param {number[]} res
51
+ * @param {number[]|Float32Array|mat4} m4
52
+ * @param {number} a0 axis index
53
+ * @param {number} a1
54
+ * @param {number} a2
55
+ */
56
+ export function eulerAnglesFromMatrix(res, m4, a0, a1, a2) {
57
+ const odd = ((a0 + 1) % 3 === a1) ? 0 : 1;
58
+
59
+ const i = a0;
60
+ const j = (a0 + 1 + odd) % 3;
61
+ const k = (a0 + 2 - odd) % 3;
62
+
63
+ if (a0 === a2) {
64
+ res[0] = atan2(coeff(m4, j, i), coeff(m4, k, i));
65
+ if ((odd && res[0] < 0) || ((!odd) && res[0] > 0)) {
66
+ if (res[0] > 0) {
67
+ res[0] -= EIGEN_PI;
68
+ } else {
69
+ res[0] += EIGEN_PI;
70
+ }
71
+ const s2 = v2_magnitude(coeff(m4, j, i), coeff(m4, k, i));
72
+ res[1] = -atan2(s2, coeff(m4, i, i));
73
+ } else {
74
+ const s2 = v2_magnitude(coeff(m4, j, i), coeff(m4, k, i));
75
+ res[1] = atan2(s2, coeff(m4, i, i));
76
+ }
77
+
78
+ // With a=(0,1,0), we have i=0; j=1; k=2, and after computing the first two angles,
79
+ // we can compute their respective rotation, and apply its inverse to M. Since the result must
80
+ // be a rotation around x, we have:
81
+ //
82
+ // c2 s1.s2 c1.s2 1 0 0
83
+ // 0 c1 -s1 * M = 0 c3 s3
84
+ // -s2 s1.c2 c1.c2 0 -s3 c3
85
+ //
86
+ // Thus: m11.c1 - m21.s1 = c3 & m12.c1 - m22.s1 = s3
87
+
88
+ const s1 = sin(res[0]);
89
+ const c1 = cos(res[0]);
90
+ res[2] = atan2(c1 * coeff(m4, j, k) - s1 * coeff(m4, k, k), c1 * coeff(m4, j, j) - s1 * coeff(m4, k, j));
91
+ } else {
92
+ res[0] = atan2(coeff(m4, j, k), coeff(m4, k, k));
93
+ const c2 = v2_magnitude(coeff(m4, i, i), coeff(m4, i, j));
94
+ if ((odd && res[0] < 0) || ((!odd) && res[0] > 0)) {
95
+ if (res[0] > 0) {
96
+ res[0] -= EIGEN_PI;
97
+ } else {
98
+ res[0] += EIGEN_PI;
99
+ }
100
+ res[1] = atan2(-coeff(m4, i, k), -c2);
101
+ } else
102
+ res[1] = atan2(-coeff(m4, i, k), c2);
103
+ const s1 = sin(res[0]);
104
+ const c1 = cos(res[0]);
105
+ res[2] = atan2(s1 * coeff(m4, k, i) - c1 * coeff(m4, j, i), c1 * coeff(m4, j, j) - s1 * coeff(m4, k, j));
106
+ }
107
+
108
+ if (!odd) {
109
+ // invert result
110
+ res[0] = -res[0];
111
+ res[1] = -res[1];
112
+ res[2] = -res[2];
113
+ }
114
+
115
+ }
@@ -482,7 +482,7 @@ export class TopoMesh {
482
482
  assert.equal(a.isTopoVertex, true, 'a.isTopoVertex !== true');
483
483
  assert.equal(b.isTopoVertex, true, 'b.isTopoVertex !== true');
484
484
 
485
- assert.notEqual(a, b, 'a === b, attempting to create an edge from vertex to itself')
485
+ assert.notEqual(a, b, 'a === b, attempting to create an edge from vertex to itself');
486
486
 
487
487
  const aEdges = a.edges;
488
488
  const n = aEdges.length;
@@ -11,7 +11,7 @@ import { sign } from "../math/sign.js";
11
11
  import { v3_dot } from "./v3_dot.js";
12
12
  import { v3_length_sqr } from "./v3_length_sqr.js";
13
13
  import { v3_length } from "./v3_length.js";
14
- import { v3_angle_between } from "./v3_angle_between.js";
14
+ import { v3_angle_between, v3_angle_between_atan2 } from "./v3_angle_between.js";
15
15
  import { v3_lerp } from "./v3_lerp.js";
16
16
  import { v3_slerp } from "./v3_slerp.js";
17
17
  import { computeHashFloat } from "../math/hash/computeHashFloat.js";
@@ -448,7 +448,7 @@ class Vector3 {
448
448
  * @returns {number}
449
449
  */
450
450
  angleTo(other) {
451
- return v3_angle_between(this.x, this.y, this.z, other.x, other.y, other.z);
451
+ return v3_angle_between_atan2(this.x, this.y, this.z, other.x, other.y, other.z);
452
452
  }
453
453
 
454
454
  /**
@@ -0,0 +1,18 @@
1
+ import { PI2 } from "../math/MathUtils.js";
2
+
3
+ /**
4
+ * Force angle to [-PI, +PI] range
5
+ * based on answer https://stackoverflow.com/a/2323034/3973586
6
+ * @param {number} angle in radians
7
+ * @return {number} in radians
8
+ */
9
+ export function normalize_angle_rad(angle) {
10
+ let r = angle % PI2;
11
+ r = (r + PI2) % PI2;
12
+
13
+ if (r > Math.PI) {
14
+ r -= PI2;
15
+ }
16
+
17
+ return r;
18
+ }
@@ -0,0 +1,45 @@
1
+ import { normalize_angle_rad } from "./normalize_angle_rad.js";
2
+
3
+ test('0', () => {
4
+ expect(normalize_angle_rad(0)).toBe(0);
5
+ });
6
+
7
+ test('PI', () => {
8
+ expect(normalize_angle_rad(Math.PI)).toBe(Math.PI);
9
+ });
10
+
11
+ test('-PI', () => {
12
+ expect(normalize_angle_rad(-Math.PI)).toBeCloseTo(Math.PI);
13
+ });
14
+
15
+ test('2*PI', () => {
16
+ expect(normalize_angle_rad(Math.PI * 2)).toBeCloseTo(0);
17
+ });
18
+
19
+ test('-2*PI', () => {
20
+ expect(normalize_angle_rad(-Math.PI * 2)).toBeCloseTo(0);
21
+ });
22
+
23
+ test('-1', () => {
24
+ expect(normalize_angle_rad(-1)).toBeCloseTo(-1);
25
+ });
26
+
27
+ test('-3', () => {
28
+ expect(normalize_angle_rad(-3)).toBeCloseTo(-3);
29
+ });
30
+
31
+ test('1', () => {
32
+ expect(normalize_angle_rad(1)).toBeCloseTo(1);
33
+ });
34
+
35
+ test('3', () => {
36
+ expect(normalize_angle_rad(3)).toBeCloseTo(3);
37
+ });
38
+
39
+ test('2*PI + 1', () => {
40
+ expect(normalize_angle_rad(Math.PI * 2 + 1)).toBeCloseTo(1);
41
+ });
42
+
43
+ test('- ( 2*PI + 1)', () => {
44
+ expect(normalize_angle_rad(-(Math.PI * 2 + 1))).toBeCloseTo(-1);
45
+ });
@@ -29,3 +29,25 @@ export function v3_angle_between(x0, y0, z0, x1, y1, z1) {
29
29
 
30
30
  return Math.acos(theta);
31
31
  }
32
+
33
+ /**
34
+ *
35
+ * @param {number} x0
36
+ * @param {number} y0
37
+ * @param {number} z0
38
+ * @param {number} x1
39
+ * @param {number} y1
40
+ * @param {number} z1
41
+ * @returns {number}
42
+ */
43
+ export function v3_angle_between_atan2(x0, y0, z0, x1, y1, z1) {
44
+ const cx = y0 * z1 - z0 * y1;
45
+ const cy = z0 * x1 - x0 * z1;
46
+ const cz = x0 * y1 - y0 * x1;
47
+
48
+ const cross_length = v3_length(cx, cy, cz);
49
+
50
+ const d = v3_dot(x0, y0, z0, x1, y1, z1);
51
+
52
+ return Math.atan2(cross_length, d);
53
+ }
@@ -10,6 +10,10 @@ import { obtainTerrain } from "../terrain/util/obtainTerrain.js";
10
10
  const v2 = new Vector2();
11
11
 
12
12
  export class FogOfWarRevealerSystem extends System {
13
+ /**
14
+ *
15
+ * @param {number} team
16
+ */
13
17
  constructor(team) {
14
18
  super();
15
19
 
@@ -19,6 +23,10 @@ export class FogOfWarRevealerSystem extends System {
19
23
 
20
24
  this.data = [];
21
25
 
26
+ /**
27
+ *
28
+ * @type {number}
29
+ */
22
30
  this.team = team;
23
31
  }
24
32
 
@@ -7,29 +7,15 @@ import Vector3 from '../../../../../core/geom/Vector3.js';
7
7
  import { assert } from "../../../../../core/assert.js";
8
8
  import { lerp } from "../../../../../core/math/lerp.js";
9
9
  import { Euler, Matrix4 } from "three";
10
- import { PI2 } from "../../../../../core/math/MathUtils.js";
10
+ import { normalize_angle_rad } from "../../../../../core/geom/normalize_angle_rad.js";
11
+ import { eulerAnglesFromMatrix } from "../../../../../core/geom/3d/eulerAnglesFromMatrix.js";
11
12
 
12
13
  const m4 = new Matrix4();
13
14
  const euler = new Euler();
15
+ const angles = [];
14
16
 
15
17
  const panOffset = new Vector3();
16
18
 
17
- /**
18
- * Force angle to [-PI, +PI] range
19
- * based on answer https://stackoverflow.com/a/2323034/3973586
20
- * @param {number} angle in radians
21
- * @return {number} in radians
22
- */
23
- function normalize_angle(angle) {
24
- let r = angle % PI2;
25
- r = (r + PI2) % PI2;
26
-
27
- if (r > Math.PI) {
28
- r -= PI2;
29
- }
30
-
31
- return r;
32
- }
33
19
 
34
20
  /**
35
21
  *
@@ -40,8 +26,8 @@ function normalize_angle(angle) {
40
26
  */
41
27
  export function rotate_from_view(displacement_x, displacement_y, input, output) {
42
28
  // crop yaw and pitch to safe range of -PI to PI
43
- const i_pitch = normalize_angle(input.pitch);
44
- const i_yaw = normalize_angle(input.yaw);
29
+ const i_pitch = normalize_angle_rad(input.pitch);
30
+ const i_yaw = normalize_angle_rad(input.yaw);
45
31
 
46
32
  // flip rotation axis when pitch is over 90deg in either direction, that is UP vector is pointing down
47
33
  const d_yaw = ((i_pitch > Math.PI || i_pitch < -Math.PI) ? -1 : 1) * displacement_x;
@@ -138,17 +124,18 @@ class TopDownCameraController {
138
124
  * @param {Vector3} [up]
139
125
  */
140
126
  setFromEyeAndTarget(eye, target, up = Vector3.up) {
127
+
141
128
  /*
142
129
  NOTE: I'm not sure why, but three.js camera points in the opposite direction to normal objects
143
130
  See: https://github.com/mrdoob/three.js/blob/412b99a7f26e117ea97f40eb53d010ab81aa3279/src/core/Object3D.js#L282
144
131
  */
145
132
  m4.lookAt(target, eye, up);
146
133
 
147
- euler.setFromRotationMatrix(m4, 'ZYX');
134
+ eulerAnglesFromMatrix(angles, m4.elements, 2, 1, 0)
148
135
 
149
- this.pitch = normalize_angle(euler.x);
150
- this.yaw = normalize_angle(euler.y);
151
- this.roll = normalize_angle(euler.z);
136
+ this.pitch = angles[2];
137
+ this.yaw = angles[1];
138
+ this.roll = angles[0];
152
139
 
153
140
  this.distance = eye.distanceTo(target);
154
141
 
@@ -11,6 +11,15 @@ test('setFromEyeAndTarget does not introduce roll when UP is aligned with +Y', (
11
11
  expect(ut.roll).toBe(0);
12
12
  });
13
13
 
14
+ test('setFromEyeAndTarget, with different directional axial combinations', () => {
15
+ const ut = new TopDownCameraController();
16
+
17
+ ut.setFromEyeAndTarget(new Vector3(0, 1, -1), new Vector3(0, 0, 0), new Vector3(0, 1, 0));
18
+ ut.setFromEyeAndTarget(new Vector3(0, 1, -1), new Vector3(0, 0, 0), new Vector3(0, 1, 0));
19
+
20
+ expect(ut.roll).toBe(0);
21
+ });
22
+
14
23
  test('setFromEyeAndTarget results in roll respecting UP vector', () => {
15
24
  const eye = new Vector3();
16
25
  const target = new Vector3();
@@ -28,9 +28,10 @@ export function deinterleaveBufferAttribute(source_attribute) {
28
28
  attribute.normalized = source_attribute.normalized;
29
29
  attribute.name = source_attribute.name;
30
30
 
31
+ let i = 0, j = 0;
31
32
  // copy data
32
- for (let i = 0; i < item_count; i++) {
33
- for (let j = 0; j < itemSize; j++) {
33
+ for (; i < item_count; i++) {
34
+ for (j = 0; j < itemSize; j++) {
34
35
  destination_array[i * itemSize + j] = source_data_array[i * stride + offset + j];
35
36
  }
36
37
  }
@@ -21,7 +21,6 @@ import { matcher_tag_traversable } from "./rules/matcher_tag_traversable.js";
21
21
  import { GridTaskGenerateRoads } from "../grid/generation/road/GridTaskGenerateRoads.js";
22
22
  import { mir_generator_place_road_decorators } from "./generators/mir_generator_place_road_decorators.js";
23
23
  import { MarkerNodeMatcherByType } from "../markers/matcher/MarkerNodeMatcherByType.js";
24
- import { mir_generator_place_buff_objects } from "./generators/interactive/mir_generator_place_buff_objects.js";
25
24
  import { GridTaskDensityMarkerDistribution } from "../grid/generation/GridTaskDensityMarkerDistribution.js";
26
25
  import { CellFilterSimplexNoise } from "../filtering/numeric/complex/CellFilterSimplexNoise.js";
27
26
  import { NumericInterval } from "../../core/math/interval/NumericInterval.js";
@@ -211,12 +210,11 @@ const prEnemyCorridorGuard = GridCellPlacementRule.from(
211
210
  ), actions: [
212
211
  ACTION_PLACE_ENEMY_MARKER,
213
212
  ACTION_PLACE_ENEMY_TAG
214
- ]
213
+ ],
214
+ probability: CellFilterLiteralFloat.from(0.5)
215
215
  }
216
216
  );
217
217
 
218
- prEnemyCorridorGuard.probability = 0.5;
219
-
220
218
  const gRuleSet2 = GridTaskActionRuleSet.from({ rules: GridActionRuleSet.from({ rules: [prEnemyTreasureGuard, prEnemyCorridorGuard] }) });
221
219
 
222
220
  gRuleSet2.addDependency(gRuleSetTreasureGuards);
@@ -226,7 +224,11 @@ const gPlaceStartingPoint = mir_generator_place_starting_point();
226
224
 
227
225
  gPlaceStartingPoint.addDependency(gConnectRooms);
228
226
 
229
- const gBuildDistanceMap = GridTaskBuildSourceDistanceMap.from({ source: MATCH_STARTING_POINT, pass: MATCH_EMPTY });
227
+ const gBuildDistanceMap = GridTaskBuildSourceDistanceMap.from({
228
+ source: MATCH_STARTING_POINT,
229
+ pass: MATCH_EMPTY,
230
+ layer: MirGridLayers.DistanceFromStart
231
+ });
230
232
 
231
233
  gBuildDistanceMap.addDependency(gPlaceStartingPoint);
232
234
 
@@ -235,11 +237,8 @@ gRuleSet1.addDependency(gBuildDistanceMap);
235
237
  const gBases = mir_generator_place_bases();
236
238
  gBases.addDependency(gMakeEmpty);
237
239
 
238
- const gBuffObjects = mir_generator_place_buff_objects();
239
- gBuffObjects.addDependency(gBases);
240
240
 
241
241
  gConnectRooms.addDependency(gBases);
242
- gConnectRooms.addDependency(gBuffObjects);
243
242
 
244
243
  gPlaceStartingPoint.addDependency(gBases);
245
244
 
@@ -250,7 +249,6 @@ const gRoads = new GridTaskGenerateRoads();
250
249
 
251
250
  gRoads.addDependency(gConnectRooms);
252
251
  gRoads.addDependency(gBases);
253
- gRoads.addDependency(gBuffObjects);
254
252
 
255
253
  const gRoadDecorators = mir_generator_place_road_decorators();
256
254
 
@@ -540,7 +538,6 @@ gFoliageLarge.addDependencies([
540
538
  gRuleSet1,
541
539
  gRuleSet2,
542
540
  gRuleSetTreasureGuards,
543
- gBuffObjects,
544
541
  gBases
545
542
  ]);
546
543
 
@@ -555,7 +552,6 @@ SampleGenerator0.addGenerator(gConnectRooms);
555
552
  SampleGenerator0.addGenerator(gRoads);
556
553
  SampleGenerator0.addGenerator(gRoadDecorators);
557
554
  SampleGenerator0.addGenerator(gBases);
558
- SampleGenerator0.addGenerator(gBuffObjects);
559
555
  SampleGenerator0.addGenerator(gPlaceStartingPoint);
560
556
  SampleGenerator0.addGenerator(gBuildDistanceMap);
561
557
  SampleGenerator0.addGenerator(gRuleSet1);
@@ -0,0 +1,215 @@
1
+ import { EngineHarness } from "../../engine/EngineHarness.js";
2
+ import RenderSystem from "../../engine/ecs/systems/RenderSystem.js";
3
+ import { Camera } from "../../engine/graphics/ecs/camera/Camera.js";
4
+ import TopDownCameraController from "../../engine/graphics/ecs/camera/topdown/TopDownCameraController.js";
5
+ import Vector3 from "../../core/geom/Vector3.js";
6
+ import { TopDownCameraLander } from "../../engine/graphics/ecs/camera/topdown/TopDownCameraLander.js";
7
+ import Terrain from "../../engine/ecs/terrain/ecs/Terrain.js";
8
+ import { TerrainFlags } from "../../engine/ecs/terrain/ecs/TerrainFlags.js";
9
+ import Water from "../../engine/graphics/ecs/water/Water.js";
10
+ import GridObstacle from "../../engine/grid/components/GridObstacle.js";
11
+ import EntityBuilder from "../../engine/ecs/EntityBuilder.js";
12
+ import GridPosition from "../../engine/grid/components/GridPosition.js";
13
+ import { Transform } from "../../engine/ecs/transform/Transform.js";
14
+ import { GridData } from "../grid/GridData.js";
15
+ import { computeStringHash } from "../../core/primitives/strings/computeStringHash.js";
16
+ import { ThemeEngine } from "../theme/ThemeEngine.js";
17
+ import { AreaTheme } from "../theme/AreaTheme.js";
18
+ import TaskGroup from "../../core/process/task/TaskGroup.js";
19
+ import { actionTask } from "../../core/process/task/TaskUtils.js";
20
+ import { generateTerrainPreview } from "../../../../generator/generator/generateTerrainPreview.js";
21
+ import { SampleTheme0 } from "./themes/SampleTheme0.js";
22
+ import { SampleGenerator0 } from "./SampleGenerator0.js";
23
+ import { configureMirGrid } from "./grid/configureMirGrid.js";
24
+ import { EngineConfiguration } from "../../engine/EngineConfiguration.js";
25
+ import TerrainSystem from "../../engine/ecs/terrain/ecs/TerrainSystem.js";
26
+ import WaterSystem from "../../engine/graphics/ecs/water/WaterSystem.js";
27
+ import { MeshSystem } from "../../engine/graphics/ecs/mesh/MeshSystem.js";
28
+ import TopDownCameraControllerSystem from "../../engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js";
29
+ import { TopDownCameraLanderSystem } from "../../engine/graphics/ecs/camera/topdown/TopDownCameraLanderSystem.js";
30
+ import LightSystem from "../../engine/graphics/ecs/light/LightSystem.js";
31
+
32
+ function makeEngineConfig(engine) {
33
+ const config = new EngineConfiguration();
34
+
35
+ const graphics = engine.graphics;
36
+
37
+ config.addSystem(new RenderSystem(graphics));
38
+ config.addSystem(new TerrainSystem(graphics, engine.assetManager));
39
+ config.addSystem(new WaterSystem(graphics));
40
+ config.addSystem(new MeshSystem(engine));
41
+ config.addSystem(new TopDownCameraControllerSystem());
42
+ config.addSystem(new TopDownCameraLanderSystem());
43
+ config.addSystem(new LightSystem(engine))
44
+
45
+ return config;
46
+ }
47
+
48
+
49
+ /**
50
+ *
51
+ * @param {EngineHarness} harness
52
+ * @return {Promise<void>}
53
+ */
54
+ async function init(harness) {
55
+ const engine = harness.engine;
56
+
57
+ await makeEngineConfig(engine).apply(engine);
58
+
59
+ await harness.initialize();
60
+
61
+ main(engine);
62
+ }
63
+
64
+ function prepare_gen_task({
65
+ SIZE_X = 64,
66
+ SIZE_Y = 64,
67
+ ecd
68
+ }) {
69
+
70
+ const camera = EngineHarness.buildCamera({
71
+ engine,
72
+ target: new Vector3(15, 0, 16),
73
+ // pitch: -PI_HALF,
74
+ pitch: 1.1,
75
+ yaw: 3.14,
76
+ distance: 13,
77
+ ecd,
78
+ distanceMin: 5,
79
+ distanceMax: 30
80
+ });
81
+
82
+ camera.add(new TopDownCameraLander());
83
+
84
+ // build lights
85
+ EngineHarness.buildLights({ engine })
86
+
87
+
88
+ // build terrain
89
+ const terrain = new Terrain();
90
+
91
+ terrain.resolution = 4;
92
+
93
+ terrain.size.set(SIZE_X, SIZE_Y);
94
+ terrain.samplerHeight.resize(SIZE_X, SIZE_Y);
95
+ terrain.splat.resize(SIZE_X * 4, SIZE_Y * 4, 0);
96
+
97
+ terrain.layers.resolution.set(512, 512);
98
+
99
+ terrain.clearFlag(TerrainFlags.Built);
100
+ terrain.build(engine.assetManager);
101
+
102
+ const water = new Water();
103
+
104
+ water.color.setRGB(0.2, 0.0, 0.5);
105
+ water.shoreColor.setRGB(0.3, 0.3, 1);
106
+ water.shoreDepthTransition.set(0.16, 0.5);
107
+ water.waveFrequency.set(1.3);
108
+ water.waveAmplitude.set(0.15);
109
+ water.waveSpeed.set(1.2);
110
+ water.scattering.set(1.2);
111
+ water.level.set(-0.3);
112
+
113
+ const obstacle = new GridObstacle();
114
+ obstacle.resize(terrain.size.x, terrain.size.y);
115
+
116
+ const terrainBuilder = new EntityBuilder();
117
+
118
+ terrainBuilder
119
+ .add(terrain)
120
+ .add(water)
121
+ .add(obstacle)
122
+ .add(new GridPosition())
123
+ .add(new Transform())
124
+ .build(ecd);
125
+
126
+
127
+ const gridData = new GridData();
128
+
129
+ configureMirGrid(gridData);
130
+
131
+ gridData.resize(terrain.size.x, terrain.size.y);
132
+ gridData.computeScale(1);
133
+
134
+ const generator = SampleGenerator0;
135
+
136
+ const tGenerationTask = generator.generate(ecd, gridData, computeStringHash('alexx855'));
137
+
138
+
139
+ const themeEngine = new ThemeEngine();
140
+ themeEngine.assetManager = engine.assetManager;
141
+
142
+ const theme0 = new AreaTheme();
143
+
144
+ const width = gridData.width;
145
+ const height = gridData.height;
146
+
147
+ theme0.mask.resize(width, height);
148
+ theme0.mask.mask.fill(0, 0, width, height, [1]);
149
+ theme0.theme = SampleTheme0;
150
+
151
+ themeEngine.add(theme0);
152
+
153
+
154
+ themeEngine.random.setCurrentSeed(1);
155
+ const tTheme = themeEngine.apply(gridData, ecd);
156
+
157
+ tTheme.addDependency(tGenerationTask);
158
+
159
+
160
+ const tBuildLevel = new TaskGroup([tGenerationTask, tTheme]);
161
+
162
+
163
+ const tAddGameplayElements = actionTask(() => {
164
+ // prepare gameplay-specific things on top of the generated level
165
+
166
+ //build minimap
167
+ const terrainPreview = generateTerrainPreview(terrainBuilder.entity, ecd);
168
+ terrain.preview.copy(terrainPreview);
169
+ });
170
+
171
+ tAddGameplayElements.addDependency(tBuildLevel);
172
+
173
+ return new TaskGroup([tBuildLevel, tAddGameplayElements]);
174
+ }
175
+
176
+ /**
177
+ *
178
+ * @param {Engine} engine
179
+ */
180
+ function main(engine) {
181
+ const ecd = engine.entityManager.dataset;
182
+
183
+ const tBuildLevel = prepare_gen_task({ ecd });
184
+
185
+ engine.loadSlowTask(tBuildLevel);
186
+
187
+ engine.executor.runGroup(tBuildLevel);
188
+
189
+ tBuildLevel.promise().then(() => {
190
+ console.log('Generation took ', tBuildLevel.getExecutedCpuTime(), 'ms');
191
+
192
+ const camera = ecd.getAnyComponent(Camera);
193
+
194
+ EngineHarness.buildOrbitalCameraController({
195
+ cameraEntity: camera.entity,
196
+ engine
197
+ });
198
+
199
+ // tweak camera for prototyping
200
+ const ctrl = ecd.getComponent(camera.entity, TopDownCameraController);
201
+ if (ctrl !== undefined) {
202
+ ctrl.distanceMax = 200;
203
+ }
204
+
205
+ });
206
+
207
+ // Resume audio context
208
+ engine.devices.pointer.on.down.addOne(engine.sound.resumeContext, engine.sound);
209
+ engine.devices.pointer.on.wheel.addOne(engine.sound.resumeContext, engine.sound);
210
+
211
+ }
212
+
213
+ const engineHarness = new EngineHarness();
214
+
215
+ init(engineHarness);
@@ -46,6 +46,8 @@ import { SoundEmitter } from "../../../engine/sound/ecs/emitter/SoundEmitter.js"
46
46
  import { SampleGroundMoistureFilter } from "../filters/SampleGroundMoistureFilter.js";
47
47
  import GeneratedArmy from "../../../../model/game/ecs/component/generator/army/GeneratedArmy.js";
48
48
  import { MirMarkerTypes } from "../../../../generator/MirMarkerTypes.js";
49
+ import { TerrainLayerDescription } from "../../theme/TerrainLayerDescription.js";
50
+ import Vector2 from "../../../core/geom/Vector2.js";
49
51
 
50
52
  export const SampleTheme0 = new Theme();
51
53
 
@@ -123,10 +125,6 @@ const filterSand = CellFilterMultiply.from(
123
125
  filterNotRockAndNotRoad
124
126
  );
125
127
 
126
- const TERRAIN_LAYER_GRASS = 0;
127
- const TERRAIN_LAYER_ROCK = 1;
128
- const TERRAIN_LAYER_SAND = 3;
129
-
130
128
  terrainTheme.rules.push(TerrainLayerRule.from(
131
129
  CellFilterMultiply.from(
132
130
  CellFilterMultiply.from(
@@ -135,22 +133,34 @@ terrainTheme.rules.push(TerrainLayerRule.from(
135
133
  ),
136
134
  filterNotRockAndNotRoad
137
135
  ),
138
- TERRAIN_LAYER_GRASS
136
+ TerrainLayerDescription.from({
137
+ diffuse:'data/textures/materials/Sci-Fi Planet/Grass_02.png',
138
+ size:new Vector2(7,7)
139
+ })
139
140
  ));
140
141
 
141
142
  terrainTheme.rules.push(TerrainLayerRule.from(
142
143
  filterRock,
143
- TERRAIN_LAYER_ROCK
144
+ TerrainLayerDescription.from({
145
+ diffuse:'data/textures/Materials_Stylized/4k JPEG/Stylized_04_Rocks/Stylized_04_Rocks_basecolor.jpg',
146
+ size:new Vector2(7,7)
147
+ })
144
148
  ));
145
149
 
146
150
  terrainTheme.rules.push(TerrainLayerRule.from(
147
151
  filterSand,
148
- TERRAIN_LAYER_SAND
152
+ TerrainLayerDescription.from({
153
+ diffuse:'data/textures/Materials_Stylized/4k JPEG/Stylized_06_Sand/Stylized_06_Sand_basecolor.jpg',
154
+ size:new Vector2(7,7)
155
+ })
149
156
  ));
150
157
 
151
158
  terrainTheme.rules.push(TerrainLayerRule.from(
152
159
  filterRoad,
153
- 2,
160
+ TerrainLayerDescription.from({
161
+ diffuse:'data/textures/Materials_Stylized/4k JPEG/Stylized_02_Stone_Ground/Stylized_02_Stone_Ground_basecolor.jpg',
162
+ size:new Vector2(7,7)
163
+ })
154
164
  ));
155
165
 
156
166
  terrainTheme.rules.push(TerrainLayerRule.from(
@@ -158,7 +168,10 @@ terrainTheme.rules.push(TerrainLayerRule.from(
158
168
  ROAD_FILTER,
159
169
  CellFilterLerp.from(CellFilterLiteralFloat.from(1), CellFilterLiteralFloat.from(0.3), NOISE_10_a)
160
170
  ),
161
- TERRAIN_LAYER_GRASS,
171
+ TerrainLayerDescription.from({
172
+ diffuse:'data/textures/materials/Sci-Fi Planet/Grass_02.png',
173
+ size:new Vector2(7,7)
174
+ }),
162
175
  ));
163
176
 
164
177
  SampleTheme0.terrain = terrainTheme;
@@ -44,7 +44,7 @@ export class GridCellPlacementRule {
44
44
  * @param {Vector2} [offset]
45
45
  * @returns {GridCellPlacementRule}
46
46
  */
47
- static from({ matcher, actions, probability, offset = Vector2.zero }) {
47
+ static from({ matcher, actions, probability = CellFilterLiteralFloat.from(1), offset = Vector2.zero }) {
48
48
 
49
49
  assert.defined(matcher);
50
50
  assert.defined(actions);
@@ -56,10 +56,8 @@ export class GridCellPlacementRule {
56
56
  r.pattern = matcher;
57
57
  r.actions = actions;
58
58
 
59
- if (probability !== undefined) {
60
- assert.equal(probability.isCellFilter, true, 'probability.isCellFilter !== true');
61
- r.probability = probability;
62
- }
59
+ assert.equal(probability.isCellFilter, true, 'probability.isCellFilter !== true');
60
+ r.probability = probability;
63
61
 
64
62
  r.positionOffset.copy(offset);
65
63
 
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.69",
8
+ "version": "2.33.73",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",