@woosh/meep-engine 2.34.49 → 2.35.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/bvh2/BinaryNode.js +19 -35
- package/core/bvh2/aabb3/aabb3_intersects_frustum_array.js +5 -5
- package/core/bvh2/aabb3/aabb3_intersects_frustum_degree.js +6 -6
- package/core/bvh2/aabb3_combined_surface_area.js +21 -0
- package/core/bvh2/traversal/queryBinaryNode_CollectData.js +1 -2
- package/core/bvh2/traversal/queryBinaryNode_FrustumIntersections.js +42 -17
- package/core/cache/Cache.d.ts +2 -0
- package/core/cache/Cache.js +27 -23
- package/core/collection/HashMap.js +1 -1
- package/core/geom/3d/frustum/frustum_from_projection_matrix_array.js +2 -0
- package/core/math/max2.js +2 -6
- package/core/math/min2.js +2 -6
- package/engine/asset/loaders/geometry/geometry/computeGeometryEquality.js +1 -1
- package/engine/asset/loaders/geometry/geometry/computeGeometryHash.js +9 -1
- package/engine/ecs/EntityComponentDataset.js +7 -2
- package/engine/ecs/foliage/ViewState.js +13 -3
- package/engine/ecs/parent/EntityNode.js +23 -11
- package/engine/ecs/transform/Transform.js +15 -13
- package/engine/graphics/ecs/highlight/system/ShadedGeometryHighlightSystem.js +13 -4
- package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +46 -108
- package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +72 -10
- package/engine/graphics/ecs/mesh-v2/build_three_object.js +39 -0
- package/engine/graphics/ecs/mesh-v2/render/AbstractRenderAdapter.js +67 -0
- package/engine/graphics/ecs/mesh-v2/render/GenericRendererAdapter.js +48 -0
- package/engine/graphics/ecs/mesh-v2/render/InstancedRendererAdapter.js +158 -0
- package/engine/graphics/ecs/mesh-v2/render/ShadedGeometryRendererContext.js +98 -0
- package/engine/graphics/ecs/mesh-v2/sample/load_gltf.js +5 -0
- package/engine/graphics/ecs/mesh-v2/sample/prototypeShadedGeometry.js +37 -10
- package/engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.js +8 -3
- package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +29 -82
- package/engine/graphics/geometry/instancing/rewriteMaterial.js +9 -45
- package/engine/graphics/material/optimization/BUFFER_GEOMETRY_UVS.js +10 -0
- package/engine/graphics/material/optimization/MaterialDescriptor.js +46 -0
- package/engine/graphics/material/optimization/MaterialOptimizationContext.js +470 -201
- package/engine/graphics/material/optimization/UvContext.js +18 -0
- package/engine/graphics/material/optimization/is_compliant_mesh.js +71 -0
- package/engine/graphics/material/optimization/prepare_atlas_texture.js +23 -0
- package/engine/graphics/material/optimization/prototypeMaterialOptimizer.js +127 -0
- package/engine/graphics/micron/format/MicronGeometryPatch.js +1 -1
- package/engine/graphics/micron/format/VirtualGeometry.js +47 -11
- package/engine/graphics/micron/format/serialization/MicronGeometryBinarySerializationAdapter.js +16 -0
- package/engine/graphics/micron/prototypeVirtualGeometry.js +3 -2
- package/engine/graphics/micron/render/VirtualGeometryBuilder.js +5 -7
- package/engine/graphics/micron/render/instanced/AttributeDataTexture.js +1 -45
- package/engine/graphics/micron/render/instanced/ThreeInstancedAdapter.js +18 -7
- package/engine/graphics/micron/render/refinement/RefinementSpec.js +0 -1
- package/engine/graphics/micron/render/refinement/get_geometry_patch_cut.js +17 -24
- package/engine/graphics/micron/render/v1/MaterialContext.js +3 -2
- package/engine/graphics/micron/render/v1/VGThreeRenderer.js +20 -4
- package/engine/graphics/render/utils/ThreeBypassRenderer.js +3 -2
- package/engine/graphics/render/visibility/IncrementalDeltaSet.js +1 -0
- package/engine/graphics/texture/normalized_internal_format.js +48 -0
- package/engine/graphics/texture/sampler/Sampler2D.js +6 -0
- package/engine/graphics/texture/sampler/sampler2d_copy_channel_data.js +25 -0
- package/engine/graphics/texture/sampler/scaleSampler2D.js +3 -0
- package/engine/graphics/texture/sampler/util/drawSamplerHTML.js +14 -1
- package/package.json +1 -1
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { AbstractRenderAdapter } from "./AbstractRenderAdapter.js";
|
|
2
|
+
import { HashMap } from "../../../../../core/collection/HashMap.js";
|
|
3
|
+
import { InstancedMeshGroup } from "../../../geometry/instancing/InstancedMeshGroup.js";
|
|
4
|
+
import { ShadedGeometryFlags } from "../ShadedGeometryFlags.js";
|
|
5
|
+
import { DrawMode } from "../DrawMode.js";
|
|
6
|
+
|
|
7
|
+
const INSTANCED_EQUALITY_FLAGS = ShadedGeometryFlags.CastShadow
|
|
8
|
+
| ShadedGeometryFlags.ReceiveShadow
|
|
9
|
+
;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param {ShadedGeometry} a
|
|
14
|
+
* @param {ShadedGeometry} b
|
|
15
|
+
* @returns {number}
|
|
16
|
+
*/
|
|
17
|
+
function compare(a, b) {
|
|
18
|
+
const d_geometry = a.geometry.id - b.geometry.id;
|
|
19
|
+
if (d_geometry !== 0) {
|
|
20
|
+
return d_geometry;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const d_material = a.material.id - b.material.id;
|
|
24
|
+
if (d_material !== 0) {
|
|
25
|
+
return d_material;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const d_flags = (a.flags & INSTANCED_EQUALITY_FLAGS) - (b.flags & INSTANCED_EQUALITY_FLAGS);
|
|
29
|
+
if (d_flags !== 0) {
|
|
30
|
+
return d_flags;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const d_mode = a.mode - b.mode;
|
|
34
|
+
if (d_mode !== 0) {
|
|
35
|
+
return d_mode;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class InstancedRendererAdapter extends AbstractRenderAdapter {
|
|
42
|
+
constructor() {
|
|
43
|
+
super();
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
*
|
|
47
|
+
* @type {HashMap<ShadedGeometry, InstancedMeshGroup>}
|
|
48
|
+
* @private
|
|
49
|
+
*/
|
|
50
|
+
this.__instanced_meshes = new HashMap({
|
|
51
|
+
keyHashFunction(sg) {
|
|
52
|
+
return sg.geometry.id ^ sg.material.id ^ sg.mode;
|
|
53
|
+
},
|
|
54
|
+
keyEqualityFunction(a, b) {
|
|
55
|
+
return a.material.id === b.material.id
|
|
56
|
+
&& a.geometry.id === b.geometry.id
|
|
57
|
+
&& a.mode === b.mode
|
|
58
|
+
&& (a.flags & INSTANCED_EQUALITY_FLAGS) === (b.flags & INSTANCED_EQUALITY_FLAGS)
|
|
59
|
+
;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
*
|
|
65
|
+
* @type {Set<InstancedMeshGroup>}
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
68
|
+
this.__active_meshes = new Set();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
*
|
|
73
|
+
* @param {ShadedGeometry} sg
|
|
74
|
+
* @returns {InstancedMeshGroup}
|
|
75
|
+
* @private
|
|
76
|
+
*/
|
|
77
|
+
__get_instanced_mesh(sg) {
|
|
78
|
+
let im = this.__instanced_meshes.get(sg);
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
if (im !== undefined) {
|
|
82
|
+
|
|
83
|
+
return im;
|
|
84
|
+
|
|
85
|
+
} else {
|
|
86
|
+
|
|
87
|
+
if (sg.mode !== DrawMode.Triangles) {
|
|
88
|
+
throw new Error(`Unsupported draw mode ${sg.mode}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
im = new InstancedMeshGroup();
|
|
92
|
+
im.shrinkFactor = 0; // prevent shrinking as we're essentially rebuilding this geometry frame
|
|
93
|
+
|
|
94
|
+
im.setMaterial(sg.material);
|
|
95
|
+
im.setGeometry(sg.geometry);
|
|
96
|
+
|
|
97
|
+
im.mesh.castShadow = sg.getFlag(ShadedGeometryFlags.CastShadow);
|
|
98
|
+
im.mesh.receiveShadow = sg.getFlag(ShadedGeometryFlags.ReceiveShadow);
|
|
99
|
+
|
|
100
|
+
this.__instanced_meshes.set(sg, im);
|
|
101
|
+
|
|
102
|
+
return im;
|
|
103
|
+
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
add(sg) {
|
|
108
|
+
const mesh = this.__get_instanced_mesh(sg);
|
|
109
|
+
|
|
110
|
+
const index = mesh.count;
|
|
111
|
+
|
|
112
|
+
const new_size = index + 1;
|
|
113
|
+
|
|
114
|
+
if (new_size > mesh.capacity) {
|
|
115
|
+
mesh.ensureCapacity(new_size);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
mesh.setTransformAt(index, sg.transform);
|
|
119
|
+
|
|
120
|
+
mesh.count++;
|
|
121
|
+
|
|
122
|
+
this.__active_meshes.add(mesh);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
build_start() {
|
|
126
|
+
this.clear();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
build_end() {
|
|
130
|
+
const objects = this.__objects;
|
|
131
|
+
|
|
132
|
+
for (const activeMesh of this.__active_meshes) {
|
|
133
|
+
// force underlying geometry to update
|
|
134
|
+
activeMesh.setCount(activeMesh.getCount());
|
|
135
|
+
activeMesh.requestAttributeUpdate();
|
|
136
|
+
|
|
137
|
+
objects[this.__object_count++] = activeMesh.mesh;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
clear() {
|
|
143
|
+
super.clear();
|
|
144
|
+
|
|
145
|
+
for (const activeMesh of this.__active_meshes) {
|
|
146
|
+
activeMesh.setCount(0);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
this.__active_meshes.clear();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
dispose() {
|
|
153
|
+
// release GPU memory
|
|
154
|
+
this.__instanced_meshes.forEach((value, key) => {
|
|
155
|
+
value.dispose();
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { queryBinaryNode_FrustumIntersections_Data } from "../../../../../core/bvh2/traversal/queryBinaryNode_FrustumIntersections.js";
|
|
2
|
+
import { AbstractRenderAdapter } from "./AbstractRenderAdapter.js";
|
|
3
|
+
import { InstancedRendererAdapter } from "./InstancedRendererAdapter.js";
|
|
4
|
+
import { GenericRendererAdapter } from "./GenericRendererAdapter.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 1 - collect visible meshes
|
|
8
|
+
* 2 - figure out how to render a given mesh
|
|
9
|
+
*/
|
|
10
|
+
export class ShadedGeometryRendererContext {
|
|
11
|
+
constructor() {
|
|
12
|
+
/**
|
|
13
|
+
* @readonly
|
|
14
|
+
* @type {AbstractRenderAdapter[]}
|
|
15
|
+
*/
|
|
16
|
+
this.adapters = [
|
|
17
|
+
new GenericRendererAdapter(),
|
|
18
|
+
new InstancedRendererAdapter()
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
setAdapter(index, adapter) {
|
|
23
|
+
const existing = this.adapters[index];
|
|
24
|
+
if (existing !== undefined) {
|
|
25
|
+
// dispose of existing adapter
|
|
26
|
+
existing.dispose();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.adapters[index] = adapter;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
* @param {THREE.Object3D[]} destination
|
|
35
|
+
* @param {number} destination_offset
|
|
36
|
+
* @param {CameraView} view
|
|
37
|
+
* @param {BinaryNode<ShadedGeometry>} bvh
|
|
38
|
+
*/
|
|
39
|
+
collect(destination, destination_offset, view, bvh) {
|
|
40
|
+
const adapters = this.adapters;
|
|
41
|
+
const adapter_count = adapters.length;
|
|
42
|
+
|
|
43
|
+
let i = 0, j = 0;
|
|
44
|
+
for (; i < adapter_count; i++) {
|
|
45
|
+
const adapter = adapters[i];
|
|
46
|
+
|
|
47
|
+
adapter.build_start();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
// get all meshes
|
|
52
|
+
|
|
53
|
+
const data = [];
|
|
54
|
+
|
|
55
|
+
const intersection_count = queryBinaryNode_FrustumIntersections_Data(data, 0, bvh, view.frustum);
|
|
56
|
+
|
|
57
|
+
for (i = 0; i < intersection_count; i++) {
|
|
58
|
+
/**
|
|
59
|
+
*
|
|
60
|
+
* @type {ShadedGeometry}
|
|
61
|
+
*/
|
|
62
|
+
const sg = data[i];
|
|
63
|
+
|
|
64
|
+
const adapter = adapters[sg.draw_method];
|
|
65
|
+
|
|
66
|
+
adapter.add(sg);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let additions = 0;
|
|
70
|
+
|
|
71
|
+
for (i = 0; i < adapter_count; i++) {
|
|
72
|
+
const adapter = adapters[i];
|
|
73
|
+
|
|
74
|
+
adapter.build_end();
|
|
75
|
+
|
|
76
|
+
const adapter_object_count = adapter.object_count;
|
|
77
|
+
|
|
78
|
+
for (j = 0; j < adapter_object_count; j++) {
|
|
79
|
+
const object3D = adapter.objects[j];
|
|
80
|
+
|
|
81
|
+
destination[destination_offset + additions] = object3D;
|
|
82
|
+
|
|
83
|
+
additions++;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return additions;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
dispose() {
|
|
92
|
+
for (const adapter of this.adapters) {
|
|
93
|
+
if (adapter !== undefined) {
|
|
94
|
+
adapter.dispose();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -6,6 +6,7 @@ import { GLTFAssetLoader } from "../../../../asset/loaders/GLTFAssetLoader.js";
|
|
|
6
6
|
import { three_object_to_entity_composition } from "../three_object_to_entity_composition.js";
|
|
7
7
|
import { AttachmentSystem } from "../../../../ecs/attachment/AttachmentSystem.js";
|
|
8
8
|
import { TransformAttachmentSystem } from "../../../../ecs/transform-attachment/TransformAttachmentSystem.js";
|
|
9
|
+
import { Camera } from "../../camera/Camera.js";
|
|
9
10
|
|
|
10
11
|
const eh = new EngineHarness();
|
|
11
12
|
|
|
@@ -70,6 +71,10 @@ async function main(engine) {
|
|
|
70
71
|
enableTerrain: false
|
|
71
72
|
});
|
|
72
73
|
|
|
74
|
+
const camera = engine.entityManager.dataset.getAnyComponent(Camera);
|
|
75
|
+
camera.component.autoClip = false;
|
|
76
|
+
camera.component.object.far = 300;
|
|
77
|
+
|
|
73
78
|
// a small delay to let background tasks finish, so we can get better performance reading later on
|
|
74
79
|
timeout(20);
|
|
75
80
|
|
|
@@ -3,7 +3,7 @@ import { ShadedGeometrySystem } from "../ShadedGeometrySystem.js";
|
|
|
3
3
|
import EntityBuilder from "../../../../ecs/EntityBuilder.js";
|
|
4
4
|
import { Transform } from "../../../../ecs/transform/Transform.js";
|
|
5
5
|
import { ShadedGeometry } from "../ShadedGeometry.js";
|
|
6
|
-
import { BoxBufferGeometry,
|
|
6
|
+
import { BoxBufferGeometry, MeshStandardMaterial } from "three";
|
|
7
7
|
import { randomFloatBetween } from "../../../../../core/math/random/randomFloatBetween.js";
|
|
8
8
|
import { seededRandom } from "../../../../../core/math/random/seededRandom.js";
|
|
9
9
|
import ViewportPosition from "../../../../ecs/gui/position/ViewportPosition.js";
|
|
@@ -13,9 +13,16 @@ import { downloadAsFile } from "../../../../../core/binary/ByteArrayTools.js";
|
|
|
13
13
|
import { convert_bvh_to_dot_format_string } from "../../../../../core/bvh2/visual/convert_bvh_to_dot_format_string.js";
|
|
14
14
|
import ViewportPositionSystem from "../../../../ecs/gui/position/ViewportPositionSystem.js";
|
|
15
15
|
import GUIElementSystem from "../../../../ecs/gui/GUIElementSystem.js";
|
|
16
|
+
import Vector3 from "../../../../../core/geom/Vector3.js";
|
|
17
|
+
import Quaternion from "../../../../../core/geom/Quaternion.js";
|
|
18
|
+
import { ShadedGeometryFlags } from "../ShadedGeometryFlags.js";
|
|
19
|
+
import { randomFromArray } from "../../../../../core/math/random/randomFromArray.js";
|
|
16
20
|
|
|
17
21
|
const eh = new EngineHarness();
|
|
18
22
|
|
|
23
|
+
const DRAW_METHOD_PLAIN = 0;
|
|
24
|
+
const DRAW_METHOD_INSTANCED = 1;
|
|
25
|
+
|
|
19
26
|
/**
|
|
20
27
|
*
|
|
21
28
|
* @param {Engine} engine
|
|
@@ -26,12 +33,18 @@ async function main(engine) {
|
|
|
26
33
|
|
|
27
34
|
await em.addSystem(new ShadedGeometrySystem(engine));
|
|
28
35
|
|
|
29
|
-
EngineHarness.buildBasics({
|
|
36
|
+
EngineHarness.buildBasics({
|
|
37
|
+
engine,
|
|
38
|
+
yaw: -0.8,
|
|
39
|
+
pitch: 1,
|
|
40
|
+
distance: 46,
|
|
41
|
+
focus: new Vector3(10, 0, 10)
|
|
42
|
+
});
|
|
30
43
|
|
|
31
44
|
|
|
32
45
|
const ecd = em.dataset;
|
|
33
46
|
|
|
34
|
-
const material = new
|
|
47
|
+
const material = new MeshStandardMaterial({
|
|
35
48
|
color: 0xFFFFFF
|
|
36
49
|
});
|
|
37
50
|
|
|
@@ -39,22 +52,36 @@ async function main(engine) {
|
|
|
39
52
|
|
|
40
53
|
window.material = material;
|
|
41
54
|
|
|
42
|
-
const geometry = new BoxBufferGeometry();
|
|
43
|
-
|
|
44
55
|
const random = seededRandom(0);
|
|
45
56
|
|
|
46
|
-
|
|
57
|
+
const INSTANCE_COUNT = 20000;
|
|
58
|
+
|
|
59
|
+
const geometries = [];
|
|
60
|
+
|
|
61
|
+
for (let i = 0; i < 1000; i++) {
|
|
62
|
+
geometries.push(new BoxBufferGeometry());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (let i = 0; i < INSTANCE_COUNT; i++) {
|
|
66
|
+
const geometry = randomFromArray(random, geometries);
|
|
67
|
+
const sg = ShadedGeometry.from(geometry, material);
|
|
68
|
+
|
|
69
|
+
// sg.draw_method = DRAW_METHOD_PLAIN;
|
|
70
|
+
sg.draw_method = DRAW_METHOD_INSTANCED;
|
|
71
|
+
// sg.draw_method = random() > 0.01 ? DRAW_METHOD_INSTANCED : DRAW_METHOD_PLAIN;
|
|
72
|
+
sg.clearFlag(ShadedGeometryFlags.CastShadow | ShadedGeometryFlags.ReceiveShadow);
|
|
47
73
|
|
|
48
74
|
new EntityBuilder()
|
|
49
75
|
.add(Transform.fromJSON({
|
|
50
76
|
position: {
|
|
51
|
-
x: randomFloatBetween(random,
|
|
77
|
+
x: randomFloatBetween(random, 0, 20),
|
|
52
78
|
y: randomFloatBetween(random, 0, 10),
|
|
53
|
-
z: randomFloatBetween(random,
|
|
79
|
+
z: randomFloatBetween(random, 0, 20)
|
|
54
80
|
},
|
|
55
|
-
|
|
81
|
+
rotation: Quaternion.random(random),
|
|
82
|
+
scale: 0.05
|
|
56
83
|
}))
|
|
57
|
-
.add(
|
|
84
|
+
.add(sg)
|
|
58
85
|
.build(ecd);
|
|
59
86
|
}
|
|
60
87
|
|
|
@@ -12,24 +12,29 @@ export function three_object_to_entity_composition(root) {
|
|
|
12
12
|
|
|
13
13
|
node.transform.fromMatrix4(root.matrix.elements);
|
|
14
14
|
|
|
15
|
-
node.entity
|
|
15
|
+
const entity = node.entity;
|
|
16
|
+
|
|
17
|
+
entity.add(new Transform());
|
|
16
18
|
|
|
17
19
|
if (root.isMesh) {
|
|
18
20
|
if (root.isSkinnedMesh) {
|
|
19
21
|
throw new Error(`Skinned meshes are not supported`);
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
const sg = ShadedGeometry.from(root.geometry, root.material);
|
|
25
|
+
sg.draw_method = 1;
|
|
26
|
+
|
|
27
|
+
entity.add(sg);
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
const children = root.children;
|
|
26
31
|
const child_count = children.length;
|
|
32
|
+
|
|
27
33
|
for (let i = 0; i < child_count; i++) {
|
|
28
34
|
const child = children[i];
|
|
29
35
|
|
|
30
36
|
const child_node = three_object_to_entity_composition(child);
|
|
31
37
|
|
|
32
|
-
|
|
33
38
|
node.addChild(child_node);
|
|
34
39
|
}
|
|
35
40
|
|
|
@@ -3,13 +3,15 @@ import {
|
|
|
3
3
|
InstancedBufferAttribute,
|
|
4
4
|
InstancedBufferGeometry,
|
|
5
5
|
MeshDepthMaterial,
|
|
6
|
-
RGBADepthPacking
|
|
6
|
+
RGBADepthPacking,
|
|
7
|
+
StreamDrawUsage
|
|
7
8
|
} from 'three';
|
|
8
9
|
import ThreeFactory from "../../three/ThreeFactory.js";
|
|
9
10
|
import { StaticMaterialCache } from "../../../asset/loaders/material/StaticMaterialCache.js";
|
|
10
11
|
import { composeCompile } from "../../material/composeCompile.js";
|
|
11
12
|
import { typed_array_copy } from "../../../../core/collection/array/typed/typed_array_copy.js";
|
|
12
13
|
import { rewriteMaterial } from "./rewriteMaterial.js";
|
|
14
|
+
import { max3 } from "../../../../core/math/max3.js";
|
|
13
15
|
|
|
14
16
|
export class InstancedMeshGroup {
|
|
15
17
|
/**
|
|
@@ -66,21 +68,14 @@ export class InstancedMeshGroup {
|
|
|
66
68
|
* @type {InstancedBufferAttribute}
|
|
67
69
|
* @private
|
|
68
70
|
*/
|
|
69
|
-
this.
|
|
71
|
+
this.__attributeTransform = null;
|
|
70
72
|
|
|
71
73
|
/**
|
|
72
|
-
*
|
|
73
|
-
* @type {
|
|
74
|
-
* @private
|
|
75
|
-
*/
|
|
76
|
-
this.__attributeRotation = null;
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
*
|
|
80
|
-
* @type {InstancedBufferAttribute}
|
|
74
|
+
* Shortcut to buffer attribute data
|
|
75
|
+
* @type {Float32Array}
|
|
81
76
|
* @private
|
|
82
77
|
*/
|
|
83
|
-
this.
|
|
78
|
+
this.__attributeTransformArray = null;
|
|
84
79
|
|
|
85
80
|
/**
|
|
86
81
|
*
|
|
@@ -180,7 +175,7 @@ export class InstancedMeshGroup {
|
|
|
180
175
|
if (currentCapacity < size) {
|
|
181
176
|
// grow
|
|
182
177
|
|
|
183
|
-
const newCapacityRaw =
|
|
178
|
+
const newCapacityRaw = max3(
|
|
184
179
|
size,
|
|
185
180
|
currentCapacity * this.growFactor,
|
|
186
181
|
currentCapacity + this.growConstant
|
|
@@ -203,39 +198,16 @@ export class InstancedMeshGroup {
|
|
|
203
198
|
/**
|
|
204
199
|
*
|
|
205
200
|
* @param {number} index
|
|
206
|
-
* @param {number}
|
|
207
|
-
* @param {number} y
|
|
208
|
-
* @param {number} z
|
|
201
|
+
* @param {number[]|ArrayLike<number>|Float32Array} transform 4x4 transform matrix of the instance
|
|
209
202
|
*/
|
|
210
|
-
|
|
211
|
-
this.
|
|
212
|
-
this.__attributePosition.needsUpdate = true;
|
|
203
|
+
setTransformAt(index, transform) {
|
|
204
|
+
this.__attributeTransformArray.set(transform, index * 16);
|
|
213
205
|
}
|
|
214
206
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
* @param {number} index
|
|
218
|
-
* @param {number} x
|
|
219
|
-
* @param {number} y
|
|
220
|
-
* @param {number} z
|
|
221
|
-
* @param {number} w
|
|
222
|
-
*/
|
|
223
|
-
setRotationAt(index, x, y, z, w) {
|
|
224
|
-
this.__attributeRotation.setXYZW(index, x, y, z, w);
|
|
225
|
-
this.__attributeRotation.needsUpdate = true;
|
|
207
|
+
requestAttributeUpdate() {
|
|
208
|
+
this.__attributeTransform.needsUpdate = true;
|
|
226
209
|
}
|
|
227
210
|
|
|
228
|
-
/**
|
|
229
|
-
*
|
|
230
|
-
* @param {number} index
|
|
231
|
-
* @param {number} x
|
|
232
|
-
* @param {number} y
|
|
233
|
-
* @param {number} z
|
|
234
|
-
*/
|
|
235
|
-
setScaleAt(index, x, y, z) {
|
|
236
|
-
this.__attributeScale.setXYZ(index, x, y, z);
|
|
237
|
-
this.__attributeScale.needsUpdate = true;
|
|
238
|
-
}
|
|
239
211
|
|
|
240
212
|
/**
|
|
241
213
|
* Swap position in attribute arrays of two elements
|
|
@@ -327,25 +299,13 @@ export class InstancedMeshGroup {
|
|
|
327
299
|
delete references[index];
|
|
328
300
|
} else {
|
|
329
301
|
//not at the end, move the very last reference to this place, effectively shrinking the set
|
|
330
|
-
const index3 = index * 3;
|
|
331
|
-
const start3 = lastIndex * 3;
|
|
332
|
-
const end3 = start3 + 3;
|
|
333
302
|
|
|
334
|
-
const index4 = index * 4;
|
|
335
|
-
const start4 = lastIndex * 4;
|
|
336
|
-
const end4 = start4 + 4;
|
|
337
303
|
|
|
338
|
-
const
|
|
339
|
-
const a_rotation = this.__attributeRotation;
|
|
340
|
-
const a_scale = this.__attributeScale;
|
|
304
|
+
const a_transform = this.__attributeTransform;
|
|
341
305
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
a_scale.array.copyWithin(index3, start3, end3);
|
|
306
|
+
a_transform.array.copyWithin(index * 16, lastIndex * 16, lastIndex * 16 + 16);
|
|
307
|
+
a_transform.needsUpdate = true;
|
|
345
308
|
|
|
346
|
-
a_position.needsUpdate = true;
|
|
347
|
-
a_rotation.needsUpdate = true;
|
|
348
|
-
a_scale.needsUpdate = true;
|
|
349
309
|
|
|
350
310
|
//update moved reference index
|
|
351
311
|
const movedReference = references[lastIndex];
|
|
@@ -391,39 +351,26 @@ export class InstancedMeshGroup {
|
|
|
391
351
|
}
|
|
392
352
|
|
|
393
353
|
//build instanced attributes
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
//copy old data
|
|
400
|
-
const oldPositionArray = this.__attributePosition.array;
|
|
401
|
-
typed_array_copy(oldPositionArray, newPositionArray);
|
|
402
|
-
}
|
|
403
|
-
if (this.__attributeRotation !== null) {
|
|
404
|
-
//copy old data
|
|
405
|
-
const oldRotationArray = this.__attributeRotation.array;
|
|
406
|
-
typed_array_copy(oldRotationArray, newRotationArray);
|
|
407
|
-
}
|
|
408
|
-
if (this.__attributeScale !== null) {
|
|
409
|
-
const oldScaleArray = this.__attributeScale.array;
|
|
410
|
-
typed_array_copy(oldScaleArray, newScaleArray);
|
|
354
|
+
const newTransformArray = new Float32Array(this.capacity * 16);
|
|
355
|
+
|
|
356
|
+
if (this.__attributeTransform !== null) {
|
|
357
|
+
const oldTransformArray = this.__attributeTransform.array;
|
|
358
|
+
typed_array_copy(oldTransformArray, newTransformArray);
|
|
411
359
|
|
|
412
|
-
if (
|
|
413
|
-
//fill the rest of the array with
|
|
414
|
-
|
|
360
|
+
if (newTransformArray.length > oldTransformArray.length) {
|
|
361
|
+
//fill the rest of the array with 0s
|
|
362
|
+
// TODO consider writing identity matrix instead
|
|
363
|
+
newTransformArray.fill(0, oldTransformArray.length);
|
|
415
364
|
}
|
|
416
365
|
}
|
|
417
366
|
|
|
418
367
|
//rewrite old attributes
|
|
419
|
-
this.
|
|
420
|
-
this.
|
|
421
|
-
this.
|
|
368
|
+
this.__attributeTransformArray = newTransformArray;
|
|
369
|
+
this.__attributeTransform = new InstancedBufferAttribute(newTransformArray, 16);
|
|
370
|
+
this.__attributeTransform.setUsage(StreamDrawUsage);
|
|
422
371
|
|
|
423
372
|
//add attributes to newly created geometry
|
|
424
|
-
geometry.setAttribute("
|
|
425
|
-
geometry.setAttribute("instanceRotation", this.__attributeRotation);
|
|
426
|
-
geometry.setAttribute("instanceScale", this.__attributeScale);
|
|
373
|
+
geometry.setAttribute("instanceMatrix", this.__attributeTransform);
|
|
427
374
|
|
|
428
375
|
|
|
429
376
|
this.mesh.geometry = geometry;
|
|
@@ -1,55 +1,19 @@
|
|
|
1
1
|
const SHADER_PREAMBLE = `
|
|
2
|
-
attribute
|
|
3
|
-
|
|
4
|
-
attribute vec3 instanceScale;
|
|
5
|
-
|
|
6
|
-
vec3 instanceTransform( in vec3 position, vec3 T, vec4 R, vec3 S ){
|
|
7
|
-
vec3 result = position;
|
|
8
|
-
|
|
9
|
-
//applies the scale
|
|
10
|
-
result *= S;
|
|
11
|
-
//computes the rotation where R is a (vec4) quaternion
|
|
12
|
-
result += 2.0 * cross( R.xyz, cross( R.xyz, result ) + R.w * result );
|
|
13
|
-
//translates the transformed 'blueprint'
|
|
14
|
-
result += T;
|
|
15
|
-
|
|
16
|
-
return result;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
mat4 getInstanceMatrix(){
|
|
20
|
-
vec4 q = instanceRotation;
|
|
21
|
-
vec3 s = instanceScale;
|
|
22
|
-
vec3 v = instancePosition;
|
|
23
|
-
|
|
24
|
-
vec3 q2 = q.xyz + q.xyz;
|
|
25
|
-
vec3 a = q.xxx * q2.xyz;
|
|
26
|
-
vec3 b = q.yyz * q2.yzz;
|
|
27
|
-
vec3 c = q.www * q2.xyz;
|
|
28
|
-
|
|
29
|
-
vec3 r0 = vec3( 1.0 - (b.x + b.z) , a.y + c.z , a.z - c.y ) * s.xxx;
|
|
30
|
-
vec3 r1 = vec3( a.y - c.z , 1.0 - (a.x + b.z) , b.y + c.x ) * s.yyy;
|
|
31
|
-
vec3 r2 = vec3( a.z + c.y , b.y - c.x , 1.0 - (a.x + b.x) ) * s.zzz;
|
|
32
|
-
|
|
33
|
-
return mat4(
|
|
34
|
-
r0 , 0.0,
|
|
35
|
-
r1 , 0.0,
|
|
36
|
-
r2 , 0.0,
|
|
37
|
-
v , 1.0
|
|
38
|
-
);
|
|
39
|
-
}\n`;
|
|
2
|
+
attribute mat4 instanceMatrix;
|
|
3
|
+
\n`;
|
|
40
4
|
|
|
41
5
|
export function rewriteMaterial(shader) {
|
|
42
6
|
const originalVertexShader = shader.vertexShader;
|
|
43
7
|
|
|
44
8
|
const newVertexShader = SHADER_PREAMBLE
|
|
45
9
|
+ originalVertexShader
|
|
46
|
-
.replace(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
)
|
|
10
|
+
// .replace(
|
|
11
|
+
// '#include <uv_vertex>',
|
|
12
|
+
// `
|
|
13
|
+
// mat4 instanceMatrix = getInstanceMatrix();
|
|
14
|
+
// #include <uv_vertex>
|
|
15
|
+
// `
|
|
16
|
+
// )
|
|
53
17
|
.replace(
|
|
54
18
|
'#include <begin_vertex>',
|
|
55
19
|
`
|