MORE RE-ORGANIZING
This commit is contained in:
@@ -1,146 +0,0 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
const halfWidth = 1.68
|
||||
const bottom = -2.24
|
||||
const seam = 1.97
|
||||
const top = 2.28
|
||||
const midDepth = -0.09
|
||||
const smooth = THREE.MathUtils.smoothstep
|
||||
|
||||
function wrapperTexture(back = false) {
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = 1024
|
||||
canvas.height = 1400
|
||||
const context = canvas.getContext('2d')!
|
||||
context.fillStyle = '#263a40'
|
||||
context.fillRect(0, 0, 1024, 1400)
|
||||
const gradient = context.createLinearGradient(0, 0, 1024, 1400)
|
||||
gradient.addColorStop(0, '#ffffff0c')
|
||||
gradient.addColorStop(0.45, '#ffffff00')
|
||||
gradient.addColorStop(1, '#00000022')
|
||||
context.fillStyle = gradient
|
||||
context.fillRect(0, 0, 1024, 1400)
|
||||
context.strokeStyle = '#a59569'
|
||||
context.lineWidth = 2
|
||||
context.strokeRect(66, 155, 892, 1100)
|
||||
context.strokeRect(80, 169, 864, 1072)
|
||||
context.fillStyle = '#baa775'
|
||||
context.fillRect(0, 0, 1024, 100)
|
||||
context.fillRect(0, 1315, 1024, 85)
|
||||
context.textAlign = 'center'
|
||||
context.fillStyle = '#283339'
|
||||
context.font = 'bold 19px sans-serif'
|
||||
context.fillText('P U L L T O O P E N →', 512, 58)
|
||||
context.fillStyle = '#d8c994'
|
||||
context.save()
|
||||
context.translate(512, 480)
|
||||
context.rotate(Math.PI / 4)
|
||||
context.strokeRect(-100, -100, 200, 200)
|
||||
context.strokeRect(-80, -80, 160, 160)
|
||||
context.restore()
|
||||
context.font = '56px Georgia'
|
||||
context.fillText('S', 512, 500)
|
||||
context.font = '44px Georgia'
|
||||
context.fillText('SANCTIFICATION', 512, 740)
|
||||
context.font = '21px sans-serif'
|
||||
context.fillText('C O L L E C T O R S E R I E S', 512, 795)
|
||||
context.font = '19px sans-serif'
|
||||
context.fillText(back ? 'AUTHORED EDITION / 001' : 'THREE CARDS / ONE COLLECTION', 512, 1175)
|
||||
const texture = new THREE.CanvasTexture(canvas)
|
||||
texture.colorSpace = THREE.SRGBColorSpace
|
||||
return texture
|
||||
}
|
||||
|
||||
/**
|
||||
* Two joined pouch surfaces and one sealed ribbon, never a label over a shell.
|
||||
* All deformation is evaluated from immutable UVs, not accumulated frame deltas.
|
||||
*/
|
||||
export function createFoilWrapper() {
|
||||
const root = new THREE.Group()
|
||||
root.name = 'PACK_FOIL_WRAPPER'
|
||||
const frontTexture = wrapperTexture()
|
||||
const frontMaterial = new THREE.MeshStandardMaterial({
|
||||
map: frontTexture, roughness: 0.43, metalness: 0.68, side: THREE.DoubleSide,
|
||||
})
|
||||
const backMaterial = frontMaterial.clone()
|
||||
backMaterial.map = wrapperTexture(true)
|
||||
const stripMaterial = frontMaterial.clone()
|
||||
const sheets = [1, -1].map((side) => {
|
||||
const geometry = new THREE.PlaneGeometry(halfWidth * 2, seam - bottom, 64, 80)
|
||||
const mesh = new THREE.Mesh(geometry, side === 1 ? frontMaterial : backMaterial)
|
||||
mesh.name = side === 1 ? 'FOIL_FRONT' : 'FOIL_BACK'
|
||||
// UVs span the original, uncut printed sheet; geometry UVs never change.
|
||||
const uv = geometry.getAttribute('uv')
|
||||
for (let i = 0; i < uv.count; i++) uv.setY(i, uv.getY(i) * (seam - bottom) / (top - bottom))
|
||||
root.add(mesh)
|
||||
return { mesh, side }
|
||||
})
|
||||
// Match the pouch's horizontal tessellation so the attached tear boundary shares every sample.
|
||||
const stripGeometry = new THREE.PlaneGeometry(halfWidth * 2, top - seam, 64, 10)
|
||||
const stripUV = stripGeometry.getAttribute('uv')
|
||||
for (let i = 0; i < stripUV.count; i++) {
|
||||
stripUV.setY(i, (seam - bottom + stripUV.getY(i) * (top - seam)) / (top - bottom))
|
||||
}
|
||||
const strip = new THREE.Mesh(stripGeometry, stripMaterial)
|
||||
strip.name = 'FOIL_TEAR_STRIP'
|
||||
root.add(strip)
|
||||
|
||||
function tornEdge(u: number) {
|
||||
return 0.011 * Math.sin(u * Math.PI * 96) + 0.005 * Math.sin(u * 173)
|
||||
}
|
||||
|
||||
function deform(tear: number, detach: number, mouth: number, touch: THREE.Vector3, stripExit = 4.8) {
|
||||
for (const { mesh, side } of sheets) {
|
||||
const geometry = mesh.geometry
|
||||
const positions = geometry.getAttribute('position')
|
||||
const uv = geometry.getAttribute('uv')
|
||||
for (let i = 0; i < positions.count; i++) {
|
||||
const u = uv.getX(i)
|
||||
const y = bottom + uv.getY(i) * (top - bottom)
|
||||
const x = (u * 2 - 1) * halfWidth
|
||||
const across = u === 0 || u === 1 ? 0 : Math.pow(Math.sin(u * Math.PI), 0.24)
|
||||
const lower = smooth(y, bottom + 0.14, bottom + 0.47)
|
||||
const neck = 1 - smooth(y, 1.65, seam)
|
||||
const opening = mouth * smooth(y, 0.6, seam)
|
||||
const envelope = across * lower
|
||||
const diagonal = Math.sin(x * 13 + y * 7) * Math.sin(y * 3.1 - x * 2)
|
||||
const folds = (0.009 * diagonal + 0.003 * Math.sin(x * 28 - y * 5))
|
||||
* envelope * (0.35 + 0.65 * Math.pow(Math.abs(x / halfWidth), 1.5))
|
||||
const crimp = 0.013 * (1 + 0.7 * Math.sin(x * 36))
|
||||
* (1 - lower) * smooth(y, bottom, bottom + 0.1) * across
|
||||
const dent = side === 1 ? -0.045 * touch.z
|
||||
* Math.exp(-((x - touch.x) ** 2 + (y - touch.y) ** 2) * 5) * envelope * neck : 0
|
||||
const depth = 0.37 * neck + 0.64 * opening
|
||||
const lip = mouth * (side === 1 ? -0.28 : 0.08) * across * smooth(y, 1, seam)
|
||||
positions.setXYZ(i, x, y + tornEdge(u) * smooth(y, 1.85, seam) + lip,
|
||||
midDepth + side * (depth * envelope + folds * neck + crimp) + dent)
|
||||
}
|
||||
positions.needsUpdate = true
|
||||
geometry.computeVertexNormals()
|
||||
geometry.computeBoundingSphere()
|
||||
}
|
||||
const positions = stripGeometry.getAttribute('position')
|
||||
for (let i = 0; i < positions.count; i++) {
|
||||
const u = stripUV.getX(i)
|
||||
const v = (bottom + stripUV.getY(i) * (top - bottom) - seam) / (top - seam)
|
||||
const length = Math.max(0, tear - u) * halfWidth * 2
|
||||
const curvature = 0.55 + detach * 0.5
|
||||
const angle = length * curvature
|
||||
const anchor = (Math.max(tear, u) * 2 - 1) * halfWidth
|
||||
const release = smooth(length, 0, 0.3)
|
||||
// The released end rolls up around the moving tear front; the attached end stays sealed.
|
||||
positions.setXYZ(i,
|
||||
anchor - Math.sin(angle) / curvature + detach * stripExit,
|
||||
seam + v * (top - seam) + tornEdge(u) * (1 - v)
|
||||
+ 0.36 * (1 - Math.cos(angle)) / curvature + release * 0.035 + detach * 0.4,
|
||||
midDepth + 0.012 * Math.sin((u * 2 - 1) * halfWidth * 36) * Math.sin(v * Math.PI)
|
||||
- 0.85 * (1 - Math.cos(angle)) / curvature - detach * 0.65)
|
||||
}
|
||||
positions.needsUpdate = true
|
||||
stripGeometry.computeVertexNormals()
|
||||
stripGeometry.computeBoundingSphere()
|
||||
strip.visible = detach < 1
|
||||
}
|
||||
deform(0, 0, 0, new THREE.Vector3())
|
||||
return { root, deform }
|
||||
}
|
||||
Reference in New Issue
Block a user