@selvajs/visualization 1.6.0-beta.9 → 1.6.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/dist/{gpu-dispose-fRI12OCL.cjs → gpu-dispose-D66GqIWS.cjs} +48 -3
- package/dist/gpu-dispose-D66GqIWS.cjs.map +1 -0
- package/dist/{gpu-dispose-Cl3RXm-v.js → gpu-dispose-Di_nxeXF.js} +31 -4
- package/dist/gpu-dispose-Di_nxeXF.js.map +1 -0
- package/dist/parse.cjs +264 -17
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.d.cts +15 -2
- package/dist/parse.d.ts +15 -2
- package/dist/parse.js +264 -17
- package/dist/parse.js.map +1 -1
- package/dist/render.cjs +762 -142
- package/dist/render.cjs.map +1 -1
- package/dist/render.d.cts +20 -6
- package/dist/render.d.ts +20 -6
- package/dist/render.js +762 -142
- package/dist/render.js.map +1 -1
- package/package.json +13 -7
- package/dist/gpu-dispose-Cl3RXm-v.js.map +0 -1
- package/dist/gpu-dispose-fRI12OCL.cjs.map +0 -1
package/dist/parse.d.cts
CHANGED
|
@@ -104,13 +104,26 @@ interface SerializableMaterial {
|
|
|
104
104
|
* when the writer sent UVs in mm. Omitted: maps stretch once over the UVs.
|
|
105
105
|
*/
|
|
106
106
|
mapSize?: number;
|
|
107
|
-
/**
|
|
108
|
-
finish?: 'brushed' | 'rolled' | 'wood' | 'concrete' | 'plaster' | 'brick' | 'corten';
|
|
107
|
+
/** A procedural surface drawn from UVs in mm: grain on metal, a whole material, or `molded` plastic grain. */
|
|
108
|
+
finish?: 'brushed' | 'rolled' | 'wood' | 'oak' | 'maple' | 'wenge' | 'walnut' | 'concrete' | 'plaster' | 'brick' | 'corten' | 'molded' | 'limestone' | 'travertine' | 'granite' | 'marble' | 'slate' | 'terrazzo' | 'paving' | 'setts' | 'gravel';
|
|
109
109
|
/** Scales the finish's streaks; 0 draws none. Omitted: 1. */
|
|
110
110
|
finishStrength?: number;
|
|
111
111
|
/** Light passing through (glass); unlike `opacity` it keeps reflections. */
|
|
112
112
|
transmission?: number;
|
|
113
113
|
ior?: number;
|
|
114
|
+
/**
|
|
115
|
+
* Glass body depth, in model units. Above 0 the glass refracts and tints, at the cost of drawing
|
|
116
|
+
* the scene twice a frame. Omitted: a thin pane.
|
|
117
|
+
*/
|
|
118
|
+
thickness?: number;
|
|
119
|
+
/** Hex colour light takes on after crossing `thickness` of glass. Omitted: clear. */
|
|
120
|
+
attenuationColor?: string;
|
|
121
|
+
/** 0 to 1. The soft glow at grazing angles of rubber, soft-touch plastic and cloth. Omitted: none. */
|
|
122
|
+
sheen?: number;
|
|
123
|
+
/** Hex colour of the sheen. Omitted: white. */
|
|
124
|
+
sheenColor?: string;
|
|
125
|
+
/** 0 to 1. Omitted: 0.5. */
|
|
126
|
+
sheenRoughness?: number;
|
|
114
127
|
}
|
|
115
128
|
/**
|
|
116
129
|
* `vertexStart`/`vertexCount` and `indexStart`/`indexCount` are in **element units** of the
|
package/dist/parse.d.ts
CHANGED
|
@@ -104,13 +104,26 @@ interface SerializableMaterial {
|
|
|
104
104
|
* when the writer sent UVs in mm. Omitted: maps stretch once over the UVs.
|
|
105
105
|
*/
|
|
106
106
|
mapSize?: number;
|
|
107
|
-
/**
|
|
108
|
-
finish?: 'brushed' | 'rolled' | 'wood' | 'concrete' | 'plaster' | 'brick' | 'corten';
|
|
107
|
+
/** A procedural surface drawn from UVs in mm: grain on metal, a whole material, or `molded` plastic grain. */
|
|
108
|
+
finish?: 'brushed' | 'rolled' | 'wood' | 'oak' | 'maple' | 'wenge' | 'walnut' | 'concrete' | 'plaster' | 'brick' | 'corten' | 'molded' | 'limestone' | 'travertine' | 'granite' | 'marble' | 'slate' | 'terrazzo' | 'paving' | 'setts' | 'gravel';
|
|
109
109
|
/** Scales the finish's streaks; 0 draws none. Omitted: 1. */
|
|
110
110
|
finishStrength?: number;
|
|
111
111
|
/** Light passing through (glass); unlike `opacity` it keeps reflections. */
|
|
112
112
|
transmission?: number;
|
|
113
113
|
ior?: number;
|
|
114
|
+
/**
|
|
115
|
+
* Glass body depth, in model units. Above 0 the glass refracts and tints, at the cost of drawing
|
|
116
|
+
* the scene twice a frame. Omitted: a thin pane.
|
|
117
|
+
*/
|
|
118
|
+
thickness?: number;
|
|
119
|
+
/** Hex colour light takes on after crossing `thickness` of glass. Omitted: clear. */
|
|
120
|
+
attenuationColor?: string;
|
|
121
|
+
/** 0 to 1. The soft glow at grazing angles of rubber, soft-touch plastic and cloth. Omitted: none. */
|
|
122
|
+
sheen?: number;
|
|
123
|
+
/** Hex colour of the sheen. Omitted: white. */
|
|
124
|
+
sheenColor?: string;
|
|
125
|
+
/** 0 to 1. Omitted: 0.5. */
|
|
126
|
+
sheenRoughness?: number;
|
|
114
127
|
}
|
|
115
128
|
/**
|
|
116
129
|
* `vertexStart`/`vertexCount` and `indexStart`/`indexCount` are in **element units** of the
|
package/dist/parse.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { S as VisualizationError, a as observeMaxAnisotropy, c as computeCombinedBoundingBox, d as EMISSIVE_LIGHT, h as lookEnvMapIntensity, i as isGlass, l as parseColor, m as OWN_ENV_MAP_INTENSITY, n as GLASS, p as OWN_EMISSIVE, s as applyOffset, t as disposeObjectTree, x as ErrorCodes, y as getLogger } from "./gpu-dispose-Di_nxeXF.js";
|
|
2
2
|
import { a as shareGeometry, o as unshareGeometry } from "./gpu-ownership-Bs9l7hrU.js";
|
|
3
3
|
import * as THREE from "three";
|
|
4
4
|
import { Line2 } from "three/addons/lines/Line2.js";
|
|
@@ -1679,6 +1679,32 @@ function applyTexture(material, url, slot, mapSize) {
|
|
|
1679
1679
|
});
|
|
1680
1680
|
}
|
|
1681
1681
|
// src/parse/webdisplay/batch/surface-finish.ts
|
|
1682
|
+
const f$1 = (n) => n.toFixed(4);
|
|
1683
|
+
const PITH = 120;
|
|
1684
|
+
const PITH_SPREAD = 120;
|
|
1685
|
+
const PLANK = 180;
|
|
1686
|
+
const LEAN = .0105;
|
|
1687
|
+
const wood = ({ ring, latewood, band, pores, streaks }) => `
|
|
1688
|
+
float plank = floor( uv.y / ${f$1(PLANK)} );
|
|
1689
|
+
float s = uv.y - ( plank + 0.5 ) * ${f$1(PLANK)};
|
|
1690
|
+
float pith = ${f$1(PITH)} + selvaHash( vec2( plank, 2.0 ) ) * ${f$1(PITH_SPREAD)};
|
|
1691
|
+
// Rings wander a few mm and vary in spacing, as growth does from year to year.
|
|
1692
|
+
float wander = ( selvaFbm( uv * vec2( 0.003, 0.008 ) ) - 0.5 ) * ${f$1(ring * 2)};
|
|
1693
|
+
float r = sqrt( pith * pith + s * s ) + uv.x * ${f$1(LEAN)} + wander;
|
|
1694
|
+
r += selvaFbm( vec2( r * 0.02, plank ) ) * ${f$1(ring * 3)} + selvaHash( vec2( plank, 1.0 ) ) * ${f$1(ring * 20)};
|
|
1695
|
+
float year = fract( r / ${f$1(ring)} );
|
|
1696
|
+
float late = smoothstep( ${f$1(1 - band)}, ${f$1(1 - band * .4)}, year ) * ( 1.0 - smoothstep( 0.95, 1.0, year ) );
|
|
1697
|
+
// Open-pored wood shows its rings as a broken band of pores, not a clean line.
|
|
1698
|
+
late *= 1.0 - ${f$1(pores * .6)} * ( 1.0 - smoothstep( 0.3, 0.7, selvaNoise( uv * vec2( 0.25, 2.0 ) ) ) );
|
|
1699
|
+
late *= selvaDetail( ${f$1(ring)} );
|
|
1700
|
+
float fibre = ( selvaNoise( uv * vec2( 0.04, 1.6 ) ) - 0.5 ) * selvaDetail( 1.0 );
|
|
1701
|
+
float pore = smoothstep( 0.62, 0.8, selvaNoise( uv * vec2( 0.3, 2.5 ) ) ) * ( 1.0 - 0.6 * late );
|
|
1702
|
+
pore *= ${f$1(pores)} * selvaDetail( 0.8 );
|
|
1703
|
+
float streak = ( selvaFbm( uv * vec2( 0.0015, 0.012 ) ) - 0.5 ) * ${f$1(streaks)};
|
|
1704
|
+
float shade = 1.0 + ${f$1(latewood * band)} - late * ${f$1(latewood)} + fibre * 0.1 - pore * 0.45 + streak * 0.6;
|
|
1705
|
+
tone = vec3( shade );
|
|
1706
|
+
gloss = 1.0 + late * 0.08 + pore * 0.4 - fibre * 0.2;
|
|
1707
|
+
height = ( fibre * 0.00008 - pore * 0.00015 ) * selvaDetail( 0.6 );`;
|
|
1682
1708
|
/**
|
|
1683
1709
|
* Procedural surfaces, so a preset looks like its material with no texture files to ship or upload.
|
|
1684
1710
|
* UVs are millimetres, so a brick is brick-sized at any model scale. Each recipe sets `tone` (albedo
|
|
@@ -1687,15 +1713,41 @@ function applyTexture(material, url, slot, mapSize) {
|
|
|
1687
1713
|
* samples to static, so a whole building has to settle to its flat colour.
|
|
1688
1714
|
*/
|
|
1689
1715
|
const RECIPES$1 = {
|
|
1690
|
-
wood:
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1716
|
+
wood: wood({
|
|
1717
|
+
ring: 4,
|
|
1718
|
+
latewood: .3,
|
|
1719
|
+
band: .4,
|
|
1720
|
+
pores: .4,
|
|
1721
|
+
streaks: .35
|
|
1722
|
+
}),
|
|
1723
|
+
walnut: wood({
|
|
1724
|
+
ring: 5,
|
|
1725
|
+
latewood: .2,
|
|
1726
|
+
band: .4,
|
|
1727
|
+
pores: 1,
|
|
1728
|
+
streaks: .45
|
|
1729
|
+
}),
|
|
1730
|
+
oak: wood({
|
|
1731
|
+
ring: 3,
|
|
1732
|
+
latewood: .3,
|
|
1733
|
+
band: .35,
|
|
1734
|
+
pores: .9,
|
|
1735
|
+
streaks: .25
|
|
1736
|
+
}),
|
|
1737
|
+
maple: wood({
|
|
1738
|
+
ring: 3,
|
|
1739
|
+
latewood: .2,
|
|
1740
|
+
band: .1,
|
|
1741
|
+
pores: 0,
|
|
1742
|
+
streaks: .12
|
|
1743
|
+
}),
|
|
1744
|
+
wenge: wood({
|
|
1745
|
+
ring: 1.5,
|
|
1746
|
+
latewood: .7,
|
|
1747
|
+
band: .5,
|
|
1748
|
+
pores: .6,
|
|
1749
|
+
streaks: .4
|
|
1750
|
+
}),
|
|
1699
1751
|
concrete: `
|
|
1700
1752
|
float blotch = ( selvaFbm( uv * 0.0035 ) - 0.5 ) * selvaDetail( 40.0 );
|
|
1701
1753
|
float holes = smoothstep( 0.8, 0.88, selvaNoise( uv * 0.22 ) ) * selvaDetail( 5.0 );
|
|
@@ -1720,6 +1772,127 @@ const RECIPES$1 = {
|
|
|
1720
1772
|
tone = mix( vec3( face ), vec3( 1.5, 1.7, 1.8 ), joint );
|
|
1721
1773
|
gloss = 1.0 + joint * 0.15;
|
|
1722
1774
|
height = -joint * 0.005;`,
|
|
1775
|
+
limestone: `
|
|
1776
|
+
float cloud = ( selvaFbm( uv * 0.004 ) - 0.5 ) * selvaDetail( 60.0 );
|
|
1777
|
+
float mottle = ( selvaFbm( uv * 0.025 + vec2( 3.0, 7.0 ) ) - 0.5 ) * selvaDetail( 12.0 );
|
|
1778
|
+
float bed = ( selvaNoise( vec2( uv.x * 0.003, uv.y * 0.05 ) ) - 0.5 ) * selvaDetail( 10.0 );
|
|
1779
|
+
float fleck = smoothstep( 0.8, 0.9, selvaNoise( uv * 0.5 ) ) * selvaDetail( 2.0 );
|
|
1780
|
+
vec3 fossil = selvaCell( uv / 14.0 );
|
|
1781
|
+
float shell = ( 1.0 - smoothstep( 0.08, 0.16, fossil.x ) ) * step( 0.85, fossil.z ) * selvaDetail( 6.0 );
|
|
1782
|
+
tone = vec3( 1.0 + cloud * 0.14 + mottle * 0.1 + bed * 0.03 - fleck * 0.12 - shell * 0.1 );
|
|
1783
|
+
gloss = 1.0 + cloud * 0.1;
|
|
1784
|
+
height = -( fleck + shell ) * 0.00005;`,
|
|
1785
|
+
travertine: `
|
|
1786
|
+
float band = selvaFbm( vec2( uv.x * 0.002, uv.y * 0.012 ) );
|
|
1787
|
+
float layer = ( band - 0.5 ) * selvaDetail( 25.0 );
|
|
1788
|
+
float pocket = selvaNoise( uv * vec2( 0.025, 0.15 ) ) * 0.7 + selvaNoise( uv * vec2( 0.08, 0.4 ) ) * 0.3;
|
|
1789
|
+
float voids = smoothstep( 0.68, 0.78, pocket ) * smoothstep( 0.35, 0.6, band ) * selvaDetail( 4.0 );
|
|
1790
|
+
float grain = ( selvaNoise( uv * 0.6 ) - 0.5 ) * selvaDetail( 1.5 );
|
|
1791
|
+
tone = vec3( 1.0 + layer * 0.2 + grain * 0.04 ) * mix( vec3( 1.0 ), vec3( 0.62, 0.56, 0.48 ), voids );
|
|
1792
|
+
gloss = 1.0 + voids * 0.3;
|
|
1793
|
+
height = -voids * 0.002;`,
|
|
1794
|
+
granite: `
|
|
1795
|
+
vec3 crystal = selvaCell( uv / 4.0 );
|
|
1796
|
+
// Feldspar, quartz and biotite in about the shares of a grey granite. The dark crystals
|
|
1797
|
+
// gather in clusters, which is what still reads as granite from a few metres.
|
|
1798
|
+
float cluster = selvaFbm( uv * 0.04 ) - 0.5;
|
|
1799
|
+
float dark = 0.2 + cluster * 0.2;
|
|
1800
|
+
float mineral = crystal.z < dark ? 0.35 : ( crystal.z < dark + 0.3 ? 1.35 : 1.0 );
|
|
1801
|
+
mineral *= 1.0 + ( selvaHash( vec2( crystal.z, 3.0 ) ) - 0.5 ) * 0.15;
|
|
1802
|
+
mineral = mix( 1.0 - cluster * 0.25 * selvaDetail( 15.0 ), mineral, selvaDetail( 4.0 ) );
|
|
1803
|
+
float cloud = ( selvaFbm( uv * 0.005 ) - 0.5 ) * selvaDetail( 50.0 );
|
|
1804
|
+
tone = vec3( mineral + cloud * 0.06 );
|
|
1805
|
+
gloss = 1.0 - ( mineral - 1.0 ) * 0.15;
|
|
1806
|
+
height = ( selvaNoise( uv * 0.8 ) - 0.5 ) * 0.0002 * selvaDetail( 1.2 );`,
|
|
1807
|
+
marble: `
|
|
1808
|
+
vec2 w = vec2( uv.x * 0.7 + uv.y * 0.7, uv.y * 0.7 - uv.x * 0.7 ) * vec2( 0.0012, 0.004 );
|
|
1809
|
+
w += vec2( selvaFbm( w * 2.0 ), selvaFbm( w * 2.0 + vec2( 4.0, 2.0 ) ) ) * 0.6;
|
|
1810
|
+
float feather = smoothstep( 0.3, 0.7, selvaFbm( uv * 0.006 + vec2( 2.0, 5.0 ) ) );
|
|
1811
|
+
float major = 1.0 - smoothstep( 0.0, 0.05, abs( selvaFbm( w ) - 0.5 ) );
|
|
1812
|
+
float fine = 1.0 - smoothstep( 0.0, 0.02, abs( selvaFbm( w * 3.0 + vec2( 9.0, 1.0 ) ) - 0.5 ) );
|
|
1813
|
+
float cloud = ( selvaFbm( uv * 0.003 ) - 0.5 ) * selvaDetail( 60.0 );
|
|
1814
|
+
float vein = ( major * 0.35 * selvaDetail( 6.0 ) + fine * 0.2 * selvaDetail( 2.0 ) ) * feather;
|
|
1815
|
+
tone = vec3( 1.0 + cloud * 0.06 - vein - feather * 0.04 );
|
|
1816
|
+
gloss = 1.0 + vein * 0.3;`,
|
|
1817
|
+
slate: `
|
|
1818
|
+
float cleft = selvaFbm( uv * vec2( 0.012, 0.02 ) );
|
|
1819
|
+
float flake = mix( cleft, floor( cleft * 4.0 ) / 4.0, 0.6 );
|
|
1820
|
+
float seen = selvaDetail( 6.0 );
|
|
1821
|
+
float glint = ( selvaNoise( uv * 0.3 ) - 0.5 ) * selvaDetail( 3.0 );
|
|
1822
|
+
tone = vec3( 1.0 + ( flake - 0.5 ) * 0.18 * seen + glint * 0.06 );
|
|
1823
|
+
gloss = 1.0 - ( flake - 0.5 ) * 0.3 * seen + glint * 0.2;
|
|
1824
|
+
height = flake * 0.0015 * seen;`,
|
|
1825
|
+
terrazzo: `
|
|
1826
|
+
vec3 chipCell = selvaCell( uv / 9.0 );
|
|
1827
|
+
float chip = smoothstep( 0.1, 0.16, chipCell.y - chipCell.x ) * step( 0.12, selvaHash( vec2( chipCell.z, 5.0 ) ) );
|
|
1828
|
+
float k = selvaHash( vec2( chipCell.z, 7.0 ) );
|
|
1829
|
+
vec3 stone = k < 0.6 ? vec3( 1.1 ) : k < 0.75 ? vec3( 0.65 ) : k < 0.85 ? vec3( 0.22 ) : k < 0.9 ? vec3( 0.85, 0.69, 0.44 ) : k < 0.95 ? vec3( 0.64, 0.35, 0.28 ) : vec3( 0.93, 0.72, 0.66 );
|
|
1830
|
+
vec3 gritCell = selvaCell( uv / 2.5 );
|
|
1831
|
+
float grit = smoothstep( 0.1, 0.2, gritCell.y - gritCell.x ) * step( 0.6, selvaHash( vec2( gritCell.z, 2.0 ) ) );
|
|
1832
|
+
float g = selvaHash( vec2( gritCell.z, 9.0 ) ) < 0.5 ? 0.4 : 1.1;
|
|
1833
|
+
vec3 near = mix( vec3( 1.0 ), stone, chip );
|
|
1834
|
+
near = mix( near, vec3( g ), grit * ( 1.0 - chip ) * selvaDetail( 2.5 ) );
|
|
1835
|
+
// From afar the chips average to a slightly darker matrix.
|
|
1836
|
+
tone = mix( vec3( 0.92 ), near, selvaDetail( 9.0 ) );
|
|
1837
|
+
gloss = 1.0 + chip * 0.1;`,
|
|
1838
|
+
paving: `
|
|
1839
|
+
vec2 size = vec2( 600.0, 400.0 );
|
|
1840
|
+
float row = floor( uv.y / size.y );
|
|
1841
|
+
vec2 laid = vec2( uv.x + mod( row, 2.0 ) * size.x * 0.5, uv.y );
|
|
1842
|
+
vec2 id = floor( laid / size );
|
|
1843
|
+
vec2 edge = abs( fract( laid / size ) - 0.5 ) * size;
|
|
1844
|
+
float inset = min( size.x * 0.5 - edge.x, size.y * 0.5 - edge.y );
|
|
1845
|
+
float joint = ( 1.0 - smoothstep( 2.5, 3.5, inset ) ) * selvaDetail( 5.0 );
|
|
1846
|
+
float chamfer = ( 1.0 - smoothstep( 2.5, 6.0, inset ) ) * selvaDetail( 4.0 );
|
|
1847
|
+
float slab = ( selvaHash( id ) - 0.5 ) * 0.12 * selvaDetail( 100.0 );
|
|
1848
|
+
float blotch = ( selvaFbm( uv * 0.006 ) - 0.5 ) * 0.12 * selvaDetail( 40.0 );
|
|
1849
|
+
float speck = ( selvaNoise( uv * 0.7 ) - 0.5 ) * 0.06 * selvaDetail( 1.5 );
|
|
1850
|
+
tone = vec3( 1.0 + slab + blotch + speck ) * mix( 1.0, 0.55, joint );
|
|
1851
|
+
gloss = 1.0 + joint * 0.1;
|
|
1852
|
+
height = -chamfer * 0.002 - joint * 0.003;`,
|
|
1853
|
+
setts: `
|
|
1854
|
+
vec2 size = vec2( 110.0, 100.0 );
|
|
1855
|
+
vec2 warped = uv + ( vec2( selvaNoise( uv * 0.03 ), selvaNoise( uv * 0.03 + vec2( 3.0, 8.0 ) ) ) - 0.5 ) * 10.0;
|
|
1856
|
+
float row = floor( warped.y / size.y );
|
|
1857
|
+
vec2 laid = vec2( warped.x + selvaHash( vec2( row, 4.0 ) ) * size.x, warped.y );
|
|
1858
|
+
vec2 id = floor( laid / size );
|
|
1859
|
+
vec2 edge = abs( fract( laid / size ) - 0.5 ) * size;
|
|
1860
|
+
float inset = min( size.x * 0.5 - edge.x, size.y * 0.5 - edge.y );
|
|
1861
|
+
float joint = ( 1.0 - smoothstep( 4.0, 6.0, inset ) ) * selvaDetail( 10.0 );
|
|
1862
|
+
float dome = smoothstep( 4.0, 25.0, inset );
|
|
1863
|
+
vec3 crystal = selvaCell( uv / 4.0 );
|
|
1864
|
+
// Feldspar, quartz and biotite in about the shares of a grey granite. The dark crystals
|
|
1865
|
+
// gather in clusters, which is what still reads as granite from a few metres.
|
|
1866
|
+
float cluster = selvaFbm( uv * 0.04 ) - 0.5;
|
|
1867
|
+
float dark = 0.2 + cluster * 0.2;
|
|
1868
|
+
float mineral = crystal.z < dark ? 0.35 : ( crystal.z < dark + 0.3 ? 1.35 : 1.0 );
|
|
1869
|
+
mineral *= 1.0 + ( selvaHash( vec2( crystal.z, 3.0 ) ) - 0.5 ) * 0.15;
|
|
1870
|
+
mineral = mix( 1.0 - cluster * 0.25 * selvaDetail( 15.0 ), mineral, selvaDetail( 4.0 ) );
|
|
1871
|
+
float tint = selvaHash( id + vec2( 5.0, 2.0 ) ) - 0.5;
|
|
1872
|
+
vec3 sett = ( 1.0 + ( selvaHash( id ) - 0.5 ) * 0.16 ) * ( 1.0 + vec3( 0.05, 0.0, -0.06 ) * tint * 2.0 );
|
|
1873
|
+
sett = mix( vec3( 1.0 ), sett, selvaDetail( 100.0 ) );
|
|
1874
|
+
tone = mix( sett * mineral, vec3( 1.05, 0.97, 0.82 ), joint );
|
|
1875
|
+
gloss = 1.0 + joint * 0.1;
|
|
1876
|
+
height = ( dome * 0.006 + ( selvaNoise( uv * 0.15 ) - 0.5 ) * 0.001 ) * selvaDetail( 10.0 );`,
|
|
1877
|
+
gravel: `
|
|
1878
|
+
vec3 pebble = selvaCell( uv / 12.0 );
|
|
1879
|
+
float gap = 1.0 - smoothstep( 0.04, 0.18, pebble.y - pebble.x );
|
|
1880
|
+
float dome = sqrt( clamp( 1.0 - pebble.x * 1.4, 0.0, 1.0 ) );
|
|
1881
|
+
float k = selvaHash( vec2( pebble.z, 3.0 ) );
|
|
1882
|
+
vec3 stone = k < 0.3 ? vec3( 1.13, 1.14, 1.11 ) : k < 0.55 ? vec3( 0.96, 0.85, 0.71 ) : k < 0.7 ? vec3( 0.84, 0.64, 0.59 ) : k < 0.9 ? vec3( 0.74, 0.8, 0.89 ) : vec3( 0.47, 0.5, 0.56 );
|
|
1883
|
+
stone *= 1.0 + ( selvaHash( vec2( pebble.z, 8.0 ) ) - 0.5 ) * 0.15;
|
|
1884
|
+
float seen = selvaDetail( 12.0 );
|
|
1885
|
+
tone = mix( vec3( 0.85 ), stone * mix( 1.0, 0.35, gap ) * ( 0.85 + 0.15 * dome ), seen );
|
|
1886
|
+
gloss = 1.0 + gap * 0.1 - dome * 0.1;
|
|
1887
|
+
height = dome * 0.004 * seen;`,
|
|
1888
|
+
molded: `
|
|
1889
|
+
float pit = selvaNoise( uv * 1.6 );
|
|
1890
|
+
float mottle = selvaNoise( uv * 0.5 + vec2( 7.0, 3.0 ) );
|
|
1891
|
+
float grit = ( pit * 0.65 + mottle * 0.35 ) - 0.5;
|
|
1892
|
+
float seen = selvaDetail( 0.6 );
|
|
1893
|
+
tone = vec3( 1.0 + grit * 0.1 * seen );
|
|
1894
|
+
gloss = 1.0 + grit * 0.9 * seen;
|
|
1895
|
+
height = ( grit + 0.5 ) * 0.00008 * seen;`,
|
|
1723
1896
|
corten: `
|
|
1724
1897
|
float bloom = 0.5 + ( selvaFbm( uv * vec2( 0.006, 0.0025 ) ) - 0.5 ) * selvaDetail( 25.0 );
|
|
1725
1898
|
float scale = 0.5 + ( selvaFbm( uv * 0.06 ) - 0.5 ) * selvaDetail( 8.0 );
|
|
@@ -1728,7 +1901,6 @@ const RECIPES$1 = {
|
|
|
1728
1901
|
gloss = 1.0 + ( scale - 0.5 ) * 0.25;
|
|
1729
1902
|
height = scale * 0.0004;`
|
|
1730
1903
|
};
|
|
1731
|
-
const f$1 = (n) => n.toFixed(4);
|
|
1732
1904
|
/** Null for the finishes `finish.ts` draws itself. */
|
|
1733
1905
|
function surfacePatch(finish, strength) {
|
|
1734
1906
|
const recipe = RECIPES$1[finish];
|
|
@@ -1762,6 +1934,30 @@ function surfacePatch(finish, strength) {
|
|
|
1762
1934
|
}
|
|
1763
1935
|
return sum / 0.9375;
|
|
1764
1936
|
}
|
|
1937
|
+
// Cellular noise: distance to the nearest and second-nearest jittered point, and the
|
|
1938
|
+
// nearest one's id. y - x is small along a cell border.
|
|
1939
|
+
vec3 selvaCell( vec2 p ) {
|
|
1940
|
+
vec2 i = floor( p );
|
|
1941
|
+
vec2 fp = fract( p );
|
|
1942
|
+
float f1 = 8.0;
|
|
1943
|
+
float f2 = 8.0;
|
|
1944
|
+
float id = 0.0;
|
|
1945
|
+
for ( int y = -1; y <= 1; y ++ ) {
|
|
1946
|
+
for ( int x = -1; x <= 1; x ++ ) {
|
|
1947
|
+
vec2 g = vec2( float( x ), float( y ) );
|
|
1948
|
+
vec2 o = vec2( selvaHash( i + g ), selvaHash( i + g + vec2( 19.0, 7.0 ) ) );
|
|
1949
|
+
float d = length( g + o * 0.8 + 0.1 - fp );
|
|
1950
|
+
if ( d < f1 ) {
|
|
1951
|
+
f2 = f1;
|
|
1952
|
+
f1 = d;
|
|
1953
|
+
id = selvaHash( i + g + vec2( 3.0, 11.0 ) );
|
|
1954
|
+
} else if ( d < f2 ) {
|
|
1955
|
+
f2 = d;
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
return vec3( f1, f2, id );
|
|
1960
|
+
}
|
|
1765
1961
|
float selvaDetail( float featureMm ) {
|
|
1766
1962
|
return 1.0 - smoothstep( 0.5, 1.5, selvaFootprint / featureMm );
|
|
1767
1963
|
}`).replace("#include <color_fragment>", `#include <color_fragment>
|
|
@@ -1898,6 +2094,10 @@ function cullsBackFaces(matData, options) {
|
|
|
1898
2094
|
if (options?.appearance?.cullBackfaces) return true;
|
|
1899
2095
|
return (options?.closed ?? false) && !matData.transparent && matData.opacity >= 1;
|
|
1900
2096
|
}
|
|
2097
|
+
const FROSTED = .2;
|
|
2098
|
+
function isPane(matData) {
|
|
2099
|
+
return (matData.transmission ?? 0) > 0 && !((matData.thickness ?? 0) > 0) && matData.roughness < FROSTED;
|
|
2100
|
+
}
|
|
1901
2101
|
function createMaterialPicker(materialsSrc, options) {
|
|
1902
2102
|
const built = /* @__PURE__ */ new Map();
|
|
1903
2103
|
return (materialId, closed) => {
|
|
@@ -1950,15 +2150,18 @@ function createMaterial(matData, options) {
|
|
|
1950
2150
|
material.clearcoat = matData.clearcoat;
|
|
1951
2151
|
material.clearcoatRoughness = matData.clearcoatRoughness ?? 0;
|
|
1952
2152
|
}
|
|
2153
|
+
if (matData.sheen != null && matData.sheen > 0) {
|
|
2154
|
+
material.sheen = matData.sheen;
|
|
2155
|
+
material.sheenColor.set(matData.sheenColor ?? "#ffffff");
|
|
2156
|
+
material.sheenRoughness = matData.sheenRoughness ?? .5;
|
|
2157
|
+
}
|
|
1953
2158
|
if (matData.anisotropy != null && matData.anisotropy > 0 && options?.hasUvs) {
|
|
1954
2159
|
material.anisotropy = matData.anisotropy;
|
|
1955
2160
|
material.anisotropyRotation = matData.anisotropyRotation ?? 0;
|
|
1956
2161
|
}
|
|
1957
|
-
if (matData.transmission != null) {
|
|
1958
|
-
material.transmission = matData.transmission;
|
|
1959
|
-
material.thickness = 0;
|
|
1960
|
-
}
|
|
1961
2162
|
if (matData.ior != null) material.ior = matData.ior;
|
|
2163
|
+
if (matData.transmission != null && matData.transmission > 0) applyGlass(material, matData);
|
|
2164
|
+
if (matData.metalness > 0) dimSunOnMetal(material);
|
|
1962
2165
|
if (vertexColors) applyVertexColorSRGBDecode(material);
|
|
1963
2166
|
const finish = matData.finish && options?.hasUvs ? finishPatch(matData.finish, matData.finishStrength ?? 1) : null;
|
|
1964
2167
|
if (finish) {
|
|
@@ -1974,6 +2177,50 @@ function createMaterial(matData, options) {
|
|
|
1974
2177
|
if (matData.normalMap) applyTexture(material, matData.normalMap, "normalMap", mapSize);
|
|
1975
2178
|
return material;
|
|
1976
2179
|
}
|
|
2180
|
+
const SUN_ON_METAL = .1;
|
|
2181
|
+
const SUN_BEGIN = "#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )";
|
|
2182
|
+
const SUN_END = "#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )";
|
|
2183
|
+
/** Scales the directional lights' specular by metalness; point lights keep theirs. */
|
|
2184
|
+
function dimSunOnMetal(material) {
|
|
2185
|
+
addShaderPatch(material, "sun-on-metal", (shader) => {
|
|
2186
|
+
shader.fragmentShader = shader.fragmentShader.replace("#include <lights_fragment_begin>", THREE.ShaderChunk.lights_fragment_begin.replace(SUN_BEGIN, `vec3 specularBeforeSun = reflectedLight.directSpecular;
|
|
2187
|
+
${SUN_BEGIN}`).replace(SUN_END, `reflectedLight.directSpecular = specularBeforeSun + ( reflectedLight.directSpecular - specularBeforeSun ) * mix( 1.0, ${SUN_ON_METAL.toFixed(2)}, metalnessFactor );
|
|
2188
|
+
${SUN_END}`));
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
/**
|
|
2192
|
+
* three's transmission draws every opaque object a second time into its own buffer each frame, and
|
|
2193
|
+
* that buffer never holds other glass: one pane seen through another vanishes. So only glass that
|
|
2194
|
+
* needs it gets it: with a thickness, to refract and tint, or frosted, to blur. A pane blends
|
|
2195
|
+
* instead: cheap, and panes stack.
|
|
2196
|
+
*/
|
|
2197
|
+
function applyGlass(material, matData) {
|
|
2198
|
+
const transmission = Math.min(1, matData.transmission);
|
|
2199
|
+
material.userData[GLASS] = true;
|
|
2200
|
+
if (!isPane(matData)) {
|
|
2201
|
+
const thickness = matData.thickness ?? 0;
|
|
2202
|
+
material.transmission = transmission;
|
|
2203
|
+
material.thickness = thickness;
|
|
2204
|
+
if (matData.attenuationColor && thickness > 0) {
|
|
2205
|
+
material.attenuationColor.set(matData.attenuationColor);
|
|
2206
|
+
material.attenuationDistance = thickness;
|
|
2207
|
+
addShaderPatch(material, "glass-attenuation", (shader) => {
|
|
2208
|
+
shader.fragmentShader = shader.fragmentShader.replace("#include <transmission_fragment>", THREE.ShaderChunk.transmission_fragment.replace("material.attenuationDistance = attenuationDistance;", "material.attenuationDistance = attenuationDistance * length( modelMatrix[ 0 ].xyz );"));
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
2211
|
+
return;
|
|
2212
|
+
}
|
|
2213
|
+
material.transparent = true;
|
|
2214
|
+
material.opacity = matData.opacity * (1 - transmission * .96);
|
|
2215
|
+
material.depthWrite = false;
|
|
2216
|
+
material.blending = THREE.CustomBlending;
|
|
2217
|
+
material.blendSrc = THREE.OneFactor;
|
|
2218
|
+
material.blendDst = THREE.OneMinusSrcAlphaFactor;
|
|
2219
|
+
addShaderPatch(material, "glass-pane", (shader) => {
|
|
2220
|
+
shader.fragmentShader = shader.fragmentShader.replace("#include <opaque_fragment>", `vec3 paneSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;
|
|
2221
|
+
gl_FragColor = vec4( ( outgoingLight - paneSpecular ) * diffuseColor.a + paneSpecular, diffuseColor.a );`);
|
|
2222
|
+
});
|
|
2223
|
+
}
|
|
1977
2224
|
/**
|
|
1978
2225
|
* Chains `patch` after any earlier one. three caches compiled programs by `customProgramCacheKey`,
|
|
1979
2226
|
* which defaults to the `onBeforeCompile` source; a chained wrapper's source is the same whatever
|
|
@@ -2208,7 +2455,7 @@ function finalizeMergedMesh(geometry, group, material) {
|
|
|
2208
2455
|
const firstMesh = group.meshes[0];
|
|
2209
2456
|
const meshNames = group.meshes.map((m) => m.name).filter((name) => name && name.length > 0);
|
|
2210
2457
|
threeMesh.name = meshNames.length > 0 ? meshNames[0] : `merged_material_${group.materialId}`;
|
|
2211
|
-
threeMesh.castShadow =
|
|
2458
|
+
threeMesh.castShadow = !isGlass(material);
|
|
2212
2459
|
threeMesh.receiveShadow = true;
|
|
2213
2460
|
threeMesh.userData = {
|
|
2214
2461
|
source: "compute",
|
|
@@ -2259,7 +2506,7 @@ function finalizeSingleMesh(geometry, meshMeta, material) {
|
|
|
2259
2506
|
trackingKey: meshMeta.id,
|
|
2260
2507
|
metadata: meshMeta.metadata ?? {}
|
|
2261
2508
|
};
|
|
2262
|
-
mesh.castShadow =
|
|
2509
|
+
mesh.castShadow = !isGlass(material);
|
|
2263
2510
|
mesh.receiveShadow = true;
|
|
2264
2511
|
return mesh;
|
|
2265
2512
|
}
|