@woosh/meep-engine 2.39.2 → 2.39.5
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/bvh2/{traversal/__detailed_box_volume_intersection.js → aabb3/aabb3_detailed_volume_intersection.js} +47 -27
- package/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.js +13 -0
- package/core/bvh2/traversal/ThreeClippingPlaneComputingBVHVisitor.js +5 -3
- package/core/bvh2/traversal/__process_point_if_within_planes.js +1 -0
- package/core/bvh2/traversal/aabb3_detailed_volume_intersection_callback_based.js +60 -0
- package/core/geom/3d/plane/is_point_within_planes.js +46 -0
- package/core/geom/3d/topology/bounds/{computeTraingleClusterNormalBoundingCone.js → computeTriangleClusterNormalBoundingCone.js} +8 -1
- package/core/geom/Vector3.d.ts +8 -0
- package/editor/actions/concrete/ActionUpdateTexture.js +21 -0
- package/editor/actions/concrete/ArrayCopyAction.js +39 -0
- package/editor/actions/concrete/ModifyPatchTextureArray2DAction.js +182 -0
- package/editor/enableEditor.js +30 -2
- package/editor/tools/paint/TerrainPaintTool.js +19 -3
- package/editor/tools/paint/TerrainTexturePaintTool.js +19 -57
- package/editor/tools/paint/prototypeTerrainEditor.js +67 -0
- package/editor/view/ecs/ComponentControlView.js +105 -10
- package/editor/view/ecs/EntityEditor.js +1 -1
- package/engine/ecs/fow/FogOfWarSystem.js +6 -3
- package/engine/ecs/terrain/ecs/Terrain.js +57 -47
- package/engine/ecs/terrain/ecs/layers/TerrainLayer.js +16 -2
- package/engine/ecs/terrain/ecs/layers/TerrainLayers.js +17 -0
- package/engine/ecs/terrain/ecs/splat/SplatMapping.js +24 -78
- package/engine/ecs/terrain/ecs/splat/loadLegacyTerrainSplats.js +73 -0
- package/engine/graphics/camera/camera_compute_distance_to_fit_length.d.ts +1 -0
- package/engine/graphics/camera/camera_compute_distance_to_fit_length.js +16 -0
- package/engine/graphics/camera/testClippingPlaneComputation.js +7 -4
- package/engine/graphics/ecs/camera/Camera.js +2 -2
- package/engine/graphics/ecs/camera/CameraClippingPlaneComputer.js +5 -8
- package/engine/graphics/ecs/camera/CameraSystem.js +18 -184
- package/engine/graphics/ecs/camera/auto_set_camera_clipping_planes.js +32 -0
- package/engine/graphics/ecs/camera/build_three_camera_object.js +29 -0
- package/engine/graphics/ecs/camera/compute_perspective_camera_focal_position.js +27 -0
- package/engine/graphics/ecs/camera/frustum_from_camera.js +20 -0
- package/engine/graphics/ecs/camera/is_valid_distance_value.js +11 -0
- package/engine/graphics/ecs/camera/set_camera_aspect_ratio.js +46 -0
- package/engine/graphics/ecs/camera/update_camera_transform.js +17 -0
- package/engine/graphics/ecs/path/testPathDisplaySystem.js +19 -15
- package/engine/graphics/ecs/path/tube/TubePathStyle.js +1 -0
- package/engine/graphics/{Utils.js → makeModelView.js} +1 -10
- package/engine/graphics/micron/build/PatchRepresentation.js +3 -3
- package/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +2 -2
- package/engine/graphics/render/forward_plus/LightManager.js +2 -2
- package/engine/graphics/render/view/CameraView.js +2 -2
- package/engine/graphics/texture/sampler/Sampler2D.js +1293 -1267
- package/engine/graphics/texture/texture_array_2d_copy.js +45 -0
- package/engine/graphics/util/makeMeshPreviewScene.js +2 -2
- package/package.json +1 -1
- package/view/renderModel.js +1 -1
- package/view/string_tag_to_css_class_name.js +12 -0
- package/engine/graphics/util/computeMeshPreviewCameraDistance.js +0 -10
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { aabb3_build_corners } from "../../geom/3d/aabb/aabb3_build_corners.js";
|
|
2
2
|
import { aabb_build_frustum } from "../../geom/3d/aabb/aabb3_build_frustum.js";
|
|
3
3
|
import { computePointDistanceToPlane, plane3_computeLineSegmentIntersection } from "../../geom/Plane.js";
|
|
4
|
-
import { aabb3_corner_edge_mapping } from "
|
|
5
|
-
import { aabb3_edge_corner_mapping } from "
|
|
6
|
-
import { __process_point_if_within_planes } from "./__process_point_if_within_planes.js";
|
|
4
|
+
import { aabb3_corner_edge_mapping } from "./aabb3_corner_edge_mapping.js";
|
|
5
|
+
import { aabb3_edge_corner_mapping } from "./aabb3_edge_corner_mapping.js";
|
|
7
6
|
import { plane_computeConvex3PlaneIntersection } from "../../geom/3d/plane/plane_computeConvex3PlaneIntersection.js";
|
|
8
7
|
import { EPSILON } from "../../math/MathUtils.js";
|
|
8
|
+
import { is_point_within_planes } from "../../geom/3d/plane/is_point_within_planes.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Common piece of continuous memory for better cache coherence
|
|
@@ -50,28 +50,26 @@ const scratch_v3_array = new Float32Array(__scratch_buffer, 224, 3);
|
|
|
50
50
|
* where where traditionally only AABB corners would be considered,
|
|
51
51
|
* we're instead able to compute minimum bounds of the overlap of the two
|
|
52
52
|
*
|
|
53
|
-
* @param {
|
|
53
|
+
* @param {number[]} destination
|
|
54
|
+
* @param {number} destination_offset
|
|
55
|
+
* @param {number} x0
|
|
56
|
+
* @param {number} y0
|
|
57
|
+
* @param {number} z0
|
|
58
|
+
* @param {number} x1
|
|
59
|
+
* @param {number} y1
|
|
60
|
+
* @param {number} z1
|
|
54
61
|
* @param {number[]} planes
|
|
55
|
-
* @param {number} plane_count
|
|
56
62
|
* @param {number} plane_mask
|
|
57
|
-
* @param {
|
|
58
|
-
* @
|
|
59
|
-
* @private
|
|
63
|
+
* @param {number} plane_count
|
|
64
|
+
* @returns {number}
|
|
60
65
|
*/
|
|
61
|
-
export function
|
|
62
|
-
|
|
66
|
+
export function aabb3_detailed_volume_intersection(
|
|
67
|
+
destination, destination_offset,
|
|
68
|
+
x0, y0, z0, x1, y1, z1,
|
|
63
69
|
plane_mask,
|
|
64
70
|
planes,
|
|
65
|
-
plane_count
|
|
66
|
-
callback,
|
|
67
|
-
thisArg
|
|
71
|
+
plane_count
|
|
68
72
|
) {
|
|
69
|
-
const x0 = box.x0;
|
|
70
|
-
const y0 = box.y0;
|
|
71
|
-
const z0 = box.z0;
|
|
72
|
-
const x1 = box.x1;
|
|
73
|
-
const y1 = box.y1;
|
|
74
|
-
const z1 = box.z1;
|
|
75
73
|
|
|
76
74
|
aabb3_build_corners(scratch_aabb_corners, 0, x0, y0, z0, x1, y1, z1);
|
|
77
75
|
aabb_build_frustum(scratch_aabb_planes, x0, y0, z0, x1, y1, z1);
|
|
@@ -94,7 +92,9 @@ export function __detailed_box_volume_intersection(
|
|
|
94
92
|
*/
|
|
95
93
|
let corner_plane_out_bitfield = 0;
|
|
96
94
|
|
|
97
|
-
|
|
95
|
+
let destination_cursor = destination_offset;
|
|
96
|
+
|
|
97
|
+
// find out which corners are outside the frustum
|
|
98
98
|
for (let corner_index = 0; corner_index < 8; corner_index++) {
|
|
99
99
|
const corner_offset = corner_index * 3;
|
|
100
100
|
|
|
@@ -148,7 +148,9 @@ export function __detailed_box_volume_intersection(
|
|
|
148
148
|
|
|
149
149
|
if (corner_plane_out_bitfield === 0) {
|
|
150
150
|
// corner is fully inside the frustum
|
|
151
|
-
|
|
151
|
+
destination[destination_cursor++] = x;
|
|
152
|
+
destination[destination_cursor++] = y;
|
|
153
|
+
destination[destination_cursor++] = z;
|
|
152
154
|
}
|
|
153
155
|
}
|
|
154
156
|
|
|
@@ -211,14 +213,18 @@ export function __detailed_box_volume_intersection(
|
|
|
211
213
|
|
|
212
214
|
if (edge_intersects) {
|
|
213
215
|
// a possible candidate, let's see if this point is within rest of the planes
|
|
214
|
-
|
|
216
|
+
const inside_planes = is_point_within_planes(
|
|
215
217
|
scratch_v3_array[0], scratch_v3_array[1], scratch_v3_array[2],
|
|
216
218
|
(plane_mask ^ plane_bit_mask),
|
|
217
219
|
planes,
|
|
218
|
-
plane_count
|
|
219
|
-
callback,
|
|
220
|
-
thisArg
|
|
220
|
+
plane_count
|
|
221
221
|
);
|
|
222
|
+
|
|
223
|
+
if (inside_planes) {
|
|
224
|
+
destination[destination_cursor++] = scratch_v3_array[0];
|
|
225
|
+
destination[destination_cursor++] = scratch_v3_array[1];
|
|
226
|
+
destination[destination_cursor++] = scratch_v3_array[2];
|
|
227
|
+
}
|
|
222
228
|
}
|
|
223
229
|
|
|
224
230
|
|
|
@@ -258,16 +264,30 @@ export function __detailed_box_volume_intersection(
|
|
|
258
264
|
|
|
259
265
|
if (intersection_exists) {
|
|
260
266
|
|
|
261
|
-
const
|
|
267
|
+
const intersection_point_x = scratch_v3_array[0];
|
|
268
|
+
const intersection_point_y = scratch_v3_array[1];
|
|
269
|
+
const intersection_point_z = scratch_v3_array[2];
|
|
270
|
+
|
|
271
|
+
const point_within_box = !(
|
|
272
|
+
(intersection_point_x + EPSILON) < x0 || (intersection_point_x - EPSILON) > x1
|
|
273
|
+
|| (intersection_point_y + EPSILON) < y0 || (intersection_point_y - EPSILON) > y1
|
|
274
|
+
|| (intersection_point_z + EPSILON) < z0 || (intersection_point_z - EPSILON) > z1
|
|
275
|
+
);
|
|
262
276
|
|
|
263
277
|
if (point_within_box) {
|
|
264
278
|
// point is within the AABB bounds
|
|
265
279
|
|
|
266
280
|
// ThreeClippingPlaneComputingBVHVisitor.DEBUG_POINTS_2.send3(scratch_v3.x, scratch_v3.y, scratch_v3.z);
|
|
267
281
|
|
|
268
|
-
|
|
282
|
+
destination[destination_cursor++] = intersection_point_x;
|
|
283
|
+
destination[destination_cursor++] = intersection_point_y;
|
|
284
|
+
destination[destination_cursor++] = intersection_point_z;
|
|
269
285
|
}
|
|
270
286
|
}
|
|
271
287
|
}
|
|
272
288
|
}
|
|
289
|
+
|
|
290
|
+
const address_change = destination_cursor - destination_offset;
|
|
291
|
+
|
|
292
|
+
return address_change / 3;
|
|
273
293
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { computePointDistanceToPlane } from "../../geom/Plane.js";
|
|
2
2
|
import { BVHVisitor } from "./BVHVisitor.js";
|
|
3
3
|
import { read_frustum_planes_to_array } from "../../geom/3d/frustum/read_frustum_planes_to_array.js";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
aabb3_detailed_volume_intersection_callback_based
|
|
6
|
+
} from "./aabb3_detailed_volume_intersection_callback_based.js";
|
|
5
7
|
|
|
6
8
|
export class ThreeClippingPlaneComputingBVHVisitor extends BVHVisitor {
|
|
7
9
|
constructor() {
|
|
@@ -131,8 +133,8 @@ export class ThreeClippingPlaneComputingBVHVisitor extends BVHVisitor {
|
|
|
131
133
|
this.__traverseBoxNearCorner(box);
|
|
132
134
|
|
|
133
135
|
|
|
134
|
-
|
|
135
|
-
box,
|
|
136
|
+
aabb3_detailed_volume_intersection_callback_based(
|
|
137
|
+
box.x0, box.y0, box.z0, box.x1, box.y1, box.z1,
|
|
136
138
|
plane_mask,
|
|
137
139
|
this.__planes_f32,
|
|
138
140
|
this.__planeCount,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { aabb3_detailed_volume_intersection } from "../aabb3/aabb3_detailed_volume_intersection.js";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @type {number[]}
|
|
7
|
+
*/
|
|
8
|
+
const scratch_points_data = [];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Figures out intersection volume between an AABB and a frustum for much tighter bounds
|
|
12
|
+
*
|
|
13
|
+
* Especially beneficial when trying to intersect a large AABB against a small frustum,
|
|
14
|
+
* where where traditionally only AABB corners would be considered,
|
|
15
|
+
* we're instead able to compute minimum bounds of the overlap of the two
|
|
16
|
+
*
|
|
17
|
+
* @param {number} x0
|
|
18
|
+
* @param {number} y0
|
|
19
|
+
* @param {number} z0
|
|
20
|
+
* @param {number} x1
|
|
21
|
+
* @param {number} y1
|
|
22
|
+
* @param {number} z1
|
|
23
|
+
* @param {number[]} planes
|
|
24
|
+
* @param {number} plane_count
|
|
25
|
+
* @param {number} plane_mask
|
|
26
|
+
* @param {function(x:number,y:number,z:number)} callback
|
|
27
|
+
* @param {*} [thisArg]
|
|
28
|
+
*/
|
|
29
|
+
export function aabb3_detailed_volume_intersection_callback_based(
|
|
30
|
+
x0, y0, z0, x1, y1, z1,
|
|
31
|
+
plane_mask,
|
|
32
|
+
planes,
|
|
33
|
+
plane_count,
|
|
34
|
+
callback,
|
|
35
|
+
thisArg
|
|
36
|
+
) {
|
|
37
|
+
const point_count = aabb3_detailed_volume_intersection(
|
|
38
|
+
scratch_points_data, 0,
|
|
39
|
+
x0, y0, z0, x1, y1, z1,
|
|
40
|
+
plane_mask,
|
|
41
|
+
planes,
|
|
42
|
+
plane_count
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
for (let i = 0; i < point_count; i++) {
|
|
46
|
+
const address = i * 3;
|
|
47
|
+
const x = scratch_points_data[address];
|
|
48
|
+
const y = scratch_points_data[address + 1];
|
|
49
|
+
const z = scratch_points_data[address + 2];
|
|
50
|
+
callback.call(thisArg,
|
|
51
|
+
x,
|
|
52
|
+
y,
|
|
53
|
+
z
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Gizmo.color= [1,1,1,1];
|
|
57
|
+
// Gizmo.draw_solid_sphere([x, y, z], 0.1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { computePointDistanceToPlane } from "../../Plane.js";
|
|
2
|
+
import { EPSILON } from "../../../math/MathUtils.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {number} x
|
|
7
|
+
* @param {number} y
|
|
8
|
+
* @param {number} z
|
|
9
|
+
* @param {number} plane_mask
|
|
10
|
+
* @param {number[]} planes
|
|
11
|
+
* @param {number} plane_count
|
|
12
|
+
* @return {boolean}
|
|
13
|
+
* @private
|
|
14
|
+
*/
|
|
15
|
+
export function is_point_within_planes(
|
|
16
|
+
x, y, z,
|
|
17
|
+
plane_mask,
|
|
18
|
+
planes,
|
|
19
|
+
plane_count
|
|
20
|
+
) {
|
|
21
|
+
|
|
22
|
+
for (let plane_index = 0; plane_index < plane_count; plane_index++) {
|
|
23
|
+
const plane_bit_mask = 1 << plane_index;
|
|
24
|
+
|
|
25
|
+
if ((plane_mask & plane_bit_mask) === 0) {
|
|
26
|
+
// non-intersecting plane, ignore
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const plane_address = plane_index * 4;
|
|
31
|
+
|
|
32
|
+
const nx = planes[plane_address];
|
|
33
|
+
const ny = planes[plane_address + 1];
|
|
34
|
+
const nz = planes[plane_address + 2];
|
|
35
|
+
const c = planes[plane_address + 3];
|
|
36
|
+
|
|
37
|
+
const distance_to_plane = computePointDistanceToPlane(x, y, z, nx, ny, nz, c);
|
|
38
|
+
|
|
39
|
+
if (distance_to_plane < -EPSILON) {
|
|
40
|
+
// point is outside of one of the planes
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
@@ -11,7 +11,7 @@ import { v3_angle_between } from "../../../v3_angle_between.js";
|
|
|
11
11
|
* @param {ConicRay} result
|
|
12
12
|
* @param {Iterable<TopoTriangle>} faces
|
|
13
13
|
*/
|
|
14
|
-
export function
|
|
14
|
+
export function computeTriangleClusterNormalBoundingCone(result, faces) {
|
|
15
15
|
const normal_data = [];
|
|
16
16
|
let normal_count = 0;
|
|
17
17
|
|
|
@@ -45,6 +45,13 @@ export function computeTraingleClusterNormalBoundingCone(result, faces) {
|
|
|
45
45
|
|
|
46
46
|
const miniball_center = miniball.center();
|
|
47
47
|
|
|
48
|
+
if (Number.isNaN(miniball_center[0]) || Number.isNaN(miniball_center[1]) || Number.isNaN(miniball_center[2])) {
|
|
49
|
+
// something went wrong, center is undefined, fall-back to an open cone
|
|
50
|
+
result_direction.set(1, 0, 0);
|
|
51
|
+
result.angle = Math.PI;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
48
55
|
result_direction.readFromArray(miniball_center, 0);
|
|
49
56
|
const center_vector_magnitude = result_direction.length();
|
|
50
57
|
|
package/core/geom/Vector3.d.ts
CHANGED
|
@@ -13,6 +13,10 @@ export default class Vector3 implements Vector3Like {
|
|
|
13
13
|
y: number
|
|
14
14
|
z: number
|
|
15
15
|
|
|
16
|
+
0: number
|
|
17
|
+
1: number
|
|
18
|
+
2: number
|
|
19
|
+
|
|
16
20
|
public readonly onChanged: Signal<number, number, number, number, number, number>;
|
|
17
21
|
|
|
18
22
|
constructor()
|
|
@@ -87,6 +91,10 @@ export default class Vector3 implements Vector3Like {
|
|
|
87
91
|
|
|
88
92
|
writeToArray(array: number[] | ArrayLike<number>, offset: number): void
|
|
89
93
|
|
|
94
|
+
static distance(a: Vector3Like, b: Vector3Like): number
|
|
95
|
+
|
|
96
|
+
static dot(a: Vector3Like, b: Vector3Like): number
|
|
97
|
+
|
|
90
98
|
static readonly zero: Vector3
|
|
91
99
|
static readonly one: Vector3
|
|
92
100
|
static readonly up: Vector3
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Action } from "../../../core/process/undo/Action.js";
|
|
2
|
+
|
|
3
|
+
export class ActionUpdateTexture extends Action {
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {THREE.Texture} texture
|
|
7
|
+
*/
|
|
8
|
+
constructor(texture) {
|
|
9
|
+
super();
|
|
10
|
+
|
|
11
|
+
this.__texture = texture;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async apply(context) {
|
|
15
|
+
this.__texture.needsUpdate = true;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async revert(context) {
|
|
19
|
+
this.__texture.needsUpdate = true;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Action } from "../../../core/process/undo/Action.js";
|
|
2
|
+
import { array_copy } from "../../../core/collection/array/copyArray.js";
|
|
3
|
+
|
|
4
|
+
export class ArrayCopyAction extends Action {
|
|
5
|
+
/**
|
|
6
|
+
* @template T
|
|
7
|
+
* @param {ArrayLike<T>|T[]|Uint8Array|Float32Array} source
|
|
8
|
+
* @param {ArrayLike<T>|T[]|Uint8Array|Float32Array} destination
|
|
9
|
+
*/
|
|
10
|
+
constructor(source, destination) {
|
|
11
|
+
super();
|
|
12
|
+
|
|
13
|
+
this.__source = source;
|
|
14
|
+
this.__destination = destination;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* @type {ArrayLike<T>|null}
|
|
19
|
+
* @private
|
|
20
|
+
*/
|
|
21
|
+
this.__restore_value = null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async apply(context) {
|
|
25
|
+
|
|
26
|
+
const CTOR = Object.getPrototypeOf(this.__destination).constructor;
|
|
27
|
+
this.__restore_value = new CTOR(this.__destination.length);
|
|
28
|
+
|
|
29
|
+
// remember old value
|
|
30
|
+
array_copy(this.__destination, 0, this.__restore_value, 0, this.__destination.length);
|
|
31
|
+
|
|
32
|
+
// copy data across
|
|
33
|
+
array_copy(this.__source, 0, this.__destination, 0, this.__source.length);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async revert(context) {
|
|
37
|
+
array_copy(this.__restore_value, 0, this.__destination, 0, this.__restore_value.length);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { Action } from "../../../core/process/undo/Action.js";
|
|
2
|
+
import { assert } from "../../../core/assert.js";
|
|
3
|
+
import { texture_array_2d_copy } from "../../../engine/graphics/texture/texture_array_2d_copy.js";
|
|
4
|
+
import { clamp } from "../../../core/math/clamp.js";
|
|
5
|
+
|
|
6
|
+
export class ModifyPatchTextureArray2DAction extends Action {
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param {number[]|Uint8Array|Uint32Array|Float32Array} data
|
|
10
|
+
* @param {number[]} data_resolution
|
|
11
|
+
* @param {number[]} bounds
|
|
12
|
+
*/
|
|
13
|
+
constructor(data, data_resolution, bounds) {
|
|
14
|
+
super();
|
|
15
|
+
|
|
16
|
+
assert.isArrayLike(data, 'data');
|
|
17
|
+
|
|
18
|
+
this.__data = data;
|
|
19
|
+
this.__data_resolution = data_resolution;
|
|
20
|
+
this.__bounds = bounds;
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
const width = this.__bounds[2] - this.__bounds[0];
|
|
24
|
+
const height = this.__bounds[3] - this.__bounds[1];
|
|
25
|
+
|
|
26
|
+
const depth = data_resolution[2];
|
|
27
|
+
|
|
28
|
+
const CTOR = Object.getPrototypeOf(data).constructor;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @type {ArrayLike<number>}
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
34
|
+
this.__patch_new = new CTOR(height * width * depth);
|
|
35
|
+
/**
|
|
36
|
+
* @type {ArrayLike<number>}
|
|
37
|
+
* @private
|
|
38
|
+
*/
|
|
39
|
+
this.__patch_old = new CTOR(height * width * depth);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
*
|
|
44
|
+
* @param {number} center_x
|
|
45
|
+
* @param {number} center_y
|
|
46
|
+
* @param {Sampler2D} stamp
|
|
47
|
+
* @param {number} layer_index
|
|
48
|
+
* @param {number} weight
|
|
49
|
+
* @param {number} clamp_min
|
|
50
|
+
* @param {number} clamp_max
|
|
51
|
+
*/
|
|
52
|
+
applyWeightStampToLayer(
|
|
53
|
+
center_x, center_y,
|
|
54
|
+
stamp, layer_index, weight,
|
|
55
|
+
clamp_min = Number.NEGATIVE_INFINITY, clamp_max = Number.POSITIVE_INFINITY
|
|
56
|
+
) {
|
|
57
|
+
|
|
58
|
+
const bounds_x0 = this.__bounds[0];
|
|
59
|
+
const bounds_y0 = this.__bounds[1];
|
|
60
|
+
|
|
61
|
+
const bounds_x1 = this.__bounds[2];
|
|
62
|
+
const bounds_y1 = this.__bounds[3];
|
|
63
|
+
|
|
64
|
+
const width = bounds_x1 - bounds_x0;
|
|
65
|
+
const height = bounds_y1 - bounds_y0;
|
|
66
|
+
|
|
67
|
+
const center_offset_x = (bounds_x0 + bounds_x1) * 0.5 - center_x;
|
|
68
|
+
const center_offset_y = (bounds_y0 + bounds_y1) * 0.5 - center_y;
|
|
69
|
+
|
|
70
|
+
const source_data = this.__data;
|
|
71
|
+
|
|
72
|
+
const source_width = this.__data_resolution[0];
|
|
73
|
+
const source_height = this.__data_resolution[1];
|
|
74
|
+
const depth = this.__data_resolution[2];
|
|
75
|
+
|
|
76
|
+
for (let y = 0; y < height; y++) {
|
|
77
|
+
const marker_v = (y + center_offset_y) / (height - 1);
|
|
78
|
+
|
|
79
|
+
for (let x = 0; x < width; x++) {
|
|
80
|
+
const marker_u = (x + center_offset_x) / (width - 1);
|
|
81
|
+
|
|
82
|
+
const stamp_value = stamp.sampleChannelBilinearUV(marker_u, marker_v, 3);
|
|
83
|
+
|
|
84
|
+
if (Number.isNaN(stamp_value)) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const weighed_stamp_value = stamp_value * weight;
|
|
89
|
+
|
|
90
|
+
const source_x = (x + bounds_x0);
|
|
91
|
+
const source_y = (y + bounds_y0);
|
|
92
|
+
|
|
93
|
+
const weight_reciprocal = weighed_stamp_value / (depth - 1);
|
|
94
|
+
|
|
95
|
+
for (let i = 0; i < depth; i++) {
|
|
96
|
+
const source_layer_address = source_width * source_height * i;
|
|
97
|
+
const source_texel_address = source_layer_address + source_y * source_width + source_x;
|
|
98
|
+
|
|
99
|
+
const destination_texel_address = width * height * i + y * width + x;
|
|
100
|
+
|
|
101
|
+
const source_value = source_data[source_texel_address];
|
|
102
|
+
|
|
103
|
+
let destination_value;
|
|
104
|
+
if (i === layer_index) {
|
|
105
|
+
// the channel that we are stamping
|
|
106
|
+
destination_value = source_value + weighed_stamp_value;
|
|
107
|
+
} else {
|
|
108
|
+
destination_value = source_value - weight_reciprocal;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.__patch_new[destination_texel_address] = clamp(destination_value, clamp_min, clamp_max);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
get patch_data() {
|
|
118
|
+
return this.__patch_new;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
computeByteSize() {
|
|
122
|
+
// TODO take size of array elements into account
|
|
123
|
+
return this.__patch_new.length + this.__patch_old.length + 280;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async apply(context) {
|
|
127
|
+
const data = this.__data;
|
|
128
|
+
|
|
129
|
+
const depth = this.__data_resolution[2];
|
|
130
|
+
|
|
131
|
+
const bounds_x0 = this.__bounds[0];
|
|
132
|
+
const bounds_y0 = this.__bounds[1];
|
|
133
|
+
|
|
134
|
+
const bounds_x1 = this.__bounds[2];
|
|
135
|
+
const bounds_y1 = this.__bounds[3];
|
|
136
|
+
|
|
137
|
+
const width = bounds_x1 - bounds_x0;
|
|
138
|
+
const height = bounds_y1 - bounds_y0;
|
|
139
|
+
|
|
140
|
+
const source_resolution_x = this.__data_resolution[0];
|
|
141
|
+
const source_resolution_y = this.__data_resolution[1];
|
|
142
|
+
|
|
143
|
+
// store old data
|
|
144
|
+
texture_array_2d_copy(
|
|
145
|
+
data, bounds_x0, bounds_y0, source_resolution_x, source_resolution_y,
|
|
146
|
+
this.__patch_old, 0, 0, width, height,
|
|
147
|
+
width, height, depth
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
texture_array_2d_copy(
|
|
151
|
+
this.__patch_new, 0, 0, width, height,
|
|
152
|
+
data, bounds_x0, bounds_y0, source_resolution_x, source_resolution_y,
|
|
153
|
+
width, height, depth
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async revert(context) {
|
|
159
|
+
|
|
160
|
+
const data = this.__data;
|
|
161
|
+
|
|
162
|
+
const depth = this.__data_resolution[2];
|
|
163
|
+
|
|
164
|
+
const bounds_x0 = this.__bounds[0];
|
|
165
|
+
const bounds_y0 = this.__bounds[1];
|
|
166
|
+
|
|
167
|
+
const bounds_x1 = this.__bounds[2];
|
|
168
|
+
const bounds_y1 = this.__bounds[3];
|
|
169
|
+
|
|
170
|
+
const width = bounds_x1 - bounds_x0;
|
|
171
|
+
const height = bounds_y1 - bounds_y0;
|
|
172
|
+
|
|
173
|
+
const source_resolution_x = this.__data_resolution[0];
|
|
174
|
+
const source_resolution_y = this.__data_resolution[1];
|
|
175
|
+
|
|
176
|
+
texture_array_2d_copy(
|
|
177
|
+
this.__patch_old, 0, 0, width, height,
|
|
178
|
+
data, bounds_x0, bounds_y0, source_resolution_x, source_resolution_y,
|
|
179
|
+
width, height, depth
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
package/editor/enableEditor.js
CHANGED
|
@@ -5,14 +5,14 @@ import { noop } from "../core/function/Functions.js";
|
|
|
5
5
|
*
|
|
6
6
|
* @param {Engine} engine
|
|
7
7
|
* @param {function(Editor):*} [initialization]
|
|
8
|
+
* @returns {{enable:function, disable: function, toggle: function, editor: Editor}}
|
|
8
9
|
*/
|
|
9
10
|
export function enableEditor(engine, initialization = noop) {
|
|
10
11
|
let editor = null;
|
|
11
12
|
|
|
12
13
|
let enabled = false;
|
|
13
14
|
|
|
14
|
-
function
|
|
15
|
-
console.log('Enabling editor');
|
|
15
|
+
function ensureEditor() {
|
|
16
16
|
if (editor === null) {
|
|
17
17
|
editor = new Editor();
|
|
18
18
|
editor.engine = engine;
|
|
@@ -21,11 +21,28 @@ export function enableEditor(engine, initialization = noop) {
|
|
|
21
21
|
|
|
22
22
|
initialization(editor);
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
return editor;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function attachEditor() {
|
|
29
|
+
if (enabled) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log('Enabling editor');
|
|
34
|
+
|
|
35
|
+
ensureEditor();
|
|
36
|
+
|
|
24
37
|
editor.attach(engine);
|
|
25
38
|
enabled = true;
|
|
26
39
|
}
|
|
27
40
|
|
|
28
41
|
function detachEditor() {
|
|
42
|
+
if (!enabled) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
29
46
|
console.log('Disabling editor');
|
|
30
47
|
if (editor !== null) {
|
|
31
48
|
editor.detach();
|
|
@@ -54,4 +71,15 @@ export function enableEditor(engine, initialization = noop) {
|
|
|
54
71
|
});
|
|
55
72
|
|
|
56
73
|
console.warn('Editor mode enabled, use NumLock key to toggle editor mode');
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
toggle: toggleEditor,
|
|
77
|
+
enable: attachEditor,
|
|
78
|
+
disable: detachEditor,
|
|
79
|
+
get editor() {
|
|
80
|
+
ensureEditor();
|
|
81
|
+
|
|
82
|
+
return editor;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
57
85
|
}
|
|
@@ -31,13 +31,16 @@ export class TerrainPaintTool extends Tool {
|
|
|
31
31
|
this.__brushPosition = new Vector2();
|
|
32
32
|
|
|
33
33
|
this.__brushImage = document.createElement('canvas');
|
|
34
|
+
|
|
35
|
+
this.__paint_debt = 0;
|
|
36
|
+
this.__paint_pending = false;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
/**
|
|
37
40
|
*
|
|
38
41
|
* @param {number} timeDelta
|
|
39
42
|
*/
|
|
40
|
-
paint(timeDelta) {
|
|
43
|
+
async paint(timeDelta) {
|
|
41
44
|
|
|
42
45
|
}
|
|
43
46
|
|
|
@@ -46,14 +49,25 @@ export class TerrainPaintTool extends Tool {
|
|
|
46
49
|
* @param {number} timeDelta
|
|
47
50
|
*/
|
|
48
51
|
update(timeDelta) {
|
|
49
|
-
|
|
50
52
|
this.updateBrushPosition();
|
|
51
53
|
this.updateOverlay();
|
|
52
54
|
|
|
53
55
|
if (this.isRunning()) {
|
|
54
56
|
|
|
55
|
-
this.
|
|
57
|
+
this.__paint_debt += timeDelta;
|
|
58
|
+
|
|
59
|
+
if (this.__paint_pending) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.__paint_pending = true;
|
|
64
|
+
|
|
65
|
+
const paint_promise = this.paint(timeDelta);
|
|
66
|
+
this.__paint_debt = 0;
|
|
56
67
|
|
|
68
|
+
paint_promise.finally(() => {
|
|
69
|
+
this.__paint_pending = false;
|
|
70
|
+
});
|
|
57
71
|
}
|
|
58
72
|
|
|
59
73
|
}
|
|
@@ -156,6 +170,8 @@ export class TerrainPaintTool extends Tool {
|
|
|
156
170
|
const engine = this.engine;
|
|
157
171
|
const editor = this.editor;
|
|
158
172
|
|
|
173
|
+
this.__paint_debt = 0;
|
|
174
|
+
|
|
159
175
|
this.terrain = obtainTerrain(engine.entityManager.dataset, (t, entity) => {
|
|
160
176
|
this.terrainEntity = entity;
|
|
161
177
|
});
|