@woosh/meep-engine 2.43.8 → 2.43.11
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/url_to_data_url.js +28 -0
- package/core/collection/queue/Deque.js +2 -2
- package/editor/ecs/component/editors/ImagePathEditor.js +1 -29
- package/engine/graphics/ecs/path/PathDisplaySystem.js +92 -4
- package/engine/graphics/ecs/path/tube/build/{GeometryOutput.js → StreamGeometryBuilder.js} +1 -1
- package/engine/graphics/ecs/path/tube/build/TubePathBuilder.js +1 -12
- package/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.spec.js +32 -0
- package/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +6 -1
- package/engine/graphics/ecs/path/tube/build/makeTubeGeometry.js +6 -4
- package/engine/graphics/ecs/path/tube/build/make_cap.js +10 -10
- package/engine/graphics/ecs/path/tube/build/make_ring_faces.js +1 -1
- package/engine/graphics/ecs/path/tube/build/make_ring_vertices.js +1 -1
- package/engine/graphics/impostors/octahedral/shader/ImpostorShaderV0.js +143 -27
- package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +3 -1
- package/engine/graphics/micron/plugin/MicronRenderPlugin.js +0 -11
- package/engine/graphics/micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js +5 -0
- package/engine/graphics/particles/particular/engine/utils/volume/ParticleVolume.js +12 -1
- package/engine/navigation/ecs/components/Path.spec.js +21 -0
- package/package.json +1 -1
- package/samples/terrain/from_image_2.js +33 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param {string} url
|
|
4
|
+
* @returns {string}
|
|
5
|
+
*/
|
|
6
|
+
export async function url_to_data_url(url) {
|
|
7
|
+
return fetch(url)
|
|
8
|
+
.then(response => {
|
|
9
|
+
return response.blob();
|
|
10
|
+
})
|
|
11
|
+
.then(blob => {
|
|
12
|
+
const fr = new FileReader();
|
|
13
|
+
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
|
|
16
|
+
fr.onload = () => {
|
|
17
|
+
resolve(fr.result);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
fr.onerror = reject;
|
|
21
|
+
fr.onabort = reject;
|
|
22
|
+
|
|
23
|
+
fr.readAsDataURL(blob);
|
|
24
|
+
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -302,7 +302,7 @@ Deque.prototype.peek = Deque.prototype.getFirst;
|
|
|
302
302
|
Deque.prototype.push = Deque.prototype.addFirst;
|
|
303
303
|
Deque.prototype.pop = Deque.prototype.removeFirst;
|
|
304
304
|
|
|
305
|
-
|
|
306
|
-
Standard queue method
|
|
305
|
+
/**
|
|
306
|
+
* Standard queue method
|
|
307
307
|
*/
|
|
308
308
|
Deque.prototype.add = Deque.prototype.addLast;
|
|
@@ -5,6 +5,7 @@ import ImageView from "../../../../view/elements/image/ImageView.js";
|
|
|
5
5
|
import ObservedString from "../../../../core/model/ObservedString.js";
|
|
6
6
|
import { ObservedStringEditor } from "./ObservedStringEditor.js";
|
|
7
7
|
import ButtonView from "../../../../view/elements/button/ButtonView.js";
|
|
8
|
+
import { url_to_data_url } from "../../../../core/binary/url_to_data_url.js";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
*
|
|
@@ -19,35 +20,6 @@ function is_data_url(url) {
|
|
|
19
20
|
return url.startsWith('data:');
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
/**
|
|
23
|
-
*
|
|
24
|
-
* @param {string} url
|
|
25
|
-
* @returns {string}
|
|
26
|
-
*/
|
|
27
|
-
async function url_to_data_url(url) {
|
|
28
|
-
return fetch(url)
|
|
29
|
-
.then(response => {
|
|
30
|
-
return response.blob();
|
|
31
|
-
})
|
|
32
|
-
.then(blob => {
|
|
33
|
-
const fr = new FileReader();
|
|
34
|
-
|
|
35
|
-
return new Promise((resolve, reject) => {
|
|
36
|
-
|
|
37
|
-
fr.onload = () => {
|
|
38
|
-
resolve(fr.result);
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
fr.onerror = reject;
|
|
42
|
-
fr.onabort = reject;
|
|
43
|
-
|
|
44
|
-
fr.readAsDataURL(blob);
|
|
45
|
-
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
23
|
export class ImagePathEditor extends TypeEditor {
|
|
52
24
|
inline = true;
|
|
53
25
|
|
|
@@ -11,6 +11,7 @@ import { RibbonPathBuilder } from "./ribbon/RibbonPathBuilder.js";
|
|
|
11
11
|
import { PathEvents } from "../../../navigation/ecs/components/PathEvents.js";
|
|
12
12
|
import { assert } from "../../../../core/assert.js";
|
|
13
13
|
import { TubePathBuilder } from "./tube/build/TubePathBuilder.js";
|
|
14
|
+
import { Deque } from "../../../../core/collection/queue/Deque.js";
|
|
14
15
|
|
|
15
16
|
const builders = {
|
|
16
17
|
[PathDisplayType.None]: function (style, path, result) {
|
|
@@ -82,6 +83,14 @@ const builders = {
|
|
|
82
83
|
}
|
|
83
84
|
};
|
|
84
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Maximum amount of time allowed per system tick to spend on processing update queue
|
|
88
|
+
* in milliseconds
|
|
89
|
+
* @readonly
|
|
90
|
+
* @type {number}
|
|
91
|
+
*/
|
|
92
|
+
const UPDATE_PROCESSING_BUDGET_MS = 10;
|
|
93
|
+
|
|
85
94
|
class PathDisplayContext extends SystemEntityContext {
|
|
86
95
|
constructor() {
|
|
87
96
|
super();
|
|
@@ -100,6 +109,8 @@ class PathDisplayContext extends SystemEntityContext {
|
|
|
100
109
|
const ownedEntities = this.__owned_entities;
|
|
101
110
|
ownedEntities.splice(0, ownedEntities.length);
|
|
102
111
|
|
|
112
|
+
// todo check that the main entity still exists before we decide to spawn new entities
|
|
113
|
+
|
|
103
114
|
/**
|
|
104
115
|
* @type {PathDisplay}
|
|
105
116
|
*/
|
|
@@ -180,6 +191,16 @@ class PathDisplayContext extends SystemEntityContext {
|
|
|
180
191
|
this.__build();
|
|
181
192
|
}
|
|
182
193
|
|
|
194
|
+
request_update() {
|
|
195
|
+
/**
|
|
196
|
+
*
|
|
197
|
+
* @type {PathDisplaySystem}
|
|
198
|
+
*/
|
|
199
|
+
const system = this.system;
|
|
200
|
+
|
|
201
|
+
system.request_entity_update(this.entity);
|
|
202
|
+
}
|
|
203
|
+
|
|
183
204
|
link() {
|
|
184
205
|
super.link();
|
|
185
206
|
|
|
@@ -189,8 +210,8 @@ class PathDisplayContext extends SystemEntityContext {
|
|
|
189
210
|
|
|
190
211
|
const entity = this.entity;
|
|
191
212
|
|
|
192
|
-
ecd.addEntityEventListener(entity, PathEvents.Changed, this.
|
|
193
|
-
ecd.addEntityEventListener(entity, PathDisplayEvents.Changed, this.
|
|
213
|
+
ecd.addEntityEventListener(entity, PathEvents.Changed, this.request_update, this);
|
|
214
|
+
ecd.addEntityEventListener(entity, PathDisplayEvents.Changed, this.request_update, this);
|
|
194
215
|
}
|
|
195
216
|
|
|
196
217
|
unlink() {
|
|
@@ -200,8 +221,16 @@ class PathDisplayContext extends SystemEntityContext {
|
|
|
200
221
|
|
|
201
222
|
const entity = this.entity;
|
|
202
223
|
|
|
203
|
-
ecd.removeEntityEventListener(entity, PathEvents.Changed, this.
|
|
204
|
-
ecd.removeEntityEventListener(entity, PathDisplayEvents.Changed, this.
|
|
224
|
+
ecd.removeEntityEventListener(entity, PathEvents.Changed, this.request_update, this);
|
|
225
|
+
ecd.removeEntityEventListener(entity, PathDisplayEvents.Changed, this.request_update, this);
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
*
|
|
229
|
+
* @type {PathDisplaySystem}
|
|
230
|
+
*/
|
|
231
|
+
const system = this.system;
|
|
232
|
+
|
|
233
|
+
system.cancel_entity_update(entity);
|
|
205
234
|
|
|
206
235
|
// destroy existing owned entities
|
|
207
236
|
this.__destroy_existing_entities();
|
|
@@ -238,6 +267,35 @@ export class PathDisplaySystem extends AbstractContextSystem {
|
|
|
238
267
|
* @type {Reference<RibbonXPlugin>}
|
|
239
268
|
*/
|
|
240
269
|
this.plugin = null;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Deferred queue of entities slated to be rebuilt
|
|
273
|
+
* @type {Deque<number>}
|
|
274
|
+
* @private
|
|
275
|
+
*/
|
|
276
|
+
this.__rebuild_queue = new Deque();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Request that path displays be rebuilt
|
|
281
|
+
* @package
|
|
282
|
+
* @param {number} entity
|
|
283
|
+
*/
|
|
284
|
+
request_entity_update(entity) {
|
|
285
|
+
assert.isNonNegativeInteger(entity, 'entity');
|
|
286
|
+
|
|
287
|
+
if (!this.__rebuild_queue.has(entity)) {
|
|
288
|
+
this.__rebuild_queue.add(entity);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Cancel any pending updates
|
|
294
|
+
* @package
|
|
295
|
+
* @param {number} entity
|
|
296
|
+
*/
|
|
297
|
+
cancel_entity_update(entity) {
|
|
298
|
+
this.__rebuild_queue.remove(entity);
|
|
241
299
|
}
|
|
242
300
|
|
|
243
301
|
async startup(entityManager, readyCallback, errorCallback) {
|
|
@@ -253,4 +311,34 @@ export class PathDisplaySystem extends AbstractContextSystem {
|
|
|
253
311
|
|
|
254
312
|
super.shutdown(entityManager, readyCallback, errorCallback);
|
|
255
313
|
}
|
|
314
|
+
|
|
315
|
+
update(time_delta) {
|
|
316
|
+
// process update queue
|
|
317
|
+
|
|
318
|
+
const queue = this.__rebuild_queue;
|
|
319
|
+
if (queue.isEmpty()) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const t0 = performance.now();
|
|
324
|
+
|
|
325
|
+
do {
|
|
326
|
+
// note that we don't need to check if the queue is empty here for the first iteration, as we already checked earlier
|
|
327
|
+
const entity = queue.pop();
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
*
|
|
331
|
+
* @type {PathDisplayContext|undefined}
|
|
332
|
+
*/
|
|
333
|
+
const ctx = this.__getEntityContext(entity);
|
|
334
|
+
|
|
335
|
+
// check that entity still exists
|
|
336
|
+
if (ctx === undefined) {
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
ctx.rebuild();
|
|
341
|
+
|
|
342
|
+
} while ((performance.now() - t0) < UPDATE_PROCESSING_BUDGET_MS && !queue.isEmpty());
|
|
343
|
+
}
|
|
256
344
|
}
|
|
@@ -4,7 +4,7 @@ import { BufferAttribute, BufferGeometry } from "three";
|
|
|
4
4
|
/**
|
|
5
5
|
* Helper class, allowing us to treat geometry build process as a stream without having to create intermediate arrays and performing unnecessary copying
|
|
6
6
|
*/
|
|
7
|
-
export class
|
|
7
|
+
export class StreamGeometryBuilder {
|
|
8
8
|
constructor() {
|
|
9
9
|
/**
|
|
10
10
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BufferGeometry, MeshBasicMaterial, MeshMatcapMaterial, MeshStandardMaterial
|
|
1
|
+
import { BufferGeometry, MeshBasicMaterial, MeshMatcapMaterial, MeshStandardMaterial } from "three";
|
|
2
2
|
import { InterpolationType } from "../../../../../navigation/ecs/components/InterpolationType.js";
|
|
3
3
|
import EntityBuilder from "../../../../../ecs/EntityBuilder.js";
|
|
4
4
|
import { Transform } from "../../../../../ecs/transform/Transform.js";
|
|
@@ -129,8 +129,6 @@ export class TubePathBuilder {
|
|
|
129
129
|
// generate three.js curve from path
|
|
130
130
|
const path_component = this.path;
|
|
131
131
|
|
|
132
|
-
const three_points = [];
|
|
133
|
-
|
|
134
132
|
const pointCount = path_component.getPointCount();
|
|
135
133
|
|
|
136
134
|
if (pointCount < 2) {
|
|
@@ -138,15 +136,6 @@ export class TubePathBuilder {
|
|
|
138
136
|
return;
|
|
139
137
|
}
|
|
140
138
|
|
|
141
|
-
for (let i = 0; i < pointCount; i++) {
|
|
142
|
-
const v3 = new Vector3();
|
|
143
|
-
|
|
144
|
-
path_component.getPosition(i, v3);
|
|
145
|
-
|
|
146
|
-
three_points.push(v3);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
139
|
const material_def = make_material(style, this.assetManager);
|
|
151
140
|
|
|
152
141
|
material_def.color.set(style.color.toUint());
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import Path from "../../../../../navigation/ecs/components/Path.js";
|
|
2
|
+
import { build_geometry_catmullrom } from "./build_geometry_catmullrom.js";
|
|
3
|
+
import { TubePathStyle } from "../TubePathStyle.js";
|
|
4
|
+
import { CapType } from "../CapType.js";
|
|
5
|
+
|
|
6
|
+
test('very tiny segment of a path', () => {
|
|
7
|
+
const path = Path.fromJSON({
|
|
8
|
+
points: [3, 1, 1, 3, 1, 3, 7, 1, 3, 7, 1, 7, 3, 1, 7, 3, 1, 8]
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const style = new TubePathStyle();
|
|
12
|
+
style.cap_type = CapType.Flat;
|
|
13
|
+
style.width = 0.1;
|
|
14
|
+
style.path_mask = [0, 0.00005900000113712167];
|
|
15
|
+
style.resolution = 10;
|
|
16
|
+
style.shape = [1, -2.4492937051703357e-16, 0.9238795042037964, -0.3826834261417389, 0.7071067690849304, -0.7071067690849304, 0.3826834261417389, -0.9238795042037964, -1.8369701465288538e-16, -1, -0.3826834261417389, -0.9238795042037964, -0.7071067690849304, -0.7071067690849304, -0.9238795042037964, -0.3826834261417389, -1, 1.2246468525851679e-16, -0.9238795042037964, 0.3826834261417389, -0.7071067690849304, 0.7071067690849304, -0.3826834261417389, 0.9238795042037964, 6.123234262925839e-17, 1, 0.3826834261417389, 0.9238795042037964, 0.7071067690849304, 0.7071067690849304, 0.9238795042037964, 0.3826834261417389];
|
|
17
|
+
|
|
18
|
+
const result = build_geometry_catmullrom(path, style, style.shape, [
|
|
19
|
+
1.131973639570565e-16, 1, 0.3826834559440613, 0.9238795042037964, 0.7071067690849304, 0.7071067690849304, 0.9238795042037964, 0.3826834559440613, 1, -8.489801965906992e-17, 0.9238795042037964, -0.3826834559440613, 0.7071067690849304, -0.7071067690849304, 0.3826834559440613, -0.9238795042037964, -5.659868197852824e-17, -1, -0.3826834559440613, -0.9238795042037964, -0.7071067690849304, -0.7071067690849304, -0.9238795042037964, -0.3826834559440613, -1, 4.244900982953496e-17, -0.9238795042037964, 0.3826834559440613, -0.7071067690849304, 0.7071067690849304, -0.3826834559440613, 0.9238795042037964
|
|
20
|
+
], [
|
|
21
|
+
0.10000000149011612, 0, 0, -0, 0.10000000149011612, 0, 0, 0, 1
|
|
22
|
+
], 0, 0.00005900000113712167);
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
const attribute_position = result.getAttribute("position");
|
|
26
|
+
expect(attribute_position).toBeDefined();
|
|
27
|
+
expect(attribute_position).not.toBeNull();
|
|
28
|
+
|
|
29
|
+
for (let i = 0; i < attribute_position.array.length; i++) {
|
|
30
|
+
expect(attribute_position.array[i]).not.toBeNaN();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
@@ -101,8 +101,13 @@ export function computeFrenetFrames(points, closed = false, normal_hint) {
|
|
|
101
101
|
// no tangent, copy previous one
|
|
102
102
|
if (i > 0) {
|
|
103
103
|
tangents[i] = tangents[i - 1];
|
|
104
|
-
|
|
104
|
+
} else {
|
|
105
|
+
// very first tangent is undefined, set something arbitrary
|
|
106
|
+
// TODO take normal_hint into account
|
|
107
|
+
tangents[i] = new Vector3(0, 0, 1);
|
|
105
108
|
}
|
|
109
|
+
continue;
|
|
110
|
+
|
|
106
111
|
}
|
|
107
112
|
|
|
108
113
|
tangents[i] = new Vector3(
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { BufferGeometry, Vector3 } from "three";
|
|
2
|
-
import {
|
|
2
|
+
import { StreamGeometryBuilder } from "./StreamGeometryBuilder.js";
|
|
3
3
|
import { CapType } from "../CapType.js";
|
|
4
4
|
import { make_ring_vertices } from "./make_ring_vertices.js";
|
|
5
5
|
import { assert } from "../../../../../../core/assert.js";
|
|
6
6
|
import { make_ring_faces } from "./make_ring_faces.js";
|
|
7
|
-
import {
|
|
7
|
+
import { append_compute_cap_geometry_size, make_cap } from "./make_cap.js";
|
|
8
8
|
import { v3_angle_cos_between } from "../../../../../../core/geom/v3_angle_between.js";
|
|
9
9
|
|
|
10
10
|
|
|
@@ -12,6 +12,7 @@ const v4_array = new Float32Array(4);
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @see https://github.com/mrdoob/three.js/blob/master/src/geometries/TubeGeometry.js
|
|
15
|
+
* @see https://github.com/hofk/THREEg.js/blob/488f1128a25321a76888aa1fa19db64750318444/THREEg.js#L3483
|
|
15
16
|
* @param {Float32Array|number[]} in_positions
|
|
16
17
|
* @param {Vector3[]} in_normals
|
|
17
18
|
* @param {Vector3[]} in_binormals
|
|
@@ -34,7 +35,7 @@ export function makeTubeGeometry(
|
|
|
34
35
|
assert.isNumber(shape_length, 'shape_length');
|
|
35
36
|
assert.isArrayLike(shape, 'shape');
|
|
36
37
|
|
|
37
|
-
const out = new
|
|
38
|
+
const out = new StreamGeometryBuilder();
|
|
38
39
|
|
|
39
40
|
// helper variables
|
|
40
41
|
|
|
@@ -48,7 +49,7 @@ export function makeTubeGeometry(
|
|
|
48
49
|
};
|
|
49
50
|
|
|
50
51
|
if (!closed) {
|
|
51
|
-
|
|
52
|
+
append_compute_cap_geometry_size(2, geometry_size, shape_length, cap_type);
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
out.allocate(
|
|
@@ -57,6 +58,7 @@ export function makeTubeGeometry(
|
|
|
57
58
|
);
|
|
58
59
|
|
|
59
60
|
// create buffer data
|
|
61
|
+
|
|
60
62
|
if (!closed) {
|
|
61
63
|
// start cap
|
|
62
64
|
make_cap(
|
|
@@ -50,7 +50,7 @@ function compute_shape_radius(shape, shape_length, cx = 0, cy = 0) {
|
|
|
50
50
|
* @param {Vector3[]} in_normals
|
|
51
51
|
* @param {Vector3[]} in_binormals
|
|
52
52
|
* @param {Vector3[]} in_tangents
|
|
53
|
-
* @param {
|
|
53
|
+
* @param {StreamGeometryBuilder} out
|
|
54
54
|
* @param {number[]} shape
|
|
55
55
|
* @param {number[]|Float32Array} shape_normal
|
|
56
56
|
* @param {number} shape_length
|
|
@@ -106,9 +106,9 @@ function make_cap_round(
|
|
|
106
106
|
|
|
107
107
|
for (i = 0; i <= cap_segment_count; i++) {
|
|
108
108
|
|
|
109
|
-
const j = direction>0? i:cap_segment_count-i;
|
|
109
|
+
const j = direction > 0 ? i : cap_segment_count - i;
|
|
110
110
|
|
|
111
|
-
const angle_b = j * angular_step_i
|
|
111
|
+
const angle_b = j * angular_step_i;
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
const cos_b = Math.cos(angle_b);
|
|
@@ -151,9 +151,9 @@ const v4_no_bend = new Float32Array([0, 1, 0, 0]);
|
|
|
151
151
|
* @param {Vector3[]} in_normals
|
|
152
152
|
* @param {Vector3[]} in_binormals
|
|
153
153
|
* @param {Vector3[]} in_tangents
|
|
154
|
-
* @param {
|
|
154
|
+
* @param {StreamGeometryBuilder} out
|
|
155
155
|
* @param {number[]} shape
|
|
156
|
-
* @param {number[]|Float32Array}
|
|
156
|
+
* @param {number[]|Float32Array} shape_normal
|
|
157
157
|
* @param {number} shape_length
|
|
158
158
|
* @param {number[]} shape_transform
|
|
159
159
|
* @param {number} direction
|
|
@@ -211,7 +211,7 @@ function make_cap_flat(
|
|
|
211
211
|
* @param {Float32Array|number[]} in_positions
|
|
212
212
|
* @param {Vector3[]} in_normals
|
|
213
213
|
* @param {Vector3[]} in_binormals
|
|
214
|
-
* @param {
|
|
214
|
+
* @param {StreamGeometryBuilder} out
|
|
215
215
|
* @param {Vector3[]} in_tangents
|
|
216
216
|
* @param {number[]} shape
|
|
217
217
|
* @param {number[]|Float32Array} shape_normal
|
|
@@ -250,13 +250,13 @@ export function make_cap(
|
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
/**
|
|
253
|
-
*
|
|
253
|
+
* Increases target geometry buffers to accommodate a given number of caps
|
|
254
254
|
* @param {number} count how many caps
|
|
255
|
-
* @param {{polygon_count:number, vertex_count:number}} out
|
|
256
|
-
* @param {number} radial_segments
|
|
255
|
+
* @param {{polygon_count:number, vertex_count:number}} out where to increment
|
|
256
|
+
* @param {number} radial_segments number of lines that make up profile of the extruded shape
|
|
257
257
|
* @param {CapType} type
|
|
258
258
|
*/
|
|
259
|
-
export function
|
|
259
|
+
export function append_compute_cap_geometry_size(count, out, radial_segments, type) {
|
|
260
260
|
|
|
261
261
|
if (type === CapType.None) {
|
|
262
262
|
// do nothing
|
|
@@ -27,13 +27,28 @@ const shader_vx = `
|
|
|
27
27
|
out vec3 local_ray_far;
|
|
28
28
|
|
|
29
29
|
flat out vec2 frame1;
|
|
30
|
+
flat out vec3 frame1_normal;
|
|
30
31
|
out vec2 uv_frame1;
|
|
31
32
|
out vec2 xy_frame1;
|
|
32
33
|
|
|
34
|
+
flat out vec2 frame2;
|
|
35
|
+
flat out vec3 frame2_normal;
|
|
36
|
+
out vec2 uv_frame2;
|
|
37
|
+
out vec2 xy_frame2;
|
|
38
|
+
|
|
39
|
+
flat out vec2 frame3;
|
|
40
|
+
flat out vec3 frame3_normal;
|
|
41
|
+
out vec2 uv_frame3;
|
|
42
|
+
out vec2 xy_frame3;
|
|
43
|
+
|
|
44
|
+
out vec4 blend_weights;
|
|
45
|
+
|
|
33
46
|
uniform mat4 modelViewMatrix;
|
|
34
47
|
uniform mat4 projectionMatrix;
|
|
35
48
|
uniform mat3 normalMatrix;
|
|
36
49
|
uniform mat4 modelMatrix;
|
|
50
|
+
uniform mat4 viewMatrix;
|
|
51
|
+
uniform vec3 cameraPosition;
|
|
37
52
|
|
|
38
53
|
uniform vec3 uOffset;
|
|
39
54
|
uniform float uRadius;
|
|
@@ -143,6 +158,16 @@ const shader_vx = `
|
|
|
143
158
|
return res;
|
|
144
159
|
}
|
|
145
160
|
|
|
161
|
+
vec3 projectOnPlaneBasis(vec3 ray, vec3 plane_normal, vec3 plane_x, vec3 plane_y)
|
|
162
|
+
{
|
|
163
|
+
//reproject plane normal onto planeXY basos
|
|
164
|
+
return normalize(vec3(
|
|
165
|
+
dot(plane_x, ray),
|
|
166
|
+
dot(plane_y, ray),
|
|
167
|
+
dot(plane_normal, ray)
|
|
168
|
+
));
|
|
169
|
+
}
|
|
170
|
+
|
|
146
171
|
void calcuateXYbasis(vec3 plane_normal, out vec3 plane_x, out vec3 plane_y)
|
|
147
172
|
{
|
|
148
173
|
vec3 up = vec3(0,1,0);
|
|
@@ -155,14 +180,56 @@ const shader_vx = `
|
|
|
155
180
|
plane_y = normalize(cross(plane_x, plane_normal));
|
|
156
181
|
}
|
|
157
182
|
|
|
183
|
+
//this function works well in orthogonal projection. It works okeyish with further distances of perspective projection
|
|
184
|
+
vec2 virtualPlaneUV(vec3 plane_normal, vec3 plane_x, vec3 plane_y, vec3 pivotToCameraRay, vec3 vertexToCameraRay, float size)
|
|
185
|
+
{
|
|
186
|
+
plane_normal = normalize(plane_normal);
|
|
187
|
+
plane_x = normalize(plane_x);
|
|
188
|
+
plane_y = normalize(plane_y);
|
|
189
|
+
|
|
190
|
+
// plane_normal is normalized but pivotToCameraRay & vertexToCameraRay are NOT
|
|
191
|
+
// so what are we doing here ?
|
|
192
|
+
// We are calculating length of pivotToCameraRay projected onto plane_normal
|
|
193
|
+
// so pivotToCameraRay is vector to camera from CENTER OF object
|
|
194
|
+
// we are recalculting this distance taking into account new plane normal
|
|
195
|
+
float projectedNormalRayLength = dot(plane_normal, pivotToCameraRay);
|
|
196
|
+
// tihs is direction is almost the same as origin, but its to individual vertex
|
|
197
|
+
// not sure this is correct for perspective projection
|
|
198
|
+
float projectedVertexRayLength = dot(plane_normal, vertexToCameraRay);
|
|
199
|
+
// basically its length difference betwen center and vertex - "not precise"
|
|
200
|
+
// projectedVertexRayLength is bigger than projectedNormalRayLength when vertex is
|
|
201
|
+
// further than "main front facing billboard"
|
|
202
|
+
// so offsetLength is getting smaller, otherwise is getting bigger
|
|
203
|
+
float offsetLength = projectedNormalRayLength/projectedVertexRayLength;
|
|
204
|
+
|
|
205
|
+
// ok so offsetLength is just a length
|
|
206
|
+
// we want a vector so we multiply it by vertexToCameraRay to get this offset
|
|
207
|
+
// now what are we REALY doing is calculuating distance difference
|
|
208
|
+
// se are SUBSTRACTING pivotToCameraRay vector
|
|
209
|
+
// we would get difference between center of plane and vertex rotated
|
|
210
|
+
vec3 offsetVector = vertexToCameraRay * offsetLength - pivotToCameraRay;
|
|
211
|
+
|
|
212
|
+
// we got the offset of rotated vertex, but we need to offset it from correct plane axis
|
|
213
|
+
// so again we projecting length of intersection (offset of rotated vertex) onto plane_x
|
|
214
|
+
// and plane_y
|
|
215
|
+
vec2 duv = vec2(
|
|
216
|
+
dot(plane_x, offsetVector),
|
|
217
|
+
dot(plane_y, offsetVector)
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
//we are in space -1 to 1
|
|
221
|
+
duv /= 2.0 * size;
|
|
222
|
+
duv += 0.5;
|
|
223
|
+
return duv;
|
|
224
|
+
}
|
|
158
225
|
|
|
159
226
|
void main() {
|
|
160
227
|
vUv = uv;
|
|
161
228
|
|
|
162
229
|
vec2 framesMinusOne = uFrames - vec2(1.0);
|
|
163
230
|
|
|
164
|
-
vec3 cameraPos_WS =
|
|
165
|
-
vec3 cameraPos_OS = (inverse(
|
|
231
|
+
vec3 cameraPos_WS = cameraPosition;
|
|
232
|
+
vec3 cameraPos_OS = (inverse(viewMatrix) * vec4(cameraPos_WS, 1.0)).xyz;
|
|
166
233
|
|
|
167
234
|
//TODO: check if this is correct. We are using orho projected images, so
|
|
168
235
|
// camera far away
|
|
@@ -206,7 +273,7 @@ const shader_vx = `
|
|
|
206
273
|
//get 2D projection of this vertex in normalized device coordinates
|
|
207
274
|
vec2 pos = gl_Position.xy/gl_Position.w;
|
|
208
275
|
|
|
209
|
-
vec3 projected = position;
|
|
276
|
+
vec3 projected = object_scale*position;
|
|
210
277
|
vec3 vertexToCameraRay = (pivotToCameraRay - (projected));
|
|
211
278
|
vec3 vertexToCameraDir = normalize(vertexToCameraRay);
|
|
212
279
|
|
|
@@ -221,25 +288,44 @@ const shader_vx = `
|
|
|
221
288
|
//
|
|
222
289
|
vec3 view_direction = normalize(local_ray_near-local_ray_far);
|
|
223
290
|
|
|
224
|
-
vec2
|
|
225
|
-
|
|
291
|
+
vec2 grid = VectorToGrid(-view_direction);
|
|
292
|
+
//bias and scale to 0 to 1
|
|
293
|
+
grid = clamp((grid + 1.0) * 0.5, vec2(0, 0), vec2(1, 1));
|
|
294
|
+
grid *= framesMinusOne;
|
|
295
|
+
grid = clamp(grid, vec2(0), vec2(framesMinusOne));
|
|
296
|
+
vec2 gridFloor = min(floor(grid), framesMinusOne);
|
|
297
|
+
vec2 gridFract = fract(grid);
|
|
226
298
|
|
|
227
|
-
|
|
228
|
-
vec2 gridFloor = floor(grid);
|
|
299
|
+
blend_weights = TriangleInterpolate( gridFract );
|
|
229
300
|
|
|
230
|
-
|
|
301
|
+
frame1 = gridFloor;
|
|
302
|
+
frame2 = gridFloor + mix(vec2(0,1),vec2(1,0),blend_weights.w);
|
|
303
|
+
frame3 = gridFloor + vec2(1.0,1.0);
|
|
231
304
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
305
|
+
vec3 projectedQuadADir = FrameXYToRay(frame1, framesMinusOne);
|
|
306
|
+
vec3 projectedQuadBDir = FrameXYToRay(frame2, framesMinusOne);
|
|
307
|
+
vec3 projectedQuadCDir = FrameXYToRay(frame3, framesMinusOne);
|
|
308
|
+
|
|
309
|
+
frame1_normal = (modelViewMatrix *vec4(projectedQuadADir, 0)).xyz;
|
|
310
|
+
frame2_normal = (modelViewMatrix *vec4(projectedQuadBDir, 0)).xyz;
|
|
311
|
+
frame3_normal = (modelViewMatrix *vec4(projectedQuadCDir, 0)).xyz;
|
|
235
312
|
|
|
236
313
|
vec3 plane_x1, plane_y1, plane_x2, plane_y2, plane_x3, plane_y3;
|
|
237
|
-
|
|
314
|
+
|
|
238
315
|
calcuateXYbasis(projectedQuadADir, plane_x1, plane_y1);
|
|
239
316
|
|
|
240
|
-
uv_frame1 = virtualPlaneUV(projectedQuadADir, plane_x1, plane_y1, pivotToCameraRay, vertexToCameraRay,
|
|
317
|
+
uv_frame1 = virtualPlaneUV(projectedQuadADir, plane_x1, plane_y1, pivotToCameraRay, vertexToCameraRay, uRadius);
|
|
241
318
|
xy_frame1 = projectOnPlaneBasis(-vertexToCameraDir, projectedQuadADir, plane_x1, plane_y1).xy;
|
|
319
|
+
|
|
320
|
+
calcuateXYbasis(projectedQuadBDir, plane_x2, plane_y2);
|
|
321
|
+
uv_frame2 = virtualPlaneUV(projectedQuadBDir, plane_x2, plane_y2, pivotToCameraRay, vertexToCameraRay, uRadius);
|
|
322
|
+
xy_frame2 = projectOnPlaneBasis(-vertexToCameraDir, projectedQuadBDir, plane_x2, plane_y2).xy;
|
|
242
323
|
|
|
324
|
+
calcuateXYbasis(projectedQuadCDir, plane_x3, plane_y3);
|
|
325
|
+
uv_frame3 = virtualPlaneUV(projectedQuadCDir, plane_x3, plane_y3, pivotToCameraRay, vertexToCameraRay, uRadius);
|
|
326
|
+
xy_frame3 = projectOnPlaneBasis(-vertexToCameraDir, projectedQuadCDir, plane_x3, plane_y3).xy;
|
|
327
|
+
|
|
328
|
+
|
|
243
329
|
}
|
|
244
330
|
`;
|
|
245
331
|
const shader_fg = `
|
|
@@ -269,6 +355,23 @@ const shader_fg = `
|
|
|
269
355
|
in vec3 local_ray_near;
|
|
270
356
|
in vec3 local_ray_far;
|
|
271
357
|
|
|
358
|
+
flat in vec2 frame1;
|
|
359
|
+
flat in vec3 frame1_normal;
|
|
360
|
+
in vec2 uv_frame1;
|
|
361
|
+
in vec2 xy_frame1;
|
|
362
|
+
|
|
363
|
+
flat in vec2 frame2;
|
|
364
|
+
flat in vec3 frame2_normal;
|
|
365
|
+
in vec2 uv_frame2;
|
|
366
|
+
in vec2 xy_frame2;
|
|
367
|
+
|
|
368
|
+
flat in vec2 frame3;
|
|
369
|
+
flat in vec3 frame3_normal;
|
|
370
|
+
in vec2 uv_frame3;
|
|
371
|
+
in vec2 xy_frame3;
|
|
372
|
+
|
|
373
|
+
in vec4 blend_weights;
|
|
374
|
+
|
|
272
375
|
struct Material{
|
|
273
376
|
vec3 diffuse;
|
|
274
377
|
vec3 normal;
|
|
@@ -394,14 +497,14 @@ const shader_fg = `
|
|
|
394
497
|
));
|
|
395
498
|
}
|
|
396
499
|
|
|
397
|
-
vec2 recompute_uv(vec2 frame, vec2 frame_uv, sampler2D gBuffer){
|
|
500
|
+
vec2 recompute_uv(vec2 frame, vec2 frame_uv, vec2 xy_f, sampler2D gBuffer){
|
|
398
501
|
vec2 frame_size = vec2(1.0/ uFrames);
|
|
399
502
|
|
|
400
503
|
vec2 source_uv = (frame + frame_uv)*frame_size;
|
|
401
504
|
|
|
402
505
|
float n_depth = texture(gBuffer, source_uv).a;
|
|
403
506
|
|
|
404
|
-
vec2 offset = clamp(
|
|
507
|
+
vec2 offset = clamp(xy_f * vec2(0.5-n_depth ) * depth_scale,0.0, 1.0);
|
|
405
508
|
|
|
406
509
|
vec2 uv_f = clamp(frame_uv+offset, 0.0, 1.0);
|
|
407
510
|
|
|
@@ -411,6 +514,21 @@ const shader_fg = `
|
|
|
411
514
|
// return source_uv;
|
|
412
515
|
}
|
|
413
516
|
|
|
517
|
+
vec2 recalculateUV(vec2 uv_f, vec2 frame, vec2 xy_f, vec2 frame_size, float d_scale, sampler2D depthTexture)
|
|
518
|
+
{
|
|
519
|
+
//clamp for parallax sampling
|
|
520
|
+
uv_f = clamp(uv_f, vec2(0.0), vec2(1.0));
|
|
521
|
+
vec2 uv_quad = frame_size * (frame + uv_f);
|
|
522
|
+
//paralax
|
|
523
|
+
vec4 n_depth = (texture( depthTexture, uv_quad));
|
|
524
|
+
uv_f = xy_f * (0.5-n_depth.a) * d_scale + uv_f;
|
|
525
|
+
//clamp parallax offset
|
|
526
|
+
uv_f = clamp(uv_f, vec2(0.0), vec2(1.0));
|
|
527
|
+
uv_f = frame_size * (frame + uv_f);
|
|
528
|
+
//clamped full UV
|
|
529
|
+
return clamp(uv_f, vec2(0.0), vec2(1.0));
|
|
530
|
+
}
|
|
531
|
+
|
|
414
532
|
void main(){
|
|
415
533
|
vec3 view_direction = normalize(local_ray_near-local_ray_far);
|
|
416
534
|
|
|
@@ -420,19 +538,17 @@ const shader_fg = `
|
|
|
420
538
|
vec2 gridFrac = fract(grid);
|
|
421
539
|
vec2 gridFloor = floor(grid);
|
|
422
540
|
|
|
423
|
-
vec4 weights =
|
|
541
|
+
vec4 weights = blend_weights;
|
|
424
542
|
|
|
425
543
|
vec2 frame_uv = vUv;
|
|
426
544
|
|
|
427
545
|
//3 nearest frames
|
|
428
|
-
vec2
|
|
429
|
-
vec2
|
|
430
|
-
vec2
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
vec2 uv2 = recompute_uv(frame2, frame_uv, tGeometry);
|
|
435
|
-
|
|
546
|
+
vec2 quad_size = vec2(1.0) / uFrames;
|
|
547
|
+
vec2 uv_f1 = recalculateUV(uv_frame1, frame1, xy_frame1, quad_size, depth_scale, tGeometry);
|
|
548
|
+
vec2 uv_f2 = recalculateUV(uv_frame2, frame2, xy_frame2, quad_size, depth_scale, tGeometry);
|
|
549
|
+
vec2 uv_f3 = recalculateUV(uv_frame3, frame3, xy_frame3, quad_size, depth_scale, tGeometry);
|
|
550
|
+
|
|
551
|
+
|
|
436
552
|
vec4 ddxy = vec4( dFdx(vUv.xy), dFdy(vUv.xy) );
|
|
437
553
|
|
|
438
554
|
vec2 frame_size = vec2(1.0/ uFrames);
|
|
@@ -440,9 +556,9 @@ const shader_fg = `
|
|
|
440
556
|
vec4 texel_color = ImposterBlendWeights(
|
|
441
557
|
// vec4 texel_color = ImposterBlendWeightsNearest(
|
|
442
558
|
tBase,
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
559
|
+
uv_f1,
|
|
560
|
+
uv_f2,
|
|
561
|
+
uv_f3,
|
|
446
562
|
weights, ddxy
|
|
447
563
|
);
|
|
448
564
|
|
|
@@ -145,7 +145,9 @@ export class GLTFAssetTransformer extends AssetTransformer {
|
|
|
145
145
|
geometry[MICRON_GEOMETRY_FIELD] = micron_geometry;
|
|
146
146
|
|
|
147
147
|
// push data into cache
|
|
148
|
-
this.__cache
|
|
148
|
+
if (this.__cache !== undefined) {
|
|
149
|
+
this.__cache.set(geometry, micron_geometry);
|
|
150
|
+
}
|
|
149
151
|
}
|
|
150
152
|
|
|
151
153
|
|
|
@@ -2,17 +2,6 @@ import { EnginePlugin } from "../../../plugin/EnginePlugin.js";
|
|
|
2
2
|
import { VGThreeRenderer } from "../render/v1/VGThreeRenderer.js";
|
|
3
3
|
import { RenderLayer } from "../../render/layers/RenderLayer.js";
|
|
4
4
|
import { RenderPassType } from "../../render/RenderPassType.js";
|
|
5
|
-
import { Cache } from "../../../../core/cache/Cache.js";
|
|
6
|
-
import { computeGeometryHash } from "../../geometry/buffered/computeGeometryHash.js";
|
|
7
|
-
import { computeGeometryEquality } from "../../geometry/buffered/computeGeometryEquality.js";
|
|
8
|
-
import { CachedAsyncMap } from "../../../../core/collection/map/CachedAsyncMap.js";
|
|
9
|
-
import { AsyncRemoteHashMap } from "../../../../core/collection/map/AsyncRemoteHashMap.js";
|
|
10
|
-
import { BufferGeometrySerializationAdapter } from "./serialization/BufferGeometrySerializationAdapter.js";
|
|
11
|
-
import {
|
|
12
|
-
MicronGeometryBinarySerializationAdapter
|
|
13
|
-
} from "../format/serialization/MicronGeometryBinarySerializationAdapter.js";
|
|
14
|
-
import { AsyncMapWrapper } from "../../../../core/collection/map/AsyncMapWrapper.js";
|
|
15
|
-
import { MicronGeometry } from "../format/MicronGeometry.js";
|
|
16
5
|
import { ArrayBufferLoader } from "../../../asset/loaders/ArrayBufferLoader.js";
|
|
17
6
|
import { ASSET_TYPE_ARRAY_BUFFER, GLTFAssetTransformer } from "./GLTFAssetTransformer.js";
|
|
18
7
|
import { ShadedGeometrySystem } from "../../ecs/mesh-v2/ShadedGeometrySystem.js";
|
|
@@ -139,6 +139,10 @@ export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
|
|
|
139
139
|
|
|
140
140
|
scratch_mvc.material = sg.material;
|
|
141
141
|
scratch_mvc.spec = micron_geometry.vertex_spec;
|
|
142
|
+
|
|
143
|
+
// clear all flags
|
|
144
|
+
scratch_mvc.flags = 0;
|
|
145
|
+
|
|
142
146
|
scratch_mvc.writeFlag(MaterialVertexSpecFlags.CastShadow, sg.getFlag(ShadedGeometryFlags.CastShadow));
|
|
143
147
|
scratch_mvc.writeFlag(MaterialVertexSpecFlags.ReceiveShadow, sg.getFlag(ShadedGeometryFlags.ReceiveShadow));
|
|
144
148
|
|
|
@@ -175,6 +179,7 @@ export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
|
|
|
175
179
|
// clear context
|
|
176
180
|
const material_contexts = this.ctx.contexts_array;
|
|
177
181
|
const contexts_count = material_contexts.length;
|
|
182
|
+
|
|
178
183
|
for (let i = 0; i < contexts_count; i++) {
|
|
179
184
|
const mc = material_contexts[i];
|
|
180
185
|
|
|
@@ -32,6 +32,12 @@ const EXPOSED_ATTRIBUTES = [
|
|
|
32
32
|
"blending"
|
|
33
33
|
];
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Maximum allowed number of particles per emitter, used as a sanity constraint to prevent renderer from crashing
|
|
37
|
+
* @type {number}
|
|
38
|
+
*/
|
|
39
|
+
const LIMIT_PARTICLE_COUNT = 1000000;
|
|
40
|
+
|
|
35
41
|
export class ParticleVolume {
|
|
36
42
|
|
|
37
43
|
constructor() {
|
|
@@ -436,8 +442,13 @@ export class ParticleVolume {
|
|
|
436
442
|
const volume_value = this.__shape.volume;
|
|
437
443
|
|
|
438
444
|
// total particle count
|
|
439
|
-
|
|
445
|
+
let particle_count = Math.max(1, Math.ceil(volume_value * this.__density));
|
|
440
446
|
|
|
447
|
+
if (particle_count > LIMIT_PARTICLE_COUNT) {
|
|
448
|
+
console.warn(`ParticleVolume exceeds maximum allowed particle count (limit=${LIMIT_PARTICLE_COUNT}, requested=${particle_count}), clamping to limit. Consider reducing density (current = ${this.__density} particles per unit of volume)`);
|
|
449
|
+
|
|
450
|
+
particle_count = LIMIT_PARTICLE_COUNT;
|
|
451
|
+
}
|
|
441
452
|
|
|
442
453
|
this.__ensure_initialized();
|
|
443
454
|
|
|
@@ -158,3 +158,24 @@ test("getPosition on a path with 1 point", () => {
|
|
|
158
158
|
|
|
159
159
|
expect(v.toJSON()).toEqual({ x: 1, y: 3, z: 7 });
|
|
160
160
|
});
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
test("sample_catmull_rom with very small offsets",()=>{
|
|
164
|
+
const path = Path.fromJSON({
|
|
165
|
+
points:[3, 1, 1, 3, 1, 3, 7, 1, 3, 7, 1, 7, 3, 1, 7, 3, 1, 8]
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const v = new Vector3();
|
|
169
|
+
|
|
170
|
+
path.sample_catmull_rom(v,0);
|
|
171
|
+
|
|
172
|
+
expect(v.x).not.toBeNaN();
|
|
173
|
+
expect(v.y).not.toBeNaN();
|
|
174
|
+
expect(v.z).not.toBeNaN();
|
|
175
|
+
|
|
176
|
+
path.sample_catmull_rom(v,0.00005900000113712167);
|
|
177
|
+
|
|
178
|
+
expect(v.x).not.toBeNaN();
|
|
179
|
+
expect(v.y).not.toBeNaN();
|
|
180
|
+
expect(v.z).not.toBeNaN();
|
|
181
|
+
});
|
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.43.
|
|
8
|
+
"version": "2.43.11",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"gl-matrix": "3.4.3",
|
|
11
11
|
"fast-levenshtein": "2.0.6",
|
|
@@ -15,6 +15,9 @@ import { TerrainLayer } from "../../engine/ecs/terrain/ecs/layers/TerrainLayer.j
|
|
|
15
15
|
import Mesh from "../../engine/graphics/ecs/mesh/Mesh.js";
|
|
16
16
|
import { Transform } from "../../engine/ecs/transform/Transform.js";
|
|
17
17
|
import { MeshSystem } from "../../engine/graphics/ecs/mesh/MeshSystem.js";
|
|
18
|
+
import { downloadAsFile } from "../../core/binary/ByteArrayTools.js";
|
|
19
|
+
import { url_to_data_url } from "../../core/binary/url_to_data_url.js";
|
|
20
|
+
import ButtonView from "../../view/elements/button/ButtonView.js";
|
|
18
21
|
|
|
19
22
|
const HEIGHT_RANGE = 64;
|
|
20
23
|
|
|
@@ -149,6 +152,36 @@ async function main(engine) {
|
|
|
149
152
|
load_gltf("moicon/23_Nov_21_Skogplanter/02/model.gltf", engine, transform);
|
|
150
153
|
// load_gltf("moicon/23_Nov_21_Skogplanter/03/model.gltf", engine, transform);
|
|
151
154
|
// load_gltf("moicon/23_Nov_21_Skogplanter/04/model.gltf", engine, transform);
|
|
155
|
+
|
|
156
|
+
engine.gui.view.addChild(new ButtonView({
|
|
157
|
+
name: 'Download',
|
|
158
|
+
async action() {
|
|
159
|
+
for (const layer of terrain.layers.layers.data) {
|
|
160
|
+
// embed textures
|
|
161
|
+
layer.textureDiffuseURL = await url_to_data_url(layer.textureDiffuseURL);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// we wrap the terrain data into "component json" format that meep editor recognizes to make this compatible with the standalone terrain editor
|
|
165
|
+
const json_payload = {
|
|
166
|
+
type: "Terrain",
|
|
167
|
+
data: terrain.toJSON()
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
downloadAsFile(
|
|
171
|
+
JSON.stringify(json_payload),
|
|
172
|
+
'terrain.json'
|
|
173
|
+
);
|
|
174
|
+
},
|
|
175
|
+
css: {
|
|
176
|
+
bottom: '4px',
|
|
177
|
+
left: '4px',
|
|
178
|
+
position: 'absolute',
|
|
179
|
+
background: 'white',
|
|
180
|
+
border: '1px solid black',
|
|
181
|
+
padding: '2px',
|
|
182
|
+
pointerEvents: 'auto'
|
|
183
|
+
}
|
|
184
|
+
}));
|
|
152
185
|
}
|
|
153
186
|
|
|
154
187
|
/**
|