phoria2 1.0.0 → 1.1.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 +6 -6
- package/dist/bloom.js +4 -4
- package/dist/bloom.js.map +1 -1
- package/dist/charts.js +377 -377
- package/dist/charts.js.map +1 -1
- package/dist/core.js +1052 -558
- package/dist/core.js.map +1 -1
- package/dist/edit.js +48 -48
- package/dist/edit.js.map +1 -1
- package/dist/explain.js +1 -1
- package/dist/gltf.js +207 -162
- package/dist/gltf.js.map +1 -1
- package/dist/motion.js +27 -27
- package/dist/motion.js.map +1 -1
- package/dist/particles.js +50 -50
- package/dist/particles.js.map +1 -1
- package/dist/passes.js +199 -97
- package/dist/passes.js.map +1 -1
- package/dist/phoria2.js +1 -1
- package/dist/picking.js +16 -21
- package/dist/picking.js.map +1 -1
- package/dist/snapshot.js +1 -1
- package/dist/tooltip.js +3 -3
- package/dist/tooltip.js.map +1 -1
- package/dist/types/View.d.ts +5 -0
- package/dist/types/geometry/Geometry.d.ts +6 -0
- package/dist/types/material/Material.d.ts +125 -0
- package/dist/types/material/Texture.d.ts +37 -5
- package/dist/types/webgl/WebGLRenderer.d.ts +6 -0
- package/dist/types/webgl/passes.d.ts +37 -13
- package/dist/types/webgl/resources.d.ts +24 -2
- package/dist/types/webgl/shaders.d.ts +4 -2
- package/llms-full.txt +96 -7
- package/package.json +1 -1
- package/schema/scene.schema.json +127 -0
- package/src/View.js +4 -2
- package/src/geometry/Geometry.js +8 -1
- package/src/gltf/load.js +50 -6
- package/src/material/Material.js +104 -19
- package/src/material/Texture.js +116 -11
- package/src/watch.js +19 -0
- package/src/webgl/WebGLRenderer.js +51 -12
- package/src/webgl/passes.js +190 -32
- package/src/webgl/picking.js +0 -1
- package/src/webgl/resources.js +89 -8
- package/src/webgl/shaders.js +350 -48
package/llms-full.txt
CHANGED
|
@@ -15,8 +15,8 @@ editors that drag, snap and undo. Picking, tooltips, hover highlights, HTML labe
|
|
|
15
15
|
image-based light are built in, and a view draws only when something changes, so idle pages cost
|
|
16
16
|
nothing. A dashboard of many small views just works - each tile its own `new View({ canvas })`,
|
|
17
17
|
with no limit on WebGL contexts to manage (after the first four, views share one). Apps come out short: typically a few dozen lines. For games, custom shaders or
|
|
18
|
-
post-processing pipelines, three.js is the better fit: phoria2 has no shader hook (no
|
|
19
|
-
|
|
18
|
+
post-processing pipelines, three.js is the better fit: phoria2 has no shader hook (no custom
|
|
19
|
+
effects), no refraction, and no shadow-softness setting.
|
|
20
20
|
|
|
21
21
|
## Install
|
|
22
22
|
|
|
@@ -285,8 +285,9 @@ extrude({
|
|
|
285
285
|
})
|
|
286
286
|
```
|
|
287
287
|
|
|
288
|
-
Your own: `new Geometry({ positions, normals?, uvs?, colors?, indices? })` - flat arrays, xyz per
|
|
289
|
-
vertex; `colors` are rgb 0-1 per vertex (use `material: { vertexColors: true }`)
|
|
288
|
+
Your own: `new Geometry({ positions, normals?, uvs?, uvs2?, colors?, indices? })` - flat arrays, xyz per
|
|
289
|
+
vertex; `colors` are rgb 0-1 per vertex (use `material: { vertexColors: true }`); `uvs2` is a second UV
|
|
290
|
+
set, for baked occlusion and light maps (below).
|
|
290
291
|
|
|
291
292
|
## Materials
|
|
292
293
|
|
|
@@ -296,7 +297,14 @@ One material type, given as a plain object:
|
|
|
296
297
|
{ color: 'white' } // lit, physically based
|
|
297
298
|
{ color: '#c33', roughness: 0.2, metalness: 1 } // metal
|
|
298
299
|
{ color: 'orange', unlit: true } // flat colour, ignores lights
|
|
299
|
-
{ color: '#8cf', opacity: 0.4 } // transparent
|
|
300
|
+
{ color: '#8cf', opacity: 0.4 } // transparent: fades the whole surface
|
|
301
|
+
{ transmission: 1, roughness: 0.05 } // clear glass
|
|
302
|
+
{ color: '#a3121f', metalness: 0.6, clearcoat: 1 } // car paint: a glossy coat over it
|
|
303
|
+
{ color: '#1f2d6e', roughness: 0.9, sheen: 1 } // velvet: cloth's bright rim
|
|
304
|
+
{ color: '#ccc', metalness: 1, anisotropy: 0.8 } // brushed metal
|
|
305
|
+
{ color: '#f2e4c4', translucency: 0.8 } // paper: lit from behind too
|
|
306
|
+
{ matcap: 'clay.png' } // the look from a picture of a lit sphere
|
|
307
|
+
{ color: '#e4572e', toon: 3 } // cartoon: three flat tones
|
|
300
308
|
{ emissive: '#ff0', emissiveIntensity: 3 } // glows
|
|
301
309
|
{ map: 'wood.jpg', normalMap: 'wood_n.png' } // textures from urls
|
|
302
310
|
```
|
|
@@ -313,7 +321,8 @@ Properties: `color`, `opacity`, `roughness` (0.5), `metalness` (0), `emissive`,
|
|
|
313
321
|
(`'normal'`, `'additive'` - adds light, for glows and sparks - or `'multiply'` - darkens what is behind, as a colour
|
|
314
322
|
filter does: cyan over magenta is blue), `alphaTest` (cut-outs: drop pixels with less alpha - leaves, fences),
|
|
315
323
|
`map`, `normalMap`/`normalScale`, `bumpMap`/`bumpScale`, `roughnessMap`, `metalnessMap`, `aoMap`,
|
|
316
|
-
`emissiveMap
|
|
324
|
+
`emissiveMap`, `lightMap`/`lightMapIntensity`, `environmentIntensity` (1), `transmission`, `clearcoat`/`clearcoatRoughness`, `sheen`/`sheenColor`/`sheenRoughness`, `ior`/
|
|
325
|
+
`specularIntensity`/`specularColor`, `anisotropy`/`anisotropyRotation`, `translucency`/`translucencyColor`, `matcap`, `toon` (below); `size`/`sizeAttenuation` for Points, `width` for Lines. Change later through
|
|
317
326
|
`mesh.material.color.set('#0f0')`, `mesh.material.opacity = 0.5`, or a whole new one: `mesh.material = { color: 'red' }`
|
|
318
327
|
(options become a Material, as in the constructor; `mesh.geometry = box(2)` likewise).
|
|
319
328
|
|
|
@@ -324,6 +333,83 @@ into the distance. A cut-out (`alphaTest`) casts the shadow of what is left of i
|
|
|
324
333
|
data map (`normalMap`, `bumpMap`, `roughnessMap`, `metalnessMap`, `aoMap`) is read as data, not colour,
|
|
325
334
|
unless its `colorSpace` was set on purpose - `normalMap: await loadTexture('n.png')` just works.
|
|
326
335
|
|
|
336
|
+
**Canvas and video textures**: a texture's `image` can be a canvas - draw on it, then call
|
|
337
|
+
`texture.needsUpdate()` - or a `<video>`, whose frames upload by themselves while it plays, with
|
|
338
|
+
nothing to call. `new Texture({ url: 'promo.mp4' })` (or .webm, .ogv, .mov, .m4v) makes the video
|
|
339
|
+
for you, muted and looping so browsers let it start by itself; `texture.image` is that `<video>` -
|
|
340
|
+
pause, seek or unmute it as any other. A browser that blocks autoplay leaves it on its first frame
|
|
341
|
+
until `texture.image.play()` from a click. Video textures make no mipmaps (they would be remade
|
|
342
|
+
every frame) unless `mipmaps: true`. Put one on an unlit plane for a screen:
|
|
343
|
+
`{ map: new Texture({ url: 'promo.mp4', wrap: 'clamp' }), unlit: true }`.
|
|
344
|
+
|
|
345
|
+
**Placing a texture**: `repeat` ([u, v] times across), `offset`, and `rotation` (radians, about
|
|
346
|
+
`center`: [0, 0], the corner, by default - [0.5, 0.5] for the middle) - applied in that order, as glTF's
|
|
347
|
+
`KHR_texture_transform` does, and each map keeps its own: a fabric's colour can repeat 7 times while
|
|
348
|
+
its normal map repeats twice. Animate them to scroll or spin a pattern.
|
|
349
|
+
|
|
350
|
+
**Baked occlusion and light maps**: occlusion and lighting baked in Blender, Unreal or Unity need
|
|
351
|
+
every surface on its own patch of texture, so they come on a second UV set while the first tiles
|
|
352
|
+
colour and detail. Give the geometry `uvs2` and the texture `uvSet: 1` - glTF models do both
|
|
353
|
+
for you (`TEXCOORD_1`, `texCoord: 1`). Read through the first set, baked occlusion lands in the
|
|
354
|
+
wrong places as dark smudges. `lightMap` is light baked into a texture, added as light from all
|
|
355
|
+
round (`lightMapIntensity`, 1) - the soft, cheap lighting of architectural scenes.
|
|
356
|
+
|
|
357
|
+
**Environment strength per material**: `environmentIntensity` (1) scales how much of the
|
|
358
|
+
scene's environment - its light and its reflections - one material takes: lower for a matte floor in
|
|
359
|
+
a shiny showroom, higher to make one product catch the light, without changing the scene's.
|
|
360
|
+
|
|
361
|
+
**Glass**: `transmission` (0-1) lets light through as glass does. Unlike `opacity`, which fades
|
|
362
|
+
everything - reflections too, so faded things look washed out, as dimmed chart bars should - glass
|
|
363
|
+
keeps its reflections whole, and shows less of what is behind it towards its edges, where it
|
|
364
|
+
reflects nearly everything. Its colour shows in proportion to `1 - transmission`: `transmission: 0.6`
|
|
365
|
+
with a colour is tinted glass. Keep `roughness` low for clear glass, higher for frosted. There is
|
|
366
|
+
no refraction - what is behind isn't bent. It shines with an `environment` to reflect
|
|
367
|
+
(`'studio'`, or better an `.hdr`).
|
|
368
|
+
|
|
369
|
+
**Clearcoat**: `clearcoat` (0-1) puts a clear glossy layer over the surface - car paint, lacquered
|
|
370
|
+
wood, varnish - with its own sharp reflection over the base's softer one; `clearcoatRoughness` (0,
|
|
371
|
+
a mirror) makes it satin. Normal and bump maps show under a smooth coat.
|
|
372
|
+
|
|
373
|
+
**Sheen**: `sheen` (0-1) is the soft bright rim of cloth at grazing angles - velvet, felt,
|
|
374
|
+
upholstery - which plain roughness can't give: without it fabric looks like matte plastic.
|
|
375
|
+
`sheenColor` (white) tints it, often lighter than the cloth or another colour entirely;
|
|
376
|
+
`sheenRoughness` (0.5) runs from a thin bright rim (0) to a broad soft glow (1).
|
|
377
|
+
|
|
378
|
+
**Reflectance**: `ior` (1.5) sets how strongly a non-metal reflects - water 1.33, glass and plastic
|
|
379
|
+
1.5, diamond 2.4 - and glass with `transmission` reflects more the higher it is.
|
|
380
|
+
`specularIntensity` (1) scales a non-metal's reflections, 0 for none (chalk, clay, unglazed
|
|
381
|
+
pottery); `specularColor` tints them face on. Metals take their reflections from `color`, so
|
|
382
|
+
these leave them alone.
|
|
383
|
+
|
|
384
|
+
**Brushed metal**: `anisotropy` (0-1) - the material's, not a texture's filtering - draws highlights and reflections out along the brushing -
|
|
385
|
+
brushed aluminium, steel appliances, the bottom of a pan. The brushing runs along the UVs' u (round
|
|
386
|
+
a sphere or cylinder, say; round the up axis on a mesh with no UVs); `anisotropyRotation`
|
|
387
|
+
(radians) turns it - `Math.PI / 2` runs it along v. Use it with `metalness: 1` and a middling
|
|
388
|
+
`roughness` (0.2-0.4).
|
|
389
|
+
|
|
390
|
+
**Translucency**: `translucency` (0-1) lets light through thin surfaces, so they are lit from
|
|
391
|
+
behind as well as in front - leaves against the sun, paper lanterns and lampshades with a light
|
|
392
|
+
inside, curtains. What passes through is taken from what is scattered back, so at 1 a surface is lit
|
|
393
|
+
only through it. `translucencyColor` (white) tints the light that comes through. It is light
|
|
394
|
+
through a thin sheet, not light scattering inside a solid: a lantern glows, marble stays marble.
|
|
395
|
+
|
|
396
|
+
**Matcap**: `matcap` is a picture of a lit sphere - clay, wax, chrome, a painted ball - that gives the
|
|
397
|
+
whole look: each point takes the colour from where a sphere facing the same way would show it. It is
|
|
398
|
+
the clay look of sculpting tools and CAD viewers, good for reading a model's shape, and costs one
|
|
399
|
+
texture lookup - no lights, shadows or environment, which it ignores. Its shine stays put as the camera
|
|
400
|
+
turns, since the light is in the picture, so it looks out of place beside lit things. `color` and
|
|
401
|
+
`map` multiply it, so one grey matcap serves any colour; normal and bump maps and `flatShading`
|
|
402
|
+
still shape it. Any matcap image works, or draw one on a canvas (`new Texture({ image: canvas })`).
|
|
403
|
+
|
|
404
|
+
**Toon**: `toon: 3` shades in that many flat tones (2 or more; 0, the default, is smooth), with one
|
|
405
|
+
hard-edged highlight - a cartoon. Lights and shadows work as usual; the shaded tone is the scene's
|
|
406
|
+
ambient and hemisphere light, so add some for a lighter shade. Metals keep their colour, with a
|
|
407
|
+
highlight of it. Pair it with `outline` on the nodes for inked edges.
|
|
408
|
+
|
|
409
|
+
glTF models bring glass, clearcoat, sheen, reflectance, brushed metal and translucency from `KHR_materials_transmission`, `KHR_materials_clearcoat`,
|
|
410
|
+
`KHR_materials_sheen`, `KHR_materials_ior`, `KHR_materials_specular`, `KHR_materials_anisotropy`
|
|
411
|
+
and `KHR_materials_diffuse_transmission` (by their factors; their textures aren't read).
|
|
412
|
+
|
|
327
413
|
**Models without UVs** (scans, CAD exports) can still take maps: `triplanar: 40` projects them
|
|
328
414
|
from the mesh's three axes instead of using UVs, repeating 40 times per unit of the mesh's own space.
|
|
329
415
|
The maps stay on the mesh as it moves; normal maps follow the surface; a map's `repeat` and `offset` apply
|
|
@@ -458,7 +544,10 @@ chair.onDrag = (drag) => {
|
|
|
458
544
|
```
|
|
459
545
|
|
|
460
546
|
- **Outline**: `outline: '#ffcc00'` or `{ color, width }` (pixels, up to 8; default 2) on a node draws
|
|
461
|
-
a line round its silhouette, over everything - for what is selected
|
|
547
|
+
a line round its silhouette, over everything - for what is selected, or as an inked edge for a
|
|
548
|
+
drawn look. Set it any time; `null` removes it. Outlines are antialiased - soft edges and round
|
|
549
|
+
corners - as the canvas is; `outlineAntialias: false` on the View (or `view.renderer.outlineAntialias =
|
|
550
|
+
false`, any time) draws them in hard pixel steps instead.
|
|
462
551
|
- `view.controls.cancel()` lets go of the orbit gesture in progress, for apps that take over a press.
|
|
463
552
|
|
|
464
553
|
## Charts: `phoria2/charts`
|
package/package.json
CHANGED
package/schema/scene.schema.json
CHANGED
|
@@ -703,6 +703,23 @@
|
|
|
703
703
|
"minItems": 2,
|
|
704
704
|
"maxItems": 2
|
|
705
705
|
},
|
|
706
|
+
"rotation": {
|
|
707
|
+
"type": "number",
|
|
708
|
+
"description": "Turns the image on the surface, in radians, about center."
|
|
709
|
+
},
|
|
710
|
+
"center": {
|
|
711
|
+
"type": "array",
|
|
712
|
+
"description": "The point rotation turns about, in repeated UVs ([0, 0], the corner, by default).",
|
|
713
|
+
"items": {
|
|
714
|
+
"type": "number"
|
|
715
|
+
},
|
|
716
|
+
"minItems": 2,
|
|
717
|
+
"maxItems": 2
|
|
718
|
+
},
|
|
719
|
+
"uvSet": {
|
|
720
|
+
"enum": [0, 1],
|
|
721
|
+
"description": "Which UV set to read: 0 the first, 1 the second (uvs2) - baked occlusion and light maps usually come on the second."
|
|
722
|
+
},
|
|
706
723
|
"filter": {
|
|
707
724
|
"enum": ["linear", "nearest"]
|
|
708
725
|
},
|
|
@@ -801,6 +818,28 @@
|
|
|
801
818
|
}
|
|
802
819
|
]
|
|
803
820
|
},
|
|
821
|
+
"lightMap": {
|
|
822
|
+
"description": "Light baked into a texture, added as light from all round - usually on the second UV set.",
|
|
823
|
+
"anyOf": [
|
|
824
|
+
{
|
|
825
|
+
"$ref": "#/$defs/texture"
|
|
826
|
+
},
|
|
827
|
+
{
|
|
828
|
+
"type": "null"
|
|
829
|
+
}
|
|
830
|
+
]
|
|
831
|
+
},
|
|
832
|
+
"matcap": {
|
|
833
|
+
"description": "A picture of a lit sphere that gives the whole look - clay, wax, chrome - picked by which way the surface faces the camera; lights are ignored.",
|
|
834
|
+
"anyOf": [
|
|
835
|
+
{
|
|
836
|
+
"$ref": "#/$defs/texture"
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
"type": "null"
|
|
840
|
+
}
|
|
841
|
+
]
|
|
842
|
+
},
|
|
804
843
|
"unlit": {
|
|
805
844
|
"type": "boolean",
|
|
806
845
|
"description": "Ignore lights: draws exactly its colour."
|
|
@@ -873,6 +912,15 @@
|
|
|
873
912
|
"minimum": 0,
|
|
874
913
|
"maximum": 1
|
|
875
914
|
},
|
|
915
|
+
"lightMapIntensity": {
|
|
916
|
+
"type": "number",
|
|
917
|
+
"minimum": 0
|
|
918
|
+
},
|
|
919
|
+
"environmentIntensity": {
|
|
920
|
+
"type": "number",
|
|
921
|
+
"description": "This material's share of the environment's light and reflections (1).",
|
|
922
|
+
"minimum": 0
|
|
923
|
+
},
|
|
876
924
|
"triplanar": {
|
|
877
925
|
"type": "number",
|
|
878
926
|
"description": "Repeats per unit, projecting maps from three axes instead of using UVs; 0 uses the UVs.",
|
|
@@ -881,6 +929,78 @@
|
|
|
881
929
|
"bumpScale": {
|
|
882
930
|
"type": "number"
|
|
883
931
|
},
|
|
932
|
+
"transmission": {
|
|
933
|
+
"type": "number",
|
|
934
|
+
"description": "How much light passes through, as through glass: reflections stay whole, and less of what is behind shows towards the edges. Unlike opacity, which fades the whole surface.",
|
|
935
|
+
"minimum": 0,
|
|
936
|
+
"maximum": 1
|
|
937
|
+
},
|
|
938
|
+
"clearcoat": {
|
|
939
|
+
"type": "number",
|
|
940
|
+
"description": "A clear glossy layer over the surface - car paint, lacquer, varnish.",
|
|
941
|
+
"minimum": 0,
|
|
942
|
+
"maximum": 1
|
|
943
|
+
},
|
|
944
|
+
"clearcoatRoughness": {
|
|
945
|
+
"type": "number",
|
|
946
|
+
"description": "How glossy the coat is, 0 a mirror.",
|
|
947
|
+
"minimum": 0,
|
|
948
|
+
"maximum": 1
|
|
949
|
+
},
|
|
950
|
+
"sheen": {
|
|
951
|
+
"type": "number",
|
|
952
|
+
"description": "The soft bright rim of cloth at grazing angles - velvet, felt, upholstery.",
|
|
953
|
+
"minimum": 0,
|
|
954
|
+
"maximum": 1
|
|
955
|
+
},
|
|
956
|
+
"sheenColor": {
|
|
957
|
+
"$ref": "#/$defs/color"
|
|
958
|
+
},
|
|
959
|
+
"sheenRoughness": {
|
|
960
|
+
"type": "number",
|
|
961
|
+
"description": "A thin bright rim at 0, a broad soft glow at 1.",
|
|
962
|
+
"minimum": 0,
|
|
963
|
+
"maximum": 1
|
|
964
|
+
},
|
|
965
|
+
"ior": {
|
|
966
|
+
"type": "number",
|
|
967
|
+
"description": "Index of refraction: how strongly a non-metal reflects - water 1.33, glass and plastic 1.5 (the default), diamond 2.4.",
|
|
968
|
+
"minimum": 1
|
|
969
|
+
},
|
|
970
|
+
"specularIntensity": {
|
|
971
|
+
"type": "number",
|
|
972
|
+
"description": "Scales a non-metal's reflections, 0 for none.",
|
|
973
|
+
"minimum": 0,
|
|
974
|
+
"maximum": 1
|
|
975
|
+
},
|
|
976
|
+
"specularColor": {
|
|
977
|
+
"$ref": "#/$defs/color"
|
|
978
|
+
},
|
|
979
|
+
"anisotropy": {
|
|
980
|
+
"type": "number",
|
|
981
|
+
"description": "Brushed metal: highlights drawn out along the brushing, which runs along the UVs' u.",
|
|
982
|
+
"minimum": 0,
|
|
983
|
+
"maximum": 1
|
|
984
|
+
},
|
|
985
|
+
"anisotropyRotation": {
|
|
986
|
+
"type": "number",
|
|
987
|
+
"description": "Turns the brushing from the UVs' u, in radians."
|
|
988
|
+
},
|
|
989
|
+
"translucency": {
|
|
990
|
+
"type": "number",
|
|
991
|
+
"description": "Light through thin surfaces, lighting them from behind - leaves, paper, lampshades.",
|
|
992
|
+
"minimum": 0,
|
|
993
|
+
"maximum": 1
|
|
994
|
+
},
|
|
995
|
+
"translucencyColor": {
|
|
996
|
+
"$ref": "#/$defs/color"
|
|
997
|
+
},
|
|
998
|
+
"toon": {
|
|
999
|
+
"type": "number",
|
|
1000
|
+
"description": "Cartoon shading in this many flat tones (2 or more), with a hard-edged highlight; 0 is smooth.",
|
|
1001
|
+
"minimum": 0,
|
|
1002
|
+
"maximum": 16
|
|
1003
|
+
},
|
|
884
1004
|
"blending": {
|
|
885
1005
|
"enum": ["normal", "additive", "multiply"]
|
|
886
1006
|
}
|
|
@@ -1129,6 +1249,13 @@
|
|
|
1129
1249
|
"type": "number"
|
|
1130
1250
|
}
|
|
1131
1251
|
},
|
|
1252
|
+
"uvs2": {
|
|
1253
|
+
"type": "array",
|
|
1254
|
+
"description": "A second UV set, for baked occlusion and light maps.",
|
|
1255
|
+
"items": {
|
|
1256
|
+
"type": "number"
|
|
1257
|
+
}
|
|
1258
|
+
},
|
|
1132
1259
|
"colors": {
|
|
1133
1260
|
"type": "array",
|
|
1134
1261
|
"items": {
|
package/src/View.js
CHANGED
|
@@ -53,6 +53,8 @@ import { checkOneOf, checkOptions, checkRange, describe, fail, warnOnce } from '
|
|
|
53
53
|
* @property {(hit: Hit) => TooltipContent} [tooltip] what to show for the thing under the pointer - see tooltip()
|
|
54
54
|
* @property {number} [pixelRatio] default min(devicePixelRatio, 2)
|
|
55
55
|
* @property {boolean} [antialias] (default true)
|
|
56
|
+
* @property {boolean} [outlineAntialias] soft-edged outlines with round corners (default: as antialias); change it
|
|
57
|
+
* any time with view.renderer.outlineAntialias
|
|
56
58
|
* @property {'auto' | 'own' | 'shared'} [context] which WebGL context draws it: 'own' gives the canvas one of
|
|
57
59
|
* its own; 'shared' draws with the page's one shared context and copies the picture over, so a page can hold
|
|
58
60
|
* any number of views (browsers allow about 16 live contexts). 'auto' (default): own for the first four
|
|
@@ -69,7 +71,7 @@ import { checkOneOf, checkOptions, checkRange, describe, fail, warnOnce } from '
|
|
|
69
71
|
/** @typedef {(hit: Hit | null, event: PointerEvent | MouseEvent | null) => void} ViewHandler */
|
|
70
72
|
|
|
71
73
|
const OPTIONS = ['canvas', 'scene', 'camera', 'orbit', 'background', 'renderMode', 'framing', 'highlight', 'tooltip', 'pixelRatio',
|
|
72
|
-
'antialias', 'context', 'toneMapping', 'exposure', 'bloom', 'autoLight', 'maxDelta']
|
|
74
|
+
'antialias', 'outlineAntialias', 'context', 'toneMapping', 'exposure', 'bloom', 'autoLight', 'maxDelta']
|
|
73
75
|
const FRAMING_OPTIONS = ['keep', 'aspect']
|
|
74
76
|
const HINTS = {
|
|
75
77
|
renderer: 'the View makes its renderer - read it from view.renderer',
|
|
@@ -213,7 +215,7 @@ export class View {
|
|
|
213
215
|
// the renderer checks these too; checked here, the error names the View
|
|
214
216
|
if (options.exposure !== undefined) checkRange('View', 'exposure', options.exposure, 0)
|
|
215
217
|
const shared = context === 'shared' || (context === 'auto' && ownContexts >= OWN_CONTEXTS)
|
|
216
|
-
this.renderer = new WebGLRenderer({ canvas: this.canvas, shared, antialias: options.antialias, toneMapping: options.toneMapping, exposure: options.exposure, bloom: options.bloom })
|
|
218
|
+
this.renderer = new WebGLRenderer({ canvas: this.canvas, shared, antialias: options.antialias, outlineAntialias: options.outlineAntialias, toneMapping: options.toneMapping, exposure: options.exposure, bloom: options.bloom })
|
|
217
219
|
if (!shared) {
|
|
218
220
|
this.#ownContext = true
|
|
219
221
|
ownContexts++
|
package/src/geometry/Geometry.js
CHANGED
|
@@ -6,11 +6,13 @@ import { checkOptions, describe, fail } from '../validate.js'
|
|
|
6
6
|
* @property {ArrayLike<number>} positions xyz per vertex
|
|
7
7
|
* @property {ArrayLike<number>} [normals] xyz per vertex; computed (smooth) when missing
|
|
8
8
|
* @property {ArrayLike<number>} [uvs] uv per vertex - glTF convention, (0, 0) is the image's top left
|
|
9
|
+
* @property {ArrayLike<number>} [uvs2] a second uv per vertex, for maps that need every surface on its own
|
|
10
|
+
* patch of texture - baked occlusion and light maps (a texture's uvSet: 1 reads it)
|
|
9
11
|
* @property {ArrayLike<number>} [colors] rgb per vertex, sRGB 0-1 like every colour input
|
|
10
12
|
* @property {ArrayLike<number>} [indices] vertex indices; triangles for meshes, pairs for lines
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
|
-
const OPTIONS = ['positions', 'normals', 'uvs', 'colors', 'indices']
|
|
15
|
+
const OPTIONS = ['positions', 'normals', 'uvs', 'uvs2', 'colors', 'indices']
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Vertex data: flat typed arrays plus an optional index. Generators such as box() and sphere()
|
|
@@ -21,6 +23,7 @@ export class Geometry {
|
|
|
21
23
|
static OPTIONS = OPTIONS
|
|
22
24
|
/** @type {Float32Array} */ positions
|
|
23
25
|
/** @type {Float32Array | null} */ uvs = null
|
|
26
|
+
/** A second UV set, for baked occlusion and light maps. @type {Float32Array | null} */ uvs2 = null
|
|
24
27
|
/** @type {Float32Array | null} */ colors = null
|
|
25
28
|
/** @type {Uint16Array | Uint32Array | null} */ indices = null
|
|
26
29
|
|
|
@@ -55,6 +58,7 @@ export class Geometry {
|
|
|
55
58
|
const n = this.vertexCount
|
|
56
59
|
if (options.normals) this.#normals = toFloat32('normals', options.normals, 3, n)
|
|
57
60
|
if (options.uvs) this.uvs = toFloat32('uvs', options.uvs, 2, n)
|
|
61
|
+
if (options.uvs2) this.uvs2 = toFloat32('uvs2', options.uvs2, 2, n)
|
|
58
62
|
if (options.colors) this.colors = toFloat32('colors', options.colors, 3, n)
|
|
59
63
|
if (options.indices) {
|
|
60
64
|
// -1: no indices at all refer to no vertex, so an empty geometry can say so
|
|
@@ -172,6 +176,7 @@ export class Geometry {
|
|
|
172
176
|
positions: this.positions,
|
|
173
177
|
normals: this.#normals ?? undefined,
|
|
174
178
|
uvs: this.uvs ?? undefined,
|
|
179
|
+
uvs2: this.uvs2 ?? undefined,
|
|
175
180
|
colors: this.colors ?? undefined,
|
|
176
181
|
indices: this.indices ?? undefined,
|
|
177
182
|
})
|
|
@@ -198,6 +203,7 @@ export class Geometry {
|
|
|
198
203
|
const json = { positions: Array.from(this.positions) }
|
|
199
204
|
if (this.#normals) json.normals = Array.from(this.#normals)
|
|
200
205
|
if (this.uvs) json.uvs = Array.from(this.uvs)
|
|
206
|
+
if (this.uvs2) json.uvs2 = Array.from(this.uvs2)
|
|
201
207
|
if (this.colors) json.colors = Array.from(this.colors)
|
|
202
208
|
if (this.indices) json.indices = Array.from(this.indices)
|
|
203
209
|
return json
|
|
@@ -221,6 +227,7 @@ export class Geometry {
|
|
|
221
227
|
}
|
|
222
228
|
this.positions = /** @type {Float32Array} */ (expand(this.positions, 3))
|
|
223
229
|
this.uvs = expand(this.uvs, 2)
|
|
230
|
+
this.uvs2 = expand(this.uvs2, 2)
|
|
224
231
|
this.colors = expand(this.colors, 3)
|
|
225
232
|
this.indices = null
|
|
226
233
|
this.source = null
|
package/src/gltf/load.js
CHANGED
|
@@ -25,7 +25,8 @@ import { Animation } from './Animation.js'
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
const SUPPORTED = new Set(['KHR_materials_emissive_strength', 'KHR_texture_transform', 'KHR_lights_punctual',
|
|
28
|
-
'KHR_materials_unlit', 'KHR_mesh_quantization'
|
|
28
|
+
'KHR_materials_unlit', 'KHR_mesh_quantization', 'KHR_materials_transmission', 'KHR_materials_clearcoat', 'KHR_materials_sheen',
|
|
29
|
+
'KHR_materials_ior', 'KHR_materials_specular', 'KHR_materials_anisotropy', 'KHR_materials_diffuse_transmission'])
|
|
29
30
|
const COMPRESSION = new Set(['KHR_draco_mesh_compression', 'EXT_meshopt_compression', 'KHR_texture_basisu'])
|
|
30
31
|
|
|
31
32
|
const COMPONENTS = { SCALAR: 1, VEC2: 2, VEC3: 3, VEC4: 4, MAT2: 4, MAT3: 9, MAT4: 16 }
|
|
@@ -266,6 +267,7 @@ class Builder {
|
|
|
266
267
|
const options = { positions: this.#accessor(a.POSITION) }
|
|
267
268
|
if (a.NORMAL !== undefined) options.normals = this.#accessor(a.NORMAL)
|
|
268
269
|
if (a.TEXCOORD_0 !== undefined) options.uvs = this.#accessor(a.TEXCOORD_0)
|
|
270
|
+
if (a.TEXCOORD_1 !== undefined) options.uvs2 = this.#accessor(a.TEXCOORD_1)
|
|
269
271
|
if (a.COLOR_0 !== undefined) options.colors = srgbColors(/** @type {Float32Array} */ (this.#accessor(a.COLOR_0)), COMPONENTS[this.json.accessors[a.COLOR_0].type])
|
|
270
272
|
if (p.indices !== undefined) options.indices = this.#accessor(p.indices, true)
|
|
271
273
|
const geometry = new Geometry(options)
|
|
@@ -364,6 +366,43 @@ class Builder {
|
|
|
364
366
|
}
|
|
365
367
|
material.emissiveIntensity = m.extensions?.KHR_materials_emissive_strength?.emissiveStrength ?? 1
|
|
366
368
|
if (m.extensions?.KHR_materials_unlit) material.unlit = true
|
|
369
|
+
// glass, clear coat, cloth, reflectance, brushed metal and translucency, by their factors - their
|
|
370
|
+
// own textures aren't read
|
|
371
|
+
const clamp = (/** @type {number} */ v) => Math.min(Math.max(v, 0), 1)
|
|
372
|
+
const x = m.extensions ?? {}
|
|
373
|
+
const transmission = x.KHR_materials_transmission, coat = x.KHR_materials_clearcoat, sheen = x.KHR_materials_sheen
|
|
374
|
+
if (transmission) material.transmission = clamp(transmission.transmissionFactor ?? 0)
|
|
375
|
+
if (coat) {
|
|
376
|
+
material.clearcoat = clamp(coat.clearcoatFactor ?? 0)
|
|
377
|
+
material.clearcoatRoughness = clamp(coat.clearcoatRoughnessFactor ?? 0)
|
|
378
|
+
}
|
|
379
|
+
if (sheen) {
|
|
380
|
+
const c = sheen.sheenColorFactor ?? [0, 0, 0]
|
|
381
|
+
// glTF's sheen is its colour alone: black is none
|
|
382
|
+
if (Math.max(c[0], c[1], c[2]) > 0) {
|
|
383
|
+
material.sheen = 1
|
|
384
|
+
material.sheenColor.setLinear(c[0], c[1], c[2])
|
|
385
|
+
}
|
|
386
|
+
material.sheenRoughness = clamp(sheen.sheenRoughnessFactor ?? 0)
|
|
387
|
+
}
|
|
388
|
+
if (x.KHR_materials_ior?.ior >= 1) material.ior = x.KHR_materials_ior.ior
|
|
389
|
+
const specular = x.KHR_materials_specular
|
|
390
|
+
if (specular) {
|
|
391
|
+
material.specularIntensity = clamp(specular.specularFactor ?? 1)
|
|
392
|
+
const c = specular.specularColorFactor ?? [1, 1, 1]
|
|
393
|
+
material.specularColor.setLinear(c[0], c[1], c[2])
|
|
394
|
+
}
|
|
395
|
+
const brushed = x.KHR_materials_anisotropy
|
|
396
|
+
if (brushed) {
|
|
397
|
+
material.anisotropy = clamp(brushed.anisotropyStrength ?? 0)
|
|
398
|
+
material.anisotropyRotation = brushed.anisotropyRotation ?? 0
|
|
399
|
+
}
|
|
400
|
+
const translucent = x.KHR_materials_diffuse_transmission
|
|
401
|
+
if (translucent) {
|
|
402
|
+
material.translucency = clamp(translucent.diffuseTransmissionFactor ?? 0)
|
|
403
|
+
const c = translucent.diffuseTransmissionColorFactor ?? [1, 1, 1]
|
|
404
|
+
material.translucencyColor.setLinear(c[0], c[1], c[2])
|
|
405
|
+
}
|
|
367
406
|
if (m.alphaMode === 'BLEND') {
|
|
368
407
|
material.opacity = base[3]
|
|
369
408
|
material.transparent = true
|
|
@@ -390,10 +429,12 @@ class Builder {
|
|
|
390
429
|
*/
|
|
391
430
|
#texture(info, colorSpace) {
|
|
392
431
|
const transform = info.extensions?.KHR_texture_transform
|
|
393
|
-
|
|
394
|
-
if (texCoord
|
|
395
|
-
|
|
396
|
-
|
|
432
|
+
let texCoord = transform?.texCoord ?? info.texCoord ?? 0
|
|
433
|
+
if (texCoord > 1) {
|
|
434
|
+
warnOnce(this.model, `textures on a third UV set or later (TEXCOORD_${texCoord}) are drawn with the first`)
|
|
435
|
+
texCoord = 0
|
|
436
|
+
}
|
|
437
|
+
const key = JSON.stringify([info.index, colorSpace, transform?.offset, transform?.scale, transform?.rotation, texCoord])
|
|
397
438
|
const cached = this.#textures.get(key)
|
|
398
439
|
if (cached) return cached
|
|
399
440
|
|
|
@@ -420,6 +461,8 @@ class Builder {
|
|
|
420
461
|
flipY: false,
|
|
421
462
|
repeat: transform?.scale ?? [1, 1],
|
|
422
463
|
offset: transform?.offset ?? [0, 0],
|
|
464
|
+
rotation: transform?.rotation ?? 0,
|
|
465
|
+
uvSet: texCoord,
|
|
423
466
|
})
|
|
424
467
|
this.#textures.set(key, texture)
|
|
425
468
|
return texture
|
|
@@ -538,5 +581,6 @@ function lineList(g, mode) {
|
|
|
538
581
|
function withIndices(g, indices, triangles) {
|
|
539
582
|
// only normals the file gave - reading g.normals would work them out from the strip's own indices,
|
|
540
583
|
// taken as a triangle list; the new geometry works them out from its real triangles instead
|
|
541
|
-
return new Geometry({ positions: g.positions, normals: triangles && g.hasNormals ? g.normals : undefined, uvs: g.uvs ?? undefined,
|
|
584
|
+
return new Geometry({ positions: g.positions, normals: triangles && g.hasNormals ? g.normals : undefined, uvs: g.uvs ?? undefined, uvs2: g.uvs2 ?? undefined,
|
|
585
|
+
colors: g.colors ?? undefined, indices })
|
|
542
586
|
}
|