Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions app/stickers/Background.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"use client";

import * as THREE from "three";
import { memo } from "react";
import { Environment } from "@react-three/drei";

export const Background = memo(
({
stickerMap,
scene,
}: {
stickerMap: THREE.Texture;
scene: THREE.Scene;
}) => {
return (
<>
<Environment
frames={1}
files={"/env/empty_warehouse_01_1k.hdr"}
environmentIntensity={0.8}
scene={scene}
/>
<mesh scale={100}>
<sphereGeometry args={[3, 32, 32]} />
<meshBasicMaterial
color={new THREE.Color(0x666666)}
map={stickerMap}
side={THREE.BackSide}
/>
</mesh>
</>
);
}
);
Background.displayName = "Background";
4 changes: 2 additions & 2 deletions app/stickers/CanvasState.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as THREE from "three";
import { CLICKED_WOBBLE_STRENGTH } from "./StickerBall";
import { STICKER_TEXTURES_LENGTH } from "./useStickers";
import { STICKER_TEXTURES_LENGTH } from "./StickerBall/useStickers";

export class CanvasState {
private static instance: CanvasState;
Expand All @@ -18,7 +18,7 @@ export class CanvasState {
};

public CAMERA_Z = {
zoom: 3.5,
zoom: 3.2,
default: 4,
};

Expand Down
139 changes: 94 additions & 45 deletions app/stickers/Playground.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,81 @@
"use client";

import * as THREE from "three";
import { memo } from "react";
import { Environment, OrbitControls } from "@react-three/drei";
import { useFrame } from "@react-three/fiber";
import { useStickers } from "./useStickers";
import { useMemo, useRef } from "react";
import { OrbitControls } from "@react-three/drei";
import { useFrame, createPortal, extend, useThree } from "@react-three/fiber";
import { useStickers } from "./StickerBall/useStickers";
import { CanvasState } from "./CanvasState";
import { StickerBall } from "./StickerBall";
import { Easing, Utils } from "@/packages/use-shader-fx/src";
import {
Easing,
Utils,
useResizeBoundary,
useSingleFBO,
} from "@/packages/use-shader-fx/src";
import { Background } from "./Background";
import { FxMaterial, FxMaterialProps } from "./romanticism/FxMaterial";
import { useRomanticism } from "./romanticism/useRomanticism";

const Background = memo(({ stickerMap }: { stickerMap: THREE.Texture }) => {
return (
<>
<Environment preset="warehouse" environmentIntensity={0.5} />
<mesh scale={100}>
<sphereGeometry args={[3, 32, 32]} />
<meshBasicMaterial
color={new THREE.Color(0x444444)}
map={stickerMap}
side={THREE.BackSide}
/>
</mesh>
</>
);
});
Background.displayName = "Background";
extend({ FxMaterial });

export const Playground = () => {
const { stickerMap, normalMap, isReady, silhouette } = useStickers();
const { camera, size, viewport, gl } = useThree();

const canvasState = CanvasState.getInstance();

useFrame(({ camera, clock }, delta) => {
// 1000以上リサイズした場合のみリサイズする
const resizeBoundary = useResizeBoundary({
gl,
size,
boundFor: "larger",
threshold: 1000,
});

// stickers
const { stickerMap, normalMap, isReady, silhouette } =
useStickers(resizeBoundary);

// offscreen to stickers
const offscreenScene = useMemo(() => new THREE.Scene(), []);
const [portalStickers, updatePortalStickers] = useSingleFBO({
scene: offscreenScene,
camera,
size,
dpr: viewport.dpr,
depthBuffer: true,
isSizeUpdate: resizeBoundary.isUpdate,
});

// romanticism
const romanticism = useRomanticism(portalStickers.texture);
const materialRef = useRef<FxMaterialProps>(null);

useFrame(({ camera, clock, pointer, gl }) => {
if (!isReady) {
return;
}

// control camera state
if (canvasState.cameraState.point.z < canvasState.CAMERA_Z.default) {
canvasState.cameraState.point.z += delta;
// update portalized stickers
updatePortalStickers(gl);

// update romanticism
if (materialRef.current) {
materialRef.current.u_time = clock.getElapsedTime();
}
camera.position.lerp(canvasState.cameraState.point, 0.16);

// control camera state
const _pointer = pointer.clone().multiplyScalar(0.32);
canvasState.cameraState.point.lerp(
{
..._pointer, // uncomment this line to enable camera movement
// x: 0,
// y: 0,
z: canvasState.CAMERA_Z.default,
},
0.12
);
camera.position.lerp(canvasState.cameraState.point, 0.14);
camera.lookAt(0, 0, 0);

// control clock state
Expand All @@ -61,23 +96,37 @@ export const Playground = () => {
}
});
return (
<mesh visible={isReady}>
<StickerBall
stickerMap={stickerMap}
normalMap={normalMap}
silhouetteMap={silhouette}
/>
<Background stickerMap={stickerMap} />
<OrbitControls
enabled={true}
enableZoom={false}
enablePan={false}
rotateSpeed={0.12}
minAzimuthAngle={-0.785} // -45
maxAzimuthAngle={0.785} // 45
minPolarAngle={1.134} // 65
maxPolarAngle={1.919} // 110
/>
</mesh>
<>
{createPortal(
<mesh visible={isReady}>
<StickerBall
stickerMap={stickerMap}
normalMap={normalMap}
silhouetteMap={silhouette}
/>
<Background stickerMap={stickerMap} scene={offscreenScene} />
<OrbitControls
enabled={true}
enableZoom={false}
enablePan={false}
rotateSpeed={0.12}
minAzimuthAngle={-0.785} // -45
maxAzimuthAngle={0.785} // 45
minPolarAngle={1.134} // 65
maxPolarAngle={1.919} // 110
/>
</mesh>,
offscreenScene
)}
<mesh visible={isReady}>
<planeGeometry args={[2, 2]} />
<fxMaterial
ref={materialRef}
u_romance={romanticism}
u_original={portalStickers.texture}
key={FxMaterial.key}
/>
</mesh>
</>
);
};
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import * as THREE from "three";
import {
useMemo,
useCallback,
useReducer,
useRef,
useState,
useEffect,
} from "react";
import { useFrame, useThree, useLoader } from "@react-three/fiber";
import { useBlank, useBrush } from "@/packages/use-shader-fx/src";
import { CanvasState } from "./CanvasState";
import { useMemo, useCallback, useRef, useState } from "react";
import { useFrame, useThree, useLoader, Size } from "@react-three/fiber";
import { ResizeBoundary, Utils, useBlank } from "@/packages/use-shader-fx/src";
import { CanvasState } from "../CanvasState";

const STICKER_TEXCOORD = `
vec2 stamp = uStampPoint;
Expand Down Expand Up @@ -61,7 +54,7 @@ const STICKER_TEXTURES = [

export const STICKER_TEXTURES_LENGTH = STICKER_TEXTURES.length;

export const useStickers = () => {
export const useStickers = (resizeBoundary: ResizeBoundary) => {
const canvasState = CanvasState.getInstance();

const textures = useLoader(THREE.TextureLoader, [
Expand All @@ -82,7 +75,8 @@ export const useStickers = () => {

const [updateSticker, _, { output: stickerMap }] = useBlank({
size,
dpr: 6,
dpr: Math.min(resizeBoundary.maxDpr, 6),
isSizeUpdate: resizeBoundary.isUpdate,
onBeforeInit: useCallback(
(parameters: any) => {
Object.assign(parameters.uniforms, {
Expand Down Expand Up @@ -120,6 +114,11 @@ export const useStickers = () => {
usf_FragColor = finalColor;
`
);

parameters.fragmentShader = parameters.fragmentShader.replace(
"precision highp float;",
"precision lowp float;"
);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
Expand All @@ -128,7 +127,8 @@ export const useStickers = () => {

const [updateNormal, __, { output: normalMap }] = useBlank({
size,
dpr: 4,
dpr: Math.min(resizeBoundary.maxDpr, 4),
isSizeUpdate: resizeBoundary.isUpdate,
onBeforeInit: useCallback(
(shader: any) => {
Object.assign(shader.uniforms, {
Expand Down Expand Up @@ -201,6 +201,11 @@ export const useStickers = () => {
usf_FragColor = finalColor;
`
);

shader.fragmentShader = shader.fragmentShader.replace(
"precision highp float;",
"precision lowp float;"
);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
Expand All @@ -209,6 +214,13 @@ export const useStickers = () => {

const tickCount = useRef(-2);

if (
resizeBoundary.isUpdate &&
tickCount.current === canvasState.stickerState.count
) {
tickCount.current -= 2;
}

const [isReady, setIsReady] = useState(false);

useFrame((state) => {
Expand All @@ -218,7 +230,7 @@ export const useStickers = () => {

tickCount.current++;

if (tickCount.current === 0) {
if (tickCount.current === 0 && !isReady) {
setIsReady(true);
}

Expand Down
2 changes: 1 addition & 1 deletion app/stickers/UI/Cursor/GifPreloader.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { memo } from "react";
import Image from "next/image";
import { STICKER_TEXTURES_LENGTH } from "../../useStickers";
import { STICKER_TEXTURES_LENGTH } from "../../StickerBall/useStickers";

const GIF_IMAGES = [...Array(STICKER_TEXTURES_LENGTH)].map(
(_, i) => `/stickers/gif/gif${i}.gif`
Expand Down
28 changes: 7 additions & 21 deletions app/stickers/UI/Cursor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import { useCallback, useEffect, useRef, useState } from "react";
import gsap from "gsap";
import { GifPreloader } from "./GifPreloader";
import { Confetti } from "./Confetti";
import { useIsTouchDevice } from "@funtech-inc/spice";
import { useIsTouchDevice, useFrame } from "@funtech-inc/spice";
import s from "./index.module.scss";

const updateTargetPoint = (target: HTMLDivElement, point: THREE.Vector2) => {
const targetRect = target.getBoundingClientRect();
target.style.left = `${point.x - targetRect.width / 2}px`;
target.style.top = `${point.y - targetRect.height / 2}px`;
const width = target.clientWidth;
const height = target.clientHeight;
target.style.left = `${point.x - width / 2}px`;
target.style.top = `${point.y - height / 2}px`;
};

const CURSOR_LERP = 0.8;
Expand Down Expand Up @@ -58,7 +59,6 @@ export const CursorUI = () => {
canvasState.stickerState.nextStickerIndex
);

const rafID = useRef<number>(0);
const cursorRef = useRef<HTMLDivElement>(null);
const imageRef = useRef<HTMLImageElement>(null);
const confettiRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -170,7 +170,7 @@ export const CursorUI = () => {
[canvasState]
);

const handleFrame = useCallback(() => {
useFrame(() => {
const isOver = canvasState.cursorState.isOver;

// update sticker index
Expand All @@ -194,21 +194,7 @@ export const CursorUI = () => {
updateCursorPoint(false);
}
prevIsOver.current = isOver;
rafID.current = requestAnimationFrame(handleFrame);
}, [
canvasState.stickerState,
stickerIndex,
canvasState.cursorState,
onStickerChange,
onOver,
onOut,
updateCursorPoint,
]);

useEffect(() => {
rafID.current = requestAnimationFrame(handleFrame);
return () => cancelAnimationFrame(rafID.current);
}, [handleFrame]);
});

return (
<div className={s.container}>
Expand Down
Loading