@woosh/meep-engine 2.34.1 → 2.34.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/geom/3d/topology/TopoVertex.spec.js +6 -0
- package/core/model/object/objectDeepEquals.spec.js +1 -1
- package/editor/Editor.js +2 -2
- package/engine/EngineHarness.js +0 -1
- package/engine/asset/loaders/image/ImageDecoderWorker.js +1 -1
- package/engine/asset/loaders/image/ImageRGBADataLoader.js +10 -4
- package/engine/asset/loaders/image/codec/NativeImageDecoder.js +3 -1
- package/engine/asset/loaders/image/codec/ThreadedImageDecoder.js +2 -1
- package/engine/asset/loaders/image/png/PNG.js +1 -2
- package/engine/ecs/storage/BinaryBufferSerializer.js +2 -2
- package/engine/ecs/terrain/ecs/layers/TerrainLayers.js +9 -0
- package/engine/ecs/terrain/ecs/splat/SplatMapping.js +33 -0
- package/engine/graphics/geometry/VertexDataSpec.js +20 -0
- package/engine/graphics/micron/format/MicronGeometry.d.ts +6 -0
- package/engine/graphics/micron/format/MicronGeometry.js +156 -4
- package/package.json +1 -1
- package/samples/terrain/editor.js +350 -0
- package/view/controller/controls/NativeListController.js +6 -3
- package/engine/graphics/texture/sampler/debug/paintSamplerOnHTML.js +0 -32
|
@@ -88,9 +88,15 @@ test('replaceAnotherVertex', () => {
|
|
|
88
88
|
v_b.addEdge(e_b);
|
|
89
89
|
|
|
90
90
|
f_a.setVertexAt(0, v_a);
|
|
91
|
+
f_a.setVertexAt(1, new TopoVertex());
|
|
92
|
+
f_a.setVertexAt(2, new TopoVertex());
|
|
93
|
+
|
|
91
94
|
e_a.v0 = v_a;
|
|
92
95
|
|
|
93
96
|
f_b.setVertexAt(0, v_b);
|
|
97
|
+
f_b.setVertexAt(1, new TopoVertex());
|
|
98
|
+
f_b.setVertexAt(2, new TopoVertex());
|
|
99
|
+
|
|
94
100
|
e_b.v0 = v_b;
|
|
95
101
|
|
|
96
102
|
// verify assumptions about topology
|
package/editor/Editor.js
CHANGED
|
@@ -25,7 +25,7 @@ import TopDownCameraController from "../engine/graphics/ecs/camera/topdown/TopDo
|
|
|
25
25
|
import { Transform } from "../engine/ecs/transform/Transform.js";
|
|
26
26
|
import TopDownCameraControllerSystem, { setCameraControllerFromTransform } from "../engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js";
|
|
27
27
|
import { downloadAsFile } from "../core/binary/ByteArrayTools.js";
|
|
28
|
-
import
|
|
28
|
+
import BinaryBufferSerializer from "../engine/ecs/storage/BinaryBufferSerializer.js";
|
|
29
29
|
import { EncodingBinaryBuffer } from "../core/binary/EncodingBinaryBuffer.js";
|
|
30
30
|
import { EntityComponentDataset } from "../engine/ecs/EntityComponentDataset.js";
|
|
31
31
|
import InputControllerSystem from "../engine/input/ecs/systems/InputControllerSystem.js";
|
|
@@ -383,7 +383,7 @@ function Editor() {
|
|
|
383
383
|
// Set music tracks back to time=0
|
|
384
384
|
resetSoundEmitterTracks(dataset);
|
|
385
385
|
|
|
386
|
-
const serializer = new
|
|
386
|
+
const serializer = new BinaryBufferSerializer();
|
|
387
387
|
serializer.registry = engine.binarySerializationRegistry;
|
|
388
388
|
|
|
389
389
|
const state = new EncodingBinaryBuffer();
|
package/engine/EngineHarness.js
CHANGED
|
@@ -41,6 +41,12 @@ export class ImageRGBADataLoader extends AssetLoader {
|
|
|
41
41
|
return await this.decoder.decode(new Uint8Array(array_buffer));
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
*
|
|
46
|
+
* @param {String} path
|
|
47
|
+
* @return {Promise<ImageRGBADataAsset>}
|
|
48
|
+
* @private
|
|
49
|
+
*/
|
|
44
50
|
async __standard(path) {
|
|
45
51
|
|
|
46
52
|
const bitmap = await this.__decode_via_worker(path);
|
|
@@ -48,19 +54,19 @@ export class ImageRGBADataLoader extends AssetLoader {
|
|
|
48
54
|
|
|
49
55
|
let data;
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
case 8:
|
|
57
|
+
const bitDepth = bitmap.bitDepth;
|
|
53
58
|
|
|
59
|
+
switch (bitDepth) {
|
|
60
|
+
case 8:
|
|
54
61
|
data = new Uint8Array(bitmap.data);
|
|
55
62
|
break
|
|
56
63
|
case 16:
|
|
57
64
|
data = new Uint16Array(bitmap.data);
|
|
58
65
|
break;
|
|
59
66
|
default:
|
|
60
|
-
throw new Error(`Unsupported bit depth '${
|
|
67
|
+
throw new Error(`Unsupported bit depth '${bitDepth}'. path='${path}'`);
|
|
61
68
|
}
|
|
62
69
|
|
|
63
|
-
|
|
64
70
|
return new ImageRGBADataAsset(data, bitmap.width, bitmap.height, bitmap.itemSize);
|
|
65
71
|
}
|
|
66
72
|
|
|
@@ -2,7 +2,7 @@ import { Codec } from "./Codec.js";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
*
|
|
5
|
-
* @param {Image|ImageBitmap} img
|
|
5
|
+
* @param {Image|ImageBitmap|HTMLImageElement} img
|
|
6
6
|
* @returns {Uint8Array}
|
|
7
7
|
*/
|
|
8
8
|
function decode(img) {
|
|
@@ -28,10 +28,12 @@ export class NativeImageDecoder extends Codec {
|
|
|
28
28
|
async decode(data) {
|
|
29
29
|
const image = new Image();
|
|
30
30
|
|
|
31
|
+
// convert binary data to image URL that we can load
|
|
31
32
|
const url = URL.createObjectURL(new Blob([data]));
|
|
32
33
|
|
|
33
34
|
image.src = url;
|
|
34
35
|
|
|
36
|
+
// give browser a chance to decode image in async
|
|
35
37
|
await image.decode();
|
|
36
38
|
|
|
37
39
|
const rgba_data = decode(image);
|
|
@@ -48,7 +48,7 @@ function isEntityTransient(entity, dataset, smComponentIndex, componentInstance)
|
|
|
48
48
|
return isTransient;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
class
|
|
51
|
+
class BinaryBufferSerializer {
|
|
52
52
|
constructor() {
|
|
53
53
|
/**
|
|
54
54
|
*
|
|
@@ -171,4 +171,4 @@ class BinaryBufferSerialization {
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
export default
|
|
174
|
+
export default BinaryBufferSerializer;
|
|
@@ -267,6 +267,15 @@ export class TerrainLayers {
|
|
|
267
267
|
return index;
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
+
/**
|
|
271
|
+
*
|
|
272
|
+
* @param {TerrainLayer} layer
|
|
273
|
+
* @return {boolean}
|
|
274
|
+
*/
|
|
275
|
+
removeLayer(layer) {
|
|
276
|
+
return this.layers.removeOneOf(layer);
|
|
277
|
+
}
|
|
278
|
+
|
|
270
279
|
/**
|
|
271
280
|
*
|
|
272
281
|
* @param {TerrainLayer} layer
|
|
@@ -159,6 +159,39 @@ export class SplatMapping {
|
|
|
159
159
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
/**
|
|
163
|
+
*
|
|
164
|
+
* @param {Sampler2D} destination
|
|
165
|
+
* @param {number} index
|
|
166
|
+
* @param {number} channel
|
|
167
|
+
*/
|
|
168
|
+
readLayerToSampler(destination, index, channel = 0) {
|
|
169
|
+
const weightData = this.weightData;
|
|
170
|
+
|
|
171
|
+
const width = this.size.x;
|
|
172
|
+
const height = this.size.y;
|
|
173
|
+
|
|
174
|
+
assert.equal(destination.width, width, 'destination dimensions mismatch');
|
|
175
|
+
assert.equal(destination.height, height, 'destination dimensions mismatch');
|
|
176
|
+
|
|
177
|
+
const dataOffset = index * width * height;
|
|
178
|
+
|
|
179
|
+
for (let y = 0; y < height; y++) {
|
|
180
|
+
|
|
181
|
+
for (let x = 0; x < width; x++) {
|
|
182
|
+
|
|
183
|
+
const targetIndex = y * width + x;
|
|
184
|
+
const targetAddress = dataOffset + targetIndex;
|
|
185
|
+
|
|
186
|
+
const value = weightData[targetAddress];
|
|
187
|
+
|
|
188
|
+
destination.writeChannel(x, y, channel, value);
|
|
189
|
+
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
}
|
|
194
|
+
|
|
162
195
|
/**
|
|
163
196
|
*
|
|
164
197
|
* @param {Uint8Array} source
|
|
@@ -41,6 +41,26 @@ export class VertexDataSpec {
|
|
|
41
41
|
return undefined;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* @returns {number} -1 if not found
|
|
46
|
+
* @param {string} name
|
|
47
|
+
*/
|
|
48
|
+
getAttributeIndexByName(name) {
|
|
49
|
+
|
|
50
|
+
const attributes = this.attributes;
|
|
51
|
+
const n = attributes.length;
|
|
52
|
+
|
|
53
|
+
for (let i = 0; i < n; i++) {
|
|
54
|
+
const attribute = attributes[i];
|
|
55
|
+
|
|
56
|
+
if (attribute.name === name) {
|
|
57
|
+
return i;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
44
64
|
/**
|
|
45
65
|
*
|
|
46
66
|
* @param {AttributeSpec} attribute
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import {BufferGeometry} from "three";
|
|
2
2
|
import {VertexDataSpec} from "../../geometry/VertexDataSpec";
|
|
3
|
+
import {SurfacePoint3} from "../../../../core/geom/3d/SurfacePoint3";
|
|
3
4
|
|
|
4
5
|
export class MicronGeometry {
|
|
5
6
|
vertex_spec: VertexDataSpec
|
|
6
7
|
public proxy: BufferGeometry
|
|
7
8
|
|
|
8
9
|
initialize(): void
|
|
10
|
+
|
|
11
|
+
raycast(
|
|
12
|
+
origin_x: number, origin_y: number, origin_z: number,
|
|
13
|
+
direction_x: number, direction_y: number, direction_z: number
|
|
14
|
+
): SurfacePoint3[]
|
|
9
15
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { VertexDataSpec } from "../../geometry/VertexDataSpec.js";
|
|
2
2
|
import { assert } from "../../../../core/assert.js";
|
|
3
|
+
import { intersectRay } from "../../../../core/bvh2/aabb3/AABB3Math.js";
|
|
4
|
+
import { computeTriangleRayIntersection } from "../../../../core/geom/3d/triangle/computeTriangleRayIntersection.js";
|
|
5
|
+
import { SurfacePoint3 } from "../../../../core/geom/3d/SurfacePoint3.js";
|
|
3
6
|
|
|
4
7
|
let id_counter = 0;
|
|
5
8
|
|
|
@@ -55,6 +58,151 @@ export class MicronGeometry {
|
|
|
55
58
|
this.__proxy = null;
|
|
56
59
|
}
|
|
57
60
|
|
|
61
|
+
/**
|
|
62
|
+
*
|
|
63
|
+
* @param {number} origin_x
|
|
64
|
+
* @param {number} origin_y
|
|
65
|
+
* @param {number} origin_z
|
|
66
|
+
* @param {number} direction_x
|
|
67
|
+
* @param {number} direction_y
|
|
68
|
+
* @param {number} direction_z
|
|
69
|
+
* @returns {SurfacePoint3[]}
|
|
70
|
+
*/
|
|
71
|
+
raycast(
|
|
72
|
+
origin_x, origin_y, origin_z,
|
|
73
|
+
direction_x, direction_y, direction_z
|
|
74
|
+
) {
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
*
|
|
78
|
+
* @type {SurfacePoint3[]}
|
|
79
|
+
*/
|
|
80
|
+
const hits = [];
|
|
81
|
+
|
|
82
|
+
let stack_cursor = 1;
|
|
83
|
+
const stack = [this.root];
|
|
84
|
+
|
|
85
|
+
const spec = this.vertex_spec;
|
|
86
|
+
|
|
87
|
+
const position_attribute_index = spec.getAttributeIndexByName('position');
|
|
88
|
+
|
|
89
|
+
// check that position attribute actually exists
|
|
90
|
+
assert.isNonNegativeInteger(position_attribute_index, 'position_attribute_index');
|
|
91
|
+
|
|
92
|
+
const temp_hit = new SurfacePoint3();
|
|
93
|
+
|
|
94
|
+
while (stack_cursor > 0) {
|
|
95
|
+
stack_cursor--;
|
|
96
|
+
|
|
97
|
+
const patch = stack[stack_cursor];
|
|
98
|
+
|
|
99
|
+
// using group bounding box because it's conservative, as we want to be able to hit LOD_0
|
|
100
|
+
const bb = patch.group_bounding_box;
|
|
101
|
+
|
|
102
|
+
const bb_intersection_exists = intersectRay(
|
|
103
|
+
bb[0], bb[1], bb[2], bb[3], bb[4], bb[5],
|
|
104
|
+
origin_x, origin_y, origin_z,
|
|
105
|
+
direction_x, direction_y, direction_z
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
if (!bb_intersection_exists) {
|
|
109
|
+
// no intersection
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (patch.lod === 0) {
|
|
114
|
+
// LOD 0 - source geometry
|
|
115
|
+
|
|
116
|
+
// raycast against geometry
|
|
117
|
+
const indices = patch.face_indices;
|
|
118
|
+
const index_count = indices.length;
|
|
119
|
+
|
|
120
|
+
const vertices = patch.vertex_attribute_arrays[position_attribute_index];
|
|
121
|
+
|
|
122
|
+
for (let i = 0; i < index_count;) {
|
|
123
|
+
const a = indices[i++];
|
|
124
|
+
const b = indices[i++];
|
|
125
|
+
const c = indices[i++];
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
const a_address = a * 3;
|
|
129
|
+
const b_address = b * 3;
|
|
130
|
+
const c_address = c * 3;
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
const ax = vertices[a_address];
|
|
134
|
+
const ay = vertices[a_address + 1];
|
|
135
|
+
const az = vertices[a_address + 2];
|
|
136
|
+
|
|
137
|
+
const bx = vertices[b_address];
|
|
138
|
+
const by = vertices[b_address + 1];
|
|
139
|
+
const bz = vertices[b_address + 2];
|
|
140
|
+
|
|
141
|
+
const cx = vertices[c_address];
|
|
142
|
+
const cy = vertices[c_address + 1];
|
|
143
|
+
const cz = vertices[c_address + 2];
|
|
144
|
+
|
|
145
|
+
if (computeTriangleRayIntersection(
|
|
146
|
+
temp_hit,
|
|
147
|
+
origin_x, origin_y, origin_z,
|
|
148
|
+
direction_x, direction_y, direction_z,
|
|
149
|
+
ax, ay, az,
|
|
150
|
+
bx, by, bz,
|
|
151
|
+
cx, cy, cz
|
|
152
|
+
)) {
|
|
153
|
+
|
|
154
|
+
const contact = new SurfacePoint3();
|
|
155
|
+
|
|
156
|
+
contact.copy(temp_hit);
|
|
157
|
+
|
|
158
|
+
hits.push(contact);
|
|
159
|
+
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
} else {
|
|
164
|
+
// refine more
|
|
165
|
+
const children = patch.children;
|
|
166
|
+
|
|
167
|
+
const child_count = children.length;
|
|
168
|
+
|
|
169
|
+
for (let i = 0; i < child_count; i++) {
|
|
170
|
+
const child = children[i];
|
|
171
|
+
|
|
172
|
+
// push to stack
|
|
173
|
+
stack[stack_cursor] = child;
|
|
174
|
+
|
|
175
|
+
stack_cursor++;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return hits;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
*
|
|
185
|
+
* @param {number} lod
|
|
186
|
+
* @returns {number}
|
|
187
|
+
*/
|
|
188
|
+
computeLodFaceCount(lod) {
|
|
189
|
+
let r = 0;
|
|
190
|
+
|
|
191
|
+
const patches = this.patches;
|
|
192
|
+
const patch_count = patches.length;
|
|
193
|
+
for (let i = 0; i < patch_count; i++) {
|
|
194
|
+
const patch = patches[i];
|
|
195
|
+
|
|
196
|
+
if (patch.lod !== lod) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
r += patch.polygon_count;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return r;
|
|
204
|
+
}
|
|
205
|
+
|
|
58
206
|
get patches() {
|
|
59
207
|
return this.__patches;
|
|
60
208
|
}
|
|
@@ -84,17 +232,21 @@ export class MicronGeometry {
|
|
|
84
232
|
|
|
85
233
|
/**
|
|
86
234
|
* Build geometry proxy from the patches
|
|
87
|
-
* @param {number}
|
|
235
|
+
* @param {number} fraction percentage of the original geometry to keep
|
|
236
|
+
* @param {number} min minimum number of triangles to keep, this will be respected first before percentage value
|
|
88
237
|
*/
|
|
89
|
-
buildProxy(
|
|
238
|
+
buildProxy(fraction, min) {
|
|
90
239
|
throw new Error('Not Implemented');
|
|
91
240
|
}
|
|
92
241
|
|
|
93
242
|
computeByteSize() {
|
|
94
243
|
let result = 0;
|
|
95
244
|
|
|
96
|
-
|
|
97
|
-
|
|
245
|
+
const patches = this.__patches;
|
|
246
|
+
const patch_count = patches.length;
|
|
247
|
+
|
|
248
|
+
for (let i = 0; i < patch_count; i++) {
|
|
249
|
+
result += patches[i].computeByteSize();
|
|
98
250
|
}
|
|
99
251
|
|
|
100
252
|
return result;
|
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.34.
|
|
8
|
+
"version": "2.34.5",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"gl-matrix": "3.4.3",
|
|
11
11
|
"fast-levenshtein": "2.0.6",
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { EngineHarness } from "../../engine/EngineHarness.js";
|
|
2
|
+
import EmptyView from "../../view/elements/EmptyView.js";
|
|
3
|
+
import { EngineConfiguration } from "../../engine/EngineConfiguration.js";
|
|
4
|
+
import { buildCubeURLs } from "../../engine/graphics/texture/cubemap/buildCubeURLs.js";
|
|
5
|
+
import { ArrayBufferLoader } from "../../engine/asset/loaders/ArrayBufferLoader.js";
|
|
6
|
+
import { TerrainFlags } from "../../engine/ecs/terrain/ecs/TerrainFlags.js";
|
|
7
|
+
import Signal from "../../core/events/signal/Signal.js";
|
|
8
|
+
import { NativeListController } from "../../view/controller/controls/NativeListController.js";
|
|
9
|
+
import { TerrainLayer } from "../../engine/ecs/terrain/ecs/layers/TerrainLayer.js";
|
|
10
|
+
import CheckersTextureURI from "../../engine/graphics/texture/CheckersTextureURI.js";
|
|
11
|
+
import { obtainTerrain } from "../../engine/ecs/terrain/util/obtainTerrain.js";
|
|
12
|
+
import { Cache } from "../../core/cache/Cache.js";
|
|
13
|
+
import { computeStringHash } from "../../core/primitives/strings/computeStringHash.js";
|
|
14
|
+
import { noop, strictEquals } from "../../core/function/Functions.js";
|
|
15
|
+
import { scaleSampler2D } from "../../engine/graphics/texture/sampler/scaleSampler2D.js";
|
|
16
|
+
import { Sampler2D } from "../../engine/graphics/texture/sampler/Sampler2D.js";
|
|
17
|
+
import { DataType } from "../../core/collection/table/DataType.js";
|
|
18
|
+
import { compute_typed_array_constructor_from_data_type } from "../../core/collection/table/DataType2TypedArrayConstructorMapping.js";
|
|
19
|
+
import { copy_Sampler2D_channel_data } from "../../engine/graphics/texture/sampler/copy_Sampler2D_channel_data.js";
|
|
20
|
+
import { TextureAssetLoader } from "../../engine/asset/loaders/texture/TextureAssetLoader.js";
|
|
21
|
+
import { CanvasView } from "../../view/elements/CanvasView.js";
|
|
22
|
+
import convertSampler2D2Canvas from "../../engine/graphics/texture/sampler/Sampler2D2Canvas.js";
|
|
23
|
+
|
|
24
|
+
const engineHarness = new EngineHarness();
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
*
|
|
28
|
+
* @param {Engine} engine
|
|
29
|
+
*/
|
|
30
|
+
function makeConfig(engine) {
|
|
31
|
+
|
|
32
|
+
const config = new EngineConfiguration();
|
|
33
|
+
|
|
34
|
+
config.addLoader('arraybuffer', new ArrayBufferLoader());
|
|
35
|
+
config.addLoader('texture', new TextureAssetLoader());
|
|
36
|
+
|
|
37
|
+
return config;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class EditorTextureSlot {
|
|
41
|
+
constructor() {
|
|
42
|
+
this.onChanged = new Signal();
|
|
43
|
+
|
|
44
|
+
this.sampler = null;
|
|
45
|
+
|
|
46
|
+
this.__read = noop;
|
|
47
|
+
this.__write = noop;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
*
|
|
51
|
+
* @type {number}
|
|
52
|
+
*/
|
|
53
|
+
this.itemSize = 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
read() {
|
|
57
|
+
this.__read(this.sampler);
|
|
58
|
+
|
|
59
|
+
this.onChanged.send0();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
write() {
|
|
63
|
+
this.__write(this.sampler);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
*
|
|
68
|
+
* @param {(Sampler2D)=>any} read
|
|
69
|
+
* @param {(Sampler2D)=>any} write
|
|
70
|
+
* @param {number} [itemSize]
|
|
71
|
+
* @param {DataType} [dataType]
|
|
72
|
+
* @return {EditorTextureSlot}
|
|
73
|
+
*/
|
|
74
|
+
static from({ read, write, itemSize = 1, dataType = DataType.Uint8 }) {
|
|
75
|
+
const r = new EditorTextureSlot();
|
|
76
|
+
|
|
77
|
+
r.__read = read;
|
|
78
|
+
r.__write = write;
|
|
79
|
+
|
|
80
|
+
const TA = compute_typed_array_constructor_from_data_type(dataType);
|
|
81
|
+
|
|
82
|
+
r.sampler = new Sampler2D(new TA(itemSize), itemSize, 1, 1);
|
|
83
|
+
|
|
84
|
+
return r;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
class TextureLayerEditor {
|
|
89
|
+
constructor() {
|
|
90
|
+
this.index = 0;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
*
|
|
94
|
+
* @type {Terrain|null}
|
|
95
|
+
* @private
|
|
96
|
+
*/
|
|
97
|
+
this.__terrain = null;
|
|
98
|
+
|
|
99
|
+
this.__splat = EditorTextureSlot.from({
|
|
100
|
+
read: (target) => {
|
|
101
|
+
const terrain = this.__terrain;
|
|
102
|
+
|
|
103
|
+
target.resize(terrain.splat.size.x, terrain.splat.size.y)
|
|
104
|
+
|
|
105
|
+
terrain.splat.readLayerToSampler(target, this.index, 0);
|
|
106
|
+
},
|
|
107
|
+
write: (source) => {
|
|
108
|
+
this.__terrain.splat.writeLayerFromSampler(source, this.index, 0);
|
|
109
|
+
},
|
|
110
|
+
itemSize: 1
|
|
111
|
+
});
|
|
112
|
+
this.__diffuse = EditorTextureSlot.from({
|
|
113
|
+
read: (target) => {
|
|
114
|
+
|
|
115
|
+
const layers = this.__terrain.layers;
|
|
116
|
+
|
|
117
|
+
const layer = layers.get(this.index);
|
|
118
|
+
|
|
119
|
+
target.resize(layer.diffuse.width, layer.diffuse.height);
|
|
120
|
+
copy_Sampler2D_channel_data(layer.diffuse, target);
|
|
121
|
+
},
|
|
122
|
+
write: (source) => {
|
|
123
|
+
const layer = this.__terrain.layers.get(this.index);
|
|
124
|
+
|
|
125
|
+
copy_Sampler2D_channel_data(source, layer.diffuse);
|
|
126
|
+
|
|
127
|
+
this.__terrain.layers.writeLayerDataIntoTexture(this.index);
|
|
128
|
+
},
|
|
129
|
+
itemSize: 3
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @param {Terrain} t
|
|
135
|
+
*/
|
|
136
|
+
set terrain(t) {
|
|
137
|
+
this.__terrain = t;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
init() {
|
|
141
|
+
this.__splat.read();
|
|
142
|
+
this.__diffuse.read();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
*
|
|
148
|
+
* @param {EditorTextureSlot} slot
|
|
149
|
+
*/
|
|
150
|
+
function makeTextureSlotView({ slot }) {
|
|
151
|
+
|
|
152
|
+
const preview = new CanvasView();
|
|
153
|
+
preview.size.set(64, 64);
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
function update() {
|
|
157
|
+
convertSampler2D2Canvas(slot.sampler, 1, 0, preview.el);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
preview.on.linked.add(update);
|
|
161
|
+
preview.bindSignal(slot.onChanged, update);
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
return preview;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
*
|
|
169
|
+
* @param {View} root
|
|
170
|
+
* @param {Terrain} terrain
|
|
171
|
+
* @param {Engine} engine
|
|
172
|
+
*/
|
|
173
|
+
function makeTerrainEditor(root, terrain, engine) {
|
|
174
|
+
/**
|
|
175
|
+
*
|
|
176
|
+
* @type {Cache<string, Sampler2D>}
|
|
177
|
+
*/
|
|
178
|
+
const PREVIEW_CACHE = new Cache({
|
|
179
|
+
keyHashFunction: computeStringHash,
|
|
180
|
+
keyEqualityFunction: strictEquals
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const PREVIEW_SIZE = 64;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
*
|
|
187
|
+
* @param {string} url
|
|
188
|
+
* @return {Promise<Sampler2D>}
|
|
189
|
+
*/
|
|
190
|
+
async function getPreviewFromURL(url) {
|
|
191
|
+
if (PREVIEW_CACHE.contains(url)) {
|
|
192
|
+
return PREVIEW_CACHE.get(url);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const asset = await engine.assetManager.promise(url, 'image');
|
|
196
|
+
|
|
197
|
+
// check cache again, might have been loaded in the meanwhile
|
|
198
|
+
if (PREVIEW_CACHE.contains(url)) {
|
|
199
|
+
return PREVIEW_CACHE.get(url);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const source = asset.create();
|
|
203
|
+
|
|
204
|
+
const preview = Sampler2D.uint8(4, PREVIEW_SIZE, PREVIEW_SIZE);
|
|
205
|
+
|
|
206
|
+
scaleSampler2D(source, preview);
|
|
207
|
+
|
|
208
|
+
PREVIEW_CACHE.put(url, preview);
|
|
209
|
+
|
|
210
|
+
return preview;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
*
|
|
215
|
+
* @type {EditorTextureSlot|null}
|
|
216
|
+
*/
|
|
217
|
+
let active_slot = null;
|
|
218
|
+
|
|
219
|
+
function rebuild() {
|
|
220
|
+
// rebuild terrain
|
|
221
|
+
terrain.clearFlag(TerrainFlags.Built);
|
|
222
|
+
terrain.build(engine.assetManager);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function build_splat_editor() {
|
|
226
|
+
/**
|
|
227
|
+
*
|
|
228
|
+
* @type {Map<TerrainLayer, TextureLayerEditor>}
|
|
229
|
+
*/
|
|
230
|
+
const editors = new Map();
|
|
231
|
+
|
|
232
|
+
return new NativeListController({
|
|
233
|
+
model: terrain.layers.layers,
|
|
234
|
+
elementFactory() {
|
|
235
|
+
return TerrainLayer.from(CheckersTextureURI, 1, 1);
|
|
236
|
+
},
|
|
237
|
+
/**
|
|
238
|
+
*
|
|
239
|
+
* @param {TerrainLayer} layer
|
|
240
|
+
*/
|
|
241
|
+
elementViewFactory(layer) {
|
|
242
|
+
const e = new TextureLayerEditor();
|
|
243
|
+
|
|
244
|
+
editors.set(layer, e);
|
|
245
|
+
|
|
246
|
+
const layer_index = terrain.layers.layers.indexOf(layer);
|
|
247
|
+
|
|
248
|
+
e.terrain = terrain;
|
|
249
|
+
e.index = layer_index;
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
e.init();
|
|
253
|
+
|
|
254
|
+
const view = new EmptyView();
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
view.addChild(makeTextureSlotView({
|
|
258
|
+
slot: e.__diffuse
|
|
259
|
+
}));
|
|
260
|
+
view.addChild(makeTextureSlotView({
|
|
261
|
+
slot: e.__splat
|
|
262
|
+
}));
|
|
263
|
+
|
|
264
|
+
view.bindSignal(layer.onChanged, e.__diffuse.read, e.__diffuse);
|
|
265
|
+
|
|
266
|
+
return view;
|
|
267
|
+
},
|
|
268
|
+
operationAdd(list, el) {
|
|
269
|
+
terrain.layers.addLayer(el);
|
|
270
|
+
terrain.splat.addWeightLayer();
|
|
271
|
+
},
|
|
272
|
+
operationRemove(list, el) {
|
|
273
|
+
const index = terrain.layers.layers.indexOf(el);
|
|
274
|
+
terrain.layers.removeLayer(el);
|
|
275
|
+
terrain.splat.removeWeightLayer(index);
|
|
276
|
+
|
|
277
|
+
editors.delete(el);
|
|
278
|
+
|
|
279
|
+
// re-shuffle remaining editors
|
|
280
|
+
|
|
281
|
+
for (const [layer, editor] of editors) {
|
|
282
|
+
if (editor.index > index) {
|
|
283
|
+
editor.index--;
|
|
284
|
+
editor.init();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
root.addChild(build_splat_editor());
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
*
|
|
296
|
+
* @param {Engine} engine
|
|
297
|
+
* @return {string}
|
|
298
|
+
*/
|
|
299
|
+
async function main(engine) {
|
|
300
|
+
|
|
301
|
+
//disable drag and drop on game canvas
|
|
302
|
+
engine.graphics.domElement.addEventListener('dragover', (e) => e.preventDefault());
|
|
303
|
+
engine.graphics.domElement.addEventListener('drop', (e) => e.preventDefault());
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
EngineHarness.buildLights({ engine });
|
|
307
|
+
const camera = EngineHarness.buildCamera({ engine });
|
|
308
|
+
EngineHarness.buildOrbitalCameraController({ engine, cameraEntity: camera.entity })
|
|
309
|
+
|
|
310
|
+
await EngineHarness.buildTerrain({ engine });
|
|
311
|
+
|
|
312
|
+
const image = await engine.assetManager.promise("diffuseMap1.png", 'image');
|
|
313
|
+
|
|
314
|
+
console.log(image.create());
|
|
315
|
+
|
|
316
|
+
const vMain = new EmptyView({ classList: ['ui-texture-testing-tool-view'] });
|
|
317
|
+
|
|
318
|
+
vMain.css({
|
|
319
|
+
position: "absolute",
|
|
320
|
+
left: "0",
|
|
321
|
+
top: "0",
|
|
322
|
+
pointerEvents: "initial"
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
makeTerrainEditor(vMain, obtainTerrain(engine.entityManager.dataset), engine);
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
document.body.appendChild(vMain.el);
|
|
329
|
+
vMain.link();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
*
|
|
335
|
+
* @param {EngineHarness} harness
|
|
336
|
+
*/
|
|
337
|
+
async function init(harness) {
|
|
338
|
+
|
|
339
|
+
const engine = harness.engine;
|
|
340
|
+
|
|
341
|
+
await makeConfig(engine).apply(engine);
|
|
342
|
+
|
|
343
|
+
await engine.graphics.loadEnvironmentMap(buildCubeURLs('data/textures/cubemaps/hip_miramar/32/', '.png'));
|
|
344
|
+
|
|
345
|
+
await harness.initialize();
|
|
346
|
+
|
|
347
|
+
main(engine);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
init(engineHarness);
|
|
@@ -27,8 +27,8 @@ export class NativeListController extends View {
|
|
|
27
27
|
/**
|
|
28
28
|
* @template T
|
|
29
29
|
* @param {List<T>} model
|
|
30
|
-
* @param {
|
|
31
|
-
* @param {
|
|
30
|
+
* @param {(T)=>View} elementViewFactory
|
|
31
|
+
* @param {()=>T} elementFactory
|
|
32
32
|
* @param {string[]} [classList]
|
|
33
33
|
* @param {function(List<T>, T)} [operationAdd] Allows user to supply a special operation for adding an element to the list
|
|
34
34
|
* @param {function(List<T>, T)} [operationRemove] Allows user to supply a special operation for removing an element from the list
|
|
@@ -43,7 +43,7 @@ export class NativeListController extends View {
|
|
|
43
43
|
}) {
|
|
44
44
|
super();
|
|
45
45
|
|
|
46
|
-
assert.isFunction(elementViewFactory,'elementViewFactory');
|
|
46
|
+
assert.isFunction(elementViewFactory, 'elementViewFactory');
|
|
47
47
|
|
|
48
48
|
this.el = document.createElement('div');
|
|
49
49
|
|
|
@@ -55,6 +55,9 @@ export class NativeListController extends View {
|
|
|
55
55
|
elementFactory(el) {
|
|
56
56
|
const elementView = elementViewFactory(el);
|
|
57
57
|
|
|
58
|
+
assert.defined(elementView, 'elementView');
|
|
59
|
+
assert.equal(elementView.isView, true, 'elementView.isView !== true');
|
|
60
|
+
|
|
58
61
|
const vWrapper = new EmptyView({ classList: ['element-wrapper'] });
|
|
59
62
|
|
|
60
63
|
elementView.addClass('wrapped-element');
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { Sampler2D } from "../Sampler2D.js";
|
|
2
|
-
import { scaleSampler2D } from "../scaleSampler2D.js";
|
|
3
|
-
import convertSampler2D2Canvas from "../Sampler2D2Canvas.js";
|
|
4
|
-
import { typedArrayConstructorByInstance } from "../typedArrayConstructorByInstance.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
*
|
|
8
|
-
* @param {Sampler2D} s
|
|
9
|
-
* @param {number} x
|
|
10
|
-
* @param {number} y
|
|
11
|
-
* @param scale
|
|
12
|
-
* @param offset
|
|
13
|
-
* @param {number} resolution
|
|
14
|
-
*/
|
|
15
|
-
export function paintSamplerOnHTML(s, x, y, scale, offset, resolution=256) {
|
|
16
|
-
|
|
17
|
-
const d = resolution;
|
|
18
|
-
|
|
19
|
-
const TA = typedArrayConstructorByInstance(s.data);
|
|
20
|
-
|
|
21
|
-
const target = new Sampler2D(new TA(d * d * s.itemSize), s.itemSize, d, d);
|
|
22
|
-
|
|
23
|
-
scaleSampler2D(s, target);
|
|
24
|
-
|
|
25
|
-
const c = convertSampler2D2Canvas(target, scale, offset);
|
|
26
|
-
c.style.zIndex = 1000;
|
|
27
|
-
c.style.position = "absolute";
|
|
28
|
-
c.style.left = `${x}px`;
|
|
29
|
-
c.style.top = `${y}px`;
|
|
30
|
-
|
|
31
|
-
document.body.appendChild(c);
|
|
32
|
-
}
|