This commit is contained in:
Piotr Goszczyński committed 2025-03-17 19:46:49 +01:00
1 parent 598a22d35a
commit 83ea892b3c
7 files changed
+67 -823

No files matched your search

@@ -1,261 +1,80 @@
import * as THREE from 'three'
import { cn } from 'ui'
import { createThreeSetup, useThreeJS, createTicketMesh } from './helpers'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { useCallback, useRef, useState } from 'react'
import { useThreeJS } from './helpers'
import { useCallback, useRef } from 'react'
import SceneRenderer, { BaseScene } from './utils/SceneRenderer'
import TicketScene from './scenes/TicketScene'
const LwCanvas = ({ className }: { className?: string }) => {
const secretRef = useRef(false)
interface TicketCanvasProps {
secret?: boolean
platinum?: boolean
user: {
id?: string
name?: string
ticketNumber?: number
}
startDate: Date
playmodeRTChannel: unknown
className?: string
onUpgrade?: () => void
}
const setup = useCallback((container: HTMLElement) => {
// Create scene with postprocessing effects
const {
scene,
camera,
renderer,
composer,
resize,
crtPass,
glitchPass,
bloomPass,
stats,
debug,
orbitControls,
} = createThreeSetup(container, {
cameraPosition: new THREE.Vector3(0, 0, 5),
debug: false,
postprocessing: {
bloom: {
enabled: true,
strength: 0.25,
radius: 0.7,
threshold: 0.2,
const TicketCanvas = ({
secret,
platinum,
user,
startDate,
playmodeRTChannel,
className,
onUpgrade,
}: TicketCanvasProps) => {
const sceneRef = useRef<TicketScene | null>(null)
const setup = useCallback(
(container: HTMLElement) => {
let scene: BaseScene | null = null
const sceneRenderer = new SceneRenderer(container)
sceneRef.current = new TicketScene({
defaultSecret: secret,
defaultPlatinum: platinum,
user,
startDate,
onSeatChartButtonClicked: () => {
sceneRef.current?.showBackSide()
sceneRef.current?.upgradeToSecret()
},
glitch: {
enabled: true,
dtSize: 512, // Increase detail size for less frequent glitches
colS: 0,
onGoBackButtonClicked: () => {
sceneRef.current?.showFrontSide()
},
crt: {
enabled: true,
},
},
})
// Set renderer clear color to make background visible
renderer.setClearColor(0x000000, 0)
// Add ambient light with increased intensity
const ambientLight = new THREE.AmbientLight(0xffffff, 1.0)
scene.add(ambientLight)
// Add directional light
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5)
directionalLight.position.set(5, 5, 5)
scene.add(directionalLight)
// Add a point light to better illuminate the placeholder
const pointLight = new THREE.PointLight(0xffffff, 1.0)
pointLight.position.set(0, 0, 5)
scene.add(pointLight)
// Create ticket mesh placeholder (will be replaced when texture loads)
const planeGeometrySize = [12, 6]
const placeholderGeometry = new THREE.PlaneGeometry(...planeGeometrySize)
const placeholderMaterial = new THREE.MeshBasicMaterial({
color: 0x000000,
transparent: true,
opacity: 1,
side: THREE.DoubleSide,
})
// Use a more generic type for ticket to allow reassignment to either Mesh or Group
let ticket: THREE.Object3D = new THREE.Mesh(placeholderGeometry, placeholderMaterial)
scene.add(ticket)
// Ensure the ticket is visible in the camera's view
ticket.position.z = 0
// Load the ticket texture and create the actual ticket mesh
// Replace this URL with your actual ticket texture
const ticketScene = '/images/launchweek/14/supabase-ticket.glb'
const ticketStaticTexture = '/images/launchweek/14/supabase-static-ticket.png'
createTicketMesh(ticketScene, {
width: planeGeometrySize[0],
height: planeGeometrySize[1],
ticketTextureSource: ticketStaticTexture,
debug,
})
.then((ticketModel) => {
// Remove placeholder and add the actual ticket model
scene.remove(ticket)
scene.add(ticketModel)
// Store reference to the new ticket for animation
if (ticket instanceof THREE.Mesh) {
ticket.geometry.dispose()
if (Array.isArray(ticket.material))
ticket.material.forEach((material) => material.dispose())
else ticket.material.dispose()
}
// Update the ticket reference
ticket = ticketModel
})
.catch((error) => {
console.error('Failed to load ticket texture:', error)
})
// Add window resize listener
window.addEventListener('resize', resize)
sceneRenderer.activateScene(sceneRef.current)
// Mouse movement tracking for glitch effect and ticket following
let lastMouseX = 0
let lastMouseY = 0
let mouseIntensity = 0
let mouseIntensityDecay = 0.95 // How quickly the intensity decays
sceneRenderer.init()
// For mouse tracking of ticket
let mouseX = 0
let mouseY = 0
let targetX = 0
let targetY = 0
const handleMouseMove = (event: MouseEvent) => {
// Calculate mouse movement distance for glitch effect
const dx = event.clientX - lastMouseX
const dy = event.clientY - lastMouseY
const distance = Math.sqrt(dx * dx + dy * dy)
// Update intensity based on movement (clamped between 0 and 1)
mouseIntensity = Math.max(0, mouseIntensity + distance * 0.005)
// Update last position
lastMouseX = event.clientX
lastMouseY = event.clientY
// Update glitch intensity
if (glitchPass) {
glitchPass.setIntensity(mouseIntensity)
}
// Calculate normalized mouse position for ticket following
// Convert from screen coordinates to normalized device coordinates (-1 to +1)
const rect = container.getBoundingClientRect()
mouseX = ((event.clientX - rect.left) / container.clientWidth) * 2 - 1
mouseY = -((event.clientY - rect.top) / container.clientHeight) * 2 + 1
// Scale down the effect for subtle movement
targetX = mouseX * 0.5
targetY = mouseY * 0.3
}
// Make the container element respond to pointer events
container.style.pointerEvents = 'auto'
// Add mouse event listeners
container.addEventListener('mousemove', handleMouseMove)
// Animation function
const animate = (time?: number) => {
const currentTime = time || 0
// Update CRT shader time uniform for animation effects
if (crtPass) {
crtPass.uniforms.time.value = currentTime
crtPass.enabled = secretRef.current
}
if(glitchPass) {
glitchPass.enabled = secretRef.current
}
if(bloomPass) {
bloomPass.enabled = secretRef.current
}
// Gradually decay mouse intensity when not moving
if (mouseIntensity > 0) {
mouseIntensity *= mouseIntensityDecay
if (mouseIntensity < 0.0000001) {
mouseIntensity = 0
}
if (glitchPass) {
glitchPass.setIntensity(mouseIntensity)
}
}
if (stats.instance) {
stats.instance.begin()
}
if (orbitControls.instance) {
// Update controls in animation loop
orbitControls.instance.update()
}
// Render with post-processing
composer.render()
if (stats.instance) {
stats.instance.end()
}
}
// Cleanup function
const cleanup = () => {
window.removeEventListener('resize', resize)
// Your existing cleanup code...
orbitControls.instance?.dispose() // Dispose controls on cleanup
// Dispose of all resources
scene.traverse((object) => {
if (object instanceof THREE.Mesh) {
if (object.geometry) object.geometry.dispose()
if (object.material) {
if (Array.isArray(object.material)) {
object.material.forEach((material) => material.dispose())
} else {
object.material.dispose()
}
}
}
})
renderer.dispose()
container.removeChild(renderer.domElement)
}
return { cleanup, animate }
}, [])
return sceneRenderer
},
[platinum, secret, startDate, user]
)
const { containerRef } = useThreeJS(setup)
return (
<>
<div
className={cn(
'w-screen absolute inset-0 h-[600px] lg:min-h-full lg:max-h-[1000px] flex justify-center items-center overflow-hidden',
className
)}
>
<div
className={cn(
'w-screen absolute inset-0 h-[600px] lg:min-h-full lg:max-h-[1000px] flex justify-center items-center overflow-hidden pointer-events-none',
className
)}
>
<div ref={containerRef} className="w-full lg:h-full" />
</div>
<button
className="absolute top-4 right-4 bg-white text-black p-2 rounded-md shadow-md z-20 hover:bg-gray-200 cursor-pointer"
onClick={() => secretRef.current = !secretRef.current}
>
Toggle secret
</button>
</>
ref={containerRef}
className="w-full lg:h-full"
onClick={(e) => {
sceneRef.current?.click(e.nativeEvent)
}}
/>
</div>
)
}
export { LwCanvas }
export default TicketCanvas
@@ -1,80 +0,0 @@
import { cn } from 'ui'
import { useThreeJS } from './helpers'
import { useCallback, useRef } from 'react'
import SceneRenderer, { BaseScene } from './utils/SceneRenderer'
import TicketScene from './scenes/TicketScene'
interface TicketCanvasProps {
secret?: boolean
platinum?: boolean
user: {
id?: string
name?: string
ticketNumber?: number
}
startDate: Date
playmodeRTChannel: unknown
className?: string
onUpgrade?: () => void
}
const TicketCanvas = ({
secret,
platinum,
user,
startDate,
playmodeRTChannel,
className,
onUpgrade,
}: TicketCanvasProps) => {
const sceneRef = useRef<TicketScene | null>(null)
const setup = useCallback(
(container: HTMLElement) => {
let scene: BaseScene | null = null
const sceneRenderer = new SceneRenderer(container)
sceneRef.current = new TicketScene({
defaultSecret: secret,
defaultPlatinum: platinum,
user,
startDate,
onSeatChartButtonClicked: () => {
sceneRef.current?.showBackSide()
sceneRef.current?.upgradeToSecret()
},
onGoBackButtonClicked: () => {
sceneRef.current?.showFrontSide()
},
})
sceneRenderer.activateScene(sceneRef.current)
sceneRenderer.init()
return sceneRenderer
},
[platinum, secret, startDate, user]
)
const { containerRef } = useThreeJS(setup)
return (
<div
className={cn(
'w-screen absolute inset-0 h-[600px] lg:min-h-full lg:max-h-[1000px] flex justify-center items-center overflow-hidden',
className
)}
>
<div
ref={containerRef}
className="w-full lg:h-full"
onClick={(e) => {
sceneRef.current?.click(e.nativeEvent)
}}
/>
</div>
)
}
export default TicketCanvas
Whitespace-only changes.
@@ -1,49 +0,0 @@
import { ShaderMaterial } from "three/src/materials/ShaderMaterial";
import { Color } from "three/src/math/Color";
// vertexShader.js
const vertexShader = `
varying vec2 vUv;
void main() {
// Pass UV coords to the fragment shader
vUv = uv;
// Standard transform
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
// fragmentShader.js
const fragmentShader = `
// A sampler2D to receive your texture
uniform sampler2D myTexture;
// Base color, passed in as a uniform
uniform vec3 baseColor;
// UV coords from the vertex shader
varying vec2 vUv;
void main() {
// Fetch the texture color
vec4 texColor = texture2D(myTexture, vUv);
// Simple alpha-based blend:
// The texture's alpha decides how strongly we blend it over the base color
vec3 blendedColor = mix(baseColor, texColor.rgb, texColor.a);
// Output final color
gl_FragColor = vec4(blendedColor, 1.0);
}
`;
export const coloredTextureMaterial = {
uniforms: {
myTexture: { value: null },
// For example, a green base color
baseColor: { value: new Color(0xffffff) },
},
vertexShader,
fragmentShader,
};
@@ -1,16 +1,5 @@
import { useRef, useEffect } from 'react'
import * as THREE from 'three'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js'
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { GlitchPass } from './effects/glitch'
import { CRTShader } from './effects/crt-shader'
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js'
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { coloredTextureMaterial } from './effects/colored-texture-shader'
/**
* Helper to simplifies controlling requestAnimationFrame
@@ -42,169 +31,6 @@ export const createThreeAnimation = (callback: (time?: number) => void) => {
return { start, stop }
}
/**
* Helper function to create a Three.js setup with scene, camera, renderer, and post-processing
* @param container DOM element to render into
* @param options Configuration options
* @returns Object with scene, camera, renderer, composer, and resize function
*/
export const createThreeSetup = (
container: HTMLElement,
options: {
cameraPosition?: THREE.Vector3
postprocessing?: {
bloom?: {
enabled: boolean
strength?: number
radius?: number
threshold?: number
}
glitch?: {
enabled: boolean
dtSize?: number
colS?: number
}
crt?: {
enabled: boolean
}
}
debug?: boolean
} = {}
) => {
// Create scene
const scene = new THREE.Scene()
if (options.debug) {
const axesHelper = new THREE.AxesHelper(5)
scene.add(axesHelper)
}
// Create camera
const camera = new THREE.PerspectiveCamera(
75,
container.clientWidth / container.clientHeight,
0.1,
1000
)
camera.position.copy(options.cameraPosition || new THREE.Vector3(0, 0, 5))
// Create renderer
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
renderer.setSize(container.clientWidth, container.clientHeight)
renderer.setPixelRatio(window.devicePixelRatio)
container.appendChild(renderer.domElement)
// Enable debug info if debug mode is enabled
if (options.debug) {
renderer.info.autoReset = false
console.log('Three.js Debug Mode Enabled')
}
// Create composer for post-processing
const composer = new EffectComposer(renderer)
const renderPass = new RenderPass(scene, camera)
composer.addPass(renderPass)
// Add bloom pass if enabled
let bloomPass: UnrealBloomPass | null = null
if (options.postprocessing?.bloom?.enabled) {
bloomPass = new UnrealBloomPass(
new THREE.Vector2(container.clientWidth, container.clientHeight),
options.postprocessing.bloom.strength || 1.5,
options.postprocessing.bloom.radius || 0.4,
options.postprocessing.bloom.threshold || 0.85
)
composer.addPass(bloomPass)
}
// Add glitch pass if enabled
let glitchPass: GlitchPass | null = null
if (options.postprocessing?.glitch?.enabled) {
glitchPass = new GlitchPass(options.postprocessing.glitch.dtSize || 64)
glitchPass.uniforms.col_s.value = options.postprocessing.glitch.colS || 0
composer.addPass(glitchPass)
}
// Add CRT pass if enabled
let crtPass: ShaderPass | null = null
if (options.postprocessing?.crt?.enabled) {
crtPass = new ShaderPass(CRTShader)
crtPass.uniforms.resolution.value.set(container.clientWidth, container.clientHeight)
composer.addPass(crtPass)
}
// Resize function
const resize = () => {
camera.aspect = container.clientWidth / container.clientHeight
camera.updateProjectionMatrix()
renderer.setSize(container.clientWidth, container.clientHeight)
composer.setSize(container.clientWidth, container.clientHeight)
// Update resolution uniform for CRT shader
if (crtPass) {
crtPass.uniforms.resolution.value.set(container.clientWidth, container.clientHeight)
}
}
// Add stats display if debug mode is enabled
let stats: { instance: Stats | null } = { instance: null }
let orbitControls: { instance: OrbitControls | null } = { instance: null }
if (options.debug) {
import('three/examples/jsm/libs/stats.module.js').then(({ default: Stats }) => {
const statsInstance = new Stats()
statsInstance.showPanel(0) // 0: fps, 1: ms, 2: mb, 3+: custom
statsInstance.dom.style.position = 'absolute'
statsInstance.dom.style.top = '0px'
statsInstance.dom.style.left = '0px'
container.appendChild(statsInstance.dom)
stats.instance = statsInstance
})
import('three/examples/jsm/controls/OrbitControls.js').then(({ OrbitControls }) => {
// Create OrbitControls after the setup
const controls = new OrbitControls(camera, renderer.domElement)
// Configure controls as needed
controls.enableDamping = true // Add smooth damping
controls.dampingFactor = 0.05
controls.enableZoom = true
controls.enablePan = true
orbitControls.instance = controls
})
}
return {
scene,
camera,
renderer,
composer,
resize,
bloomPass,
glitchPass,
crtPass,
stats,
orbitControls,
debug: options.debug || false,
}
}
/**
* Helper function to load a texture
* @param url URL of the texture to load
* @returns Promise that resolves with the loaded texture
*/
export const loadTexture = (url: string): Promise<THREE.Texture> => {
return new Promise((resolve, reject) => {
const textureLoader = new THREE.TextureLoader()
textureLoader.load(
url,
(texture) => resolve(texture),
undefined,
(error) => reject(error)
)
})
}
/**
* Helper function to load a GLTF model
* @param url URL of the GLTF model to load
@@ -224,189 +50,6 @@ export const loadGLTFModel = (url: string) => {
})
}
/**
* Helper function to create a flight ticket mesh
* @param source URL of the GLTF model or texture to use for the ticket
* @param options Configuration options for the ticket
* @returns Promise that resolves with the created model
*/
export const createTicketMesh = async (
source: string,
options: {
ticketTextureSource?: string
width?: number
height?: number
enhanceEmissive?: boolean
debug?: boolean
materialOptions?: {
transparent?: boolean
emissiveColor?: THREE.Color | number
emissiveIntensity?: number
color?: THREE.Color | number
}
} = {}
): Promise<THREE.Object3D> => {
try {
const useTextureMode = !(source.endsWith('.glb') || source.endsWith('.gltf'))
if (!useTextureMode) {
// Load the GLTF model
const model = (await loadGLTFModel(source)).scene
const ticketTexture = options.ticketTextureSource
? await loadTexture(options.ticketTextureSource)
: null
if (ticketTexture) {
const ticket = model.getObjectByName('ticket')
if (ticket instanceof THREE.Mesh) {
// Store the original material properties
const newMaterial = new THREE.ShaderMaterial(coloredTextureMaterial)
newMaterial.uniforms.myTexture.value = ticketTexture
ticket.material = newMaterial
}
}
const objectsNames = ['planet', 'name', 'time', 'date', 'spicies', 'number'] as const
const namedObjects = getNamedTextObjects(objectsNames, model)
const textures = {} as {
[key in (typeof objectsNames)[number]]?: ReturnType<typeof createTextureForObject>
}
for (let i = 0; i < namedObjects.length; i++) {
const namedObject = namedObjects[i]
const objectName = objectsNames[i]
if (!namedObject) {
console.error(`Named object ${objectsNames} not found`)
continue
}
if (namedObject instanceof THREE.Mesh) {
// Spline exports some objects with additional invalid materials. Clean it up here
namedObject.material = new THREE.MeshBasicMaterial({})
}
let text = ''
switch (objectName) {
case 'planet':
text = 'Earth'
break
case 'name':
text = 'John Doe'
break
case 'time':
text = '00:12:00'
break
case 'date':
text = '03/24/2025'
break
case 'spicies':
text = 'Modern human'
break
case 'number':
text = 'A001'
break
default:
objectName satisfies never
}
const meshWithTexture = createTextureForObject(namedObject, text)
textures[objectName] = meshWithTexture
}
// Calculate scale to fit the model within the specified width and height
const box = new THREE.Box3().setFromObject(model)
const size = box.getSize(new THREE.Vector3())
// Center the model
box.setFromObject(model)
const center = box.getCenter(new THREE.Vector3())
model.position.sub(center)
// Add debug helpers if debug mode is enabled
if (options.debug) {
// Add bounding box helper
const boxHelper = new THREE.BoxHelper(model, 0xff0000)
model.add(boxHelper)
// Add wireframe helper for each mesh
model.traverse((child) => {
if (child instanceof THREE.Mesh) {
const wireframe = new THREE.WireframeGeometry(child.geometry)
const line = new THREE.LineSegments(wireframe)
line.material = new THREE.LineBasicMaterial({ color: 0x00ffff })
// Instead of adding to the model, add directly to the child mesh
// This prevents double transformation
child.add(line)
// Reset position, rotation, and scale to match the parent mesh exactly
line.position.set(0, 0, 0)
line.rotation.set(0, 0, 0)
line.scale.set(1, 1, 1)
if (!child.name) {
console.log('Debug: Mesh has no name', child)
}
console.log('Debug: Added wireframe to mesh', child.name || 'unnamed')
}
})
console.log('Debug: Model dimensions', size)
console.log('Debug: Center position', center)
}
// Only enhance emissive properties if explicitly requested
// This respects the original GLTF materials by default
if (options.enhanceEmissive) {
model.traverse((child) => {
if (child instanceof THREE.Mesh && child.material) {
if (Array.isArray(child.material)) {
child.material.forEach((mat) => {
if (mat instanceof THREE.MeshStandardMaterial) {
mat.emissive = options.materialOptions?.emissiveColor
? new THREE.Color(options.materialOptions.emissiveColor)
: new THREE.Color(0xffffff)
mat.emissiveIntensity = options.materialOptions?.emissiveIntensity ?? 0.2
}
})
} else if (child.material instanceof THREE.MeshStandardMaterial) {
child.material.emissive = options.materialOptions?.emissiveColor
? new THREE.Color(options.materialOptions.emissiveColor)
: new THREE.Color(0xffffff)
child.material.emissiveIntensity = options.materialOptions?.emissiveIntensity ?? 0.2
}
}
})
}
return model
}
throw new Error(`Failed to load model from: ${source}`)
} catch (error) {
console.error('Error loading model/texture:', error)
// Fallback to a simple plane with a default material if loading fails
const geometry = new THREE.PlaneGeometry(options.width || 4, options.height || 2)
const material = new THREE.MeshStandardMaterial({
color: 0x00ff00,
transparent: true,
side: THREE.DoubleSide,
emissive: new THREE.Color(0xffffff),
emissiveIntensity: 0.2,
})
return new THREE.Mesh(geometry, material)
}
}
/**
* Custom hook for Three.js setup and animation
* @param setupCallback Callback function for setting up the scene
@@ -440,94 +83,6 @@ export const useThreeJS = (
return { containerRef }
}
function getNamedTextObjects<TNamedObjects extends readonly string[]>(
names: TNamedObjects,
model: THREE.Group<THREE.Object3DEventMap>
): { [K in keyof TNamedObjects]: THREE.Object3D | null } {
let namedObjects = Array(names.length).fill(null) as (THREE.Object3D | null)[]
for (let i = 0; i < names.length; i++) {
const name = names[i]
const obj = model.getObjectByName(name)
if (obj) {
namedObjects[i] = obj
}
}
return namedObjects as { [K in keyof TNamedObjects]: THREE.Object3D | null }
}
function createTextureForObject(object: THREE.Object3D, text: string) {
// Update matrix in case object has moved
object.updateWorldMatrix(true, true)
const localBox = new THREE.Box3().setFromObject(object)
const localSize = localBox.getSize(new THREE.Vector3())
// Get world scale
const worldScale = new THREE.Vector3()
object.matrixWorld.decompose(new THREE.Vector3(), new THREE.Quaternion(), worldScale)
// Apply world scale to get world size. Spline by default exports models with 0.01 scale
const worldSize = new THREE.Vector3(
localSize.x / Math.abs(worldScale.x),
localSize.y / Math.abs(worldScale.y),
localSize.z / Math.abs(worldScale.z)
)
const canvas = document.createElement('canvas')
const canvasWidth = worldSize.x // Base canvas width
const canvasHeight = Math.floor(canvasWidth * (localSize.y / localSize.x)) // Maintain aspect ratio
canvas.width = canvasWidth
canvas.height = canvasHeight
// Get canvas context and set up text rendering
const context = canvas.getContext('2d')
if (!context) {
throw new Error(`Could not get 2D context for text "${text}"`)
}
// Clear canvas
context.clearRect(0, 0, canvas.width, canvas.height)
// Set text properties
context.fillStyle = 'black'
// Calculate font size based on canvas dimensions
// Adjust the divisor to control text size relative to canvas
const fontSize = Math.floor(canvasHeight)
context.font = `bold ${fontSize}px Arial`
// Center text
context.textAlign = 'left'
context.textBaseline = 'middle'
context.fillText(text, 0, canvas.height / 2)
// Create texture from canvas
const texture = new THREE.CanvasTexture(canvas)
texture.needsUpdate = true
// Create a plane geometry sized to match the named object
const textPlane = new THREE.PlaneGeometry(worldSize.x, worldSize.y)
const textMaterial = new THREE.MeshBasicMaterial({
map: texture,
transparent: true,
side: THREE.DoubleSide,
depthWrite: false, // Prevents z-fighting
})
const textMesh = new THREE.Mesh(textPlane, textMaterial)
object.add(textMesh)
// Position the text slightly in front of the object
// Adjust the z-offset as needed to prevent z-fighting
// Offset position to align top-left corner instead of center
textMesh.position.set(worldSize.x / 2, -worldSize.y / 2, 0.01)
return textMesh
}
export function colorObjToRgb(color: { rgb: number; alpha: number }) {
return `rgb(${(color.rgb >> 16) & 255} ${(color.rgb >> 8) & 255} ${(color.rgb >> 0) & 255} / ${color.alpha})`
@@ -182,7 +182,7 @@ class TicketScene implements BaseScene {
loadedTextureType: null as 'basic' | 'secret' | 'platinum' | null,
effectsIntensity: 0,
mouseIntensityDecay: 0.98, // How quickly the intensity decays
mouseIntensityGainRate: 0.005,
mouseIntensityGainRate: 0.003,
}
private _sceneConfig = {
@@ -343,7 +343,7 @@ class TicketScene implements BaseScene {
// Update glitch pass intensity
if (this._glitchPass) {
const glitchIntensity = this._internalState.mousePosition?.mouseIntensity ?? 1
this._glitchPass.setIntensity(glitchIntensity * this._internalState.effectsIntensity * 6)
this._glitchPass.setIntensity(glitchIntensity * this._internalState.effectsIntensity * 4)
}
// Update CRT shader intensity
+1 -2
View File
@@ -2,8 +2,7 @@ import { NextSeo } from 'next-seo'
import { useRouter } from 'next/router'
import { useState } from 'react'
import { Session } from '@supabase/supabase-js'
import { LwCanvas } from '~/components/LaunchWeek/14/TicketCanvas'
import TicketCanvas from '~/components/LaunchWeek/14/TicketCanvas2'
import TicketCanvas from '~/components/LaunchWeek/14/TicketCanvas'
import DefaultLayout from '~/components/Layouts/Default'
import SectionContainer from '~/components/Layouts/SectionContainer'
import { TicketState, ConfDataContext, UserData } from '~/components/LaunchWeek/hooks/use-conf-data'