{"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 = >(\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\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 & {\n __ok: boolean\n}\n\nexport function modifyShader(\n material: ModifiedMaterial,\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 ', '')\n\n // vec3 transformedNormal declaration overriden below\n .replace('#include ', '')\n\n // vec3 transformed declaration overriden below\n .replace('#include ', '')\n\n // shader override\n .replace(\n /void\\s*main\\s*\\(\\)\\s*\\{/,\n /* glsl */ `\n void main() {\n #include \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 ',\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 {\n public curveArray: Curve[]\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>(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 = /* @__PURE__ */ 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> {\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,6BAA6BC,MAAAA;AAK5B,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;;;;;;;"}