@selvajs/visualization 1.7.0-beta.0 → 1.7.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/parse.d.cts CHANGED
@@ -93,11 +93,11 @@ interface SerializableMaterial {
93
93
  clearcoat?: number;
94
94
  clearcoatRoughness?: number;
95
95
  /**
96
- * Stretches highlights along texture U, the grain of brushed or rolled sheet. Ignored when the
96
+ * Stretches highlights across texture U, the grain of brushed or rolled sheet. Ignored when the
97
97
  * batch carries no UVs: the tangent is built from them.
98
98
  */
99
99
  anisotropy?: number;
100
- /** Radians, counter-clockwise from texture U. */
100
+ /** The grain's direction in radians, counter-clockwise from texture U. */
101
101
  anisotropyRotation?: number;
102
102
  /**
103
103
  * Millimetres one repeat of every map covers across U; V follows each image's aspect. Set only
package/dist/parse.d.ts CHANGED
@@ -93,11 +93,11 @@ interface SerializableMaterial {
93
93
  clearcoat?: number;
94
94
  clearcoatRoughness?: number;
95
95
  /**
96
- * Stretches highlights along texture U, the grain of brushed or rolled sheet. Ignored when the
96
+ * Stretches highlights across texture U, the grain of brushed or rolled sheet. Ignored when the
97
97
  * batch carries no UVs: the tangent is built from them.
98
98
  */
99
99
  anisotropy?: number;
100
- /** Radians, counter-clockwise from texture U. */
100
+ /** The grain's direction in radians, counter-clockwise from texture U. */
101
101
  anisotropyRotation?: number;
102
102
  /**
103
103
  * Millimetres one repeat of every map covers across U; V follows each image's aspect. Set only
package/dist/parse.js CHANGED
@@ -1682,29 +1682,58 @@ function applyTexture(material, url, slot, mapSize) {
1682
1682
  const f$1 = (n) => n.toFixed(4);
1683
1683
  const PITH = 120;
1684
1684
  const PITH_SPREAD = 120;
1685
- const PLANK = 180;
1685
+ const PLANK = 160;
1686
+ const SEAM_JITTER = .7;
1686
1687
  const LEAN = .0105;
1688
+ const BOARD_SHADE = .14;
1689
+ const PORE_ROW = .35;
1690
+ const PORE_FILL = .3;
1691
+ const PORE_DARK = .4;
1692
+ const PORE_MEAN = PORE_FILL * .7 * .42;
1693
+ const seam = (k) => `( ( ${k} ) + ( selvaHash( vec2( ${k}, 6.0 ) ) - 0.5 ) * ${f$1(SEAM_JITTER)} ) * ${f$1(PLANK)}`;
1687
1694
  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)};
1695
+ float cell = floor( uv.y / ${f$1(PLANK)} );
1696
+ float plank = cell + ( uv.y < ${seam("cell")} ? -1.0 : ( uv.y >= ${seam("cell + 1.0")} ? 1.0 : 0.0 ) );
1697
+ float lo = ${seam("plank")};
1698
+ float hi = ${seam("plank + 1.0")};
1699
+ // Each plank starts somewhere else along its log.
1700
+ float along = uv.x + selvaHash( vec2( plank, 9.0 ) ) * 5000.0;
1701
+ // The pith lies anywhere under the plank, sometimes past its edge, which leaves straight
1702
+ // rift grain on that side.
1703
+ float heart = mix( lo, hi, selvaHash( vec2( plank, 3.0 ) ) * 1.6 - 0.3 );
1704
+ heart += ( selvaNoise( vec2( along * 0.0006, plank * 7.0 ) ) - 0.5 ) * 50.0;
1705
+ float s = uv.y - heart;
1690
1706
  float pith = ${f$1(PITH)} + selvaHash( vec2( plank, 2.0 ) ) * ${f$1(PITH_SPREAD)};
1707
+ pith += ( selvaFbm( vec2( along * 0.0004, plank * 5.0 + 1.0 ) ) - 0.5 ) * ${f$1(50)};
1708
+ float lean = ${f$1(LEAN)} * ( 0.5 + selvaHash( vec2( plank, 8.0 ) ) );
1691
1709
  // 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;
1710
+ float wander = ( selvaFbm( vec2( along * 0.003, uv.y * 0.008 ) ) - 0.5 ) * ${f$1(ring * 2)};
1711
+ float r = sqrt( pith * pith + s * s ) + along * lean + wander;
1694
1712
  r += selvaFbm( vec2( r * 0.02, plank ) ) * ${f$1(ring * 3)} + selvaHash( vec2( plank, 1.0 ) ) * ${f$1(ring * 20)};
1695
1713
  float year = fract( r / ${f$1(ring)} );
1696
1714
  float late = smoothstep( ${f$1(1 - band)}, ${f$1(1 - band * .4)}, year ) * ( 1.0 - smoothstep( 0.95, 1.0, year ) );
1697
1715
  // 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 ) ) ) );
1716
+ float broken = 1.0 - smoothstep( 0.3, 0.7, selvaNoise( uv * vec2( 0.25, 2.0 ) ) );
1717
+ late *= 1.0 - ${f$1(pores * .45)} * mix( 0.5, broken, selvaDetail( 0.5 ) );
1699
1718
  late *= selvaDetail( ${f$1(ring)} );
1700
1719
  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 );
1720
+ // Pores: thin dashes along the grain, most of each row empty. Smaller than a pixel they
1721
+ // show as their average shade, or they would be static.
1722
+ float poreRow = floor( uv.y / ${f$1(PORE_ROW)} );
1723
+ float poreAlong = uv.x / mix( 1.5, 5.0, selvaHash( vec2( poreRow, 11.0 ) ) ) + selvaHash( vec2( poreRow, 12.0 ) );
1724
+ float poreT = fract( poreAlong );
1725
+ float pore = step( ${f$1(.7)}, selvaHash( vec2( poreRow, 13.0 + 1.618 * floor( poreAlong ) ) ) );
1726
+ pore *= smoothstep( 0.0, 0.3, poreT ) * smoothstep( 1.0, 0.7, poreT );
1727
+ pore *= 1.0 - smoothstep( 0.12, 0.3, abs( fract( uv.y / ${f$1(PORE_ROW)} ) - 0.5 ) );
1728
+ pore = mix( ${f$1(PORE_MEAN)}, pore, selvaDetail( ${f$1(PORE_ROW)} ) ) * ${f$1(pores)} * ( 1.0 - 0.6 * late );
1729
+ float streak = ( selvaFbm( vec2( along * 0.0015, uv.y * 0.012 ) ) - 0.5 ) * ${f$1(streaks)};
1730
+ float shade = 1.0 + ${f$1(latewood * band)} - late * ${f$1(latewood)} + fibre * 0.1 - pore * ${f$1(PORE_DARK)} + streak * 0.6;
1731
+ float board = ( selvaHash( vec2( plank, 4.0 ) ) - 0.5 ) * selvaDetail( ${f$1(PLANK * .5)} );
1732
+ float warmth = ( selvaHash( vec2( plank, 5.0 ) ) - 0.5 ) * selvaDetail( ${f$1(PLANK * .5)} );
1733
+ tone = vec3( shade ) * ( 1.0 + board * ${f$1(BOARD_SHADE)} ) * vec3( 1.0 + warmth * 0.06, 1.0, 1.0 - warmth * 0.06 );
1706
1734
  gloss = 1.0 + late * 0.08 + pore * 0.4 - fibre * 0.2;
1707
- height = ( fibre * 0.00008 - pore * 0.00015 ) * selvaDetail( 0.6 );`;
1735
+ // No relief from pores: a bump from screen derivatives beads any dash under a few pixels wide.
1736
+ height = fibre * 0.00008 * selvaDetail( 0.6 );`;
1708
1737
  /**
1709
1738
  * Procedural surfaces, so a preset looks like its material with no texture files to ship or upload.
1710
1739
  * UVs are millimetres, so a brick is brick-sized at any model scale. Each recipe sets `tone` (albedo
@@ -2014,6 +2043,7 @@ const RECIPES = {
2014
2043
  };
2015
2044
  const TILT = .6;
2016
2045
  const FIBRE_FADE = .35;
2046
+ const FIBRE_ASPECT = 16;
2017
2047
  const f = (n) => n.toFixed(4);
2018
2048
  /** The shader patch for a finish, or null when it would draw nothing. `key` extends the program cache key. */
2019
2049
  function finishPatch(finish, strength) {
@@ -2045,7 +2075,8 @@ function finishPatch(finish, strength) {
2045
2075
  // of the part. depth * |depth| keeps most fibres faint and a few deep.
2046
2076
  float selvaFibreRow( vec2 uv, float pitch, vec2 len, float seed ) {
2047
2077
  float row = floor( uv.y / pitch );
2048
- float l = mix( len.x, len.y, selvaHash( vec2( row, seed + 1.0 ) ) );
2078
+ // A coarse cell stays a streak: as wide as it is long it reads as speckle, not grain.
2079
+ float l = max( mix( len.x, len.y, selvaHash( vec2( row, seed + 1.0 ) ) ), pitch * ${f(FIBRE_ASPECT)} );
2049
2080
  float along = uv.x / l + selvaHash( vec2( row, seed + 2.0 ) );
2050
2081
  float seg = floor( along );
2051
2082
  float depth = selvaHash( vec2( row, seed + 3.0 + 1.618 * seg ) ) * 2.0 - 1.0;
@@ -2157,7 +2188,7 @@ function createMaterial(matData, options) {
2157
2188
  }
2158
2189
  if (matData.anisotropy != null && matData.anisotropy > 0 && options?.hasUvs) {
2159
2190
  material.anisotropy = matData.anisotropy;
2160
- material.anisotropyRotation = matData.anisotropyRotation ?? 0;
2191
+ material.anisotropyRotation = (matData.anisotropyRotation ?? 0) + Math.PI / 2;
2161
2192
  }
2162
2193
  if (matData.ior != null) material.ior = matData.ior;
2163
2194
  if (matData.transmission != null && matData.transmission > 0) applyGlass(material, matData);