@woosh/meep-engine 2.39.39 → 2.39.42
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/operations/ceilPowerOfTwo.spec.js +17 -0
- package/core/bvh2/aabb3/AABB3.js +6 -6
- package/core/bvh2/binary/2/BinaryUint32BVH.js +7 -1
- package/core/bvh2/binary/2/BinaryUint32BVH.spec.js +31 -1
- package/core/bvh2/bvh3/bvh_query_user_data_nearest_to_point.js +5 -5
- package/core/bvh2/bvh3/bvh_query_user_data_nearest_to_point.spec.js +70 -0
- package/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.spec.js +2 -2
- package/core/cache/Cache.js +6 -0
- package/core/collection/IteratorUtils.js +1 -0
- package/core/collection/queue/Deque.js +5 -2
- package/core/color/Color.js +22 -4
- package/core/color/hsluv/HSLuv.js +187 -0
- package/core/geom/3d/aabb/aabb3_matrix4_project.js +1 -0
- package/core/geom/3d/topology/simplify/simplifyTopoMesh2.js +1 -1
- package/core/primitives/strings/compareStrings.js +22 -0
- package/editor/Editor.js +2 -0
- package/editor/ecs/component/editors/ecs/terrain/TerrainEditor.js +12 -6
- package/editor/tools/v2/BlenderCameraOrientationGizmo.js +22 -11
- package/engine/EngineHarness.js +7 -0
- package/engine/asset/loaders/geometry/geometry/computeBufferAttributeHash.js +5 -6
- package/engine/asset/loaders/image/codec/Codec.js +9 -0
- package/engine/asset/loaders/image/codec/CodecWithFallback.js +52 -11
- package/engine/asset/loaders/image/codec/ThreadedImageDecoder.js +12 -0
- package/engine/asset/loaders/image/png/PNGReader.js +2 -1
- package/engine/asset/loaders/image/png/PNG_HEADER_BYTES.js +1 -0
- package/engine/computeStridedIntegerArrayHash.js +19 -0
- package/engine/ecs/EntityBuilder.d.ts +2 -0
- package/engine/ecs/gui/GUIElement.d.ts +3 -1
- package/engine/ecs/terrain/ecs/layers/TerrainLayers.js +5 -3
- package/engine/graphics/ecs/light/binding/fp/FPLightBinding.js +11 -2
- package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +2 -8
- package/engine/graphics/geometry/buffered/query/GeometrySpatialQueryAccelerator.d.ts +7 -0
- package/engine/graphics/geometry/buffered/query/GeometrySpatialQueryAccelerator.js +17 -1
- package/engine/graphics/geometry/optimization/merge/merge_geometry_hierarchy.js +336 -0
- package/engine/graphics/geometry/optimization/merge/prototypeGeometryMerge.js +176 -0
- package/engine/graphics/micron/build/hierarchy/buildAbstractPatchHierarchy.js +7 -3
- package/engine/graphics/micron/build/hierarchy/merge_patches.js +0 -1
- package/engine/graphics/micron/prototypeVirtualGeometry.js +3 -3
- package/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.js +32 -1
- package/engine/graphics/render/forward_plus/LightManager.js +58 -13
- package/engine/graphics/render/forward_plus/debug/createScreenGrid.js +192 -9
- package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +42 -1
- package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_PREAMBLE.js +1 -0
- package/engine/graphics/render/forward_plus/materials/ForwardPlusThreeMaterial.js +5 -0
- package/engine/graphics/render/forward_plus/materials/fp_build_fragment_shader.js +1 -4
- package/engine/graphics/render/forward_plus/model/Decal.js +33 -5
- package/engine/graphics/render/forward_plus/model/PointLight.js +1 -1
- package/engine/graphics/render/forward_plus/plugin/MaterialTransformer.js +4 -0
- package/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +480 -12
- package/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_texture.js +49 -29
- package/engine/graphics/texture/atlas/AbstractTextureAtlas.js +11 -0
- package/engine/graphics/texture/atlas/CachingTextureAtlas.js +34 -0
- package/engine/graphics/texture/atlas/ReferencedTextureAtlas.js +9 -1
- package/engine/graphics/texture/atlas/TextureAtlas.js +29 -16
- package/engine/graphics/texture/atlas/TextureAtlasDebugger.js +75 -19
- package/engine/graphics/texture/sampler/Sampler2D.d.ts +2 -0
- package/engine/graphics/texture/sampler/Sampler2D.js +51 -10
- package/engine/sound/ecs/SoundListenerSystem.js +5 -0
- package/engine/sound/ecs/emitter/SoundEmitter.js +12 -1
- package/package.json +1 -1
- package/samples/terrain/from_image_2.js +8 -2
- package/view/tooltip/TooltipManager.js +0 -95
package/engine/EngineHarness.js
CHANGED
|
@@ -27,6 +27,8 @@ import { JsonAssetLoader } from "./asset/loaders/JsonAssetLoader.js";
|
|
|
27
27
|
import { logger } from "./logging/GlobalLogger.js";
|
|
28
28
|
import { ConsoleLoggerBackend } from "./logging/ConsoleLoggerBackend.js";
|
|
29
29
|
import { noop } from "../core/function/Functions.js";
|
|
30
|
+
import SoundListenerSystem from "./sound/ecs/SoundListenerSystem.js";
|
|
31
|
+
import SoundListener from "./sound/ecs/SoundListener.js";
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
34
|
*
|
|
@@ -174,6 +176,10 @@ export class EngineHarness {
|
|
|
174
176
|
em.addSystem(new CameraSystem(engine.graphics.scene, engine.graphics))
|
|
175
177
|
}
|
|
176
178
|
|
|
179
|
+
if (em.getSystem(SoundListenerSystem) === null) {
|
|
180
|
+
em.addSystem(new SoundListenerSystem(engine.sound.context));
|
|
181
|
+
}
|
|
182
|
+
|
|
177
183
|
if (!ecd.isComponentTypeRegistered(Tag)) {
|
|
178
184
|
ecd.registerComponentType(Tag);
|
|
179
185
|
}
|
|
@@ -199,6 +205,7 @@ export class EngineHarness {
|
|
|
199
205
|
.add(transform)
|
|
200
206
|
.add(cameraController)
|
|
201
207
|
.add(camera)
|
|
208
|
+
.add(new SoundListener())
|
|
202
209
|
.add(Tag.fromJSON(["Camera"]))
|
|
203
210
|
.build(ecd);
|
|
204
211
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { computeStridedIntegerArrayHash } from "../../../../computeStridedIntegerArrayHash.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
*
|
|
3
5
|
* @param {THREE.BufferAttribute} attribute
|
|
@@ -15,14 +17,11 @@ export function computeBufferAttributeHash(attribute) {
|
|
|
15
17
|
// compute stride so that we don't have to iterate over the entire buffer, instead picking at most X(509) values to consider
|
|
16
18
|
const stride = Math.max(1, Math.ceil(index_data_size / 509));
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
const value = index_data[i];
|
|
20
|
-
|
|
21
|
-
result = ((result * 31) + value) | 0;
|
|
22
|
-
|
|
23
|
-
}
|
|
20
|
+
result ^= computeStridedIntegerArrayHash(index_data, 0, index_data_size, stride);
|
|
24
21
|
|
|
25
22
|
}
|
|
26
23
|
|
|
27
24
|
return result;
|
|
28
25
|
}
|
|
26
|
+
|
|
27
|
+
|
|
@@ -7,4 +7,13 @@ export class Codec {
|
|
|
7
7
|
async decode(data) {
|
|
8
8
|
throw new Error('Unsupported Operation');
|
|
9
9
|
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Whether codec is able to decode this data, typically checks headers in the data
|
|
13
|
+
* @param {Uint8Array} data
|
|
14
|
+
* @return {Promise<boolean>} true if able, false otherwise
|
|
15
|
+
*/
|
|
16
|
+
async test(data) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
10
19
|
}
|
|
@@ -1,28 +1,69 @@
|
|
|
1
1
|
import { Codec } from "./Codec.js";
|
|
2
|
-
import { logger } from "../../../../logging/GlobalLogger.js";
|
|
3
2
|
|
|
4
3
|
export class CodecWithFallback extends Codec {
|
|
5
4
|
/**
|
|
6
5
|
*
|
|
7
|
-
* @param {Codec}
|
|
8
|
-
* @param {Codec} fallback
|
|
6
|
+
* @param {Codec} codecs
|
|
9
7
|
*/
|
|
10
|
-
constructor(
|
|
8
|
+
constructor(...codecs) {
|
|
11
9
|
super();
|
|
12
10
|
|
|
13
|
-
this.
|
|
14
|
-
|
|
11
|
+
this.children = codecs.slice();
|
|
12
|
+
|
|
13
|
+
if (this.children.length < 1) {
|
|
14
|
+
throw new Error('At least one codec must be provided');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async test(data) {
|
|
19
|
+
const codecs = this.children;
|
|
20
|
+
const codec_count = codecs.length;
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < codec_count; i++) {
|
|
23
|
+
const codec = codecs[i];
|
|
24
|
+
|
|
25
|
+
if (codec.test(data)) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return false;
|
|
15
31
|
}
|
|
16
32
|
|
|
17
33
|
async decode(data) {
|
|
18
34
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
} catch (e) {
|
|
22
|
-
logger.warn(`Main codec failed, will attempt fallback. Error: ${e}`);
|
|
35
|
+
const codecs = this.children;
|
|
36
|
+
const codec_count = codecs.length;
|
|
23
37
|
|
|
24
|
-
|
|
38
|
+
const errors = [];
|
|
39
|
+
|
|
40
|
+
let result;
|
|
41
|
+
|
|
42
|
+
for (let i = 0; i < codec_count; i++) {
|
|
43
|
+
const codec = codecs[i];
|
|
44
|
+
|
|
45
|
+
if (!codec.test(data)) {
|
|
46
|
+
errors.push({
|
|
47
|
+
index: i, error: "Codec doesn't support this data"
|
|
48
|
+
});
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
result = await codec.decode(data);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
|
|
56
|
+
errors.push({
|
|
57
|
+
index: i, error: e
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return result;
|
|
25
64
|
}
|
|
26
65
|
|
|
66
|
+
throw new Error(`All codecs failed, errors: ${errors.map(a => JSON.stringify(a)).join('\n')}`);
|
|
67
|
+
|
|
27
68
|
}
|
|
28
69
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Codec } from "./Codec.js";
|
|
2
2
|
import WorkerBuilder from "../../../../../core/process/worker/WorkerBuilder.js";
|
|
3
3
|
import { OnDemandWorkerManager } from "../../../../../core/process/worker/OnDemandWorkerManager.js";
|
|
4
|
+
import { PNG_HEADER_BYTES } from "../png/PNG_HEADER_BYTES.js";
|
|
4
5
|
|
|
5
6
|
export class ThreadedImageDecoder extends Codec {
|
|
6
7
|
constructor() {
|
|
@@ -23,6 +24,17 @@ export class ThreadedImageDecoder extends Codec {
|
|
|
23
24
|
this.worker.setTimeout(200);
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
async test(data) {
|
|
28
|
+
|
|
29
|
+
for (let i = 0; i < PNG_HEADER_BYTES.length; i++) {
|
|
30
|
+
if (data[i] !== PNG_HEADER_BYTES[i]) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
26
38
|
async decode(data) {
|
|
27
39
|
const result = await this.worker.request('decode', [data.buffer]);
|
|
28
40
|
return result;
|
|
@@ -6,6 +6,7 @@ import zlib from 'pako';
|
|
|
6
6
|
import { PNG } from './PNG.js';
|
|
7
7
|
import { BinaryBuffer } from "../../../../../core/binary/BinaryBuffer.js";
|
|
8
8
|
import { isArrayEqualStrict } from "../../../../../core/collection/array/isArrayEqualStrict.js";
|
|
9
|
+
import { PNG_HEADER_BYTES } from "./PNG_HEADER_BYTES.js";
|
|
9
10
|
|
|
10
11
|
function readUInt32(buffer, offset) {
|
|
11
12
|
return (buffer[offset] << 24) +
|
|
@@ -62,7 +63,7 @@ PNGReader.prototype.decodeHeader = function () {
|
|
|
62
63
|
buffer.readBytes(header, 0, 8)
|
|
63
64
|
|
|
64
65
|
// see https://www.w3.org/TR/PNG-Structure.html
|
|
65
|
-
if (!isArrayEqualStrict(header,
|
|
66
|
+
if (!isArrayEqualStrict(header, PNG_HEADER_BYTES)) {
|
|
66
67
|
throw new Error('invalid PNGReader file (bad signature)');
|
|
67
68
|
}
|
|
68
69
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PNG_HEADER_BYTES = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param {number[]|Uint32Array|Uint16Array|Uint8Array} array
|
|
4
|
+
* @param {number} offset
|
|
5
|
+
* @param {number} length
|
|
6
|
+
* @param {number} stride
|
|
7
|
+
* @return {number}
|
|
8
|
+
*/
|
|
9
|
+
export function computeStridedIntegerArrayHash(array, offset, length, stride) {
|
|
10
|
+
let result = length;
|
|
11
|
+
|
|
12
|
+
for (let i = offset; i < length; i += stride) {
|
|
13
|
+
const value = array[i];
|
|
14
|
+
|
|
15
|
+
result = ((result * 31) + value) | 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
@@ -28,4 +28,6 @@ export default class EntityBuilder {
|
|
|
28
28
|
addEventListener(eventName: string, listener: (data?: any) => void, context?: any): EntityBuilder
|
|
29
29
|
|
|
30
30
|
removeEventListener(eventName: string, listener: (data?: any) => void, context?: any): EntityBuilder
|
|
31
|
+
|
|
32
|
+
static readFromDataset(entity: number, dataset: EntityComponentDataset): EntityBuilder
|
|
31
33
|
}
|
|
@@ -390,16 +390,18 @@ export class TerrainLayers {
|
|
|
390
390
|
|
|
391
391
|
const resolution = this.resolution;
|
|
392
392
|
|
|
393
|
-
const
|
|
393
|
+
const single_layer_byte_size = resolution.x * resolution.y * 3;
|
|
394
394
|
|
|
395
|
-
if (arrayData.length <
|
|
395
|
+
if (arrayData.length < single_layer_byte_size * index) {
|
|
396
396
|
throw new Error('Texture data is too small, make sure you rebuild texture data before attempting to write');
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
-
const address =
|
|
399
|
+
const address = single_layer_byte_size * index;
|
|
400
400
|
|
|
401
401
|
const sampler = this.__obtain_layer_data_at_resolution(layer, resolution);
|
|
402
402
|
|
|
403
|
+
assert.equal(sampler.data.length, single_layer_byte_size, 'Incorrect layer data size');
|
|
404
|
+
|
|
403
405
|
arrayData.set(sampler.data, address);
|
|
404
406
|
|
|
405
407
|
//mark texture for update
|
|
@@ -8,12 +8,21 @@ export class FPLightBinding extends LightBinding {
|
|
|
8
8
|
this.__light = null;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @return {ForwardPlusRenderingPlugin}
|
|
14
|
+
* @private
|
|
15
|
+
*/
|
|
16
|
+
get __plugin(){
|
|
17
|
+
return ctx.system.__plugin.getValue();
|
|
18
|
+
}
|
|
19
|
+
|
|
11
20
|
link(ctx) {
|
|
12
21
|
/**
|
|
13
22
|
*
|
|
14
23
|
* @type {ForwardPlusRenderingPlugin}
|
|
15
24
|
*/
|
|
16
|
-
const fp =
|
|
25
|
+
const fp = this.__plugin;
|
|
17
26
|
|
|
18
27
|
const lightManager = fp.getLightManager();
|
|
19
28
|
|
|
@@ -43,7 +52,7 @@ export class FPLightBinding extends LightBinding {
|
|
|
43
52
|
*
|
|
44
53
|
* @type {ForwardPlusRenderingPlugin}
|
|
45
54
|
*/
|
|
46
|
-
const fp =
|
|
55
|
+
const fp = this.__plugin;
|
|
47
56
|
|
|
48
57
|
const lightManager = fp.getLightManager();
|
|
49
58
|
|
|
@@ -28,12 +28,6 @@ import {
|
|
|
28
28
|
*/
|
|
29
29
|
const scratch_point = new SurfacePoint3();
|
|
30
30
|
|
|
31
|
-
/**
|
|
32
|
-
*
|
|
33
|
-
* @type {Float32Array|mat4}
|
|
34
|
-
*/
|
|
35
|
-
const scratch_m4 = new Float32Array(16);
|
|
36
|
-
|
|
37
31
|
/**
|
|
38
32
|
* @readonly
|
|
39
33
|
* @type {Float32Array}
|
|
@@ -256,12 +250,12 @@ export class ShadedGeometrySystem extends System {
|
|
|
256
250
|
link(sg, t, entity) {
|
|
257
251
|
sg.__c_transform = t;
|
|
258
252
|
|
|
253
|
+
sg.update_bounds();
|
|
254
|
+
|
|
259
255
|
t.position.onChanged.add(sg.updateTransform, sg);
|
|
260
256
|
t.rotation.onChanged.add(sg.updateTransform, sg);
|
|
261
257
|
t.scale.onChanged.add(sg.updateTransform, sg);
|
|
262
258
|
|
|
263
|
-
sg.update_bounds();
|
|
264
|
-
|
|
265
259
|
// remember entity for lookups
|
|
266
260
|
sg.__entity = entity;
|
|
267
261
|
|
|
@@ -15,4 +15,11 @@ export class GeometrySpatialQueryAccelerator {
|
|
|
15
15
|
origin_x: number, origin_y: number, origin_z: number,
|
|
16
16
|
direction_x: number, direction_y: number, direction_z: number
|
|
17
17
|
): boolean
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* In bytes
|
|
21
|
+
*/
|
|
22
|
+
cache_size: number
|
|
23
|
+
|
|
24
|
+
static INSTANCE: GeometrySpatialQueryAccelerator;
|
|
18
25
|
}
|
|
@@ -33,6 +33,22 @@ export class GeometrySpatialQueryAccelerator {
|
|
|
33
33
|
this.__visitor_raycast = new RaycastNearestHitComputingVisitor();
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
*
|
|
38
|
+
* @param {number} value in bytes
|
|
39
|
+
*/
|
|
40
|
+
set cache_size(value) {
|
|
41
|
+
this.cache.setMaxWeight(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
*
|
|
46
|
+
* @return {number} in bytes
|
|
47
|
+
*/
|
|
48
|
+
get cache_size() {
|
|
49
|
+
return this.cache.maxWeight;
|
|
50
|
+
}
|
|
51
|
+
|
|
36
52
|
/**
|
|
37
53
|
*
|
|
38
54
|
* @param {THREE.BufferGeometry} geometry
|
|
@@ -184,7 +200,7 @@ export class GeometrySpatialQueryAccelerator {
|
|
|
184
200
|
const de_interleaved_position_attribute = deinterleaveBufferAttribute(position_attribute);
|
|
185
201
|
const vertices = de_interleaved_position_attribute.array;
|
|
186
202
|
|
|
187
|
-
if(index_attribute === undefined || index_attribute === null){
|
|
203
|
+
if (index_attribute === undefined || index_attribute === null) {
|
|
188
204
|
return buildUnsortedUnindexed(vertices);
|
|
189
205
|
}
|
|
190
206
|
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { StaticMaterialCache } from "../../../../asset/loaders/material/StaticMaterialCache.js";
|
|
2
|
+
import { HashMap } from "../../../../../core/collection/HashMap.js";
|
|
3
|
+
import { computeGeometryEquality } from "../../../../asset/loaders/geometry/geometry/computeGeometryEquality.js";
|
|
4
|
+
import { computeGeometryHash } from "../../../../asset/loaders/geometry/geometry/computeGeometryHash.js";
|
|
5
|
+
import { array_copy } from "../../../../../core/collection/array/copyArray.js";
|
|
6
|
+
import { mat4 } from "gl-matrix";
|
|
7
|
+
import { Group, Matrix4, Mesh } from "three";
|
|
8
|
+
import { mergeBufferGeometries } from "three/examples/jsm/utils/BufferGeometryUtils.js";
|
|
9
|
+
import { groupArrayBy } from "../../../../../core/collection/ArrayUtils.js";
|
|
10
|
+
import { collectIteratorValueToArray } from "../../../../../core/collection/IteratorUtils.js";
|
|
11
|
+
|
|
12
|
+
class GeometryContext {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.matrix = new Float32Array(16);
|
|
15
|
+
this.material = null;
|
|
16
|
+
this.geometry = null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const DEFAULT_INSTANCING_MERGE_POLYCOUNT = 1024;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
*
|
|
24
|
+
* @param {THREE.Object3D} object
|
|
25
|
+
* @param {THREE.Object3D} ancestor_root
|
|
26
|
+
* @param {Float32Array} result
|
|
27
|
+
*/
|
|
28
|
+
function compute_transform_matrix(object, ancestor_root, result) {
|
|
29
|
+
/**
|
|
30
|
+
*
|
|
31
|
+
* @type {THREE.Object3D[]}
|
|
32
|
+
*/
|
|
33
|
+
const path = [];
|
|
34
|
+
|
|
35
|
+
let node = object;
|
|
36
|
+
|
|
37
|
+
while (node !== ancestor_root) {
|
|
38
|
+
|
|
39
|
+
if (path.indexOf(node) !== -1) {
|
|
40
|
+
throw new Error('Circular dependency');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
path.push(node);
|
|
44
|
+
|
|
45
|
+
node = node.parent;
|
|
46
|
+
|
|
47
|
+
if (node === null) {
|
|
48
|
+
throw new Error('Ancestor not found');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
//
|
|
54
|
+
path.reverse();
|
|
55
|
+
|
|
56
|
+
mat4.identity(result);
|
|
57
|
+
|
|
58
|
+
for (let i = 0; i < path.length; i++) {
|
|
59
|
+
const el = path[i];
|
|
60
|
+
|
|
61
|
+
mat4.multiply(result, result, el.matrix.elements);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
*
|
|
69
|
+
* @param {THREE.BufferGeometry} a
|
|
70
|
+
* @param {THREE.BufferGeometry} b
|
|
71
|
+
* @returns {boolean}
|
|
72
|
+
*/
|
|
73
|
+
function equal_attribute_structure(a, b) {
|
|
74
|
+
if (a === b) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const a_attribute_names = Object.keys(a.attributes);
|
|
79
|
+
const b_attribute_names = Object.keys(b.attributes);
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
const n = a_attribute_names.length;
|
|
83
|
+
if (n !== b_attribute_names.length) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (let i = 0; i < n; i++) {
|
|
88
|
+
const a_key = a_attribute_names[i];
|
|
89
|
+
|
|
90
|
+
const b_attribute_index = b_attribute_names.indexOf(a_key);
|
|
91
|
+
|
|
92
|
+
if (b_attribute_index === -1) {
|
|
93
|
+
return -1;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const a_attribute = a.attributes[a_key];
|
|
97
|
+
const b_attribute = b.attributes[a_key];
|
|
98
|
+
|
|
99
|
+
if (a_attribute.itemSize !== b_attribute.itemSize) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (a_attribute.normalized !== b_attribute.normalized) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (a_attribute.array.constructor !== b_attribute.array.constructor) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
*
|
|
117
|
+
* @param {GeometryContext[]} contexts
|
|
118
|
+
* @returns {GeometryContext}
|
|
119
|
+
*/
|
|
120
|
+
function merge_contexts(contexts) {
|
|
121
|
+
|
|
122
|
+
const geometries = contexts.map(ctx => {
|
|
123
|
+
const g = ctx.geometry.clone();
|
|
124
|
+
|
|
125
|
+
const matrix4 = new Matrix4();
|
|
126
|
+
matrix4.elements = ctx.matrix;
|
|
127
|
+
|
|
128
|
+
g.applyMatrix4(matrix4);
|
|
129
|
+
|
|
130
|
+
return g;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const merged = mergeBufferGeometries(geometries);
|
|
134
|
+
|
|
135
|
+
const merged_context = new GeometryContext();
|
|
136
|
+
|
|
137
|
+
mat4.identity(merged_context.matrix);
|
|
138
|
+
merged_context.geometry = merged;
|
|
139
|
+
merged_context.material = contexts[0].material;
|
|
140
|
+
|
|
141
|
+
return merged_context;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
*
|
|
146
|
+
* @param {GeometryContext[]} input
|
|
147
|
+
* @param {GeometryContext[]} output
|
|
148
|
+
* @param {number} output_offset
|
|
149
|
+
* @returns {number}
|
|
150
|
+
*/
|
|
151
|
+
function merge_by_material(input, output, output_offset) {
|
|
152
|
+
let result_offset = output_offset;
|
|
153
|
+
|
|
154
|
+
const undecided = input.slice();
|
|
155
|
+
|
|
156
|
+
while (undecided.length > 0) {
|
|
157
|
+
for (let i = 0; i < undecided.length; i++) {
|
|
158
|
+
const ref_0 = undecided[i];
|
|
159
|
+
|
|
160
|
+
const set_indices = [i];
|
|
161
|
+
const set_contexts = [ref_0];
|
|
162
|
+
|
|
163
|
+
for (let j = i + 1; j < undecided.length; j++) {
|
|
164
|
+
const ref_1 = undecided[j];
|
|
165
|
+
|
|
166
|
+
const geo_0 = ref_0.geometry;
|
|
167
|
+
const geo_1 = ref_1.geometry;
|
|
168
|
+
|
|
169
|
+
if (geo_0 === geo_1) {
|
|
170
|
+
const tri_count = (geo_0.getIndex().count) / 3;
|
|
171
|
+
if (tri_count > DEFAULT_INSTANCING_MERGE_POLYCOUNT) {
|
|
172
|
+
// instancing, and instances are large enough for instancing to be efficient
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!equal_attribute_structure(geo_0, geo_1)) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (ref_0.material !== ref_1.material) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
set_indices.push(j);
|
|
186
|
+
set_contexts.push(ref_1);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
let out = ref_0;
|
|
190
|
+
|
|
191
|
+
for (let j = set_indices.length - 1; j >= 0; j--) {
|
|
192
|
+
undecided.splice(set_indices[j], 1);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (set_indices.length > 1) {
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
out = merge_contexts(set_contexts);
|
|
199
|
+
|
|
200
|
+
i--;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
output[result_offset++] = out;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return result_offset - output_offset;
|
|
208
|
+
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
*
|
|
213
|
+
* @param {GeometryContext} ctx
|
|
214
|
+
* @returns {Mesh}
|
|
215
|
+
*/
|
|
216
|
+
function context_to_mesh(ctx) {
|
|
217
|
+
const mesh = new Mesh(ctx.geometry, ctx.material);
|
|
218
|
+
|
|
219
|
+
matrix_array_to_three_object(ctx.matrix, mesh);
|
|
220
|
+
|
|
221
|
+
return mesh;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
*
|
|
226
|
+
* @param {number[]} matrix
|
|
227
|
+
* @param {THREE.Object3D} destination
|
|
228
|
+
*/
|
|
229
|
+
function matrix_array_to_three_object(matrix, destination) {
|
|
230
|
+
|
|
231
|
+
array_copy(matrix, 0, destination.matrix.elements, 0, 16);
|
|
232
|
+
|
|
233
|
+
destination.matrix.decompose(destination.position, destination.quaternion, destination.scale);
|
|
234
|
+
|
|
235
|
+
destination.rotation.setFromQuaternion(destination.quaternion);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
*
|
|
240
|
+
* @param {THREE.Object3D} source
|
|
241
|
+
* @param {THREE.Object3D} destination
|
|
242
|
+
*/
|
|
243
|
+
function apply_transform_to_another_three_object(source, destination) {
|
|
244
|
+
const m4 = new Matrix4();
|
|
245
|
+
|
|
246
|
+
m4.multiplyMatrices(source.matrix, destination.matrix);
|
|
247
|
+
|
|
248
|
+
matrix_array_to_three_object(m4.elements, destination);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
*
|
|
253
|
+
* @param {THREE.Object3D} input
|
|
254
|
+
* @param context
|
|
255
|
+
* @returns {THREE.Object3D}
|
|
256
|
+
*/
|
|
257
|
+
export function merge_geometry_hierarchy(input, context = new Context()) {
|
|
258
|
+
// material cache
|
|
259
|
+
const material_cache = new StaticMaterialCache();
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
const geometry_cache = new HashMap({
|
|
263
|
+
keyEqualityFunction: computeGeometryEquality,
|
|
264
|
+
keyHashFunction: computeGeometryHash
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
*
|
|
270
|
+
* @param {THREE.BufferGeometry} geo
|
|
271
|
+
* @return {THREE.BufferGeometry}
|
|
272
|
+
*/
|
|
273
|
+
function dedupe_geometry(geo) {
|
|
274
|
+
const existing = geometry_cache.get(geo);
|
|
275
|
+
|
|
276
|
+
if (existing !== undefined) {
|
|
277
|
+
return existing;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
geometry_cache.set(geo, geo);
|
|
281
|
+
|
|
282
|
+
return geo;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
let contexts = [];
|
|
286
|
+
|
|
287
|
+
// collect geometries
|
|
288
|
+
input.traverse(o => {
|
|
289
|
+
if (o.isMesh && !!(o.geometry)) {
|
|
290
|
+
|
|
291
|
+
const ctx = new GeometryContext();
|
|
292
|
+
|
|
293
|
+
ctx.material = material_cache.acquire(o.material);
|
|
294
|
+
ctx.geometry = dedupe_geometry(o.geometry);
|
|
295
|
+
|
|
296
|
+
compute_transform_matrix(o, input, ctx.matrix);
|
|
297
|
+
|
|
298
|
+
contexts.push(ctx);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const materials = collectIteratorValueToArray([], material_cache.materialCache.data.keys());
|
|
303
|
+
|
|
304
|
+
const color_groups = groupArrayBy(materials, (m => m.color.getHex()));
|
|
305
|
+
|
|
306
|
+
console.warn(color_groups);
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
const contexts_merge_stage_0 = [];
|
|
310
|
+
|
|
311
|
+
merge_by_material(contexts, contexts_merge_stage_0, 0);
|
|
312
|
+
|
|
313
|
+
// convert contexts to Meshes
|
|
314
|
+
|
|
315
|
+
const meshes = contexts_merge_stage_0.map(context_to_mesh);
|
|
316
|
+
|
|
317
|
+
let result_object;
|
|
318
|
+
|
|
319
|
+
if (meshes.length === 1) {
|
|
320
|
+
// only one mesh is present, return that
|
|
321
|
+
result_object = meshes[0];
|
|
322
|
+
} else {
|
|
323
|
+
|
|
324
|
+
result_object = new Group();
|
|
325
|
+
|
|
326
|
+
for (let i = 0; i < meshes.length; i++) {
|
|
327
|
+
const m = meshes[i];
|
|
328
|
+
result_object.add(m);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// copy transform from original
|
|
333
|
+
apply_transform_to_another_three_object(input, result_object);
|
|
334
|
+
|
|
335
|
+
return result_object;
|
|
336
|
+
}
|