@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.cjs CHANGED
@@ -1684,29 +1684,58 @@ function applyTexture(material, url, slot, mapSize) {
1684
1684
  const f$1 = (n) => n.toFixed(4);
1685
1685
  const PITH = 120;
1686
1686
  const PITH_SPREAD = 120;
1687
- const PLANK = 180;
1687
+ const PLANK = 160;
1688
+ const SEAM_JITTER = .7;
1688
1689
  const LEAN = .0105;
1690
+ const BOARD_SHADE = .14;
1691
+ const PORE_ROW = .35;
1692
+ const PORE_FILL = .3;
1693
+ const PORE_DARK = .4;
1694
+ const PORE_MEAN = PORE_FILL * .7 * .42;
1695
+ const seam = (k) => `( ( ${k} ) + ( selvaHash( vec2( ${k}, 6.0 ) ) - 0.5 ) * ${f$1(SEAM_JITTER)} ) * ${f$1(PLANK)}`;
1689
1696
  const wood = ({ ring, latewood, band, pores, streaks }) => `
1690
- float plank = floor( uv.y / ${f$1(PLANK)} );
1691
- float s = uv.y - ( plank + 0.5 ) * ${f$1(PLANK)};
1697
+ float cell = floor( uv.y / ${f$1(PLANK)} );
1698
+ float plank = cell + ( uv.y < ${seam("cell")} ? -1.0 : ( uv.y >= ${seam("cell + 1.0")} ? 1.0 : 0.0 ) );
1699
+ float lo = ${seam("plank")};
1700
+ float hi = ${seam("plank + 1.0")};
1701
+ // Each plank starts somewhere else along its log.
1702
+ float along = uv.x + selvaHash( vec2( plank, 9.0 ) ) * 5000.0;
1703
+ // The pith lies anywhere under the plank, sometimes past its edge, which leaves straight
1704
+ // rift grain on that side.
1705
+ float heart = mix( lo, hi, selvaHash( vec2( plank, 3.0 ) ) * 1.6 - 0.3 );
1706
+ heart += ( selvaNoise( vec2( along * 0.0006, plank * 7.0 ) ) - 0.5 ) * 50.0;
1707
+ float s = uv.y - heart;
1692
1708
  float pith = ${f$1(PITH)} + selvaHash( vec2( plank, 2.0 ) ) * ${f$1(PITH_SPREAD)};
1709
+ pith += ( selvaFbm( vec2( along * 0.0004, plank * 5.0 + 1.0 ) ) - 0.5 ) * ${f$1(50)};
1710
+ float lean = ${f$1(LEAN)} * ( 0.5 + selvaHash( vec2( plank, 8.0 ) ) );
1693
1711
  // Rings wander a few mm and vary in spacing, as growth does from year to year.
1694
- float wander = ( selvaFbm( uv * vec2( 0.003, 0.008 ) ) - 0.5 ) * ${f$1(ring * 2)};
1695
- float r = sqrt( pith * pith + s * s ) + uv.x * ${f$1(LEAN)} + wander;
1712
+ float wander = ( selvaFbm( vec2( along * 0.003, uv.y * 0.008 ) ) - 0.5 ) * ${f$1(ring * 2)};
1713
+ float r = sqrt( pith * pith + s * s ) + along * lean + wander;
1696
1714
  r += selvaFbm( vec2( r * 0.02, plank ) ) * ${f$1(ring * 3)} + selvaHash( vec2( plank, 1.0 ) ) * ${f$1(ring * 20)};
1697
1715
  float year = fract( r / ${f$1(ring)} );
1698
1716
  float late = smoothstep( ${f$1(1 - band)}, ${f$1(1 - band * .4)}, year ) * ( 1.0 - smoothstep( 0.95, 1.0, year ) );
1699
1717
  // Open-pored wood shows its rings as a broken band of pores, not a clean line.
1700
- late *= 1.0 - ${f$1(pores * .6)} * ( 1.0 - smoothstep( 0.3, 0.7, selvaNoise( uv * vec2( 0.25, 2.0 ) ) ) );
1718
+ float broken = 1.0 - smoothstep( 0.3, 0.7, selvaNoise( uv * vec2( 0.25, 2.0 ) ) );
1719
+ late *= 1.0 - ${f$1(pores * .45)} * mix( 0.5, broken, selvaDetail( 0.5 ) );
1701
1720
  late *= selvaDetail( ${f$1(ring)} );
1702
1721
  float fibre = ( selvaNoise( uv * vec2( 0.04, 1.6 ) ) - 0.5 ) * selvaDetail( 1.0 );
1703
- float pore = smoothstep( 0.62, 0.8, selvaNoise( uv * vec2( 0.3, 2.5 ) ) ) * ( 1.0 - 0.6 * late );
1704
- pore *= ${f$1(pores)} * selvaDetail( 0.8 );
1705
- float streak = ( selvaFbm( uv * vec2( 0.0015, 0.012 ) ) - 0.5 ) * ${f$1(streaks)};
1706
- float shade = 1.0 + ${f$1(latewood * band)} - late * ${f$1(latewood)} + fibre * 0.1 - pore * 0.45 + streak * 0.6;
1707
- tone = vec3( shade );
1722
+ // Pores: thin dashes along the grain, most of each row empty. Smaller than a pixel they
1723
+ // show as their average shade, or they would be static.
1724
+ float poreRow = floor( uv.y / ${f$1(PORE_ROW)} );
1725
+ float poreAlong = uv.x / mix( 1.5, 5.0, selvaHash( vec2( poreRow, 11.0 ) ) ) + selvaHash( vec2( poreRow, 12.0 ) );
1726
+ float poreT = fract( poreAlong );
1727
+ float pore = step( ${f$1(.7)}, selvaHash( vec2( poreRow, 13.0 + 1.618 * floor( poreAlong ) ) ) );
1728
+ pore *= smoothstep( 0.0, 0.3, poreT ) * smoothstep( 1.0, 0.7, poreT );
1729
+ pore *= 1.0 - smoothstep( 0.12, 0.3, abs( fract( uv.y / ${f$1(PORE_ROW)} ) - 0.5 ) );
1730
+ pore = mix( ${f$1(PORE_MEAN)}, pore, selvaDetail( ${f$1(PORE_ROW)} ) ) * ${f$1(pores)} * ( 1.0 - 0.6 * late );
1731
+ float streak = ( selvaFbm( vec2( along * 0.0015, uv.y * 0.012 ) ) - 0.5 ) * ${f$1(streaks)};
1732
+ float shade = 1.0 + ${f$1(latewood * band)} - late * ${f$1(latewood)} + fibre * 0.1 - pore * ${f$1(PORE_DARK)} + streak * 0.6;
1733
+ float board = ( selvaHash( vec2( plank, 4.0 ) ) - 0.5 ) * selvaDetail( ${f$1(PLANK * .5)} );
1734
+ float warmth = ( selvaHash( vec2( plank, 5.0 ) ) - 0.5 ) * selvaDetail( ${f$1(PLANK * .5)} );
1735
+ tone = vec3( shade ) * ( 1.0 + board * ${f$1(BOARD_SHADE)} ) * vec3( 1.0 + warmth * 0.06, 1.0, 1.0 - warmth * 0.06 );
1708
1736
  gloss = 1.0 + late * 0.08 + pore * 0.4 - fibre * 0.2;
1709
- height = ( fibre * 0.00008 - pore * 0.00015 ) * selvaDetail( 0.6 );`;
1737
+ // No relief from pores: a bump from screen derivatives beads any dash under a few pixels wide.
1738
+ height = fibre * 0.00008 * selvaDetail( 0.6 );`;
1710
1739
  /**
1711
1740
  * Procedural surfaces, so a preset looks like its material with no texture files to ship or upload.
1712
1741
  * UVs are millimetres, so a brick is brick-sized at any model scale. Each recipe sets `tone` (albedo
@@ -2016,6 +2045,7 @@ const RECIPES = {
2016
2045
  };
2017
2046
  const TILT = .6;
2018
2047
  const FIBRE_FADE = .35;
2048
+ const FIBRE_ASPECT = 16;
2019
2049
  const f = (n) => n.toFixed(4);
2020
2050
  /** The shader patch for a finish, or null when it would draw nothing. `key` extends the program cache key. */
2021
2051
  function finishPatch(finish, strength) {
@@ -2047,7 +2077,8 @@ function finishPatch(finish, strength) {
2047
2077
  // of the part. depth * |depth| keeps most fibres faint and a few deep.
2048
2078
  float selvaFibreRow( vec2 uv, float pitch, vec2 len, float seed ) {
2049
2079
  float row = floor( uv.y / pitch );
2050
- float l = mix( len.x, len.y, selvaHash( vec2( row, seed + 1.0 ) ) );
2080
+ // A coarse cell stays a streak: as wide as it is long it reads as speckle, not grain.
2081
+ float l = max( mix( len.x, len.y, selvaHash( vec2( row, seed + 1.0 ) ) ), pitch * ${f(FIBRE_ASPECT)} );
2051
2082
  float along = uv.x / l + selvaHash( vec2( row, seed + 2.0 ) );
2052
2083
  float seg = floor( along );
2053
2084
  float depth = selvaHash( vec2( row, seed + 3.0 + 1.618 * seg ) ) * 2.0 - 1.0;
@@ -2159,7 +2190,7 @@ function createMaterial(matData, options) {
2159
2190
  }
2160
2191
  if (matData.anisotropy != null && matData.anisotropy > 0 && options?.hasUvs) {
2161
2192
  material.anisotropy = matData.anisotropy;
2162
- material.anisotropyRotation = matData.anisotropyRotation ?? 0;
2193
+ material.anisotropyRotation = (matData.anisotropyRotation ?? 0) + Math.PI / 2;
2163
2194
  }
2164
2195
  if (matData.ior != null) material.ior = matData.ior;
2165
2196
  if (matData.transmission != null && matData.transmission > 0) applyGlass(material, matData);