@woosh/meep-engine 2.40.0 → 2.41.0
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 +6 -1
- package/core/collection/HashMap.d.ts +2 -0
- package/core/collection/HashMap.js +22 -0
- package/core/geom/3d/normal/hemioct/encode_unit3_hemioct.js +5 -0
- package/core/geom/3d/normal/octahedron/decode_octahedron_to_unit.js +31 -0
- package/core/geom/3d/normal/octahedron/encode_unit_to_octahedron.js +33 -0
- package/core/geom/3d/normal/octahedron/encoding.spec.js +29 -0
- package/core/geom/3d/topology/struct/BinaryTopology.js +112 -0
- package/core/geom/Quaternion.js +8 -8
- package/core/geom/Quaternion.spec.js +13 -0
- package/core/geom/Vector1.d.ts +2 -0
- package/core/math/sign_not_zero.js +8 -0
- package/core/parser/simple/SimpleParser.js +3 -86
- package/core/parser/simple/readUnsignedInteger.js +66 -0
- package/core/parser/simple/skipWhitespace.js +21 -0
- package/editor/view/ecs/components/common/NumberController.js +24 -6
- package/engine/EngineHarness.js +6 -1
- package/engine/asset/AssetDescription.spec.js +27 -0
- package/engine/asset/loaders/GLTFAssetLoader.js +5 -3
- package/engine/asset/loaders/image/png/PNG.js +6 -0
- package/engine/asset/loaders/image/png/PNGReader.js +41 -0
- package/engine/ecs/foliage/ImpostorFoliage.js +4 -0
- package/engine/ecs/terrain/tiles/TerrainTile.js +2 -0
- package/engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.js +22 -7
- package/engine/graphics/filter/ImageFilter.js +2 -1
- package/engine/graphics/geometry/MikkT/MikkTSpace.spec.js +7 -1
- package/engine/{asset/loaders/geometry/geometry → graphics/geometry/buffered}/computeBufferAttributeHash.js +1 -1
- package/engine/{asset/loaders/geometry/geometry → graphics/geometry/buffered}/computeGeometryEquality.js +2 -2
- package/engine/{asset/loaders/geometry/geometry → graphics/geometry/buffered}/computeGeometryHash.js +1 -1
- package/engine/graphics/geometry/optimization/merge/merge_geometry_hierarchy.js +2 -2
- package/engine/graphics/impostors/card_cluster/FacePlaneAssignment.js +30 -0
- package/engine/graphics/impostors/card_cluster/README.md +13 -0
- package/engine/graphics/impostors/octahedral/ImpostorBaker.js +332 -0
- package/engine/graphics/impostors/octahedral/ImpostorCaptureType.js +10 -0
- package/engine/graphics/impostors/octahedral/ImpostorDescription.js +63 -0
- package/engine/graphics/impostors/octahedral/README.md +29 -0
- package/engine/graphics/impostors/octahedral/bake/compute_bounding_sphere.js +42 -0
- package/engine/graphics/impostors/octahedral/bake/prepare_bake_material.js +88 -0
- package/engine/graphics/impostors/octahedral/grid/HemiOctahedralUvEncoder.js +20 -0
- package/engine/graphics/impostors/octahedral/grid/OctahedralUvEncoder.js +25 -0
- package/engine/graphics/impostors/octahedral/grid/UvEncoder.js +21 -0
- package/engine/graphics/impostors/octahedral/prototypeBaker.js +138 -0
- package/engine/graphics/impostors/octahedral/shader/BakeShaderStandard.js +157 -0
- package/engine/graphics/impostors/octahedral/shader/ImpostorShaderStandard.js +306 -0
- package/engine/graphics/impostors/octahedral/shader/glsl/common.glsl +206 -0
- package/engine/graphics/micron/convert_three_object_to_micron.js +2 -2
- package/engine/graphics/micron/plugin/MicronRenderPlugin.js +2 -2
- package/engine/graphics/particles/node-based/codegen/CodeGenerator.js +1 -1
- package/engine/graphics/particles/node-based/nodes/noise/CurlNoiseNode.js +1 -1
- package/engine/graphics/render/forward_plus/LightManager.js +25 -0
- package/engine/graphics/render/forward_plus/data/TextureBackedMemoryRegion.js +6 -2
- package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +6 -11
- package/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +268 -2
- package/engine/graphics/render/visibility/hiz/buildCanvasViewFromTexture.js +32 -10
- package/engine/graphics/shaders/ScreenSpaceQuadShader.js +4 -14
- package/engine/graphics/shaders/glsl_gen_swizzled_read.js +39 -0
- package/engine/graphics/texture/atlas/AtlasPatch.js +12 -6
- package/engine/logging/ConsoleLoggerBackend.js +15 -0
- package/engine/logging/Logger.js +24 -1
- package/engine/logging/LoggerBackend.js +1 -0
- package/engine/physics/fluid/FluidField.js +9 -0
- package/engine/physics/fluid/effector/AbstractFluidEffector.js +6 -0
- package/engine/physics/fluid/effector/GlobalFluidEffector.js +12 -0
- package/engine/physics/fluid/effector/WakeFluidEffector.js +8 -0
- package/engine/ui/GUIEngine.js +8 -1
- package/package.json +1 -1
- package/view/tooltip/gml/parser/readReferenceToken.js +1 -1
- package/engine/asset/GameAssetManager.js +0 -137
|
@@ -959,14 +959,19 @@ export class BinaryBuffer {
|
|
|
959
959
|
/**
|
|
960
960
|
*
|
|
961
961
|
* @param {number} length
|
|
962
|
+
* @param {boolean} [null_terminated]
|
|
962
963
|
* @returns {string}
|
|
963
964
|
*/
|
|
964
|
-
readASCIICharacters(length) {
|
|
965
|
+
readASCIICharacters(length, null_terminated = false) {
|
|
965
966
|
let result = "";
|
|
966
967
|
|
|
967
968
|
for (let i = 0; i < length; i++) {
|
|
968
969
|
const code = this.readUint8();
|
|
969
970
|
|
|
971
|
+
if (null_terminated && code === 0) {
|
|
972
|
+
break;
|
|
973
|
+
}
|
|
974
|
+
|
|
970
975
|
result += String.fromCharCode(code);
|
|
971
976
|
}
|
|
972
977
|
|
|
@@ -273,6 +273,28 @@ export class HashMap {
|
|
|
273
273
|
return undefined;
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
+
/**
|
|
277
|
+
*
|
|
278
|
+
* @param {Key} key
|
|
279
|
+
* @param {function(Key):V} compute
|
|
280
|
+
* @param {*} [compute_context]
|
|
281
|
+
* @return {V}
|
|
282
|
+
*/
|
|
283
|
+
getOrCompute(key, compute, compute_context) {
|
|
284
|
+
const existing = this.get(key);
|
|
285
|
+
|
|
286
|
+
if (existing !== undefined) {
|
|
287
|
+
return existing;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const value = compute.call(compute_context, key);
|
|
291
|
+
|
|
292
|
+
this.set(key, value);
|
|
293
|
+
|
|
294
|
+
return value;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
|
|
276
298
|
/**
|
|
277
299
|
*
|
|
278
300
|
* @param {K} key
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Assume normalized input on +Z hemisphere.
|
|
3
3
|
* Output is on [-1, 1].
|
|
4
4
|
*
|
|
5
|
+
* Uses hemi-octahedron for sphere approximation
|
|
6
|
+
*
|
|
5
7
|
* @see A Survey of Efficient Representations for Independent Unit Vectors (Journal of Computer Graphics Techniques Vol. 3, No. 2, 2014)
|
|
6
8
|
* @param {number[]} output
|
|
7
9
|
* @param {number} output_offset
|
|
@@ -27,6 +29,9 @@ export function encode_unit_to_hemioct(output, output_offset, x, y, z) {
|
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* @see A Survey of Efficient Representations for Independent Unit Vectors (Journal of Computer Graphics Techniques Vol. 3, No. 2, 2014)
|
|
32
|
+
*
|
|
33
|
+
* Uses hemi-octahedron for sphere approximation
|
|
34
|
+
*
|
|
30
35
|
* @param {number[]} output
|
|
31
36
|
* @param {number} output_offset
|
|
32
37
|
* @param {number} x range [-1,1]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { sign_not_zero } from "../../../../math/sign_not_zero.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @see A Survey of Efficient Representations for Independent Unit Vectors (Journal of Computer Graphics Techniques Vol. 3, No. 2, 2014) - page 13
|
|
5
|
+
* @see https://gamedev.stackexchange.com/questions/169508/octahedral-impostors-octahedral-mapping
|
|
6
|
+
* @param {number[]} output
|
|
7
|
+
* @param {number} output_offset
|
|
8
|
+
* @param {number} x range [0,1]
|
|
9
|
+
* @param {number} y range [0,1]
|
|
10
|
+
*/
|
|
11
|
+
export function decode_octahedron_to_unit(output, output_offset, x, y) {
|
|
12
|
+
let v_x = x;
|
|
13
|
+
let v_y = y;
|
|
14
|
+
|
|
15
|
+
const abs_x = Math.abs(v_x);
|
|
16
|
+
const abs_y = Math.abs(v_y);
|
|
17
|
+
|
|
18
|
+
let v_z = 1 - abs_x - abs_y;
|
|
19
|
+
|
|
20
|
+
if (v_z < 0) {
|
|
21
|
+
v_x = (1 - abs_y) * sign_not_zero(v_x);
|
|
22
|
+
v_y = (1 - abs_x) * sign_not_zero(v_y);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// normalize
|
|
26
|
+
const m = 1 / Math.hypot(v_x, v_y, v_z);
|
|
27
|
+
|
|
28
|
+
output[output_offset] = v_x * m;
|
|
29
|
+
output[output_offset + 1] = v_y * m;
|
|
30
|
+
output[output_offset + 2] = v_z * m;
|
|
31
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { sign_not_zero } from "../../../../math/sign_not_zero.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Input vector is assumed to be normalized
|
|
5
|
+
*
|
|
6
|
+
* @see A Survey of Efficient Representations for Independent Unit Vectors (Journal of Computer Graphics Techniques Vol. 3, No. 2, 2014) - page 13
|
|
7
|
+
* @see https://gamedev.stackexchange.com/questions/169508/octahedral-impostors-octahedral-mapping
|
|
8
|
+
*
|
|
9
|
+
* @param {number[]} output
|
|
10
|
+
* @param {number} output_offset
|
|
11
|
+
* @param {number} x range [-1,1]
|
|
12
|
+
* @param {number} y range [-1,1]
|
|
13
|
+
* @param {number} z range [-1,1]
|
|
14
|
+
*/
|
|
15
|
+
export function encode_unit_to_octahedron(output, output_offset, x, y, z) {
|
|
16
|
+
// Project the sphere onto the octahedron, and then onto the xy plane
|
|
17
|
+
const l2 = 1 / (Math.abs(x) + Math.abs(y) + Math.abs(z));
|
|
18
|
+
|
|
19
|
+
let p_x = x * l2;
|
|
20
|
+
let p_y = y * l2;
|
|
21
|
+
|
|
22
|
+
// Reflect the folds of the lower hemisphere over the diagonals
|
|
23
|
+
if (z <= 0) {
|
|
24
|
+
const abs_x = Math.abs(p_x);
|
|
25
|
+
const abs_y = Math.abs(p_y);
|
|
26
|
+
|
|
27
|
+
p_x = sign_not_zero(p_x) * (1 - abs_y);
|
|
28
|
+
p_y = sign_not_zero(p_y) * (1 - abs_x);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
output[output_offset] = p_x;
|
|
32
|
+
output[output_offset + 1] = p_y;
|
|
33
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { encode_unit_to_octahedron } from "./encode_unit_to_octahedron.js";
|
|
2
|
+
import { decode_octahedron_to_unit } from "./decode_octahedron_to_unit.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {number} x
|
|
7
|
+
* @param {number} y
|
|
8
|
+
*/
|
|
9
|
+
function try_one(x, y) {
|
|
10
|
+
const output_v2 = [];
|
|
11
|
+
const output_v3 = [];
|
|
12
|
+
|
|
13
|
+
decode_octahedron_to_unit(output_v3, 0, x, y);
|
|
14
|
+
encode_unit_to_octahedron(output_v2, 0, output_v3[0], output_v3[1], output_v3[2]);
|
|
15
|
+
|
|
16
|
+
expect(output_v2[0]).toBeCloseTo(x);
|
|
17
|
+
expect(output_v2[1]).toBeCloseTo(y);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
test("encoding/decoding consistency", () => {
|
|
21
|
+
// corners
|
|
22
|
+
try_one(0, 0);
|
|
23
|
+
try_one(0, 1);
|
|
24
|
+
try_one(1, 0);
|
|
25
|
+
try_one(1, 1);
|
|
26
|
+
|
|
27
|
+
// center
|
|
28
|
+
try_one(0.5, 0.5);
|
|
29
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { max2 } from "../../../../math/max2.js";
|
|
2
|
+
|
|
3
|
+
const INITIAL_CAPACITY = 128;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @readonly
|
|
7
|
+
* @type {number}
|
|
8
|
+
*/
|
|
9
|
+
const CAPACITY_GROW_MULTIPLIER = 1.2;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @readonly
|
|
13
|
+
* @type {number}
|
|
14
|
+
*/
|
|
15
|
+
const CAPACITY_GROW_MIN_STEP = 32;
|
|
16
|
+
|
|
17
|
+
class BinaryPool {
|
|
18
|
+
|
|
19
|
+
constructor(item_size, initial_capacity = INITIAL_CAPACITY) {
|
|
20
|
+
/**
|
|
21
|
+
* Size of a single pool item in bytes
|
|
22
|
+
* @type {number}
|
|
23
|
+
* @private
|
|
24
|
+
*/
|
|
25
|
+
this.__item_size = item_size;
|
|
26
|
+
/**
|
|
27
|
+
* Unused slots
|
|
28
|
+
* @type {number[]}
|
|
29
|
+
* @private
|
|
30
|
+
*/
|
|
31
|
+
this.__free = [];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
*
|
|
35
|
+
* @type {number}
|
|
36
|
+
* @private
|
|
37
|
+
*/
|
|
38
|
+
this.__free_pointer = 0;
|
|
39
|
+
|
|
40
|
+
this.__data_buffer = new ArrayBuffer(initial_capacity * item_size);
|
|
41
|
+
this.__data_uint8 = new Uint8Array(this.__data_buffer);
|
|
42
|
+
|
|
43
|
+
this.__capacity = initial_capacity;
|
|
44
|
+
|
|
45
|
+
this.__used_end = 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
__grow_capacity() {
|
|
49
|
+
const new_capacity = Math.ceil(max2(
|
|
50
|
+
this.__capacity * CAPACITY_GROW_MULTIPLIER,
|
|
51
|
+
this.__capacity + CAPACITY_GROW_MIN_STEP
|
|
52
|
+
));
|
|
53
|
+
|
|
54
|
+
const old_data_uint8 = this.__data_uint8;
|
|
55
|
+
|
|
56
|
+
const new_data_buffer = new ArrayBuffer(new_capacity * this.__item_size);
|
|
57
|
+
|
|
58
|
+
this.__data_buffer = new_data_buffer;
|
|
59
|
+
this.__data_uint8 = new Uint8Array(new_data_buffer);
|
|
60
|
+
|
|
61
|
+
// copy old data
|
|
62
|
+
this.__data_uint8.set(old_data_uint8);
|
|
63
|
+
|
|
64
|
+
this.__capacity = new_capacity;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
*
|
|
70
|
+
* @return {number}
|
|
71
|
+
*/
|
|
72
|
+
allocate() {
|
|
73
|
+
if (this.__free_pointer > 0) {
|
|
74
|
+
// get unused slot
|
|
75
|
+
this.__free_pointer--;
|
|
76
|
+
return this.__free[this.__free_pointer];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// allocate new
|
|
80
|
+
|
|
81
|
+
let result = this.__used_end;
|
|
82
|
+
|
|
83
|
+
if (result >= this.__capacity) {
|
|
84
|
+
this.__grow_capacity();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.__used_end++;
|
|
88
|
+
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
*
|
|
94
|
+
* @param {number} id
|
|
95
|
+
*/
|
|
96
|
+
release(id) {
|
|
97
|
+
this.__free[this.__free_pointer++] = id;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Heavily influenced by blender's internal mesh structure
|
|
103
|
+
* @see https://github.com/blender/blender/blob/master/source/blender/bmesh/bmesh_class.h
|
|
104
|
+
*/
|
|
105
|
+
export class BinaryTopology {
|
|
106
|
+
__vertex_pool = new BinaryPool();
|
|
107
|
+
__edge_pool = new BinaryPool();
|
|
108
|
+
__loop_pool = new BinaryPool();
|
|
109
|
+
__face_pool = new BinaryPool();
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
}
|
package/core/geom/Quaternion.js
CHANGED
|
@@ -1470,7 +1470,7 @@ class Quaternion {
|
|
|
1470
1470
|
|
|
1471
1471
|
/**
|
|
1472
1472
|
* Based on GDC talk from Bungie on destiny, compressing quaternions for animation
|
|
1473
|
-
* @param value
|
|
1473
|
+
* @param {number} value
|
|
1474
1474
|
*/
|
|
1475
1475
|
decodeFromUint32(value) {
|
|
1476
1476
|
//read components
|
|
@@ -1481,9 +1481,9 @@ class Quaternion {
|
|
|
1481
1481
|
const iv2 = (value >> 22) & 0x3FF;
|
|
1482
1482
|
|
|
1483
1483
|
//scale components back to quaternion range
|
|
1484
|
-
const v0 = (iv0 / 511
|
|
1485
|
-
const v1 = (iv1 / 511
|
|
1486
|
-
const v2 = (iv2 / 511
|
|
1484
|
+
const v0 = (iv0 / 511 - 1) * K_CONST;
|
|
1485
|
+
const v1 = (iv1 / 511 - 1) * K_CONST;
|
|
1486
|
+
const v2 = (iv2 / 511 - 1) * K_CONST;
|
|
1487
1487
|
|
|
1488
1488
|
//restore dropped component using quaternion identity: x^2 + y^2 + z^2 + w^2 = 1
|
|
1489
1489
|
const dropped_2 = 1 - v0 * v0 - v1 * v1 - v2 * v2;
|
|
@@ -1501,7 +1501,7 @@ class Quaternion {
|
|
|
1501
1501
|
}
|
|
1502
1502
|
|
|
1503
1503
|
/**
|
|
1504
|
-
*
|
|
1504
|
+
* Based on GDC talk from Bungie on destiny, compressing quaternions for animation
|
|
1505
1505
|
* @returns {number}
|
|
1506
1506
|
*/
|
|
1507
1507
|
encodeToUint32() {
|
|
@@ -1590,9 +1590,9 @@ class Quaternion {
|
|
|
1590
1590
|
const m = 1 / (l * K_CONST);
|
|
1591
1591
|
|
|
1592
1592
|
//re-normalize the remaining components to 10 bit UINT value
|
|
1593
|
-
const oV0 = ((v0 * m + 1) * 511
|
|
1594
|
-
const oV1 = ((v1 * m + 1) * 511
|
|
1595
|
-
const oV2 = ((v2 * m + 1) * 511
|
|
1593
|
+
const oV0 = Math.round((v0 * m + 1) * 511);
|
|
1594
|
+
const oV1 = Math.round((v1 * m + 1) * 511);
|
|
1595
|
+
const oV2 = Math.round((v2 * m + 1) * 511);
|
|
1596
1596
|
|
|
1597
1597
|
assert.ok(oV0 <= 1023 && oV0 >= 0, `expected 0 <= ov0 <= 1023, instead was '${oV0}'`);
|
|
1598
1598
|
assert.ok(oV1 <= 1023 && oV1 >= 0, `expected 0 <= ov1 <= 1023, instead was '${oV1}'`);
|
|
@@ -154,6 +154,19 @@ test('encoding to Uint32 consistency', () => {
|
|
|
154
154
|
check(1, 0, 0, 0);
|
|
155
155
|
});
|
|
156
156
|
|
|
157
|
+
test('encoding to Uint32 and decoding identity correctly', () => {
|
|
158
|
+
const a = new Quaternion();
|
|
159
|
+
a.copy(Quaternion.identity);
|
|
160
|
+
|
|
161
|
+
const b = new Quaternion();
|
|
162
|
+
|
|
163
|
+
const encoded = a.encodeToUint32();
|
|
164
|
+
|
|
165
|
+
b.decodeFromUint32(encoded);
|
|
166
|
+
|
|
167
|
+
expect(b.toJSON()).toEqual(a.toJSON());
|
|
168
|
+
});
|
|
169
|
+
|
|
157
170
|
|
|
158
171
|
test('to/from euler YXZ consistency', () => {
|
|
159
172
|
function check(x, y, z) {
|
package/core/geom/Vector1.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import DataType from "./DataType.js";
|
|
|
2
2
|
import ParserError from "./ParserError.js";
|
|
3
3
|
import Token from "./Token.js";
|
|
4
4
|
import TokenType from "./TokenType.js";
|
|
5
|
+
import { readUnsignedInteger } from "./readUnsignedInteger.js";
|
|
6
|
+
import { skipWhitespace } from "./skipWhitespace.js";
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
const RX_IDENTIFIER_CHAR = /^[a-zA-Z0-9_]/;
|
|
@@ -272,90 +274,6 @@ function readHex(text, cursor, length) {
|
|
|
272
274
|
return new Token(value, cursor, i, null, DataType.Number);
|
|
273
275
|
}
|
|
274
276
|
|
|
275
|
-
/**
|
|
276
|
-
*
|
|
277
|
-
* @param {string} text
|
|
278
|
-
* @param {number} cursor
|
|
279
|
-
* @param {number} length
|
|
280
|
-
* @returns {Token}
|
|
281
|
-
*/
|
|
282
|
-
function readUnsignedInteger(text, cursor, length) {
|
|
283
|
-
let i = cursor;
|
|
284
|
-
|
|
285
|
-
let value = 0;
|
|
286
|
-
|
|
287
|
-
main_loop: for (; i < length;) {
|
|
288
|
-
const char = text.charAt(i);
|
|
289
|
-
let digit;
|
|
290
|
-
switch (char) {
|
|
291
|
-
case '0':
|
|
292
|
-
digit = 0;
|
|
293
|
-
break;
|
|
294
|
-
case '1':
|
|
295
|
-
digit = 1;
|
|
296
|
-
break;
|
|
297
|
-
case '2':
|
|
298
|
-
digit = 2;
|
|
299
|
-
break;
|
|
300
|
-
case '3':
|
|
301
|
-
digit = 3;
|
|
302
|
-
break;
|
|
303
|
-
case '4':
|
|
304
|
-
digit = 4;
|
|
305
|
-
break;
|
|
306
|
-
case '5':
|
|
307
|
-
digit = 5;
|
|
308
|
-
break;
|
|
309
|
-
case '6':
|
|
310
|
-
digit = 6;
|
|
311
|
-
break;
|
|
312
|
-
case '7':
|
|
313
|
-
digit = 7;
|
|
314
|
-
break;
|
|
315
|
-
case '8':
|
|
316
|
-
digit = 8;
|
|
317
|
-
break;
|
|
318
|
-
case '9':
|
|
319
|
-
digit = 9;
|
|
320
|
-
break;
|
|
321
|
-
default:
|
|
322
|
-
if (i === cursor) {
|
|
323
|
-
//first character is not a digit
|
|
324
|
-
throw new ParserError(i, `Expected a digit [0,1,2,3,4,5,6,7,8,9] but got '${char}' instead`, text);
|
|
325
|
-
}
|
|
326
|
-
//not a digit
|
|
327
|
-
break main_loop;
|
|
328
|
-
}
|
|
329
|
-
i++;
|
|
330
|
-
value = value * 10 + digit;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
return new Token(value, cursor, i, null, DataType.Number);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
*
|
|
340
|
-
* @param {string} text
|
|
341
|
-
* @param {number} cursor
|
|
342
|
-
* @param {number} length
|
|
343
|
-
* @returns {number}
|
|
344
|
-
*/
|
|
345
|
-
export function skipWhitespace(text, cursor, length) {
|
|
346
|
-
let i = cursor;
|
|
347
|
-
let char;
|
|
348
|
-
while (i < length) {
|
|
349
|
-
char = text.charAt(i);
|
|
350
|
-
|
|
351
|
-
if (char === ' ' || char === '\n' || char === '\t') {
|
|
352
|
-
i++;
|
|
353
|
-
} else {
|
|
354
|
-
break;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
return i;
|
|
358
|
-
}
|
|
359
277
|
|
|
360
278
|
/**
|
|
361
279
|
*
|
|
@@ -542,6 +460,5 @@ export {
|
|
|
542
460
|
readLiteral,
|
|
543
461
|
readNumber,
|
|
544
462
|
readStringToken,
|
|
545
|
-
readBoolean
|
|
546
|
-
readUnsignedInteger
|
|
463
|
+
readBoolean
|
|
547
464
|
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import ParserError from "./ParserError.js";
|
|
2
|
+
import Token from "./Token.js";
|
|
3
|
+
import DataType from "./DataType.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @param {string} text
|
|
8
|
+
* @param {number} cursor
|
|
9
|
+
* @param {number} length
|
|
10
|
+
* @returns {Token}
|
|
11
|
+
*/
|
|
12
|
+
export function readUnsignedInteger(text, cursor, length) {
|
|
13
|
+
let i = cursor;
|
|
14
|
+
|
|
15
|
+
let value = 0;
|
|
16
|
+
|
|
17
|
+
main_loop: for (; i < length;) {
|
|
18
|
+
const char = text.charAt(i);
|
|
19
|
+
let digit;
|
|
20
|
+
switch (char) {
|
|
21
|
+
case '0':
|
|
22
|
+
digit = 0;
|
|
23
|
+
break;
|
|
24
|
+
case '1':
|
|
25
|
+
digit = 1;
|
|
26
|
+
break;
|
|
27
|
+
case '2':
|
|
28
|
+
digit = 2;
|
|
29
|
+
break;
|
|
30
|
+
case '3':
|
|
31
|
+
digit = 3;
|
|
32
|
+
break;
|
|
33
|
+
case '4':
|
|
34
|
+
digit = 4;
|
|
35
|
+
break;
|
|
36
|
+
case '5':
|
|
37
|
+
digit = 5;
|
|
38
|
+
break;
|
|
39
|
+
case '6':
|
|
40
|
+
digit = 6;
|
|
41
|
+
break;
|
|
42
|
+
case '7':
|
|
43
|
+
digit = 7;
|
|
44
|
+
break;
|
|
45
|
+
case '8':
|
|
46
|
+
digit = 8;
|
|
47
|
+
break;
|
|
48
|
+
case '9':
|
|
49
|
+
digit = 9;
|
|
50
|
+
break;
|
|
51
|
+
default:
|
|
52
|
+
if (i === cursor) {
|
|
53
|
+
//first character is not a digit
|
|
54
|
+
throw new ParserError(i, `Expected a digit [0,1,2,3,4,5,6,7,8,9] but got '${char}' instead`, text);
|
|
55
|
+
}
|
|
56
|
+
//not a digit
|
|
57
|
+
break main_loop;
|
|
58
|
+
}
|
|
59
|
+
i++;
|
|
60
|
+
value = value * 10 + digit;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
return new Token(value, cursor, i, null, DataType.Number);
|
|
65
|
+
}
|
|
66
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param {string} text
|
|
4
|
+
* @param {number} cursor
|
|
5
|
+
* @param {number} length
|
|
6
|
+
* @returns {number}
|
|
7
|
+
*/
|
|
8
|
+
export function skipWhitespace(text, cursor, length) {
|
|
9
|
+
let i = cursor;
|
|
10
|
+
let char;
|
|
11
|
+
while (i < length) {
|
|
12
|
+
char = text.charAt(i);
|
|
13
|
+
|
|
14
|
+
if (char === ' ' || char === '\n' || char === '\t') {
|
|
15
|
+
i++;
|
|
16
|
+
} else {
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return i;
|
|
21
|
+
}
|
|
@@ -24,7 +24,7 @@ export class NumberController extends View {
|
|
|
24
24
|
const el = document.createElement('input');
|
|
25
25
|
this.el = el;
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
this.addClasses(classList);
|
|
28
28
|
|
|
29
29
|
el.setAttribute('type', 'text');
|
|
30
30
|
el.setAttribute('spellcheck', false);
|
|
@@ -34,6 +34,7 @@ export class NumberController extends View {
|
|
|
34
34
|
|
|
35
35
|
let lockForward = false;
|
|
36
36
|
|
|
37
|
+
|
|
37
38
|
/**
|
|
38
39
|
*
|
|
39
40
|
* @param {number} num_value
|
|
@@ -44,7 +45,26 @@ export class NumberController extends View {
|
|
|
44
45
|
let str = String(num_value);
|
|
45
46
|
|
|
46
47
|
if (Math.abs(num_value) > 0 && Math.abs(num_value % 1).toString().length > (figures + 2)) {
|
|
47
|
-
|
|
48
|
+
const long_form = num_value.toFixed(figures);
|
|
49
|
+
|
|
50
|
+
const separator_index = long_form.indexOf('.');
|
|
51
|
+
|
|
52
|
+
if (separator_index !== -1) {
|
|
53
|
+
// has a decimal fraction, remove trailing zeroes
|
|
54
|
+
let i = long_form.length - 1;
|
|
55
|
+
|
|
56
|
+
while (i > 0 && long_form.charAt(i) === "0") {
|
|
57
|
+
i--;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (long_form.charAt(i) === '.') {
|
|
61
|
+
i--;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
str = long_form.slice(0, i + 1);
|
|
65
|
+
} else {
|
|
66
|
+
str = long_form;
|
|
67
|
+
}
|
|
48
68
|
}
|
|
49
69
|
|
|
50
70
|
return str;
|
|
@@ -80,10 +100,8 @@ export class NumberController extends View {
|
|
|
80
100
|
* Input field stops being edited
|
|
81
101
|
*/
|
|
82
102
|
function handle_blur() {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
el.value = format_value_string(_value.getValue());
|
|
86
|
-
}
|
|
103
|
+
// reformat contents of the input field
|
|
104
|
+
el.value = format_value_string(_value.getValue());
|
|
87
105
|
}
|
|
88
106
|
|
|
89
107
|
_value.process(data2view);
|
package/engine/EngineHarness.js
CHANGED
|
@@ -81,7 +81,7 @@ export class EngineHarness {
|
|
|
81
81
|
|
|
82
82
|
window.engine = this.engine;
|
|
83
83
|
|
|
84
|
-
logger.addBackend(
|
|
84
|
+
logger.addBackend(ConsoleLoggerBackend.INSTANCE);
|
|
85
85
|
|
|
86
86
|
this.p = null;
|
|
87
87
|
}
|
|
@@ -122,6 +122,10 @@ export class EngineHarness {
|
|
|
122
122
|
document.body.appendChild(engine.viewStack.el);
|
|
123
123
|
engine.viewStack.link();
|
|
124
124
|
|
|
125
|
+
// set css
|
|
126
|
+
document.body.style.margin = "0";
|
|
127
|
+
document.body.style.overflow = "hidden";
|
|
128
|
+
|
|
125
129
|
function handleInput() {
|
|
126
130
|
window.removeEventListener(MouseEvents.Down, handleInput);
|
|
127
131
|
|
|
@@ -437,6 +441,7 @@ export class EngineHarness {
|
|
|
437
441
|
|
|
438
442
|
const eb = new EntityBuilder();
|
|
439
443
|
|
|
444
|
+
eb.add(new Transform())
|
|
440
445
|
eb.add(terrain);
|
|
441
446
|
|
|
442
447
|
if (enableWater) {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AssetDescription } from "./AssetDescription.js";
|
|
2
|
+
|
|
3
|
+
test('equality', () => {
|
|
4
|
+
const a = new AssetDescription('a', 'b');
|
|
5
|
+
const b = new AssetDescription('a', 'b');
|
|
6
|
+
const c = new AssetDescription('b', 'a');
|
|
7
|
+
const d = new AssetDescription('b', 'b');
|
|
8
|
+
const e = new AssetDescription('a', 'a');
|
|
9
|
+
const f = new AssetDescription('x', 'y');
|
|
10
|
+
|
|
11
|
+
expect(b.equals(a)).toBe(true);
|
|
12
|
+
expect(b.equals(c)).toBe(false);
|
|
13
|
+
expect(b.equals(d)).toBe(false);
|
|
14
|
+
expect(b.equals(e)).toBe(false);
|
|
15
|
+
expect(b.equals(f)).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
test('hash stability', () => {
|
|
20
|
+
const a = new AssetDescription('a', 'b');
|
|
21
|
+
|
|
22
|
+
expect(a.hash()).toEqual(a.hash());
|
|
23
|
+
|
|
24
|
+
const b = new AssetDescription('a', 'b');
|
|
25
|
+
|
|
26
|
+
expect(a.hash()).toEqual(b.hash());
|
|
27
|
+
});
|