@woosh/meep-engine 2.39.43 → 2.40.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/core/binary/BinaryBuffer.js +14 -1
- package/core/bvh2/aabb3/AABB3.js +0 -133
- package/core/bvh2/aabb3/aabb3_intersects_line_segment.js +1 -1
- package/core/geom/3d/matrix/MATRIX_4_IDENTITY.js +9 -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/parser/simple/DataType.js +2 -2
- package/editor/tools/v2/BlenderCameraOrientationGizmo.js +28 -4
- package/editor/view/EditorView.js +25 -22
- package/editor/view/ecs/components/common/NumberController.js +56 -9
- package/engine/EngineHarness.js +2 -4
- package/engine/asset/loaders/image/png/PNG.js +6 -0
- package/engine/asset/loaders/image/png/PNGReader.js +82 -2
- package/engine/asset/loaders/image/png/crc.js +66 -0
- package/engine/ecs/speaker/VoiceSystem.js +1 -1
- package/engine/ecs/terrain/ecs/Terrain.js +58 -36
- package/engine/ecs/terrain/ecs/TerrainSystem.js +22 -4
- package/engine/ecs/terrain/tiles/TerrainTile.js +109 -131
- package/engine/ecs/terrain/tiles/TerrainTileManager.js +100 -121
- package/engine/graphics/ecs/camera/Camera.js +1 -1
- package/engine/graphics/ecs/light/binding/fp/FPLightBinding.js +4 -4
- package/engine/graphics/geometry/bvh/buffered/BVHGeometryRaycaster.js +0 -78
- package/engine/graphics/geometry/optimization/merge/merge_geometry_hierarchy.js +0 -9
- 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/texture/atlas/AtlasPatch.js +12 -6
- package/engine/intelligence/behavior/util/LogMessageBehavior.js +29 -0
- 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/package.json +1 -1
|
@@ -85,6 +85,14 @@ export class BinaryBuffer {
|
|
|
85
85
|
this.__growFactor = 1.1;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Access raw underlying bytes attached to the buffer
|
|
90
|
+
* @return {Uint8Array}
|
|
91
|
+
*/
|
|
92
|
+
get raw_bytes() {
|
|
93
|
+
return this.__data_uint8;
|
|
94
|
+
}
|
|
95
|
+
|
|
88
96
|
/**
|
|
89
97
|
* @param {ArrayBuffer} arrayBuffer
|
|
90
98
|
*/
|
|
@@ -951,14 +959,19 @@ export class BinaryBuffer {
|
|
|
951
959
|
/**
|
|
952
960
|
*
|
|
953
961
|
* @param {number} length
|
|
962
|
+
* @param {boolean} [null_terminated]
|
|
954
963
|
* @returns {string}
|
|
955
964
|
*/
|
|
956
|
-
readASCIICharacters(length) {
|
|
965
|
+
readASCIICharacters(length, null_terminated = false) {
|
|
957
966
|
let result = "";
|
|
958
967
|
|
|
959
968
|
for (let i = 0; i < length; i++) {
|
|
960
969
|
const code = this.readUint8();
|
|
961
970
|
|
|
971
|
+
if (null_terminated && code === 0) {
|
|
972
|
+
break;
|
|
973
|
+
}
|
|
974
|
+
|
|
962
975
|
result += String.fromCharCode(code);
|
|
963
976
|
}
|
|
964
977
|
|
package/core/bvh2/aabb3/AABB3.js
CHANGED
|
@@ -551,139 +551,6 @@ export class AABB3 {
|
|
|
551
551
|
return aabb3_intersects_line_segment(this.x0, this.y0, this.z0, this.x1, this.y1, this.z1, startX, startY, startZ, endX, endY, endZ);
|
|
552
552
|
}
|
|
553
553
|
|
|
554
|
-
/**
|
|
555
|
-
* @source http://stackoverflow.com/questions/3106666/intersection-of-line-segment-with-axis-aligned-box-in-c-sharp
|
|
556
|
-
* @param {number} startX
|
|
557
|
-
* @param {number} startY
|
|
558
|
-
* @param {number} startZ
|
|
559
|
-
* @param {number} endX
|
|
560
|
-
* @param {number} endY
|
|
561
|
-
* @param {number} endZ
|
|
562
|
-
* @returns {boolean}
|
|
563
|
-
*/
|
|
564
|
-
intersectSegment2(startX, startY, startZ, endX, endY, endZ) {
|
|
565
|
-
//var beginToEnd = segmentEnd - segmentBegin;
|
|
566
|
-
const deltaX = endX - startX,
|
|
567
|
-
deltaY = endY - startY,
|
|
568
|
-
deltaZ = endZ - startZ;
|
|
569
|
-
//var minToMax = new Vector3D(boxSize.X, boxSize.Y, boxSize.Z);
|
|
570
|
-
|
|
571
|
-
//var min = boxCenter - minToMax / 2;
|
|
572
|
-
//var max = boxCenter + minToMax / 2;
|
|
573
|
-
//var beginToMin = min - segmentBegin;
|
|
574
|
-
//var beginToMax = max - segmentBegin;
|
|
575
|
-
//var tNear = double.MinValue;
|
|
576
|
-
let tNear = Number.NEGATIVE_INFINITY;
|
|
577
|
-
//var tFar = double.MaxValue;
|
|
578
|
-
let tFar = Number.POSITIVE_INFINITY;
|
|
579
|
-
let t1, t2, tMin, tMax;
|
|
580
|
-
//var intersections = new List<Point3D>();
|
|
581
|
-
//var intersections = [];
|
|
582
|
-
let beginToMin = this.x0 - startX;
|
|
583
|
-
let beginToMax = this.x1 - startX;
|
|
584
|
-
if (deltaX === 0) {//parallel
|
|
585
|
-
if (beginToMin > 0 || beginToMax < 0) {
|
|
586
|
-
return false; //segment is not between planes
|
|
587
|
-
}
|
|
588
|
-
} else {
|
|
589
|
-
t1 = beginToMin / deltaX;
|
|
590
|
-
t2 = beginToMax / deltaX;
|
|
591
|
-
tMin = Math.min(t1, t2);
|
|
592
|
-
tMax = Math.max(t1, t2);
|
|
593
|
-
if (tMin > tNear) {
|
|
594
|
-
tNear = tMin;
|
|
595
|
-
}
|
|
596
|
-
if (tMax < tFar) {
|
|
597
|
-
tFar = tMax;
|
|
598
|
-
}
|
|
599
|
-
if (tNear > tFar || tFar < 0) {
|
|
600
|
-
return false;
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
beginToMin = this.y0 - startY;
|
|
604
|
-
beginToMax = this.y1 - startY;
|
|
605
|
-
if (deltaY === 0) {//parallel
|
|
606
|
-
if (beginToMin > 0 || beginToMax < 0) {
|
|
607
|
-
return false; //segment is not between planes
|
|
608
|
-
}
|
|
609
|
-
} else {
|
|
610
|
-
t1 = beginToMin / deltaY;
|
|
611
|
-
t2 = beginToMax / deltaY;
|
|
612
|
-
tMin = Math.min(t1, t2);
|
|
613
|
-
tMax = Math.max(t1, t2);
|
|
614
|
-
if (tMin > tNear) {
|
|
615
|
-
tNear = tMin;
|
|
616
|
-
}
|
|
617
|
-
if (tMax < tFar) {
|
|
618
|
-
tFar = tMax;
|
|
619
|
-
}
|
|
620
|
-
if (tNear > tFar || tFar < 0) {
|
|
621
|
-
return false;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
beginToMin = this.z0 - startZ;
|
|
625
|
-
beginToMax = this.z1 - startZ;
|
|
626
|
-
if (deltaZ === 0) {//parallel
|
|
627
|
-
if (beginToMin > 0 || beginToMax < 0) {
|
|
628
|
-
return false; //segment is not between planes
|
|
629
|
-
}
|
|
630
|
-
} else {
|
|
631
|
-
t1 = beginToMin / deltaZ;
|
|
632
|
-
t2 = beginToMax / deltaZ;
|
|
633
|
-
tMin = Math.min(t1, t2);
|
|
634
|
-
tMax = Math.max(t1, t2);
|
|
635
|
-
if (tMin > tNear) {
|
|
636
|
-
tNear = tMin;
|
|
637
|
-
}
|
|
638
|
-
if (tMax < tFar) {
|
|
639
|
-
tFar = tMax;
|
|
640
|
-
}
|
|
641
|
-
if (tNear > tFar || tFar < 0) {
|
|
642
|
-
return false;
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
//
|
|
646
|
-
if (tNear >= 0 && tNear <= 1) {
|
|
647
|
-
//intersections.push({
|
|
648
|
-
// x: startX + deltaX * tNear,
|
|
649
|
-
// y: startY + deltaY * tNear,
|
|
650
|
-
// z: startZ + deltaZ * tNear
|
|
651
|
-
//});
|
|
652
|
-
return true;
|
|
653
|
-
}
|
|
654
|
-
if (tFar >= 0 && tFar <= 1) {
|
|
655
|
-
//intersections.push({
|
|
656
|
-
// x: startX + deltaX * tFar,
|
|
657
|
-
// y: startY + deltaY * tFar,
|
|
658
|
-
// z: startZ + deltaZ * tFar
|
|
659
|
-
//});
|
|
660
|
-
return true;
|
|
661
|
-
}
|
|
662
|
-
return false;
|
|
663
|
-
//foreach (Axis axis in Enum.GetValues(typeof(Axis)))
|
|
664
|
-
//{
|
|
665
|
-
// if (beginToEnd.GetCoordinate(axis) == 0) // parallel
|
|
666
|
-
// {
|
|
667
|
-
// if (beginToMin.GetCoordinate(axis) > 0 || beginToMax.GetCoordinate(axis) < 0)
|
|
668
|
-
// return intersections; // segment is not between planes
|
|
669
|
-
// }
|
|
670
|
-
// else
|
|
671
|
-
// {
|
|
672
|
-
// var t1 = beginToMin.GetCoordinate(axis) / beginToEnd.GetCoordinate(axis);
|
|
673
|
-
// var t2 = beginToMax.GetCoordinate(axis) / beginToEnd.GetCoordinate(axis);
|
|
674
|
-
// var tMin = Math.Min(t1, t2);
|
|
675
|
-
// var tMax = Math.Max(t1, t2);
|
|
676
|
-
// if (tMin > tNear) tNear = tMin;
|
|
677
|
-
// if (tMax < tFar) tFar = tMax;
|
|
678
|
-
// if (tNear > tFar || tFar < 0) return intersections;
|
|
679
|
-
//
|
|
680
|
-
// }
|
|
681
|
-
//}
|
|
682
|
-
//if (tNear >= 0 && tNear <= 1) intersections.Add(segmentBegin + beginToEnd * tNear);
|
|
683
|
-
//if (tFar >= 0 && tFar <= 1) intersections.Add(segmentBegin + beginToEnd * tFar);
|
|
684
|
-
//return intersections;
|
|
685
|
-
}
|
|
686
|
-
|
|
687
554
|
/**
|
|
688
555
|
*
|
|
689
556
|
* @param {THREE.Box} box
|
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
|
@@ -234,6 +234,13 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
|
|
|
234
234
|
* @type {Signal<string,Vector3>}
|
|
235
235
|
*/
|
|
236
236
|
this.on.axisSelected = new Signal();
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
*
|
|
240
|
+
* @type {boolean}
|
|
241
|
+
* @private
|
|
242
|
+
*/
|
|
243
|
+
this.__needs_update = true;
|
|
237
244
|
}
|
|
238
245
|
|
|
239
246
|
link() {
|
|
@@ -262,6 +269,7 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
|
|
|
262
269
|
*/
|
|
263
270
|
onMouseMove(evt) {
|
|
264
271
|
this._setPointerPositionFromEvent(evt);
|
|
272
|
+
this.__try_update();
|
|
265
273
|
}
|
|
266
274
|
|
|
267
275
|
/**
|
|
@@ -278,6 +286,11 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
|
|
|
278
286
|
|
|
279
287
|
this.input_pointer_position = new Vector3(v2.x, v2.y, 0);
|
|
280
288
|
|
|
289
|
+
|
|
290
|
+
if (position_changed) {
|
|
291
|
+
this.__needs_update = true;
|
|
292
|
+
}
|
|
293
|
+
|
|
281
294
|
return position_changed;
|
|
282
295
|
}
|
|
283
296
|
|
|
@@ -287,6 +300,8 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
|
|
|
287
300
|
*/
|
|
288
301
|
onMouseOut(evt) {
|
|
289
302
|
this.input_pointer_position = null;
|
|
303
|
+
this.__needs_update = true;
|
|
304
|
+
this.__try_update();
|
|
290
305
|
}
|
|
291
306
|
|
|
292
307
|
/**
|
|
@@ -295,10 +310,9 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
|
|
|
295
310
|
*/
|
|
296
311
|
onMouseClick(evt) {
|
|
297
312
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
313
|
+
this._setPointerPositionFromEvent(evt);
|
|
314
|
+
|
|
315
|
+
this.__try_update();
|
|
302
316
|
|
|
303
317
|
if (this.selectedAxis !== null) {
|
|
304
318
|
this.on.axisSelected.send2(
|
|
@@ -337,7 +351,17 @@ export class BlenderCameraOrientationGizmo extends CanvasView {
|
|
|
337
351
|
ctx.closePath();
|
|
338
352
|
}
|
|
339
353
|
|
|
354
|
+
__try_update() {
|
|
355
|
+
if (!this.__needs_update) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
this.update();
|
|
359
|
+
}
|
|
360
|
+
|
|
340
361
|
update() {
|
|
362
|
+
// reset flag
|
|
363
|
+
this.__needs_update = false;
|
|
364
|
+
|
|
341
365
|
this.clear();
|
|
342
366
|
|
|
343
367
|
// Calculate the rotation matrix from the camera
|
|
@@ -28,10 +28,11 @@ import SelectionClearAction from "../actions/concrete/SelectionClearAction.js";
|
|
|
28
28
|
import EntityCreateAction from "../actions/concrete/EntityCreateAction.js";
|
|
29
29
|
import ComponentAddAction from "../actions/concrete/ComponentAddAction.js";
|
|
30
30
|
import SelectionAddAction from "../actions/concrete/SelectionAddAction.js";
|
|
31
|
-
import {
|
|
31
|
+
import { passThrough } from "../../core/function/Functions.js";
|
|
32
32
|
import { ProcessState } from "../../core/process/ProcessState.js";
|
|
33
33
|
import { MeshEvents } from "../../engine/graphics/ecs/mesh/MeshEvents.js";
|
|
34
34
|
import { obtainTerrain } from "../../engine/ecs/terrain/util/obtainTerrain.js";
|
|
35
|
+
import { SurfacePoint3 } from "../../core/geom/3d/SurfacePoint3.js";
|
|
35
36
|
|
|
36
37
|
class ViewManager extends View {
|
|
37
38
|
constructor() {
|
|
@@ -204,35 +205,37 @@ function prepareMeshLibrary(editor) {
|
|
|
204
205
|
const entityCreateAction = new EntityCreateAction();
|
|
205
206
|
actions.do(entityCreateAction);
|
|
206
207
|
|
|
207
|
-
|
|
208
|
-
function handleMeshSetEvent() {
|
|
209
|
-
const bb = mesh.boundingBox;
|
|
208
|
+
const sp3 = new SurfacePoint3();
|
|
210
209
|
|
|
211
|
-
|
|
212
|
-
const c1 = new Vector3(bb.x1, bb.y1, bb.z1);
|
|
210
|
+
const hit_found = terrain.raycastFirstSync(sp3, source.x, source.y, source.z, direction.x, direction.y, direction.z);
|
|
213
211
|
|
|
214
|
-
|
|
212
|
+
function handleMeshSetEvent() {
|
|
213
|
+
const bb = mesh.boundingBox;
|
|
215
214
|
|
|
216
|
-
|
|
215
|
+
const c0 = new Vector3(bb.x0, bb.y0, bb.z0);
|
|
216
|
+
const c1 = new Vector3(bb.x1, bb.y1, bb.z1);
|
|
217
217
|
|
|
218
|
-
|
|
218
|
+
const diagonal = c0.distanceTo(c1);
|
|
219
219
|
|
|
220
|
-
|
|
221
|
-
ecd.removeEntityEventListener(entityCreateAction.entity, MeshEvents.DataSet, handleMeshSetEvent);
|
|
222
|
-
}
|
|
220
|
+
const offset = direction.clone().multiplyScalar(diagonal);
|
|
223
221
|
|
|
224
|
-
|
|
225
|
-
//got a terrain ray hit, set world placement position to that point
|
|
226
|
-
worldPosition.copy(hit);
|
|
227
|
-
} else {
|
|
228
|
-
//set position to the source of the ray pick if there's nothing else available
|
|
229
|
-
worldPosition.copy(source);
|
|
222
|
+
transform.position.add(offset);
|
|
230
223
|
|
|
224
|
+
//remove listener
|
|
225
|
+
ecd.removeEntityEventListener(entityCreateAction.entity, MeshEvents.DataSet, handleMeshSetEvent);
|
|
226
|
+
}
|
|
231
227
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
228
|
+
if (hit_found) {
|
|
229
|
+
//got a terrain ray hit, set world placement position to that point
|
|
230
|
+
worldPosition.copy(sp3.position);
|
|
231
|
+
} else {
|
|
232
|
+
//set position to the source of the ray pick if there's nothing else available
|
|
233
|
+
worldPosition.copy(source);
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
//wait for mesh to load
|
|
237
|
+
ecd.addEntityEventListener(entityCreateAction.entity, MeshEvents.DataSet, handleMeshSetEvent);
|
|
238
|
+
}
|
|
236
239
|
|
|
237
240
|
transform.position.copy(worldPosition);
|
|
238
241
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import View from "../../../../../view/View.js";
|
|
2
2
|
import Vector1 from "../../../../../core/geom/Vector1.js";
|
|
3
3
|
|
|
4
|
+
const DEFAULT_VALUE = 0;
|
|
5
|
+
|
|
4
6
|
export class NumberController extends View {
|
|
5
7
|
/**
|
|
6
8
|
*
|
|
@@ -22,7 +24,7 @@ export class NumberController extends View {
|
|
|
22
24
|
const el = document.createElement('input');
|
|
23
25
|
this.el = el;
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
this.addClasses(classList);
|
|
26
28
|
|
|
27
29
|
el.setAttribute('type', 'text');
|
|
28
30
|
el.setAttribute('spellcheck', false);
|
|
@@ -32,6 +34,42 @@ export class NumberController extends View {
|
|
|
32
34
|
|
|
33
35
|
let lockForward = false;
|
|
34
36
|
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
*
|
|
40
|
+
* @param {number} num_value
|
|
41
|
+
* @return {string}
|
|
42
|
+
*/
|
|
43
|
+
function format_value_string(num_value) {
|
|
44
|
+
|
|
45
|
+
let str = String(num_value);
|
|
46
|
+
|
|
47
|
+
if (Math.abs(num_value) > 0 && Math.abs(num_value % 1).toString().length > (figures + 2)) {
|
|
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
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return str;
|
|
71
|
+
}
|
|
72
|
+
|
|
35
73
|
function data2view() {
|
|
36
74
|
|
|
37
75
|
if (lockForward) {
|
|
@@ -40,26 +78,35 @@ export class NumberController extends View {
|
|
|
40
78
|
|
|
41
79
|
const num_value = _value.getValue();
|
|
42
80
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (Math.abs(num_value) > 0 && Math.abs(num_value % 1).toString().length > (figures + 2)) {
|
|
46
|
-
str = num_value.toFixed(figures);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
el.value = str;
|
|
81
|
+
el.value = format_value_string(num_value);
|
|
50
82
|
|
|
51
83
|
}
|
|
52
84
|
|
|
53
85
|
function view2data() {
|
|
54
86
|
lockForward = true;
|
|
55
87
|
|
|
56
|
-
|
|
88
|
+
const value = parseFloat(el.value);
|
|
89
|
+
|
|
90
|
+
if (Number.isNaN(value)) {
|
|
91
|
+
_value.set(DEFAULT_VALUE);
|
|
92
|
+
} else {
|
|
93
|
+
_value.set(value);
|
|
94
|
+
}
|
|
57
95
|
|
|
58
96
|
lockForward = false;
|
|
59
97
|
}
|
|
60
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Input field stops being edited
|
|
101
|
+
*/
|
|
102
|
+
function handle_blur() {
|
|
103
|
+
// reformat contents of the input field
|
|
104
|
+
el.value = format_value_string(_value.getValue());
|
|
105
|
+
}
|
|
106
|
+
|
|
61
107
|
_value.process(data2view);
|
|
62
108
|
|
|
63
109
|
el.addEventListener('input', view2data);
|
|
110
|
+
el.addEventListener('blur', handle_blur);
|
|
64
111
|
}
|
|
65
112
|
}
|
package/engine/EngineHarness.js
CHANGED
|
@@ -9,7 +9,6 @@ import Vector3 from "../core/geom/Vector3.js";
|
|
|
9
9
|
import Terrain from "./ecs/terrain/ecs/Terrain.js";
|
|
10
10
|
import Vector2 from "../core/geom/Vector2.js";
|
|
11
11
|
import Water from "./graphics/ecs/water/Water.js";
|
|
12
|
-
import { loadGameClassRegistry } from "../../model/game/GameClassRegistry.js";
|
|
13
12
|
import { WebEnginePlatform } from "./platform/WebEnginePlatform.js";
|
|
14
13
|
import Tag from "./ecs/components/Tag.js";
|
|
15
14
|
import { SerializationMetadata } from "./ecs/components/SerializationMetadata.js";
|
|
@@ -82,7 +81,7 @@ export class EngineHarness {
|
|
|
82
81
|
|
|
83
82
|
window.engine = this.engine;
|
|
84
83
|
|
|
85
|
-
logger.addBackend(
|
|
84
|
+
logger.addBackend(ConsoleLoggerBackend.INSTANCE);
|
|
86
85
|
|
|
87
86
|
this.p = null;
|
|
88
87
|
}
|
|
@@ -116,8 +115,6 @@ export class EngineHarness {
|
|
|
116
115
|
|
|
117
116
|
await setLocale(engine);
|
|
118
117
|
|
|
119
|
-
loadGameClassRegistry(engine.classRegistry);
|
|
120
|
-
|
|
121
118
|
engine.sceneManager.create("test");
|
|
122
119
|
engine.sceneManager.set("test");
|
|
123
120
|
|
|
@@ -440,6 +437,7 @@ export class EngineHarness {
|
|
|
440
437
|
|
|
441
438
|
const eb = new EntityBuilder();
|
|
442
439
|
|
|
440
|
+
eb.add(new Transform())
|
|
443
441
|
eb.add(terrain);
|
|
444
442
|
|
|
445
443
|
if (enableWater) {
|