import { abs, add, clamp, cos, dFdx, dFdy, div, dot, element, floor, fract, max, mix, mul, pow, sin, step, sub, vec2, vec3, } from 'three/tsl'; const HEXTILE_SQRT3_2 = Math.sqrt( 3 ) * 2; const HEXTILE_EPSILON = 1e-6; const HEXTILE_PI_OVER_180 = Math.PI / 180; function toRadians( degrees ) { return mul( degrees, HEXTILE_PI_OVER_180 ); } function mxHextileHash( point ) { const x = element( point, 0 ); const y = element( point, 1 ); const p3Base = vec3( x, y, x ); const p3Scaled = mul( p3Base, vec3( 0.1031, 0.103, 0.0973 ) ); const p3Fract = fract( p3Scaled ); const p3YZX = vec3( element( p3Fract, 1 ), element( p3Fract, 2 ), element( p3Fract, 0 ) ); const p3Offset = add( p3YZX, 33.33 ); const p3 = add( p3Fract, dot( p3Fract, p3Offset ) ); const lhs = add( vec2( element( p3, 0 ), element( p3, 0 ) ), vec2( element( p3, 1 ), element( p3, 2 ) ) ); const rhs = vec2( element( p3, 2 ), element( p3, 1 ) ); return fract( mul( lhs, rhs ) ); } function mxSchlickGain( x, r ) { const rr = clamp( r, 0.001, 0.999 ); const a = mul( sub( div( 1, rr ), 2 ), sub( 1, mul( 2, x ) ) ); const low = div( x, add( a, 1 ) ); const high = div( sub( a, x ), sub( a, 1 ) ); return mix( low, high, step( 0.5, x ) ); } function normalizeBlendWeights( weights ) { const wx = element( weights, 0 ); const wy = element( weights, 1 ); const wz = element( weights, 2 ); const sum = add( add( wx, wy ), wz ); return div( weights, sum ); } function mxRotate2d( point, sine, cosine ) { return vec2( sub( mul( cosine, element( point, 0 ) ), mul( sine, element( point, 1 ) ) ), add( mul( sine, element( point, 0 ) ), mul( cosine, element( point, 1 ) ) ) ); } function toTileCenter( tileId ) { const scaled = div( tileId, HEXTILE_SQRT3_2 ); const sx = element( scaled, 0 ); const sy = element( scaled, 1 ); return vec2( add( sx, mul( 0.5, sy ) ), mul( 0.8660254, sy ) ); } export function mxHextileComputeBlendWeights( luminanceWeights, tileWeights, falloff ) { const weighted = mul( luminanceWeights, pow( tileWeights, vec3( 7, 7, 7 ) ) ); const normalized = normalizeBlendWeights( weighted ); const gained = vec3( mxSchlickGain( element( normalized, 0 ), falloff ), mxSchlickGain( element( normalized, 1 ), falloff ), mxSchlickGain( element( normalized, 2 ), falloff ), ); const gainedNormalized = normalizeBlendWeights( gained ); const applyFalloff = step( HEXTILE_EPSILON, abs( sub( falloff, 0.5 ) ) ); return mix( normalized, gainedNormalized, applyFalloff ); } export function mxHextileCoord( coord, rotation, rotationRange, scale, scaleRange, offset, offsetRange ) { const st = mul( coord, HEXTILE_SQRT3_2 ); const stSkewed = vec2( add( element( st, 0 ), mul( - 0.57735027, element( st, 1 ) ) ), mul( 1.15470054, element( st, 1 ) ) ); const stFrac = fract( stSkewed ); const tx = element( stFrac, 0 ); const ty = element( stFrac, 1 ); const tz = sub( sub( 1, tx ), ty ); const s = step( 0, sub( 0, tz ) ); const s2 = sub( mul( 2, s ), 1 ); const w1 = mul( sub( 0, tz ), s2 ); const w2 = sub( s, mul( ty, s2 ) ); const w3 = sub( s, mul( tx, s2 ) ); const baseId = floor( stSkewed ); const oneMinusS = sub( 1, s ); const id1 = add( baseId, vec2( s, s ) ); const id2 = add( baseId, vec2( s, oneMinusS ) ); const id3 = add( baseId, vec2( oneMinusS, s ) ); const ctr1 = toTileCenter( id1 ); const ctr2 = toTileCenter( id2 ); const ctr3 = toTileCenter( id3 ); const seedOffset = vec2( 0.12345, 0.12345 ); const rand1 = mxHextileHash( add( id1, seedOffset ) ); const rand2 = mxHextileHash( add( id2, seedOffset ) ); const rand3 = mxHextileHash( add( id3, seedOffset ) ); const rr = vec2( toRadians( element( rotationRange, 0 ) ), toRadians( element( rotationRange, 1 ) ) ); const rrMin = element( rr, 0 ); const rrMax = element( rr, 1 ); const randX = vec3( element( rand1, 0 ), element( rand2, 0 ), element( rand3, 0 ) ); const rotations = mix( vec3( rrMin, rrMin, rrMin ), vec3( rrMax, rrMax, rrMax ), mul( randX, rotation ) ); const randY = vec3( element( rand1, 1 ), element( rand2, 1 ), element( rand3, 1 ) ); const scaleMin = element( scaleRange, 0 ); const scaleMax = element( scaleRange, 1 ); const randomScale = mix( vec3( scaleMin, scaleMin, scaleMin ), vec3( scaleMax, scaleMax, scaleMax ), randY ); const scales = mix( vec3( 1, 1, 1 ), randomScale, scale ); const offsetMin = element( offsetRange, 0 ); const offsetMax = element( offsetRange, 1 ); const offset1 = mix( vec2( offsetMin, offsetMin ), vec2( offsetMax, offsetMax ), mul( rand1, offset ) ); const offset2 = mix( vec2( offsetMin, offsetMin ), vec2( offsetMax, offsetMax ), mul( rand2, offset ) ); const offset3 = mix( vec2( offsetMin, offsetMin ), vec2( offsetMax, offsetMax ), mul( rand3, offset ) ); const sampleCoord = ( center, randomOffset, rotationValue, sampleScale ) => { const delta = sub( coord, center ); const rotated = mxRotate2d( delta, sin( rotationValue ), cos( rotationValue ) ); const safeScale = max( sampleScale, HEXTILE_EPSILON ); return add( add( div( rotated, vec2( safeScale, safeScale ) ), center ), randomOffset ); }; const sampleDerivative = ( derivative, rotationValue, sampleScale ) => { const rotated = mxRotate2d( derivative, sin( rotationValue ), cos( rotationValue ) ); const safeScale = max( sampleScale, HEXTILE_EPSILON ); return div( rotated, vec2( safeScale, safeScale ) ); }; const ddx = dFdx( coord ); const ddy = dFdy( coord ); return { coords: [ sampleCoord( ctr1, offset1, element( rotations, 0 ), element( scales, 0 ) ), sampleCoord( ctr2, offset2, element( rotations, 1 ), element( scales, 1 ) ), sampleCoord( ctr3, offset3, element( rotations, 2 ), element( scales, 2 ) ), ], ddx: [ sampleDerivative( ddx, element( rotations, 0 ), element( scales, 0 ) ), sampleDerivative( ddx, element( rotations, 1 ), element( scales, 1 ) ), sampleDerivative( ddx, element( rotations, 2 ), element( scales, 2 ) ), ], ddy: [ sampleDerivative( ddy, element( rotations, 0 ), element( scales, 0 ) ), sampleDerivative( ddy, element( rotations, 1 ), element( scales, 1 ) ), sampleDerivative( ddy, element( rotations, 2 ), element( scales, 2 ) ), ], weights: vec3( w1, w2, w3 ), }; }