{"version":3,"file":"CurveModifier.cjs","sources":["../../src/modifiers/CurveModifier.ts"],"sourcesContent":["// Original src: https://github.com/zz85/threejs-path-flow\nconst CHANNELS = 4\nconst TEXTURE_WIDTH = 1024\nconst TEXTURE_HEIGHT = 4\n\nimport {\n  DataTexture,\n  RGBAFormat,\n  FloatType,\n  RepeatWrapping,\n  Mesh,\n  InstancedMesh,\n  NearestFilter,\n  DynamicDrawUsage,\n  Matrix4,\n  Material,\n  Curve,\n  BufferGeometry,\n} from 'three'\n\nimport type { IUniform } from 'three'\n\n/**\n * Make a new DataTexture to store the descriptions of the curves.\n *\n * @param { number } numberOfCurves the number of curves needed to be described by this texture.\n */\nexport const initSplineTexture = (numberOfCurves = 1): DataTexture => {\n  const dataArray = new Float32Array(TEXTURE_WIDTH * TEXTURE_HEIGHT * numberOfCurves * CHANNELS)\n  const dataTexture = new DataTexture(dataArray, TEXTURE_WIDTH, TEXTURE_HEIGHT * numberOfCurves, RGBAFormat, FloatType)\n\n  dataTexture.wrapS = RepeatWrapping\n  dataTexture.wrapT = RepeatWrapping\n  dataTexture.magFilter = NearestFilter\n  dataTexture.needsUpdate = true\n\n  return dataTexture\n}\n\n/**\n * Write the curve description to the data texture\n *\n * @param { DataTexture } texture The DataTexture to write to\n * @param { Curve } splineCurve The curve to describe\n * @param { number } offset Which curve slot to write to\n */\nexport const updateSplineTexture = <TCurve extends Curve<any>>(\n  texture: DataTexture,\n  splineCurve: TCurve,\n  offset = 0,\n): void => {\n  const numberOfPoints = Math.floor(TEXTURE_WIDTH * (TEXTURE_HEIGHT / 4))\n  splineCurve.arcLengthDivisions = numberOfPoints / 2\n  splineCurve.updateArcLengths()\n  const points = splineCurve.getSpacedPoints(numberOfPoints)\n  const frenetFrames = splineCurve.computeFrenetFrames(numberOfPoints, true)\n\n  for (let i = 0; i < numberOfPoints; i++) {\n    const rowOffset = Math.floor(i / TEXTURE_WIDTH)\n    const rowIndex = i % TEXTURE_WIDTH\n\n    let pt = points[i]\n    setTextureValue(texture, rowIndex, pt.x, pt.y, pt.z, 0 + rowOffset + TEXTURE_HEIGHT * offset)\n    pt = frenetFrames.tangents[i]\n    setTextureValue(texture, rowIndex, pt.x, pt.y, pt.z, 1 + rowOffset + TEXTURE_HEIGHT * offset)\n    pt = frenetFrames.normals[i]\n    setTextureValue(texture, rowIndex, pt.x, pt.y, pt.z, 2 + rowOffset + TEXTURE_HEIGHT * offset)\n    pt = frenetFrames.binormals[i]\n    setTextureValue(texture, rowIndex, pt.x, pt.y, pt.z, 3 + rowOffset + TEXTURE_HEIGHT * offset)\n  }\n\n  texture.needsUpdate = true\n}\n\nconst setTextureValue = (texture: DataTexture, index: number, x: number, y: number, z: number, o: number): void => {\n  const image = texture.image\n  const { data } = image\n  const i = CHANNELS * TEXTURE_WIDTH * o // Row Offset\n  data[index * CHANNELS + i + 0] = x\n  data[index * CHANNELS + i + 1] = y\n  data[index * CHANNELS + i + 2] = z\n  data[index * CHANNELS + i + 3] = 1\n}\n\nexport interface INumericUniform extends IUniform {\n  type: 'f' | 'i'\n  value: number\n}\n\nexport type CurveModifierUniforms = {\n  spineTexture: IUniform<DataTexture>\n  pathOffset: INumericUniform\n  pathSegment: INumericUniform\n  spineOffset: INumericUniform\n  spineLength: INumericUniform\n  flow: INumericUniform\n}\n\n/**\n * Create a new set of uniforms for describing the curve modifier\n *\n * @param { DataTexture } Texture which holds the curve description\n */\nexport const getUniforms = (splineTexture: DataTexture): CurveModifierUniforms => ({\n  spineTexture: { value: splineTexture },\n  pathOffset: { type: 'f', value: 0 }, // time of path curve\n  pathSegment: { type: 'f', value: 1 }, // fractional length of path\n  spineOffset: { type: 'f', value: 161 },\n  spineLength: { type: 'f', value: 400 },\n  flow: { type: 'i', value: 1 },\n})\n\nexport type ModifiedMaterial<TMaterial extends Material> = TMaterial & {\n  __ok: boolean\n}\n\nexport function modifyShader<TMaterial extends Material = Material>(\n  material: ModifiedMaterial<TMaterial>,\n  uniforms: CurveModifierUniforms,\n  numberOfCurves = 1,\n): void {\n  if (material.__ok) return\n  material.__ok = true\n\n  material.onBeforeCompile = (shader: { vertexShader: string; uniforms: { [uniform: string]: IUniform } }): void => {\n    if ((shader as any).__modified) return\n    ;(shader as any).__modified = true\n\n    Object.assign(shader.uniforms, uniforms)\n\n    const vertexShader = /* glsl */ `\n\t\tuniform sampler2D spineTexture;\n\t\tuniform float pathOffset;\n\t\tuniform float pathSegment;\n\t\tuniform float spineOffset;\n\t\tuniform float spineLength;\n\t\tuniform int flow;\n\n\t\tfloat textureLayers = ${TEXTURE_HEIGHT * numberOfCurves}.;\n\t\tfloat textureStacks = ${TEXTURE_HEIGHT / 4}.;\n\n\t\t${shader.vertexShader}\n\t\t`\n      // chunk import moved in front of modified shader below\n      .replace('#include <beginnormal_vertex>', '')\n\n      // vec3 transformedNormal declaration overriden below\n      .replace('#include <defaultnormal_vertex>', '')\n\n      // vec3 transformed declaration overriden below\n      .replace('#include <begin_vertex>', '')\n\n      // shader override\n      .replace(\n        /void\\s*main\\s*\\(\\)\\s*\\{/,\n        /* glsl */ `\n        void main() {\n        #include <beginnormal_vertex>\n\n        vec4 worldPos = modelMatrix * vec4(position, 1.);\n\n        bool bend = flow > 0;\n        float xWeight = bend ? 0. : 1.;\n\n        #ifdef USE_INSTANCING\n        float pathOffsetFromInstanceMatrix = instanceMatrix[3][2];\n        float spineLengthFromInstanceMatrix = instanceMatrix[3][0];\n        float spinePortion = bend ? (worldPos.x + spineOffset) / spineLengthFromInstanceMatrix : 0.;\n        float mt = (spinePortion * pathSegment + pathOffset + pathOffsetFromInstanceMatrix)*textureStacks;\n        #else\n        float spinePortion = bend ? (worldPos.x + spineOffset) / spineLength : 0.;\n        float mt = (spinePortion * pathSegment + pathOffset)*textureStacks;\n        #endif\n\n        mt = mod(mt, textureStacks);\n        float rowOffset = floor(mt);\n\n        #ifdef USE_INSTANCING\n        rowOffset += instanceMatrix[3][1] * ${TEXTURE_HEIGHT}.;\n        #endif\n\n        vec3 spinePos = texture2D(spineTexture, vec2(mt, (0. + rowOffset + 0.5) / textureLayers)).xyz;\n        vec3 a =        texture2D(spineTexture, vec2(mt, (1. + rowOffset + 0.5) / textureLayers)).xyz;\n        vec3 b =        texture2D(spineTexture, vec2(mt, (2. + rowOffset + 0.5) / textureLayers)).xyz;\n        vec3 c =        texture2D(spineTexture, vec2(mt, (3. + rowOffset + 0.5) / textureLayers)).xyz;\n        mat3 basis = mat3(a, b, c);\n\n        vec3 transformed = basis\n          * vec3(worldPos.x * xWeight, worldPos.y * 1., worldPos.z * 1.)\n          + spinePos;\n\n        vec3 transformedNormal = normalMatrix * (basis * objectNormal);\n\t\t\t`,\n      )\n      .replace(\n        '#include <project_vertex>',\n        /* glsl */ `vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n\t\t\t\tgl_Position = projectionMatrix * mvPosition;`,\n      )\n\n    shader.vertexShader = vertexShader\n  }\n}\n\n/**\n * A helper class for making meshes bend aroudn curves\n */\nexport class Flow<TMesh extends Mesh = Mesh> {\n  public curveArray: Curve<any>[]\n  public curveLengthArray: number[]\n\n  public object3D: TMesh\n  public splineTexure: DataTexture\n  public uniforms: CurveModifierUniforms\n\n  /**\n   * @param {Mesh} mesh The mesh to clone and modify to bend around the curve\n   * @param {number} numberOfCurves The amount of space that should preallocated for additional curves\n   */\n  constructor(mesh: TMesh, numberOfCurves = 1) {\n    const obj3D = mesh.clone() as TMesh\n    const splineTexure = initSplineTexture(numberOfCurves)\n    const uniforms = getUniforms(splineTexure)\n\n    obj3D.traverse((child) => {\n      if (child instanceof Mesh || child instanceof InstancedMesh) {\n        child.material = child.material.clone()\n        modifyShader(child.material, uniforms, numberOfCurves)\n      }\n    })\n\n    this.curveArray = new Array(numberOfCurves)\n    this.curveLengthArray = new Array(numberOfCurves)\n\n    this.object3D = obj3D\n    this.splineTexure = splineTexure\n    this.uniforms = uniforms\n  }\n\n  public updateCurve<TCurve extends Curve<any>>(index: number, curve: TCurve): void {\n    if (index >= this.curveArray.length) throw Error('Index out of range for Flow')\n    const curveLength = curve.getLength()\n    this.uniforms.spineLength.value = curveLength\n    this.curveLengthArray[index] = curveLength\n    this.curveArray[index] = curve\n    updateSplineTexture(this.splineTexure, curve, index)\n  }\n\n  public moveAlongCurve(amount: number): void {\n    this.uniforms.pathOffset.value += amount\n  }\n}\nconst matrix = new Matrix4()\n\n/**\n * A helper class for creating instanced versions of flow, where the instances are placed on the curve.\n */\nexport class InstancedFlow<\n  TGeometry extends BufferGeometry = BufferGeometry,\n  TMaterial extends Material = Material\n> extends Flow<InstancedMesh<TGeometry, TMaterial>> {\n  public offsets: number[]\n  public whichCurve: number[]\n\n  /**\n   *\n   * @param {number} count The number of instanced elements\n   * @param {number} curveCount The number of curves to preallocate for\n   * @param {Geometry} geometry The geometry to use for the instanced mesh\n   * @param {Material} material The material to use for the instanced mesh\n   */\n  constructor(count: number, curveCount: number, geometry: TGeometry, material: TMaterial) {\n    const mesh = new InstancedMesh(geometry, material, count)\n    mesh.instanceMatrix.setUsage(DynamicDrawUsage)\n    mesh.frustumCulled = false\n    super(mesh, curveCount)\n\n    this.offsets = new Array(count).fill(0)\n    this.whichCurve = new Array(count).fill(0)\n  }\n\n  /**\n   * The extra information about which curve and curve position is stored in the translation components of the matrix for the instanced objects\n   * This writes that information to the matrix and marks it as needing update.\n   *\n   * @param {number} index of the instanced element to update\n   */\n  private writeChanges(index: number): void {\n    matrix.makeTranslation(this.curveLengthArray[this.whichCurve[index]], this.whichCurve[index], this.offsets[index])\n    this.object3D.setMatrixAt(index, matrix)\n    this.object3D.instanceMatrix.needsUpdate = true\n  }\n\n  /**\n   * Move an individual element along the curve by a specific amount\n   *\n   * @param {number} index Which element to update\n   * @param {number} offset Move by how much\n   */\n  public moveIndividualAlongCurve(index: number, offset: number): void {\n    this.offsets[index] += offset\n    this.writeChanges(index)\n  }\n\n  /**\n   * Select which curve to use for an element\n   *\n   * @param {number} index the index of the instanced element to update\n   * @param {number} curveNo the index of the curve it should use\n   */\n  public setCurve(index: number, curveNo: number): void {\n    if (isNaN(curveNo)) throw Error('curve index being set is Not a Number (NaN)')\n    this.whichCurve[index] = curveNo\n    this.writeChanges(index)\n  }\n}\n"],"names":["DataTexture","RGBAFormat","FloatType","RepeatWrapping","NearestFilter","Mesh","InstancedMesh","Matrix4","DynamicDrawUsage"],"mappings":";;;;;;;;;AACA,MAAM,WAAW;AACjB,MAAM,gBAAgB;AACtB,MAAM,iBAAiB;AAwBV,MAAA,oBAAoB,CAAC,iBAAiB,MAAmB;AACpE,QAAM,YAAY,IAAI,aAAa,gBAAgB,iBAAiB,iBAAiB,QAAQ;AACvF,QAAA,cAAc,IAAIA,MAAY,YAAA,WAAW,eAAe,iBAAiB,gBAAgBC,kBAAYC,MAAAA,SAAS;AAEpH,cAAY,QAAQC;AACpB,cAAY,QAAQA;AACpB,cAAY,YAAYC;AACxB,cAAY,cAAc;AAEnB,SAAA;AACT;AASO,MAAM,sBAAsB,CACjC,SACA,aACA,SAAS,MACA;AACT,QAAM,iBAAiB,KAAK,MAAM,iBAAiB,iBAAiB,EAAE;AACtE,cAAY,qBAAqB,iBAAiB;AAClD,cAAY,iBAAiB;AACvB,QAAA,SAAS,YAAY,gBAAgB,cAAc;AACzD,QAAM,eAAe,YAAY,oBAAoB,gBAAgB,IAAI;AAEzE,WAAS,IAAI,GAAG,IAAI,gBAAgB,KAAK;AACvC,UAAM,YAAY,KAAK,MAAM,IAAI,aAAa;AAC9C,UAAM,WAAW,IAAI;AAEjB,QAAA,KAAK,OAAO,CAAC;AACD,oBAAA,SAAS,UAAU,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,YAAY,iBAAiB,MAAM;AACvF,SAAA,aAAa,SAAS,CAAC;AACZ,oBAAA,SAAS,UAAU,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,YAAY,iBAAiB,MAAM;AACvF,SAAA,aAAa,QAAQ,CAAC;AACX,oBAAA,SAAS,UAAU,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,YAAY,iBAAiB,MAAM;AACvF,SAAA,aAAa,UAAU,CAAC;AACb,oBAAA,SAAS,UAAU,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,YAAY,iBAAiB,MAAM;AAAA,EAC9F;AAEA,UAAQ,cAAc;AACxB;AAEA,MAAM,kBAAkB,CAAC,SAAsB,OAAe,GAAW,GAAW,GAAW,MAAoB;AACjH,QAAM,QAAQ,QAAQ;AAChB,QAAA,EAAE,KAAS,IAAA;AACX,QAAA,IAAI,WAAW,gBAAgB;AACrC,OAAK,QAAQ,WAAW,IAAI,CAAC,IAAI;AACjC,OAAK,QAAQ,WAAW,IAAI,CAAC,IAAI;AACjC,OAAK,QAAQ,WAAW,IAAI,CAAC,IAAI;AACjC,OAAK,QAAQ,WAAW,IAAI,CAAC,IAAI;AACnC;AAqBa,MAAA,cAAc,CAAC,mBAAuD;AAAA,EACjF,cAAc,EAAE,OAAO,cAAc;AAAA,EACrC,YAAY,EAAE,MAAM,KAAK,OAAO,EAAE;AAAA;AAAA,EAClC,aAAa,EAAE,MAAM,KAAK,OAAO,EAAE;AAAA;AAAA,EACnC,aAAa,EAAE,MAAM,KAAK,OAAO,IAAI;AAAA,EACrC,aAAa,EAAE,MAAM,KAAK,OAAO,IAAI;AAAA,EACrC,MAAM,EAAE,MAAM,KAAK,OAAO,EAAE;AAC9B;AAMO,SAAS,aACd,UACA,UACA,iBAAiB,GACX;AACN,MAAI,SAAS;AAAM;AACnB,WAAS,OAAO;AAEP,WAAA,kBAAkB,CAAC,WAAsF;AAChH,QAAK,OAAe;AAAY;AAC9B,WAAe,aAAa;AAEvB,WAAA,OAAO,OAAO,UAAU,QAAQ;AAEjC,UAAA;AAAA;AAAA,MAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAQV,iBAAiB;AAAA,0BACjB,iBAAiB;AAAA;AAAA,IAEvC,OAAO;AAAA,IAGJ,QAAQ,iCAAiC,EAAE,EAG3C,QAAQ,mCAAmC,EAAE,EAG7C,QAAQ,2BAA2B,EAAE,EAGrC;AAAA,QACC;AAAA;AAAA,QACW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8CAuB2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,EAgBvC;AAAA,QACC;AAAA;AAAA,QACW;AAAA;AAAA,MAEb;AAAA;AAEF,WAAO,eAAe;AAAA,EAAA;AAE1B;AAKO,MAAM,KAAgC;AAAA;AAAA;AAAA;AAAA;AAAA,EAY3C,YAAY,MAAa,iBAAiB,GAAG;AAXtC;AACA;AAEA;AACA;AACA;AAOC,UAAA,QAAQ,KAAK;AACb,UAAA,eAAe,kBAAkB,cAAc;AAC/C,UAAA,WAAW,YAAY,YAAY;AAEnC,UAAA,SAAS,CAAC,UAAU;AACpB,UAAA,iBAAiBC,MAAAA,QAAQ,iBAAiBC,qBAAe;AACrD,cAAA,WAAW,MAAM,SAAS,MAAM;AACzB,qBAAA,MAAM,UAAU,UAAU,cAAc;AAAA,MACvD;AAAA,IAAA,CACD;AAEI,SAAA,aAAa,IAAI,MAAM,cAAc;AACrC,SAAA,mBAAmB,IAAI,MAAM,cAAc;AAEhD,SAAK,WAAW;AAChB,SAAK,eAAe;AACpB,SAAK,WAAW;AAAA,EAClB;AAAA,EAEO,YAAuC,OAAe,OAAqB;AAC5E,QAAA,SAAS,KAAK,WAAW;AAAQ,YAAM,MAAM,6BAA6B;AACxE,UAAA,cAAc,MAAM;AACrB,SAAA,SAAS,YAAY,QAAQ;AAC7B,SAAA,iBAAiB,KAAK,IAAI;AAC1B,SAAA,WAAW,KAAK,IAAI;AACL,wBAAA,KAAK,cAAc,OAAO,KAAK;AAAA,EACrD;AAAA,EAEO,eAAe,QAAsB;AACrC,SAAA,SAAS,WAAW,SAAS;AAAA,EACpC;AACF;AACA,MAAM,SAAS,IAAIC,MAAAA;AAKZ,MAAM,sBAGH,KAA0C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWlD,YAAY,OAAe,YAAoB,UAAqB,UAAqB;AACvF,UAAM,OAAO,IAAID,MAAc,cAAA,UAAU,UAAU,KAAK;AACnD,SAAA,eAAe,SAASE,MAAAA,gBAAgB;AAC7C,SAAK,gBAAgB;AACrB,UAAM,MAAM,UAAU;AAdjB;AACA;AAeL,SAAK,UAAU,IAAI,MAAM,KAAK,EAAE,KAAK,CAAC;AACtC,SAAK,aAAa,IAAI,MAAM,KAAK,EAAE,KAAK,CAAC;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,aAAa,OAAqB;AACxC,WAAO,gBAAgB,KAAK,iBAAiB,KAAK,WAAW,KAAK,CAAC,GAAG,KAAK,WAAW,KAAK,GAAG,KAAK,QAAQ,KAAK,CAAC;AAC5G,SAAA,SAAS,YAAY,OAAO,MAAM;AAClC,SAAA,SAAS,eAAe,cAAc;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQO,yBAAyB,OAAe,QAAsB;AAC9D,SAAA,QAAQ,KAAK,KAAK;AACvB,SAAK,aAAa,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQO,SAAS,OAAe,SAAuB;AACpD,QAAI,MAAM,OAAO;AAAG,YAAM,MAAM,6CAA6C;AACxE,SAAA,WAAW,KAAK,IAAI;AACzB,SAAK,aAAa,KAAK;AAAA,EACzB;AACF;;;;;;;"}