@driftengine/assets 4.4.1 → 4.5.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/README.md +1 -1
- package/dist/drawKey.d.ts +17 -1
- package/dist/drawKey.js +9 -3
- package/dist/drftLoader.js +11 -0
- package/dist/gltf.d.ts +22 -3
- package/dist/gltf.js +36 -1
- package/dist/loadProgress.d.ts +8 -1
- package/package.json +4 -4
- package/src/drawKey.ts +26 -3
- package/src/drftLoader.ts +15 -2
- package/src/gltf.ts +68 -5
- package/src/loadProgress.ts +8 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Model readers for seven formats, and the streaming loader that uploads them.
|
|
4
4
|
|
|
5
|
-
**Cost: 15.
|
|
5
|
+
**Cost: 15.9 KB gzipped on top of core.** Measured by `scripts/size-gate.test.mjs`, which fails if it drifts more than
|
|
6
6
|
3% — the number is a fact about the build rather than a claim in a document.
|
|
7
7
|
|
|
8
8
|
Part of [DriftEngine](../../README.md). See [`docs/ARCHITECTURE.md`](../../docs/ARCHITECTURE.md)
|
package/dist/drawKey.d.ts
CHANGED
|
@@ -23,6 +23,15 @@ export interface DrawSurfaceOverride {
|
|
|
23
23
|
* gallery and read as a black blotch. 0 says dielectric.
|
|
24
24
|
*/
|
|
25
25
|
readonly metallicScale?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Glass, in place of what the file says: the share of light that passes, how milky it is, and
|
|
28
|
+
* the colour it takes (`GlassOptions` in the renderer). For a model whose panes arrive as an
|
|
29
|
+
* opaque dark material, because its format or its exporter had no word for glass. Transmission
|
|
30
|
+
* above zero draws the part blended.
|
|
31
|
+
*/
|
|
32
|
+
readonly transmission?: number;
|
|
33
|
+
readonly frost?: number;
|
|
34
|
+
readonly tint?: readonly [number, number, number];
|
|
26
35
|
}
|
|
27
36
|
/**
|
|
28
37
|
* The fields a merged group carries, resolved from a material and its override.
|
|
@@ -47,6 +56,13 @@ export interface DrawGrouping {
|
|
|
47
56
|
/** Whether both faces are drawn and lit. A merge of a two-sided part and a one-sided one would cull
|
|
48
57
|
* one of them or unhide the back of the other, so it is part of the key. */
|
|
49
58
|
readonly doubleSided: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Glass. Zero transmission is not glass, and then frost is 0 and the tint white whatever the
|
|
61
|
+
* file or the override said, so a stray value on an opaque surface never splits a draw.
|
|
62
|
+
*/
|
|
63
|
+
readonly transmission: number;
|
|
64
|
+
readonly frost: number;
|
|
65
|
+
readonly tint: readonly [number, number, number];
|
|
50
66
|
}
|
|
51
67
|
/**
|
|
52
68
|
* Resolve what a part will actually be drawn with.
|
|
@@ -56,7 +72,7 @@ export interface DrawGrouping {
|
|
|
56
72
|
* 0, which is the identity that lets a file saying nothing about a property draw the same as one
|
|
57
73
|
* that did not have the property to say.
|
|
58
74
|
*
|
|
59
|
-
* The override wins over the material for the
|
|
75
|
+
* The override wins over the material for the fields it may carry, because a consumer dressing
|
|
60
76
|
* a surface by name knows more than a default an exporter wrote. A cutout is not among them: it is
|
|
61
77
|
* a fact about the *image* a material wears, so nothing outside the file has anything to say
|
|
62
78
|
* about it.
|
package/dist/drawKey.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const CLEAR = [1, 1, 1];
|
|
1
2
|
/**
|
|
2
3
|
* Resolve what a part will actually be drawn with.
|
|
3
4
|
*
|
|
@@ -6,12 +7,14 @@
|
|
|
6
7
|
* 0, which is the identity that lets a file saying nothing about a property draw the same as one
|
|
7
8
|
* that did not have the property to say.
|
|
8
9
|
*
|
|
9
|
-
* The override wins over the material for the
|
|
10
|
+
* The override wins over the material for the fields it may carry, because a consumer dressing
|
|
10
11
|
* a surface by name knows more than a default an exporter wrote. A cutout is not among them: it is
|
|
11
12
|
* a fact about the *image* a material wears, so nothing outside the file has anything to say
|
|
12
13
|
* about it.
|
|
13
14
|
*/
|
|
14
15
|
export function resolveDrawGrouping(material, override) {
|
|
16
|
+
const transmission = Math.max(0, override?.transmission ?? material?.transmission ?? 0);
|
|
17
|
+
const glass = transmission > 0;
|
|
15
18
|
return {
|
|
16
19
|
albedo: material?.albedo ?? -1,
|
|
17
20
|
orm: material?.ormMap ?? -1,
|
|
@@ -23,8 +26,11 @@ export function resolveDrawGrouping(material, override) {
|
|
|
23
26
|
metallicScale: override?.metallicScale ?? material?.metallicScale ?? 1,
|
|
24
27
|
occlusionStrength: material?.occlusionStrength ?? 0,
|
|
25
28
|
cutout: material?.cutout ?? 0,
|
|
26
|
-
blend: material?.blend === true,
|
|
29
|
+
blend: material?.blend === true || glass,
|
|
27
30
|
doubleSided: material?.doubleSided === true,
|
|
31
|
+
transmission,
|
|
32
|
+
frost: glass ? (override?.frost ?? material?.frost ?? 0) : 0,
|
|
33
|
+
tint: glass ? (override?.tint ?? material?.tint ?? CLEAR) : CLEAR,
|
|
28
34
|
};
|
|
29
35
|
}
|
|
30
36
|
/**
|
|
@@ -40,5 +46,5 @@ export function resolveDrawGrouping(material, override) {
|
|
|
40
46
|
*/
|
|
41
47
|
export function drawKeyOf(material, override) {
|
|
42
48
|
const g = resolveDrawGrouping(material, override);
|
|
43
|
-
return `${g.albedo}:${g.orm}:${g.normal}:${g.emissive}:${g.opacity}:${g.reflectivity}:${g.roughnessScale}:${g.metallicScale}:${g.occlusionStrength}:${g.cutout}:${g.blend ? 1 : 0}:${g.doubleSided ? 1 : 0}`;
|
|
49
|
+
return `${g.albedo}:${g.orm}:${g.normal}:${g.emissive}:${g.opacity}:${g.reflectivity}:${g.roughnessScale}:${g.metallicScale}:${g.occlusionStrength}:${g.cutout}:${g.blend ? 1 : 0}:${g.doubleSided ? 1 : 0}:${g.transmission}:${g.frost}:${g.tint[0]},${g.tint[1]},${g.tint[2]}`;
|
|
44
50
|
}
|
package/dist/drftLoader.js
CHANGED
|
@@ -742,6 +742,7 @@ export class DrftLoader {
|
|
|
742
742
|
cutout: 0,
|
|
743
743
|
blend: false,
|
|
744
744
|
doubleSided: false,
|
|
745
|
+
glass: null,
|
|
745
746
|
reveal: this.outlinePart?.reveal ?? 0,
|
|
746
747
|
instances: null,
|
|
747
748
|
};
|
|
@@ -796,6 +797,7 @@ export class DrftLoader {
|
|
|
796
797
|
cutout,
|
|
797
798
|
blend,
|
|
798
799
|
doubleSided,
|
|
800
|
+
glass: glassOf(g),
|
|
799
801
|
});
|
|
800
802
|
}
|
|
801
803
|
const key = drawKeyOf(material, override);
|
|
@@ -814,6 +816,7 @@ export class DrftLoader {
|
|
|
814
816
|
cutout,
|
|
815
817
|
blend,
|
|
816
818
|
doubleSided,
|
|
819
|
+
glass: glassOf(g),
|
|
817
820
|
members: [],
|
|
818
821
|
};
|
|
819
822
|
this.groups.set(key, group);
|
|
@@ -854,6 +857,7 @@ export class DrftLoader {
|
|
|
854
857
|
cutout,
|
|
855
858
|
blend,
|
|
856
859
|
doubleSided,
|
|
860
|
+
glass: group.glass,
|
|
857
861
|
reveal: 0,
|
|
858
862
|
instances: null,
|
|
859
863
|
};
|
|
@@ -947,6 +951,7 @@ export class DrftLoader {
|
|
|
947
951
|
cutout: group.cutout,
|
|
948
952
|
blend: group.blend,
|
|
949
953
|
doubleSided: group.doubleSided,
|
|
954
|
+
glass: group.glass,
|
|
950
955
|
reveal: 1,
|
|
951
956
|
instances: null,
|
|
952
957
|
});
|
|
@@ -1068,6 +1073,12 @@ export class DrftLoader {
|
|
|
1068
1073
|
};
|
|
1069
1074
|
}
|
|
1070
1075
|
}
|
|
1076
|
+
/** A group's glass as the renderer takes it, or null for a group that lets no light through. */
|
|
1077
|
+
function glassOf(g) {
|
|
1078
|
+
if (g.transmission <= 0)
|
|
1079
|
+
return null;
|
|
1080
|
+
return { transmission: g.transmission, frost: g.frost, tint: [g.tint[0], g.tint[1], g.tint[2]] };
|
|
1081
|
+
}
|
|
1071
1082
|
/**
|
|
1072
1083
|
* The MIME type a codec's bytes carry, or `null` for one the browser has no decoder for.
|
|
1073
1084
|
*
|
package/dist/gltf.d.ts
CHANGED
|
@@ -80,16 +80,35 @@ interface GltfSpecularGlossiness {
|
|
|
80
80
|
texCoord?: number;
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* The three extensions that together say a material is glass: how much light passes
|
|
85
|
+
* (`KHR_materials_transmission`), how much of it is scattered on the way and the colour it takes
|
|
86
|
+
* (`KHR_materials_diffuse_transmission`), and the colour a thickness of it absorbs towards
|
|
87
|
+
* (`KHR_materials_volume`). Only the fields this reader maps are named.
|
|
88
|
+
*/
|
|
89
|
+
interface GltfTransmission {
|
|
90
|
+
transmissionFactor?: number;
|
|
91
|
+
}
|
|
92
|
+
interface GltfDiffuseTransmission {
|
|
93
|
+
diffuseTransmissionFactor?: number;
|
|
94
|
+
diffuseTransmissionColorFactor?: number[];
|
|
95
|
+
}
|
|
96
|
+
interface GltfVolume {
|
|
97
|
+
attenuationColor?: number[];
|
|
98
|
+
}
|
|
83
99
|
interface GltfMaterial {
|
|
84
100
|
pbrMetallicRoughness?: GltfPbrMetallicRoughness;
|
|
85
101
|
/**
|
|
86
|
-
* The material extensions this reader knows
|
|
102
|
+
* The material extensions this reader knows, and everything else is ignored.
|
|
87
103
|
*
|
|
88
|
-
* Read as
|
|
89
|
-
*
|
|
104
|
+
* Read as optional fields rather than as `Record<string, unknown>` so that adding one costs a
|
|
105
|
+
* name here and a branch where it is read, and so that a typo in either is a compile error.
|
|
90
106
|
*/
|
|
91
107
|
extensions?: {
|
|
92
108
|
KHR_materials_pbrSpecularGlossiness?: GltfSpecularGlossiness;
|
|
109
|
+
KHR_materials_transmission?: GltfTransmission;
|
|
110
|
+
KHR_materials_diffuse_transmission?: GltfDiffuseTransmission;
|
|
111
|
+
KHR_materials_volume?: GltfVolume;
|
|
93
112
|
};
|
|
94
113
|
normalTexture?: {
|
|
95
114
|
index: number;
|
package/dist/gltf.js
CHANGED
|
@@ -428,6 +428,7 @@ function materialOf(material) {
|
|
|
428
428
|
* A separate occlusion image is dropped rather than loaded into a second unit. One map, one
|
|
429
429
|
* unit, and `textureUnitBudget.test.ts` says the next unit belongs to emissive.
|
|
430
430
|
*/
|
|
431
|
+
const glass = glassOf(material, modulated ? undefined : roughness);
|
|
431
432
|
const occlusion = material?.occlusionTexture;
|
|
432
433
|
const occlusionStrength = modulated && occlusion !== undefined && occlusion.index === ormMap
|
|
433
434
|
? (occlusion.strength ?? 1)
|
|
@@ -447,7 +448,7 @@ function materialOf(material) {
|
|
|
447
448
|
* cannot say the material blends, and a leaf whose shape is all in its texture's alpha drew as a
|
|
448
449
|
* solid card. So it is said.
|
|
449
450
|
*/
|
|
450
|
-
blend: blends,
|
|
451
|
+
blend: blends || glass.transmission > 0,
|
|
451
452
|
/* Seen from both faces: a curtain, a leaf card. The specification's default is one face. */
|
|
452
453
|
doubleSided: material?.doubleSided === true,
|
|
453
454
|
/* Only in MASK mode: the specification says `alphaCutoff` has no meaning in the other two. */
|
|
@@ -463,8 +464,42 @@ function materialOf(material) {
|
|
|
463
464
|
metallicScale: modulated ? (pbr?.metallicFactor ?? 1) : 1,
|
|
464
465
|
occlusionStrength,
|
|
465
466
|
albedo: pbr?.baseColorTexture?.index ?? -1,
|
|
467
|
+
transmission: glass.transmission,
|
|
468
|
+
frost: glass.frost,
|
|
469
|
+
tint: glass.tint,
|
|
466
470
|
};
|
|
467
471
|
}
|
|
472
|
+
const unit = (value, fallback) => Math.min(1, Math.max(0, value ?? fallback));
|
|
473
|
+
/**
|
|
474
|
+
* What a material's extensions say about letting light through, as this engine's glass.
|
|
475
|
+
*
|
|
476
|
+
* **Rough glass is frosted glass.** `KHR_materials_transmission` says its roughness blurs what is
|
|
477
|
+
* seen through the surface, and that blur is exactly what `frost` is, so a transmissive material's
|
|
478
|
+
* roughness raises its frost and a diffusely transmitting share raises it too — whichever says
|
|
479
|
+
* more. Only transmission makes a material glass: a surface stating diffuse transmission alone is a
|
|
480
|
+
* leaf or a sheet of paper, which this engine's glass would draw see-through and let every shadow
|
|
481
|
+
* pass, so it stays the opaque surface it was. What that gives up is a paper shade stated by that
|
|
482
|
+
* extension alone, which arrives opaque; a per-material override is the door for it.
|
|
483
|
+
*
|
|
484
|
+
* `roughness` is `undefined` where a map carries the roughness per texel: the factor then only
|
|
485
|
+
* scales the map, so it says nothing about how rough the glass is, and frost comes from the
|
|
486
|
+
* diffuse share alone rather than from a number the file did not state.
|
|
487
|
+
*/
|
|
488
|
+
function glassOf(material, roughness) {
|
|
489
|
+
const ext = material?.extensions;
|
|
490
|
+
const transmission = unit(ext?.KHR_materials_transmission?.transmissionFactor, 0);
|
|
491
|
+
if (transmission <= 0)
|
|
492
|
+
return { transmission: 0, frost: 0, tint: [1, 1, 1] };
|
|
493
|
+
const diffuse = ext?.KHR_materials_diffuse_transmission;
|
|
494
|
+
const frost = Math.max(unit(diffuse?.diffuseTransmissionFactor, 0), unit(roughness, 0));
|
|
495
|
+
const colour = diffuse?.diffuseTransmissionColorFactor ?? ext?.KHR_materials_volume?.attenuationColor;
|
|
496
|
+
const tint = [
|
|
497
|
+
unit(colour?.[0], 1),
|
|
498
|
+
unit(colour?.[1], 1),
|
|
499
|
+
unit(colour?.[2], 1),
|
|
500
|
+
];
|
|
501
|
+
return { transmission, frost, tint };
|
|
502
|
+
}
|
|
468
503
|
/** The required extensions this reader implements, or follows far enough to fail loudly. */
|
|
469
504
|
const KNOWN_REQUIRED = new Set([
|
|
470
505
|
'KHR_materials_pbrSpecularGlossiness',
|
package/dist/loadProgress.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MeshHandle, InstancedHandle, MeshInstances } from '@driftengine/core';
|
|
1
|
+
import type { GlassOptions, MeshHandle, InstancedHandle, MeshInstances } from '@driftengine/core';
|
|
2
2
|
/**
|
|
3
3
|
* The vocabulary of a load in flight: which phase it is in, how far it has got, and what a
|
|
4
4
|
* finished part is.
|
|
@@ -120,6 +120,13 @@ export interface DrftPart {
|
|
|
120
120
|
readonly blend: boolean;
|
|
121
121
|
/** Whether both faces are seen: glTF's `doubleSided`. Drawn without culling, each face lit as the front. */
|
|
122
122
|
readonly doubleSided: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* The glass this part is, from its file or from `DrftLoaderOptions.surface`, or null for a part
|
|
125
|
+
* that lets no light through. A glass part is also `blend`, so a caller drawing blended parts
|
|
126
|
+
* translucent passes this as the draw's `glass` and every pane arrives as the file or the
|
|
127
|
+
* override said. One object per merged group, shared by its parts, so it is not rebuilt per draw.
|
|
128
|
+
*/
|
|
129
|
+
readonly glass: GlassOptions | null;
|
|
123
130
|
/**
|
|
124
131
|
* The copies this part is drawn as, or null for a part drawn once.
|
|
125
132
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@driftengine/assets",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0",
|
|
4
4
|
"description": "Model readers and the streaming loader for the .drft container",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
],
|
|
28
28
|
"sideEffects": false,
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@driftengine/drft": "4.
|
|
31
|
-
"@driftengine/texture": "4.
|
|
30
|
+
"@driftengine/drft": "4.5.0",
|
|
31
|
+
"@driftengine/texture": "4.5.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@driftengine/core": "4.
|
|
34
|
+
"@driftengine/core": "4.5.0"
|
|
35
35
|
},
|
|
36
36
|
"author": "Drift Technologies",
|
|
37
37
|
"repository": {
|
package/src/drawKey.ts
CHANGED
|
@@ -25,6 +25,15 @@ export interface DrawSurfaceOverride {
|
|
|
25
25
|
* gallery and read as a black blotch. 0 says dielectric.
|
|
26
26
|
*/
|
|
27
27
|
readonly metallicScale?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Glass, in place of what the file says: the share of light that passes, how milky it is, and
|
|
30
|
+
* the colour it takes (`GlassOptions` in the renderer). For a model whose panes arrive as an
|
|
31
|
+
* opaque dark material, because its format or its exporter had no word for glass. Transmission
|
|
32
|
+
* above zero draws the part blended.
|
|
33
|
+
*/
|
|
34
|
+
readonly transmission?: number;
|
|
35
|
+
readonly frost?: number;
|
|
36
|
+
readonly tint?: readonly [number, number, number];
|
|
28
37
|
}
|
|
29
38
|
|
|
30
39
|
/**
|
|
@@ -50,8 +59,17 @@ export interface DrawGrouping {
|
|
|
50
59
|
/** Whether both faces are drawn and lit. A merge of a two-sided part and a one-sided one would cull
|
|
51
60
|
* one of them or unhide the back of the other, so it is part of the key. */
|
|
52
61
|
readonly doubleSided: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Glass. Zero transmission is not glass, and then frost is 0 and the tint white whatever the
|
|
64
|
+
* file or the override said, so a stray value on an opaque surface never splits a draw.
|
|
65
|
+
*/
|
|
66
|
+
readonly transmission: number;
|
|
67
|
+
readonly frost: number;
|
|
68
|
+
readonly tint: readonly [number, number, number];
|
|
53
69
|
}
|
|
54
70
|
|
|
71
|
+
const CLEAR: readonly [number, number, number] = [1, 1, 1];
|
|
72
|
+
|
|
55
73
|
/**
|
|
56
74
|
* Resolve what a part will actually be drawn with.
|
|
57
75
|
*
|
|
@@ -60,7 +78,7 @@ export interface DrawGrouping {
|
|
|
60
78
|
* 0, which is the identity that lets a file saying nothing about a property draw the same as one
|
|
61
79
|
* that did not have the property to say.
|
|
62
80
|
*
|
|
63
|
-
* The override wins over the material for the
|
|
81
|
+
* The override wins over the material for the fields it may carry, because a consumer dressing
|
|
64
82
|
* a surface by name knows more than a default an exporter wrote. A cutout is not among them: it is
|
|
65
83
|
* a fact about the *image* a material wears, so nothing outside the file has anything to say
|
|
66
84
|
* about it.
|
|
@@ -69,6 +87,8 @@ export function resolveDrawGrouping(
|
|
|
69
87
|
material: DrftMaterial | undefined,
|
|
70
88
|
override?: DrawSurfaceOverride,
|
|
71
89
|
): DrawGrouping {
|
|
90
|
+
const transmission = Math.max(0, override?.transmission ?? material?.transmission ?? 0);
|
|
91
|
+
const glass = transmission > 0;
|
|
72
92
|
return {
|
|
73
93
|
albedo: material?.albedo ?? -1,
|
|
74
94
|
orm: material?.ormMap ?? -1,
|
|
@@ -80,8 +100,11 @@ export function resolveDrawGrouping(
|
|
|
80
100
|
metallicScale: override?.metallicScale ?? material?.metallicScale ?? 1,
|
|
81
101
|
occlusionStrength: material?.occlusionStrength ?? 0,
|
|
82
102
|
cutout: material?.cutout ?? 0,
|
|
83
|
-
blend: material?.blend === true,
|
|
103
|
+
blend: material?.blend === true || glass,
|
|
84
104
|
doubleSided: material?.doubleSided === true,
|
|
105
|
+
transmission,
|
|
106
|
+
frost: glass ? (override?.frost ?? material?.frost ?? 0) : 0,
|
|
107
|
+
tint: glass ? (override?.tint ?? material?.tint ?? CLEAR) : CLEAR,
|
|
85
108
|
};
|
|
86
109
|
}
|
|
87
110
|
|
|
@@ -101,5 +124,5 @@ export function drawKeyOf(
|
|
|
101
124
|
override?: DrawSurfaceOverride,
|
|
102
125
|
): string {
|
|
103
126
|
const g = resolveDrawGrouping(material, override);
|
|
104
|
-
return `${g.albedo}:${g.orm}:${g.normal}:${g.emissive}:${g.opacity}:${g.reflectivity}:${g.roughnessScale}:${g.metallicScale}:${g.occlusionStrength}:${g.cutout}:${g.blend ? 1 : 0}:${g.doubleSided ? 1 : 0}`;
|
|
127
|
+
return `${g.albedo}:${g.orm}:${g.normal}:${g.emissive}:${g.opacity}:${g.reflectivity}:${g.roughnessScale}:${g.metallicScale}:${g.occlusionStrength}:${g.cutout}:${g.blend ? 1 : 0}:${g.doubleSided ? 1 : 0}:${g.transmission}:${g.frost}:${g.tint[0]},${g.tint[1]},${g.tint[2]}`;
|
|
105
128
|
}
|
package/src/drftLoader.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import type { MeshData } from '@driftengine/drft';
|
|
4
4
|
import type { MeshHandle, RendererApi } from '@driftengine/core';
|
|
5
|
-
import type { SurfaceTextureHandle } from '@driftengine/core';
|
|
5
|
+
import type { GlassOptions, SurfaceTextureHandle } from '@driftengine/core';
|
|
6
6
|
import { MeshBuilder, concatMeshes, createMeshInstances, placeMesh } from '@driftengine/core';
|
|
7
7
|
import type { DrftMaterial } from '@driftengine/drft';
|
|
8
8
|
import type { DrftSdfvEntry } from '@driftengine/drft';
|
|
@@ -11,7 +11,7 @@ import type { DrftFieldPlacement } from './fieldPlacement.ts';
|
|
|
11
11
|
import type { DrftTexture } from '@driftengine/drft';
|
|
12
12
|
import { TextureSet, textureColorSpaces } from './drftTextures.ts';
|
|
13
13
|
import { drawKeyOf, resolveDrawGrouping } from './drawKey.ts';
|
|
14
|
-
import type { DrawSurfaceOverride } from './drawKey.ts';
|
|
14
|
+
import type { DrawGrouping, DrawSurfaceOverride } from './drawKey.ts';
|
|
15
15
|
import {
|
|
16
16
|
streamDrft,
|
|
17
17
|
CODEC_JPEG,
|
|
@@ -260,6 +260,8 @@ export class DrftLoader {
|
|
|
260
260
|
cutout: number;
|
|
261
261
|
blend: boolean;
|
|
262
262
|
doubleSided: boolean;
|
|
263
|
+
/** Built once when the group is made, and shared by every part drawn from it. */
|
|
264
|
+
glass: GlassOptions | null;
|
|
263
265
|
/** The group's parts, placed, joined once when the group is built. See `concatMeshes`. */
|
|
264
266
|
members: MeshData[];
|
|
265
267
|
}
|
|
@@ -964,6 +966,7 @@ export class DrftLoader {
|
|
|
964
966
|
cutout: 0,
|
|
965
967
|
blend: false,
|
|
966
968
|
doubleSided: false,
|
|
969
|
+
glass: null,
|
|
967
970
|
reveal: this.outlinePart?.reveal ?? 0,
|
|
968
971
|
instances: null,
|
|
969
972
|
};
|
|
@@ -1033,6 +1036,7 @@ export class DrftLoader {
|
|
|
1033
1036
|
cutout,
|
|
1034
1037
|
blend,
|
|
1035
1038
|
doubleSided,
|
|
1039
|
+
glass: glassOf(g),
|
|
1036
1040
|
});
|
|
1037
1041
|
}
|
|
1038
1042
|
|
|
@@ -1052,6 +1056,7 @@ export class DrftLoader {
|
|
|
1052
1056
|
cutout,
|
|
1053
1057
|
blend,
|
|
1054
1058
|
doubleSided,
|
|
1059
|
+
glass: glassOf(g),
|
|
1055
1060
|
members: [],
|
|
1056
1061
|
};
|
|
1057
1062
|
this.groups.set(key, group);
|
|
@@ -1093,6 +1098,7 @@ export class DrftLoader {
|
|
|
1093
1098
|
cutout,
|
|
1094
1099
|
blend,
|
|
1095
1100
|
doubleSided,
|
|
1101
|
+
glass: group.glass,
|
|
1096
1102
|
reveal: 0,
|
|
1097
1103
|
instances: null,
|
|
1098
1104
|
};
|
|
@@ -1190,6 +1196,7 @@ export class DrftLoader {
|
|
|
1190
1196
|
cutout: group.cutout,
|
|
1191
1197
|
blend: group.blend,
|
|
1192
1198
|
doubleSided: group.doubleSided,
|
|
1199
|
+
glass: group.glass,
|
|
1193
1200
|
reveal: 1,
|
|
1194
1201
|
instances: null,
|
|
1195
1202
|
});
|
|
@@ -1309,6 +1316,12 @@ export class DrftLoader {
|
|
|
1309
1316
|
}
|
|
1310
1317
|
}
|
|
1311
1318
|
|
|
1319
|
+
/** A group's glass as the renderer takes it, or null for a group that lets no light through. */
|
|
1320
|
+
function glassOf(g: DrawGrouping): GlassOptions | null {
|
|
1321
|
+
if (g.transmission <= 0) return null;
|
|
1322
|
+
return { transmission: g.transmission, frost: g.frost, tint: [g.tint[0], g.tint[1], g.tint[2]] };
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1312
1325
|
/**
|
|
1313
1326
|
* The MIME type a codec's bytes carry, or `null` for one the browser has no decoder for.
|
|
1314
1327
|
*
|
package/src/gltf.ts
CHANGED
|
@@ -77,15 +77,36 @@ interface GltfSpecularGlossiness {
|
|
|
77
77
|
/** RGB the specular colour, A the glossiness. Both multiply the factors above. */
|
|
78
78
|
specularGlossinessTexture?: { index: number; texCoord?: number };
|
|
79
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* The three extensions that together say a material is glass: how much light passes
|
|
82
|
+
* (`KHR_materials_transmission`), how much of it is scattered on the way and the colour it takes
|
|
83
|
+
* (`KHR_materials_diffuse_transmission`), and the colour a thickness of it absorbs towards
|
|
84
|
+
* (`KHR_materials_volume`). Only the fields this reader maps are named.
|
|
85
|
+
*/
|
|
86
|
+
interface GltfTransmission {
|
|
87
|
+
transmissionFactor?: number;
|
|
88
|
+
}
|
|
89
|
+
interface GltfDiffuseTransmission {
|
|
90
|
+
diffuseTransmissionFactor?: number;
|
|
91
|
+
diffuseTransmissionColorFactor?: number[];
|
|
92
|
+
}
|
|
93
|
+
interface GltfVolume {
|
|
94
|
+
attenuationColor?: number[];
|
|
95
|
+
}
|
|
80
96
|
interface GltfMaterial {
|
|
81
97
|
pbrMetallicRoughness?: GltfPbrMetallicRoughness;
|
|
82
98
|
/**
|
|
83
|
-
* The material extensions this reader knows
|
|
99
|
+
* The material extensions this reader knows, and everything else is ignored.
|
|
84
100
|
*
|
|
85
|
-
* Read as
|
|
86
|
-
*
|
|
101
|
+
* Read as optional fields rather than as `Record<string, unknown>` so that adding one costs a
|
|
102
|
+
* name here and a branch where it is read, and so that a typo in either is a compile error.
|
|
87
103
|
*/
|
|
88
|
-
extensions?: {
|
|
104
|
+
extensions?: {
|
|
105
|
+
KHR_materials_pbrSpecularGlossiness?: GltfSpecularGlossiness;
|
|
106
|
+
KHR_materials_transmission?: GltfTransmission;
|
|
107
|
+
KHR_materials_diffuse_transmission?: GltfDiffuseTransmission;
|
|
108
|
+
KHR_materials_volume?: GltfVolume;
|
|
109
|
+
};
|
|
89
110
|
normalTexture?: { index: number; texCoord?: number; scale?: number };
|
|
90
111
|
/** `strength` is a mix from 1, not a multiply: 0 means unoccluded. */
|
|
91
112
|
occlusionTexture?: { index: number; texCoord?: number; strength?: number };
|
|
@@ -640,6 +661,8 @@ function materialOf(material: GltfMaterial | undefined): DrftMaterial {
|
|
|
640
661
|
* A separate occlusion image is dropped rather than loaded into a second unit. One map, one
|
|
641
662
|
* unit, and `textureUnitBudget.test.ts` says the next unit belongs to emissive.
|
|
642
663
|
*/
|
|
664
|
+
const glass = glassOf(material, modulated ? undefined : roughness);
|
|
665
|
+
|
|
643
666
|
const occlusion = material?.occlusionTexture;
|
|
644
667
|
const occlusionStrength =
|
|
645
668
|
modulated && occlusion !== undefined && occlusion.index === ormMap
|
|
@@ -662,7 +685,7 @@ function materialOf(material: GltfMaterial | undefined): DrftMaterial {
|
|
|
662
685
|
* cannot say the material blends, and a leaf whose shape is all in its texture's alpha drew as a
|
|
663
686
|
* solid card. So it is said.
|
|
664
687
|
*/
|
|
665
|
-
blend: blends,
|
|
688
|
+
blend: blends || glass.transmission > 0,
|
|
666
689
|
/* Seen from both faces: a curtain, a leaf card. The specification's default is one face. */
|
|
667
690
|
doubleSided: material?.doubleSided === true,
|
|
668
691
|
/* Only in MASK mode: the specification says `alphaCutoff` has no meaning in the other two. */
|
|
@@ -678,9 +701,49 @@ function materialOf(material: GltfMaterial | undefined): DrftMaterial {
|
|
|
678
701
|
metallicScale: modulated ? (pbr?.metallicFactor ?? 1) : 1,
|
|
679
702
|
occlusionStrength,
|
|
680
703
|
albedo: pbr?.baseColorTexture?.index ?? -1,
|
|
704
|
+
transmission: glass.transmission,
|
|
705
|
+
frost: glass.frost,
|
|
706
|
+
tint: glass.tint,
|
|
681
707
|
};
|
|
682
708
|
}
|
|
683
709
|
|
|
710
|
+
const unit = (value: number | undefined, fallback: number): number =>
|
|
711
|
+
Math.min(1, Math.max(0, value ?? fallback));
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* What a material's extensions say about letting light through, as this engine's glass.
|
|
715
|
+
*
|
|
716
|
+
* **Rough glass is frosted glass.** `KHR_materials_transmission` says its roughness blurs what is
|
|
717
|
+
* seen through the surface, and that blur is exactly what `frost` is, so a transmissive material's
|
|
718
|
+
* roughness raises its frost and a diffusely transmitting share raises it too — whichever says
|
|
719
|
+
* more. Only transmission makes a material glass: a surface stating diffuse transmission alone is a
|
|
720
|
+
* leaf or a sheet of paper, which this engine's glass would draw see-through and let every shadow
|
|
721
|
+
* pass, so it stays the opaque surface it was. What that gives up is a paper shade stated by that
|
|
722
|
+
* extension alone, which arrives opaque; a per-material override is the door for it.
|
|
723
|
+
*
|
|
724
|
+
* `roughness` is `undefined` where a map carries the roughness per texel: the factor then only
|
|
725
|
+
* scales the map, so it says nothing about how rough the glass is, and frost comes from the
|
|
726
|
+
* diffuse share alone rather than from a number the file did not state.
|
|
727
|
+
*/
|
|
728
|
+
function glassOf(
|
|
729
|
+
material: GltfMaterial | undefined,
|
|
730
|
+
roughness: number | undefined,
|
|
731
|
+
): { transmission: number; frost: number; tint: [number, number, number] } {
|
|
732
|
+
const ext = material?.extensions;
|
|
733
|
+
const transmission = unit(ext?.KHR_materials_transmission?.transmissionFactor, 0);
|
|
734
|
+
if (transmission <= 0) return { transmission: 0, frost: 0, tint: [1, 1, 1] };
|
|
735
|
+
const diffuse = ext?.KHR_materials_diffuse_transmission;
|
|
736
|
+
const frost = Math.max(unit(diffuse?.diffuseTransmissionFactor, 0), unit(roughness, 0));
|
|
737
|
+
const colour =
|
|
738
|
+
diffuse?.diffuseTransmissionColorFactor ?? ext?.KHR_materials_volume?.attenuationColor;
|
|
739
|
+
const tint: [number, number, number] = [
|
|
740
|
+
unit(colour?.[0], 1),
|
|
741
|
+
unit(colour?.[1], 1),
|
|
742
|
+
unit(colour?.[2], 1),
|
|
743
|
+
];
|
|
744
|
+
return { transmission, frost, tint };
|
|
745
|
+
}
|
|
746
|
+
|
|
684
747
|
/** What a caller can tell the reader about what it is going to do with the result. */
|
|
685
748
|
export interface GltfReadOptions {
|
|
686
749
|
/**
|
package/src/loadProgress.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MeshHandle, InstancedHandle, MeshInstances } from '@driftengine/core';
|
|
1
|
+
import type { GlassOptions, MeshHandle, InstancedHandle, MeshInstances } from '@driftengine/core';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* The vocabulary of a load in flight: which phase it is in, how far it has got, and what a
|
|
@@ -134,6 +134,13 @@ export interface DrftPart {
|
|
|
134
134
|
readonly blend: boolean;
|
|
135
135
|
/** Whether both faces are seen: glTF's `doubleSided`. Drawn without culling, each face lit as the front. */
|
|
136
136
|
readonly doubleSided: boolean;
|
|
137
|
+
/**
|
|
138
|
+
* The glass this part is, from its file or from `DrftLoaderOptions.surface`, or null for a part
|
|
139
|
+
* that lets no light through. A glass part is also `blend`, so a caller drawing blended parts
|
|
140
|
+
* translucent passes this as the draw's `glass` and every pane arrives as the file or the
|
|
141
|
+
* override said. One object per merged group, shared by its parts, so it is not rebuilt per draw.
|
|
142
|
+
*/
|
|
143
|
+
readonly glass: GlassOptions | null;
|
|
137
144
|
/**
|
|
138
145
|
* The copies this part is drawn as, or null for a part drawn once.
|
|
139
146
|
*
|