@woosh/meep-engine 2.34.47 → 2.35.0
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/BitSet.js +10 -3
- package/core/bvh2/BinaryNode.js +19 -35
- package/core/bvh2/aabb3/aabb3_intersects_frustum_array.js +5 -5
- package/core/bvh2/aabb3_combined_surface_area.js +21 -0
- package/core/bvh2/traversal/queryBinaryNode_FrustumIntersections.js +24 -12
- package/core/cache/Cache.d.ts +2 -0
- package/core/cache/Cache.js +27 -23
- package/core/geom/3d/aabb/aabb3_estimate_projected_area2.js +1 -1
- package/core/geom/3d/frustum/frustum_from_projection_matrix_array.js +29 -24
- package/core/math/max2.js +2 -6
- package/core/math/min2.js +2 -6
- package/engine/asset/loaders/geometry/geometry/computeGeometryEquality.js +3 -2
- 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/foliage/ecs/Foliage2System.js +15 -3
- package/engine/ecs/parent/EntityNode.js +23 -11
- package/engine/ecs/transform/Transform.js +15 -13
- package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +63 -26
- package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +63 -3
- 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 +110 -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 +23 -5
- package/engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.js +8 -3
- package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +23 -85
- 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/convert_three_object_to_micron.js +0 -2
- package/engine/graphics/micron/format/MicronGeometry.js +1 -3
- package/engine/graphics/micron/format/MicronGeometryPatch.js +34 -24
- package/engine/graphics/micron/format/VirtualGeometry.js +47 -11
- package/engine/graphics/micron/format/serialization/MicronGeometryBinarySerializationAdapter.js +22 -0
- package/engine/graphics/micron/prototypeVirtualGeometry.js +10 -5
- package/engine/graphics/micron/render/VirtualGeometryBuilder.js +14 -55
- 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 -12
- package/engine/graphics/micron/render/refinement/get_geometry_patch_cut.js +118 -0
- package/engine/graphics/micron/render/v1/MaterialContext.js +3 -2
- package/engine/graphics/micron/render/v1/VGThreeRenderer.js +19 -4
- package/engine/graphics/render/utils/ThreeBypassRenderer.js +3 -2
- 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
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { traverseThreeObject } from "../../ecs/highlight/renderer/traverseThreeObject.js";
|
|
2
2
|
import { TextureAttachmentsByMaterialType } from "../../../asset/loaders/material/TextureAttachmensByMaterialType.js";
|
|
3
3
|
import { HashMap } from "../../../../core/collection/HashMap.js";
|
|
4
|
-
import Vector2 from "../../../../core/geom/Vector2.js";
|
|
5
|
-
import { traverseMaterialTextures } from "../../../asset/loaders/material/traverseMaterialTextures.js";
|
|
6
|
-
import { max2 } from "../../../../core/math/max2.js";
|
|
7
4
|
import AABB2 from "../../../../core/geom/AABB2.js";
|
|
8
5
|
import { MaxRectanglesPacker } from "../../../../core/geom/packing/MaxRectangles.js";
|
|
9
6
|
import { Sampler2D } from "../../texture/sampler/Sampler2D.js";
|
|
@@ -12,192 +9,358 @@ import { computeMaterialHash } from "../../../asset/loaders/material/computeMate
|
|
|
12
9
|
import { computeMaterialEquality } from "../../../asset/loaders/material/computeMaterialEquality.js";
|
|
13
10
|
import { collectIteratorValueToArray } from "../../../../core/collection/IteratorUtils.js";
|
|
14
11
|
import {
|
|
12
|
+
AdditiveBlending,
|
|
15
13
|
ClampToEdgeWrapping,
|
|
16
14
|
DataTexture,
|
|
17
|
-
LinearEncoding,
|
|
18
|
-
LinearFilter,
|
|
19
|
-
LinearMipmapLinearFilter,
|
|
20
15
|
Matrix3,
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
MeshStandardMaterial,
|
|
17
|
+
MultiplyBlending,
|
|
18
|
+
NormalBlending
|
|
23
19
|
} from "three";
|
|
24
20
|
import { convertTexture2Sampler2D } from "../../texture/sampler/convertTexture2Sampler2D.js";
|
|
21
|
+
import { BUFFER_GEOMETRY_UVS } from "./BUFFER_GEOMETRY_UVS.js";
|
|
22
|
+
import { is_compliant_mesh } from "./is_compliant_mesh.js";
|
|
23
|
+
import { MaterialDescriptor } from "./MaterialDescriptor.js";
|
|
24
|
+
import { computeThreeTextureTypeFromDataType } from "../../render/forward_plus/data/computeThreeTextureTypeFromDataType.js";
|
|
25
|
+
import { typedArrayToDataType } from "../../../../core/collection/array/typedArrayToDataType.js";
|
|
26
|
+
import { DataType } from "../../../../core/collection/table/DataType.js";
|
|
27
|
+
import { compute_typed_array_constructor_from_data_type } from "../../../../core/collection/table/DataType2TypedArrayConstructorMapping.js";
|
|
28
|
+
import { copy_Sampler2D_channel_data } from "../../texture/sampler/copy_Sampler2D_channel_data.js";
|
|
29
|
+
import { channelCountToThreeTextureFormat } from "../../texture/channelCountToThreeTextureFormat.js";
|
|
30
|
+
import { prepare_atlas_texture } from "./prepare_atlas_texture.js";
|
|
31
|
+
import { sampler2d_copy_channel_data } from "../../texture/sampler/sampler2d_copy_channel_data.js";
|
|
32
|
+
import { normalized_internal_format } from "../../texture/normalized_internal_format.js";
|
|
33
|
+
|
|
34
|
+
const ATLAS_PATCH_BORDER = 0;
|
|
25
35
|
|
|
26
|
-
const ATLAS_PATCH_BORDER = 4;
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
/**
|
|
38
|
+
*
|
|
39
|
+
* @param {THREE.Material} a
|
|
40
|
+
* @param {THREE.Material} b
|
|
41
|
+
* @returns {boolean}
|
|
42
|
+
*/
|
|
43
|
+
function materialsSimilarForCharting(a, b) {
|
|
44
|
+
|
|
45
|
+
return a.visible === b.visible
|
|
46
|
+
&& a.transparent === b.transparent
|
|
47
|
+
&& a.blending === b.blending
|
|
48
|
+
&& a.alphaTest === b.alphaTest
|
|
49
|
+
&& a.depthTest === b.depthTest
|
|
50
|
+
&& a.depthWrite === b.depthWrite
|
|
51
|
+
;
|
|
52
|
+
}
|
|
38
53
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
* @type {THREE.Texture|null}
|
|
54
|
-
*/
|
|
55
|
-
this.source_texture = null;
|
|
56
|
-
}
|
|
54
|
+
/**
|
|
55
|
+
*
|
|
56
|
+
* @param {THREE.Material} a
|
|
57
|
+
* @returns {number}
|
|
58
|
+
*/
|
|
59
|
+
function materialChartingHash(a) {
|
|
60
|
+
const flags = (a.visible ? 1 : 0)
|
|
61
|
+
| ((a.transparent ? 1 : 0) << 1)
|
|
62
|
+
| ((a.depthTest ? 1 : 0) << 2)
|
|
63
|
+
| ((a.depthWrite ? 1 : 0) << 3)
|
|
64
|
+
| (([NormalBlending, AdditiveBlending, MultiplyBlending].indexOf(a.blending) & 0b11) << 4)
|
|
65
|
+
| ((Math.floor(a.alphaTest * 15)) << 6) // 4 bit
|
|
66
|
+
;
|
|
67
|
+
return flags;
|
|
57
68
|
}
|
|
58
69
|
|
|
59
70
|
/**
|
|
60
71
|
*
|
|
61
|
-
* @param {THREE.
|
|
62
|
-
* @
|
|
72
|
+
* @param {THREE.DataTexture} metalness
|
|
73
|
+
* @param {THREE.DataTexture} roughness
|
|
74
|
+
* @returns {THREE.DataTexture}
|
|
63
75
|
*/
|
|
64
|
-
function
|
|
65
|
-
|
|
76
|
+
function merge_textures_metalness_rougness({
|
|
77
|
+
metalness,
|
|
78
|
+
roughness
|
|
79
|
+
}) {
|
|
80
|
+
const ref = metalness !== undefined ? metalness : roughness;
|
|
66
81
|
|
|
67
|
-
const
|
|
82
|
+
const pixel_count = ref.image.width * ref.image.height;
|
|
83
|
+
const result_data = new Uint8Array(pixel_count * 4);
|
|
68
84
|
|
|
69
|
-
const
|
|
85
|
+
const result = new DataTexture(result_data, ref.image.width, ref.image.height);
|
|
70
86
|
|
|
71
|
-
|
|
72
|
-
const attachment = attachments[i];
|
|
87
|
+
result.encoding = ref.encoding;
|
|
73
88
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
89
|
+
if (metalness !== undefined) {
|
|
90
|
+
for (let i = 0; i < pixel_count; i++) {
|
|
91
|
+
result_data[i * 4 + 2] = metalness.image.data[i];
|
|
77
92
|
}
|
|
78
|
-
|
|
79
|
-
const uv_index = attachment.uv_index;
|
|
80
|
-
|
|
81
|
-
uv_scales[uv_index * 2] = max2(uv_scales[uv_index * 2], texture.repeat.x)
|
|
82
|
-
uv_scales[uv_index * 2 + 1] = max2(uv_scales[uv_index * 2 + 1], texture.repeat.y)
|
|
83
93
|
}
|
|
84
94
|
|
|
85
|
-
|
|
95
|
+
if (roughness !== undefined) {
|
|
96
|
+
for (let i = 0; i < pixel_count; i++) {
|
|
97
|
+
result_data[i * 4 + 1] = roughness.image.data[i];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
86
100
|
|
|
87
|
-
|
|
88
|
-
const uv_metadata = BUFFER_GEOMETRY_UVS[i];
|
|
101
|
+
result.needsUpdate = true;
|
|
89
102
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
* @type {THREE.BufferAttribute }
|
|
93
|
-
*/
|
|
94
|
-
const attribute = geometry.attributes[uv_metadata.name];
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
95
105
|
|
|
96
|
-
|
|
97
|
-
|
|
106
|
+
/**
|
|
107
|
+
*
|
|
108
|
+
* @param {THREE.Material[]} source
|
|
109
|
+
* @param {Map<string, THREE.Texture>} textures
|
|
110
|
+
* @returns {Map<THREE.Material, THREE.Material>}
|
|
111
|
+
*/
|
|
112
|
+
function merge_materials(source, textures) {
|
|
113
|
+
// collect unique materials
|
|
114
|
+
const unique_materials = new HashMap({
|
|
115
|
+
keyEqualityFunction: materialsSimilarForCharting,
|
|
116
|
+
keyHashFunction: materialChartingHash
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
for (let i = 0; i < source.length; i++) {
|
|
120
|
+
const material = source[i];
|
|
121
|
+
|
|
122
|
+
let bucket = unique_materials.get(material);
|
|
123
|
+
if (bucket === undefined) {
|
|
124
|
+
bucket = [];
|
|
125
|
+
unique_materials.set(material, bucket);
|
|
98
126
|
}
|
|
99
127
|
|
|
100
|
-
|
|
101
|
-
|
|
128
|
+
bucket.push(material);
|
|
129
|
+
}
|
|
102
130
|
|
|
103
|
-
|
|
104
|
-
|
|
131
|
+
// build new materials
|
|
132
|
+
const source_unique_keys = [];
|
|
133
|
+
collectIteratorValueToArray(source_unique_keys, unique_materials.keys());
|
|
105
134
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
135
|
+
/**
|
|
136
|
+
*
|
|
137
|
+
* @type {Map<THREE.Material, THREE.Material>}
|
|
138
|
+
*/
|
|
139
|
+
const result = new Map();
|
|
109
140
|
|
|
110
|
-
|
|
111
|
-
|
|
141
|
+
for (let i = 0; i < source_unique_keys.length; i++) {
|
|
142
|
+
const source_material = source_unique_keys[i];
|
|
112
143
|
|
|
113
|
-
|
|
114
|
-
// repeating/negative UV value
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
144
|
+
const result_material = new MeshStandardMaterial();
|
|
117
145
|
|
|
118
|
-
}
|
|
146
|
+
result_material.name = `Generated charted material ${i}`;
|
|
119
147
|
|
|
120
|
-
|
|
148
|
+
result_material.visible = source_material.visible;
|
|
149
|
+
result_material.transparent = source_material.transparent;
|
|
150
|
+
result_material.blending = source_material.blending;
|
|
151
|
+
result_material.alphaTest = source_material.alphaTest;
|
|
152
|
+
result_material.depthTest = source_material.depthTest;
|
|
153
|
+
result_material.depthWrite = source_material.depthWrite;
|
|
121
154
|
|
|
122
|
-
|
|
123
|
-
|
|
155
|
+
// write atlased texture
|
|
156
|
+
const texture_attachments = TextureAttachmentsByMaterialType[result_material.type];
|
|
124
157
|
|
|
125
|
-
|
|
126
|
-
|
|
158
|
+
for (let j = 0; j < texture_attachments.length; j++) {
|
|
159
|
+
const textureAttachment = texture_attachments[j];
|
|
127
160
|
|
|
128
|
-
|
|
161
|
+
const existing_texture = textures.get(textureAttachment.name);
|
|
129
162
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
163
|
+
if (existing_texture !== undefined) {
|
|
164
|
+
textureAttachment.write(result_material, existing_texture);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// write result mapping
|
|
169
|
+
const destination_materials = unique_materials.get(source_material);
|
|
170
|
+
for (let j = 0; j < destination_materials.length; j++) {
|
|
171
|
+
result.set(destination_materials[j], result_material);
|
|
172
|
+
}
|
|
135
173
|
}
|
|
174
|
+
|
|
175
|
+
return result;
|
|
136
176
|
}
|
|
137
177
|
|
|
138
178
|
/**
|
|
139
179
|
*
|
|
140
|
-
* @param {
|
|
141
|
-
* @param {
|
|
180
|
+
* @param {Sampler2D} destination
|
|
181
|
+
* @param {Sampler2D} source
|
|
182
|
+
* @param {string} texture_name
|
|
142
183
|
*/
|
|
143
|
-
function
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
184
|
+
function extract_texture_data(destination, source, texture_name) {
|
|
185
|
+
switch (texture_name) {
|
|
186
|
+
case 'ao':
|
|
187
|
+
sampler2d_copy_channel_data(source, 0, destination, 0);
|
|
188
|
+
break;
|
|
189
|
+
case 'roughness':
|
|
190
|
+
sampler2d_copy_channel_data(source, 1, destination, 0);
|
|
191
|
+
break;
|
|
192
|
+
case 'metalness':
|
|
193
|
+
sampler2d_copy_channel_data(source, 2, destination, 0);
|
|
148
194
|
break;
|
|
149
195
|
default:
|
|
150
|
-
|
|
196
|
+
copy_Sampler2D_channel_data(source, destination);
|
|
151
197
|
}
|
|
198
|
+
}
|
|
152
199
|
|
|
153
|
-
|
|
154
|
-
|
|
200
|
+
/**
|
|
201
|
+
*
|
|
202
|
+
* @param {number[]} destination
|
|
203
|
+
* @param {THREE.Material|THREE.MeshStandardMaterial} source_material
|
|
204
|
+
* @param {string} texture_name
|
|
205
|
+
*/
|
|
206
|
+
function compute_default_texture_texel_value(destination, source_material, texture_name) {
|
|
207
|
+
switch (texture_name) {
|
|
208
|
+
case 'diffuse':
|
|
209
|
+
destination[0] = 255;
|
|
210
|
+
destination[1] = 255;
|
|
211
|
+
destination[2] = 255;
|
|
155
212
|
|
|
156
|
-
|
|
213
|
+
if (destination.length > 3) {
|
|
214
|
+
destination[3] = 255;
|
|
215
|
+
}
|
|
216
|
+
break;
|
|
217
|
+
case 'metalness':
|
|
218
|
+
for (let i = 0; i < destination.length; i++) {
|
|
219
|
+
destination[i] = Math.round(source_material.metalness * 255);
|
|
220
|
+
}
|
|
221
|
+
if (destination.length > 3) {
|
|
222
|
+
destination[3] = 255;
|
|
223
|
+
}
|
|
224
|
+
break;
|
|
225
|
+
case 'roughness':
|
|
226
|
+
for (let i = 0; i < destination.length; i++) {
|
|
227
|
+
destination[i] = Math.round(source_material.roughness * 255);
|
|
228
|
+
}
|
|
229
|
+
if (destination.length > 3) {
|
|
230
|
+
destination[3] = 255;
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
case 'ao':
|
|
234
|
+
for (let i = 0; i < destination.length; i++) {
|
|
235
|
+
destination[i] = 255;
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
case 'normal':
|
|
239
|
+
destination[0] = 127;
|
|
240
|
+
destination[1] = 127;
|
|
241
|
+
destination[2] = 255;
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
157
244
|
}
|
|
158
245
|
|
|
246
|
+
/**
|
|
247
|
+
*
|
|
248
|
+
* @param {Sampler2D} sampler
|
|
249
|
+
* @param {THREE.MeshStandardMaterial} material
|
|
250
|
+
* @param {string} texture_name
|
|
251
|
+
*/
|
|
252
|
+
function apply_material_shading_to_texture(sampler, material, texture_name) {
|
|
253
|
+
switch (texture_name) {
|
|
254
|
+
case 'diffuse': {
|
|
255
|
+
const r = material.color.r;
|
|
256
|
+
const g = material.color.g;
|
|
257
|
+
const b = material.color.b;
|
|
258
|
+
const opacity = material.opacity;
|
|
259
|
+
|
|
260
|
+
const itemSize = sampler.itemSize;
|
|
261
|
+
|
|
262
|
+
const data = sampler.data;
|
|
263
|
+
const data_size = data.length;
|
|
264
|
+
|
|
265
|
+
if (r < 1 || g < 1 || b < 1) {
|
|
266
|
+
// apply color tint
|
|
267
|
+
for (let i = 0; i < data_size; i += itemSize) {
|
|
268
|
+
data[i] *= r;
|
|
269
|
+
data[i + 1] *= g;
|
|
270
|
+
data[i + 2] *= b;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
159
273
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
* @type {THREE.Material|null}
|
|
166
|
-
*/
|
|
167
|
-
this.source_material = null;
|
|
274
|
+
if (itemSize > 3 && opacity < 1) {
|
|
275
|
+
// apply opacity
|
|
276
|
+
for (let i = 3; i < data_size; i += itemSize) {
|
|
277
|
+
data[i] *= opacity;
|
|
278
|
+
}
|
|
168
279
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
break;
|
|
283
|
+
default:
|
|
284
|
+
// do nothing
|
|
174
285
|
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
*
|
|
290
|
+
* @param {Sampler2D} destination
|
|
291
|
+
* @param {THREE.Material} source_material
|
|
292
|
+
* @param {string} texture_name
|
|
293
|
+
*/
|
|
294
|
+
function compute_default_texture_value(destination, source_material, texture_name) {
|
|
295
|
+
const itemSize = destination.itemSize;
|
|
175
296
|
|
|
176
|
-
|
|
297
|
+
const texel = new Array(itemSize);
|
|
177
298
|
|
|
178
|
-
|
|
179
|
-
const uv_index = attachment.uv_index;
|
|
299
|
+
compute_default_texture_texel_value(texel, source_material, texture_name);
|
|
180
300
|
|
|
181
|
-
|
|
301
|
+
const width = destination.width;
|
|
302
|
+
const height = destination.height;
|
|
182
303
|
|
|
183
|
-
|
|
184
|
-
uv_context = new UvContext();
|
|
185
|
-
uv_context.resolution.set(1, 1);
|
|
304
|
+
const destination_data = destination.data;
|
|
186
305
|
|
|
187
|
-
|
|
188
|
-
|
|
306
|
+
if (itemSize === 1) {
|
|
307
|
+
// special fast case
|
|
308
|
+
destination_data.fill(texel[0]);
|
|
309
|
+
} else {
|
|
189
310
|
|
|
190
|
-
|
|
311
|
+
const texel_count = width * height;
|
|
191
312
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
uv_context.resolution.y = max2(uv_context.resolution.y, image.height);
|
|
195
|
-
}
|
|
313
|
+
for (let i = 0; i < texel_count; i++) {
|
|
314
|
+
const destination_offset = i * itemSize;
|
|
196
315
|
|
|
197
|
-
|
|
316
|
+
destination_data.set(texel, destination_offset);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
198
320
|
|
|
321
|
+
/**
|
|
322
|
+
*
|
|
323
|
+
* @param {string} texture_name
|
|
324
|
+
* @returns {{type:DataType,itemSize:number, default_value:[]}}
|
|
325
|
+
*/
|
|
326
|
+
function build_atlas_sampler_options_texture_name(texture_name) {
|
|
327
|
+
let type;
|
|
328
|
+
let itemSize;
|
|
329
|
+
let default_value;
|
|
330
|
+
|
|
331
|
+
switch (texture_name) {
|
|
332
|
+
case 'normal':
|
|
333
|
+
type = DataType.Uint8;
|
|
334
|
+
itemSize = 3;
|
|
335
|
+
default_value = [127, 127, 255];
|
|
336
|
+
break;
|
|
337
|
+
case 'ao':
|
|
338
|
+
type = DataType.Uint8;
|
|
339
|
+
itemSize = 1;
|
|
340
|
+
default_value = [255];
|
|
341
|
+
break;
|
|
342
|
+
case "metalness":
|
|
343
|
+
type = DataType.Uint8;
|
|
344
|
+
itemSize = 1;
|
|
345
|
+
default_value = [0];
|
|
346
|
+
break;
|
|
347
|
+
case "roughness":
|
|
348
|
+
type = DataType.Uint8;
|
|
349
|
+
itemSize = 1;
|
|
350
|
+
default_value = [255];
|
|
351
|
+
break;
|
|
352
|
+
default:
|
|
353
|
+
type = DataType.Uint8;
|
|
354
|
+
itemSize = 4;
|
|
355
|
+
default_value = [255, 255, 255, 255];
|
|
356
|
+
break;
|
|
199
357
|
}
|
|
200
358
|
|
|
359
|
+
return {
|
|
360
|
+
type,
|
|
361
|
+
itemSize,
|
|
362
|
+
default_value
|
|
363
|
+
};
|
|
201
364
|
}
|
|
202
365
|
|
|
203
366
|
export class MaterialOptimizationContext {
|
|
@@ -304,12 +467,16 @@ export class MaterialOptimizationContext {
|
|
|
304
467
|
*/
|
|
305
468
|
build_atlas_samplers(materials) {
|
|
306
469
|
|
|
470
|
+
let i;
|
|
471
|
+
|
|
307
472
|
this.atlases.clear();
|
|
308
473
|
|
|
309
474
|
const names = [];
|
|
310
475
|
const uv_indices = [];
|
|
311
476
|
|
|
312
|
-
|
|
477
|
+
const material_count = materials.length;
|
|
478
|
+
|
|
479
|
+
for (i = 0; i < material_count; i++) {
|
|
313
480
|
const material = materials[i];
|
|
314
481
|
|
|
315
482
|
const source_material = material.source_material;
|
|
@@ -324,6 +491,13 @@ export class MaterialOptimizationContext {
|
|
|
324
491
|
for (let i = 0; i < attachments.length; i++) {
|
|
325
492
|
const attachment = attachments[i];
|
|
326
493
|
|
|
494
|
+
const attachment_texture = attachment.read(source_material);
|
|
495
|
+
|
|
496
|
+
if (!attachment_texture) {
|
|
497
|
+
// no attachment on the material
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
|
|
327
501
|
if (array_push_if_unique(names, attachment.name)) {
|
|
328
502
|
// added
|
|
329
503
|
uv_indices[names.length - 1] = attachment.uv_index;
|
|
@@ -332,13 +506,29 @@ export class MaterialOptimizationContext {
|
|
|
332
506
|
}
|
|
333
507
|
}
|
|
334
508
|
|
|
335
|
-
for (
|
|
509
|
+
for (i = 0; i < names.length; i++) {
|
|
336
510
|
const name = names[i];
|
|
337
511
|
const uv_index = uv_indices[i];
|
|
338
512
|
|
|
339
513
|
const size = this.atlas_size[uv_index];
|
|
340
514
|
|
|
341
|
-
|
|
515
|
+
const width = size.x;
|
|
516
|
+
const height = size.y;
|
|
517
|
+
|
|
518
|
+
const sampler_options = build_atlas_sampler_options_texture_name(name);
|
|
519
|
+
|
|
520
|
+
const TA = compute_typed_array_constructor_from_data_type(sampler_options.type);
|
|
521
|
+
|
|
522
|
+
const itemSize = sampler_options.itemSize;
|
|
523
|
+
const sampler_data_size = itemSize * width * height;
|
|
524
|
+
const sampler_data = new TA(sampler_data_size);
|
|
525
|
+
|
|
526
|
+
for (let j = 0; j < sampler_data_size; j += itemSize) {
|
|
527
|
+
sampler_data.set(sampler_options.default_value, j);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const atlas_sampler = new Sampler2D(sampler_data, itemSize, width, height);
|
|
531
|
+
this.atlases.set(name, atlas_sampler);
|
|
342
532
|
}
|
|
343
533
|
}
|
|
344
534
|
|
|
@@ -357,9 +547,17 @@ export class MaterialOptimizationContext {
|
|
|
357
547
|
*/
|
|
358
548
|
const attachments = TextureAttachmentsByMaterialType[materialType];
|
|
359
549
|
|
|
550
|
+
const attachment_names = Array.from(this.atlases.keys());
|
|
551
|
+
|
|
360
552
|
for (let i = 0; i < attachments.length; i++) {
|
|
361
553
|
const attachment = attachments[i];
|
|
362
554
|
|
|
555
|
+
const texture_name = attachment.name;
|
|
556
|
+
|
|
557
|
+
if (!attachment_names.includes(texture_name)) {
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
|
|
363
561
|
const uv_context = material.uvs[attachment.uv_index];
|
|
364
562
|
|
|
365
563
|
if (uv_context === undefined) {
|
|
@@ -367,28 +565,45 @@ export class MaterialOptimizationContext {
|
|
|
367
565
|
continue;
|
|
368
566
|
}
|
|
369
567
|
|
|
568
|
+
const atlas_sampler = this.atlases.get(texture_name);
|
|
569
|
+
|
|
370
570
|
let source_sampler;
|
|
371
571
|
|
|
372
572
|
const texture = attachment.read(source_material);
|
|
373
573
|
|
|
374
|
-
const
|
|
375
|
-
|
|
574
|
+
const resolution = uv_context.resolution;
|
|
575
|
+
|
|
576
|
+
const texture_width = resolution.x;
|
|
577
|
+
const texture_height = resolution.y;
|
|
578
|
+
|
|
376
579
|
if (texture !== undefined && texture !== null) {
|
|
377
580
|
// source_sampler = Sampler2D.uint8(5, texture_width, texture_height);
|
|
378
581
|
//
|
|
379
582
|
// source_sampler.fill(0, 0, texture_width, texture_height, [255, 255, 255, 255]);
|
|
380
583
|
|
|
381
584
|
source_sampler = convertTexture2Sampler2D(texture, texture_width, texture_height, false);
|
|
585
|
+
|
|
586
|
+
if (source_sampler.itemSize !== atlas_sampler.itemSize) {
|
|
587
|
+
const destination = Sampler2D.uint8(atlas_sampler.itemSize, texture_width, texture_height);
|
|
588
|
+
|
|
589
|
+
extract_texture_data(destination, source_sampler, texture_name);
|
|
590
|
+
|
|
591
|
+
source_sampler = destination;
|
|
592
|
+
}
|
|
593
|
+
|
|
382
594
|
} else {
|
|
383
|
-
//
|
|
384
|
-
source_sampler = Sampler2D.uint8(
|
|
595
|
+
// texture for a given slot is missing, create a default
|
|
596
|
+
source_sampler = Sampler2D.uint8(atlas_sampler.itemSize, texture_width, texture_height);
|
|
597
|
+
|
|
598
|
+
compute_default_texture_value(source_sampler, source_material, texture_name);
|
|
385
599
|
}
|
|
386
600
|
|
|
387
|
-
|
|
601
|
+
// apply modification to texture to remove dependency on source material
|
|
602
|
+
apply_material_shading_to_texture(source_sampler, source_material, texture_name);
|
|
388
603
|
|
|
389
604
|
const patch = uv_context.atlas_patch;
|
|
390
605
|
|
|
391
|
-
|
|
606
|
+
atlas_sampler.copy_sameItemSize(
|
|
392
607
|
source_sampler,
|
|
393
608
|
0, 0,
|
|
394
609
|
patch.x0, patch.y0, patch.getWidth(), patch.getHeight()
|
|
@@ -406,7 +621,7 @@ export class MaterialOptimizationContext {
|
|
|
406
621
|
if (material.isMeshStandardMaterial) {
|
|
407
622
|
this.meshes.push(mesh);
|
|
408
623
|
} else {
|
|
409
|
-
throw new Error(`Unsupported material type '${material.type}'`);
|
|
624
|
+
// throw new Error(`Unsupported material type '${material.type}'`);
|
|
410
625
|
}
|
|
411
626
|
}
|
|
412
627
|
|
|
@@ -488,8 +703,10 @@ export class MaterialOptimizationContext {
|
|
|
488
703
|
const unique_material_array = [];
|
|
489
704
|
collectIteratorValueToArray(unique_material_array, unique_materials.values());
|
|
490
705
|
|
|
491
|
-
|
|
492
|
-
|
|
706
|
+
const unique_material_count = unique_material_array.length;
|
|
707
|
+
|
|
708
|
+
if (unique_material_count <= 4) {
|
|
709
|
+
// too few unique materials, no point in atlasing
|
|
493
710
|
return;
|
|
494
711
|
}
|
|
495
712
|
|
|
@@ -497,28 +714,10 @@ export class MaterialOptimizationContext {
|
|
|
497
714
|
|
|
498
715
|
this.build_atlas_samplers(unique_material_array);
|
|
499
716
|
|
|
500
|
-
const textures =
|
|
501
|
-
//build new textures
|
|
502
|
-
for (const [name, sampler] of this.atlases) {
|
|
503
|
-
const texture = new DataTexture();
|
|
504
|
-
|
|
505
|
-
texture.image = {
|
|
506
|
-
data: sampler.data,
|
|
507
|
-
width: sampler.width,
|
|
508
|
-
height: sampler.height
|
|
509
|
-
};
|
|
510
|
-
|
|
511
|
-
texture.wrapS = ClampToEdgeWrapping;
|
|
512
|
-
texture.wrapT = ClampToEdgeWrapping;
|
|
513
|
-
texture.type = UnsignedByteType;
|
|
514
|
-
texture.anisotropy = 4;
|
|
515
|
-
|
|
516
|
-
prepare_atlas_texture(texture, name);
|
|
717
|
+
const textures = this.initialize_atlas_textures();
|
|
517
718
|
|
|
518
|
-
textures.set(name, texture);
|
|
519
|
-
}
|
|
520
719
|
|
|
521
|
-
for (let i = 0; i <
|
|
720
|
+
for (let i = 0; i < unique_material_count; i++) {
|
|
522
721
|
const mat = unique_material_array[i];
|
|
523
722
|
|
|
524
723
|
this.write_material_to_atlas(mat);
|
|
@@ -529,65 +728,135 @@ export class MaterialOptimizationContext {
|
|
|
529
728
|
for (let j = 0; j < geometries.length; j++) {
|
|
530
729
|
const bufferGeometry = geometries[j];
|
|
531
730
|
|
|
532
|
-
|
|
533
|
-
|
|
731
|
+
this.apply_geometry_uv_changes(bufferGeometry, mat);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/*
|
|
736
|
+
merge roughness and metalness. Three.js shaders sample roughness from G channel (index = 1) and metalness from B channel (index 2)
|
|
737
|
+
Because of this, we can't pass B/W textures with only Red channel set, so we are forced to create a merged texture from metalness and roughnes
|
|
738
|
+
*/
|
|
739
|
+
if (textures.get('roughness') !== undefined || textures.get('metalness') !== undefined) {
|
|
740
|
+
const result_texture = merge_textures_metalness_rougness({
|
|
741
|
+
roughness: textures.get('roughness'),
|
|
742
|
+
metalness: textures.get('metalness')
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
if (textures.get('roughness') !== undefined) {
|
|
746
|
+
textures.set('roughness', result_texture);
|
|
747
|
+
}
|
|
748
|
+
if (textures.get('metalness') !== undefined) {
|
|
749
|
+
textures.set('roughness', result_texture);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// update textures
|
|
754
|
+
for (const [name, texture] of textures) {
|
|
755
|
+
texture.needsUpdate = true;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// compute merged materials
|
|
759
|
+
const unique_source_materials = [];
|
|
760
|
+
collectIteratorValueToArray(unique_source_materials, unique_materials.keys());
|
|
534
761
|
|
|
535
|
-
|
|
536
|
-
|
|
762
|
+
const merged_materials = merge_materials(unique_source_materials, textures);
|
|
763
|
+
|
|
764
|
+
for (let i = 0; i < unique_material_count; i++) {
|
|
765
|
+
const mat = unique_material_array[i];
|
|
537
766
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
767
|
+
// bind material
|
|
768
|
+
const new_material = merged_materials.get(mat.source_material);
|
|
769
|
+
|
|
770
|
+
// set new material to all associated meshes
|
|
771
|
+
const meshes = material_to_mesh_map.get(mat);
|
|
772
|
+
|
|
773
|
+
for (let j = 0; j < meshes.length; j++) {
|
|
774
|
+
const mesh = meshes[j];
|
|
775
|
+
mesh.material = new_material;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
542
778
|
|
|
779
|
+
}
|
|
543
780
|
|
|
544
|
-
|
|
545
|
-
|
|
781
|
+
/**
|
|
782
|
+
*
|
|
783
|
+
* @return {Map<string, THREE.DataTexture>}
|
|
784
|
+
*/
|
|
785
|
+
initialize_atlas_textures() {
|
|
786
|
+
const textures = new Map();
|
|
787
|
+
//build new textures
|
|
788
|
+
for (const [name, sampler] of this.atlases) {
|
|
546
789
|
|
|
547
|
-
|
|
548
|
-
// no UV context present
|
|
549
|
-
// TODO need to force context to exist in this case or remove the attribute
|
|
550
|
-
continue;
|
|
551
|
-
}
|
|
790
|
+
const sampler_data_type = typedArrayToDataType(sampler.data);
|
|
552
791
|
|
|
553
|
-
|
|
792
|
+
const format = channelCountToThreeTextureFormat(sampler.itemSize);
|
|
793
|
+
const type = computeThreeTextureTypeFromDataType(sampler_data_type);
|
|
554
794
|
|
|
555
|
-
|
|
556
|
-
const atlas_height = atlas_size.y;
|
|
795
|
+
const texture = new DataTexture(sampler.data, sampler.width, sampler.height, format, type);
|
|
557
796
|
|
|
558
|
-
|
|
797
|
+
texture.wrapS = ClampToEdgeWrapping;
|
|
798
|
+
texture.wrapT = ClampToEdgeWrapping;
|
|
799
|
+
texture.name = name;
|
|
559
800
|
|
|
560
|
-
|
|
561
|
-
matrix3.setUvTransform(
|
|
562
|
-
patch.x0 / atlas_width,
|
|
563
|
-
patch.y0 / atlas_height,
|
|
564
|
-
patch.getWidth() / atlas_width,
|
|
565
|
-
patch.getHeight() / atlas_height,
|
|
566
|
-
0,
|
|
567
|
-
0, 0
|
|
568
|
-
);
|
|
801
|
+
texture.internalFormat = normalized_internal_format(sampler_data_type, sampler.itemSize);
|
|
569
802
|
|
|
570
|
-
|
|
803
|
+
prepare_atlas_texture(texture, name);
|
|
571
804
|
|
|
572
|
-
|
|
573
|
-
|
|
805
|
+
textures.set(name, texture);
|
|
806
|
+
}
|
|
807
|
+
return textures;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
*
|
|
812
|
+
* @param {THREE.BufferGeometry} bufferGeometry
|
|
813
|
+
* @param {MaterialDescriptor} mat
|
|
814
|
+
*/
|
|
815
|
+
apply_geometry_uv_changes(bufferGeometry, mat) {
|
|
816
|
+
const uv_count = BUFFER_GEOMETRY_UVS.length;
|
|
817
|
+
|
|
818
|
+
for (let k = 0; k < uv_count; k++) {
|
|
819
|
+
const uv_metadata = BUFFER_GEOMETRY_UVS[k];
|
|
820
|
+
|
|
821
|
+
// update UVs
|
|
822
|
+
const uv_attribute = bufferGeometry.attributes[uv_metadata.name];
|
|
823
|
+
|
|
824
|
+
if (uv_attribute === undefined) {
|
|
825
|
+
// no UVs, nothing to update
|
|
826
|
+
continue;
|
|
574
827
|
}
|
|
575
828
|
|
|
576
|
-
// build material
|
|
577
|
-
const new_material = mat.source_material.clone();
|
|
578
829
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
});
|
|
830
|
+
const uv_index = uv_metadata.index;
|
|
831
|
+
const uv_context = mat.uvs[uv_index];
|
|
582
832
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
mesh.material = new_material;
|
|
833
|
+
if (uv_context === undefined) {
|
|
834
|
+
// no UV context present
|
|
835
|
+
// TODO need to force context to exist in this case or remove the attribute
|
|
836
|
+
continue;
|
|
588
837
|
}
|
|
589
|
-
}
|
|
590
838
|
|
|
839
|
+
const atlas_size = this.atlas_size[uv_index];
|
|
840
|
+
|
|
841
|
+
const atlas_width = atlas_size.x;
|
|
842
|
+
const atlas_height = atlas_size.y;
|
|
843
|
+
|
|
844
|
+
const patch = uv_context.atlas_patch;
|
|
845
|
+
|
|
846
|
+
const matrix3 = new Matrix3();
|
|
847
|
+
matrix3.setUvTransform(
|
|
848
|
+
patch.x0 / atlas_width,
|
|
849
|
+
patch.y0 / atlas_height,
|
|
850
|
+
patch.getWidth() / atlas_width,
|
|
851
|
+
patch.getHeight() / atlas_height,
|
|
852
|
+
0,
|
|
853
|
+
0, 0
|
|
854
|
+
);
|
|
855
|
+
|
|
856
|
+
uv_attribute.applyMatrix3(matrix3);
|
|
857
|
+
|
|
858
|
+
uv_attribute.needsUpdate = true;
|
|
859
|
+
}
|
|
591
860
|
}
|
|
592
861
|
}
|
|
593
862
|
|