kasaframe 0.0.0-stage → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NOTICE +148 -0
- package/README.md +92 -2
- package/app/package.json +12 -0
- package/app/server/engine.ts +533 -0
- package/app/server/main.ts +209 -0
- package/app/web/env.d.ts +2 -0
- package/app/web/icons.ts +31 -0
- package/app/web/index.html +21 -0
- package/app/web/main.ts +1195 -0
- package/app/web/style.css +463 -0
- package/bin/hooks.mjs +64 -0
- package/bin/kf.mjs +32 -0
- package/docs/AGENTS.md +286 -0
- package/music/README.md +37 -0
- package/package.json +44 -4
- package/packages/adapters/package.json +36 -0
- package/packages/adapters/src/css.ts +11 -0
- package/packages/adapters/src/gsap.ts +26 -0
- package/packages/adapters/src/index.ts +9 -0
- package/packages/adapters/src/lottie.ts +22 -0
- package/packages/adapters/src/media.ts +59 -0
- package/packages/adapters/src/page.ts +229 -0
- package/packages/adapters/src/project.ts +214 -0
- package/packages/adapters/src/react.ts +36 -0
- package/packages/adapters/src/svelte.ts +22 -0
- package/packages/adapters/src/three.ts +18 -0
- package/packages/adapters/src/vue.ts +27 -0
- package/packages/check/package.json +19 -0
- package/packages/check/src/check.ts +280 -0
- package/packages/check/src/cli.ts +70 -0
- package/packages/check/src/image.ts +146 -0
- package/packages/check/src/index.ts +9 -0
- package/packages/check/src/ocr.swift +37 -0
- package/packages/check/src/probe.ts +328 -0
- package/packages/check/src/report.ts +44 -0
- package/packages/check/src/rules/assets.ts +45 -0
- package/packages/check/src/rules/beats.ts +212 -0
- package/packages/check/src/rules/camera.ts +217 -0
- package/packages/check/src/rules/captions.ts +49 -0
- package/packages/check/src/rules/flash.ts +235 -0
- package/packages/check/src/rules/frames.ts +136 -0
- package/packages/check/src/rules/layout.ts +281 -0
- package/packages/check/src/rules/page.ts +87 -0
- package/packages/check/src/rules/runs.ts +20 -0
- package/packages/check/src/rules/text.ts +326 -0
- package/packages/check/src/scan.ts +240 -0
- package/packages/check/src/serve.ts +67 -0
- package/packages/check/src/types.ts +80 -0
- package/packages/check/src/video.ts +137 -0
- package/packages/cli/package.json +30 -0
- package/packages/cli/src/blur.ts +80 -0
- package/packages/cli/src/browser.ts +203 -0
- package/packages/cli/src/command.ts +44 -0
- package/packages/cli/src/commands/app.ts +25 -0
- package/packages/cli/src/commands/beats.ts +36 -0
- package/packages/cli/src/commands/bench.ts +123 -0
- package/packages/cli/src/commands/blender.ts +293 -0
- package/packages/cli/src/commands/check.ts +25 -0
- package/packages/cli/src/commands/direct.ts +210 -0
- package/packages/cli/src/commands/doctor.ts +30 -0
- package/packages/cli/src/commands/gen.ts +173 -0
- package/packages/cli/src/commands/look.ts +46 -0
- package/packages/cli/src/commands/lyrics.ts +157 -0
- package/packages/cli/src/commands/make.ts +1121 -0
- package/packages/cli/src/commands/music.ts +78 -0
- package/packages/cli/src/commands/narrate.ts +238 -0
- package/packages/cli/src/commands/new.ts +185 -0
- package/packages/cli/src/commands/preview.ts +43 -0
- package/packages/cli/src/commands/render.ts +250 -0
- package/packages/cli/src/commands/series.ts +110 -0
- package/packages/cli/src/commands/setup.ts +102 -0
- package/packages/cli/src/commands/sheet.ts +114 -0
- package/packages/cli/src/commands/skill.ts +43 -0
- package/packages/cli/src/commands/still.ts +40 -0
- package/packages/cli/src/commands/theme.ts +172 -0
- package/packages/cli/src/commands/transitions.ts +40 -0
- package/packages/cli/src/deps.ts +122 -0
- package/packages/cli/src/ffmpeg.ts +81 -0
- package/packages/cli/src/index.ts +8 -0
- package/packages/cli/src/kf.ts +64 -0
- package/packages/cli/src/lock.ts +72 -0
- package/packages/cli/src/mix.ts +131 -0
- package/packages/cli/src/out.ts +13 -0
- package/packages/cli/src/pool.ts +34 -0
- package/packages/cli/src/segments.ts +402 -0
- package/packages/cli/src/serve.ts +124 -0
- package/packages/clock/package.json +14 -0
- package/packages/clock/src/index.ts +25 -0
- package/packages/clock/src/install.ts +351 -0
- package/packages/clock/src/types.ts +45 -0
- package/packages/core/package.json +9 -0
- package/packages/core/src/beats.ts +283 -0
- package/packages/core/src/ease.ts +87 -0
- package/packages/core/src/frame.ts +99 -0
- package/packages/core/src/index.ts +27 -0
- package/packages/core/src/project.ts +157 -0
- package/packages/core/src/rand.ts +56 -0
- package/packages/core/src/spring.ts +52 -0
- package/packages/core/src/style.ts +100 -0
- package/packages/core/src/time.ts +88 -0
- package/packages/director/package.json +23 -0
- package/packages/director/src/assemble.ts +361 -0
- package/packages/director/src/brief.ts +122 -0
- package/packages/director/src/catalog.ts +49 -0
- package/packages/director/src/content.ts +343 -0
- package/packages/director/src/fallback.ts +58 -0
- package/packages/director/src/index.ts +13 -0
- package/packages/director/src/pattern.ts +90 -0
- package/packages/director/src/patterns/fill/cells.ts +115 -0
- package/packages/director/src/patterns/formula/stack.ts +93 -0
- package/packages/director/src/patterns/util.ts +58 -0
- package/packages/director/src/scene.ts +62 -0
- package/packages/director/src/select.ts +163 -0
- package/packages/director/src/transitions.ts +115 -0
- package/packages/gen/PROVIDERS.md +90 -0
- package/packages/gen/package.json +9 -0
- package/packages/gen/src/ask.ts +132 -0
- package/packages/gen/src/audio.ts +91 -0
- package/packages/gen/src/blender.ts +54 -0
- package/packages/gen/src/capabilities.ts +151 -0
- package/packages/gen/src/generate.ts +206 -0
- package/packages/gen/src/image/codex.ts +117 -0
- package/packages/gen/src/image/og.ts +133 -0
- package/packages/gen/src/index.ts +26 -0
- package/packages/gen/src/keys.ts +86 -0
- package/packages/gen/src/media.ts +150 -0
- package/packages/gen/src/music/elevenlabs.ts +41 -0
- package/packages/gen/src/music/index.ts +120 -0
- package/packages/gen/src/music/library.ts +73 -0
- package/packages/gen/src/music/lyria.ts +52 -0
- package/packages/gen/src/music/types.ts +28 -0
- package/packages/gen/src/plan.ts +106 -0
- package/packages/gen/src/registry.ts +54 -0
- package/packages/gen/src/seam.ts +103 -0
- package/packages/gen/src/tts/dashscope.ts +64 -0
- package/packages/gen/src/tts/elevenlabs.ts +84 -0
- package/packages/gen/src/tts/gemini.ts +73 -0
- package/packages/gen/src/tts/index.ts +241 -0
- package/packages/gen/src/tts/local.ts +42 -0
- package/packages/gen/src/tts/types.ts +44 -0
- package/packages/gen/src/types.ts +90 -0
- package/packages/gen/src/video/fal.ts +140 -0
- package/packages/gen/src/video/h3.ts +112 -0
- package/packages/gen/src/video/http.ts +44 -0
- package/packages/gen/src/video/ltx.ts +60 -0
- package/packages/gen/src/video/minimax.ts +55 -0
- package/packages/gen/src/video/veo.ts +71 -0
- package/packages/jizura/package.json +16 -0
- package/packages/jizura/src/deny.ts +73 -0
- package/packages/jizura/src/index.ts +7 -0
- package/packages/jizura/src/install.ts +269 -0
- package/packages/jizura/src/layer.ts +325 -0
- package/packages/jizura/src/lyrics.ts +81 -0
- package/packages/jizura/src/pin.ts +8 -0
- package/packages/jizura/src/vendor.ts +140 -0
- package/packages/jizura/src/vite.ts +63 -0
- package/packages/jizura/vendor/JIZURA_LICENSE.txt +21 -0
- package/packages/jizura/vendor/NOTICE +43 -0
- package/packages/jizura/vendor/engine.json +47 -0
- package/packages/jizura/vendor/fonts/BlackHanSans-Regular.woff2 +0 -0
- package/packages/jizura/vendor/fonts/DotGothic16-Regular.woff2 +0 -0
- package/packages/jizura/vendor/fonts/GowunBatang-Bold.woff2 +0 -0
- package/packages/jizura/vendor/fonts/IBMPlexMono-Medium.ttf +0 -0
- package/packages/jizura/vendor/fonts/IBMPlexMono-SemiBold.ttf +0 -0
- package/packages/jizura/vendor/fonts/NotoSansKR-VF.woff2 +0 -0
- package/packages/jizura/vendor/fonts/NotoSerifKR-VF.woff2 +0 -0
- package/packages/jizura/vendor/fonts/OFL-blackhansans.txt +93 -0
- package/packages/jizura/vendor/fonts/OFL-dotgothic16.txt +93 -0
- package/packages/jizura/vendor/fonts/OFL-gowunbatang.txt +94 -0
- package/packages/jizura/vendor/fonts/OFL-ibmplexmono.txt +93 -0
- package/packages/jizura/vendor/fonts/OFL-notosanskr.txt +93 -0
- package/packages/jizura/vendor/fonts/OFL-notoserifkr.txt +94 -0
- package/packages/jizura/vendor/fonts/fonts.json +52 -0
- package/packages/jizura/vendor/jizura_engine.js +35024 -0
- package/packages/motion/README.md +153 -0
- package/packages/motion/package.json +30 -0
- package/packages/motion/src/camera.ts +97 -0
- package/packages/motion/src/captions.ts +487 -0
- package/packages/motion/src/enter.ts +128 -0
- package/packages/motion/src/hangul.ts +129 -0
- package/packages/motion/src/impact.ts +100 -0
- package/packages/motion/src/index.ts +17 -0
- package/packages/motion/src/moves/brushWipe.ts +103 -0
- package/packages/motion/src/moves/cloneArray.ts +111 -0
- package/packages/motion/src/moves/curtains.ts +81 -0
- package/packages/motion/src/moves/dress.ts +191 -0
- package/packages/motion/src/moves/echoStack.ts +70 -0
- package/packages/motion/src/moves/fit.ts +70 -0
- package/packages/motion/src/moves/flood.ts +61 -0
- package/packages/motion/src/moves/ghostStack.ts +88 -0
- package/packages/motion/src/moves/gridFloor.ts +100 -0
- package/packages/motion/src/moves/imageSwarm.ts +113 -0
- package/packages/motion/src/moves/index.ts +38 -0
- package/packages/motion/src/moves/irisBlades.ts +113 -0
- package/packages/motion/src/moves/karaokeLine.ts +108 -0
- package/packages/motion/src/moves/ledBoard.ts +94 -0
- package/packages/motion/src/moves/liquidTab.ts +72 -0
- package/packages/motion/src/moves/markerWrite.ts +63 -0
- package/packages/motion/src/moves/menu.ts +138 -0
- package/packages/motion/src/moves/morphBox.ts +96 -0
- package/packages/motion/src/moves/neonSign.ts +78 -0
- package/packages/motion/src/moves/panelPop.ts +78 -0
- package/packages/motion/src/moves/paperTear.ts +75 -0
- package/packages/motion/src/moves/path.ts +64 -0
- package/packages/motion/src/moves/pathText.ts +107 -0
- package/packages/motion/src/moves/pointSphere.ts +109 -0
- package/packages/motion/src/moves/ransomLetters.ts +73 -0
- package/packages/motion/src/moves/shapeMorph.ts +143 -0
- package/packages/motion/src/moves/slamWords.ts +96 -0
- package/packages/motion/src/moves/sparkTrail.ts +130 -0
- package/packages/motion/src/moves/speedLines.ts +91 -0
- package/packages/motion/src/moves/splitFlap.ts +149 -0
- package/packages/motion/src/moves/stepWords.ts +128 -0
- package/packages/motion/src/moves/strikeOut.ts +98 -0
- package/packages/motion/src/moves/sunburst.ts +58 -0
- package/packages/motion/src/moves/tickerRows.ts +91 -0
- package/packages/motion/src/moves/tokenPicker.ts +115 -0
- package/packages/motion/src/moves/truchet.ts +85 -0
- package/packages/motion/src/moves/wordWall.ts +78 -0
- package/packages/motion/src/moves/worldRoll.ts +32 -0
- package/packages/motion/src/particles.ts +102 -0
- package/packages/motion/src/react/index.tsx +21 -0
- package/packages/motion/src/react/parts.tsx +431 -0
- package/packages/motion/src/react/time.tsx +97 -0
- package/packages/motion/src/text.ts +103 -0
- package/packages/motion/src/transition.ts +82 -0
- package/packages/motion/src/transition3d/cards.ts +65 -0
- package/packages/motion/src/transition3d/chain.ts +84 -0
- package/packages/motion/src/transition3d/dive.ts +181 -0
- package/packages/motion/src/transition3d/fold.ts +110 -0
- package/packages/motion/src/transition3d/gl.ts +219 -0
- package/packages/motion/src/transition3d/index.ts +235 -0
- package/packages/motion/src/transition3d/live.ts +70 -0
- package/packages/motion/src/transition3d/portal.ts +115 -0
- package/packages/motion/src/transition3d/react.tsx +29 -0
- package/packages/motion/src/transition3d/rig.ts +600 -0
- package/packages/motion/src/transition3d/shatter.ts +104 -0
- package/packages/motion/src/transition3d/space.ts +173 -0
- package/packages/motion/src/transition3d/whip.ts +78 -0
- package/packages/motion/src/transition3d/world.ts +737 -0
- package/packages/motion/src/transition3d/zoomOut.ts +114 -0
- package/packages/motion/src/transitions2d/catalog.ts +127 -0
- package/packages/motion/src/transitions2d/css.d.ts +2 -0
- package/packages/motion/src/transitions2d/cult-ui/LICENSE.md +9 -0
- package/packages/motion/src/transitions2d/cult-ui/RollingNumber.tsx +43 -0
- package/packages/motion/src/transitions2d/gl-transitions/LICENSE +25 -0
- package/packages/motion/src/transitions2d/gl-transitions/shaders.ts +1931 -0
- package/packages/motion/src/transitions2d/index.ts +2 -0
- package/packages/motion/src/transitions2d/kokonutui/LICENSE +21 -0
- package/packages/motion/src/transitions2d/kokonutui/MatrixText.css +16 -0
- package/packages/motion/src/transitions2d/kokonutui/MatrixText.tsx +94 -0
- package/packages/motion/src/transitions2d/magicui/HyperText.css +15 -0
- package/packages/motion/src/transitions2d/magicui/HyperText.tsx +115 -0
- package/packages/motion/src/transitions2d/magicui/LICENSE.md +21 -0
- package/packages/motion/src/transitions2d/magicui/MorphingText.css +25 -0
- package/packages/motion/src/transitions2d/magicui/MorphingText.tsx +130 -0
- package/packages/motion/src/transitions2d/magicui/WordRotate.tsx +42 -0
- package/packages/motion/src/transitions2d/react.ts +13 -0
- package/packages/motion/src/transitions2d/skiper-ui/ClipDiv.css +39 -0
- package/packages/motion/src/transitions2d/skiper-ui/ClipDiv.tsx +32 -0
- package/packages/motion/src/transitions2d/smoothui/LICENSE +21 -0
- package/packages/motion/src/transitions2d/smoothui/SharedAxisY.tsx +50 -0
- package/packages/motion/src/transitions2d/smoothui/TextMorph.tsx +90 -0
- package/packages/motion/src/transitions2d/smoothui/shaders.ts +223 -0
- package/packages/motion/src/transitions2d/unlumen/LICENSE +21 -0
- package/packages/motion/src/transitions2d/unlumen/ScrambleText.tsx +114 -0
- package/packages/motion/src/transitions2d/vengeance-ui/LICENSE +21 -0
- package/packages/motion/src/transitions2d/vengeance-ui/MorphText.css +69 -0
- package/packages/motion/src/transitions2d/vengeance-ui/MorphText.tsx +45 -0
- package/packages/motion/src/type.ts +131 -0
- package/packages/motion/src/ui.ts +172 -0
- package/packages/patterns/package.json +15 -0
- package/packages/patterns/src/bars/rank.ts +119 -0
- package/packages/patterns/src/before-after/shrink.ts +127 -0
- package/packages/patterns/src/before-after/strike.ts +125 -0
- package/packages/patterns/src/code/terminal.ts +133 -0
- package/packages/patterns/src/compare/bars.ts +142 -0
- package/packages/patterns/src/compare/duel.ts +103 -0
- package/packages/patterns/src/end/lockup.ts +85 -0
- package/packages/patterns/src/end/slam.ts +91 -0
- package/packages/patterns/src/flow/graph.ts +250 -0
- package/packages/patterns/src/hook-number/counter.ts +101 -0
- package/packages/patterns/src/hook-number/roll.ts +179 -0
- package/packages/patterns/src/hook-number/slam.ts +91 -0
- package/packages/patterns/src/index.ts +38 -0
- package/packages/patterns/src/kit.ts +449 -0
- package/packages/patterns/src/limit/gauge.ts +117 -0
- package/packages/patterns/src/list/cards.ts +131 -0
- package/packages/patterns/src/list/cycle.ts +22 -0
- package/packages/patterns/src/media/panels.ts +100 -0
- package/packages/patterns/src/objects.ts +234 -0
- package/packages/patterns/src/quote/karaoke.ts +126 -0
- package/packages/patterns/src/quote/slam.ts +22 -0
- package/packages/patterns/src/screen/focus.ts +85 -0
- package/packages/patterns/src/steps/path.ts +137 -0
- package/packages/patterns/src/steps/stack.ts +111 -0
- package/packages/patterns/src/term-reveal/chips.ts +92 -0
- package/packages/patterns/src/term-reveal/expand.ts +127 -0
- package/packages/patterns/src/timeline/axis.ts +144 -0
- package/packages/patterns/src/title/mask.ts +85 -0
- package/packages/patterns/src/title/slam.ts +23 -0
- package/packages/patterns/src/toggle-sim/switch.ts +92 -0
- package/packages/patterns/src/trend/columns.ts +108 -0
- package/packages/patterns/src/trend/draw.ts +140 -0
- package/packages/patterns/src/types.ts +67 -0
- package/packages/patterns/src/words/moves.ts +42 -0
- package/packages/patterns/src/words/slam.ts +4 -0
- package/packages/plan/package.json +15 -0
- package/packages/plan/src/asks.ts +256 -0
- package/packages/plan/src/brief.ts +383 -0
- package/packages/plan/src/captions.ts +78 -0
- package/packages/plan/src/detect.ts +93 -0
- package/packages/plan/src/index.ts +27 -0
- package/packages/plan/src/layout.ts +176 -0
- package/packages/plan/src/llm.ts +172 -0
- package/packages/plan/src/look.ts +222 -0
- package/packages/plan/src/plan.ts +179 -0
- package/packages/plan/src/request.ts +55 -0
- package/packages/plan/src/series.ts +76 -0
- package/packages/plan/src/studio.ts +53 -0
- package/packages/plan/src/templates.ts +42 -0
- package/packages/plan/src/themes.ts +48 -0
- package/packages/plan/src/types.ts +157 -0
- package/packages/studio/package.json +12 -0
- package/packages/studio/src/index.ts +1 -0
- package/packages/studio/src/plugin.ts +236 -0
- package/packages/studio/src/ui/main.ts +503 -0
- package/packages/studio/src/ui/studio.css +165 -0
- package/packages/studio/src/ui/timeline.ts +317 -0
- package/packages/themes/kit/THEME-MAKE.md +68 -0
- package/packages/themes/kit/THEME.md +30 -0
- package/packages/themes/kit/briefs/og-intro-15s/brief.json +16 -0
- package/packages/themes/kit/briefs/og-intro-15s/facts.ts +29 -0
- package/packages/themes/kit/briefs/og-intro-15s/logo.svg +12 -0
- package/packages/themes/kit/effects.ts +5 -0
- package/packages/themes/kit/sample/index.html +11 -0
- package/packages/themes/kit/sample/main.ts +9 -0
- package/packages/themes/kit/sample/scenes/scene.ts +15 -0
- package/packages/themes/package.json +19 -0
- package/packages/themes/src/factory.ts +296 -0
- package/packages/themes/src/fx.ts +14 -0
- package/packages/themes/src/index.ts +114 -0
- package/packages/themes/src/slotkit.ts +79 -0
- package/packages/themes/src/slots.ts +46 -0
- package/packages/themes/src/stage3d.ts +197 -0
- package/packages/themes/src/types.ts +58 -0
- package/presets/kawaii-pastel.json +94 -0
- package/presets/neutral-dark.json +82 -0
- package/presets/og.json +98 -0
- package/series/og-shorts.json +14 -0
- package/skills/kasaframe/SKILL.md +33 -0
- package/templates/_shared/project/index.html +11 -0
- package/templates/_shared/project/lib/react.ts +22 -0
- package/templates/_shared/project/lib/stage.ts +292 -0
- package/templates/_shared/project/main.ts +9 -0
- package/templates/_shared/scenes/default.ts +21 -0
- package/templates/_shared/scenes/drop.ts +33 -0
- package/templates/_shared/scenes/end.ts +26 -0
- package/templates/_shared/scenes/hook.ts +18 -0
- package/templates/_shared/scenes/seam.ts +41 -0
- package/templates/_shared/scenes/step.ts +49 -0
- package/templates/ad/preview.png +0 -0
- package/templates/ad/template.json +151 -0
- package/templates/demo/preview.png +0 -0
- package/templates/demo/template.json +141 -0
- package/templates/explainer/preview.png +0 -0
- package/templates/explainer/template.json +193 -0
- package/templates/launch/preview.png +0 -0
- package/templates/launch/template.json +163 -0
- package/templates/mv/preview.png +0 -0
- package/templates/mv/scenes/default.ts +98 -0
- package/templates/mv/template.json +178 -0
- package/templates/promo/preview.png +0 -0
- package/templates/promo/template.json +185 -0
- package/templates/shorts/preview.png +0 -0
- package/templates/shorts/scenes/hook.ts +37 -0
- package/templates/shorts/template.json +196 -0
- package/templates/square/preview.png +0 -0
- package/templates/square/template.json +133 -0
- package/themes/debug-hud/THEME.md +39 -0
- package/themes/debug-hud/effects.ts +389 -0
- package/themes/debug-hud/sample/assets/og/logo.svg +12 -0
- package/themes/debug-hud/sample/beats.json +1 -0
- package/themes/debug-hud/sample/index.html +11 -0
- package/themes/debug-hud/sample/kf.json +59 -0
- package/themes/debug-hud/sample/lib/hudkit.ts +49 -0
- package/themes/debug-hud/sample/lib/og.ts +29 -0
- package/themes/debug-hud/sample/main.ts +9 -0
- package/themes/debug-hud/sample/scenes/end.ts +62 -0
- package/themes/debug-hud/sample/scenes/gather.ts +67 -0
- package/themes/debug-hud/sample/scenes/models.ts +70 -0
- package/themes/debug-hud/sample/scenes/price.ts +79 -0
- package/themes/debug-hud/sample/style.json +91 -0
- package/themes/debug-hud/slots.ts +99 -0
- package/themes/debug-hud/still.png +0 -0
- package/themes/debug-hud/style.json +91 -0
- package/themes/debug-hud/theme.json +68 -0
- package/themes/og-measure/THEME.md +47 -0
- package/themes/og-measure/effects.ts +432 -0
- package/themes/og-measure/sample/assets/brand/og-logo-full.svg +12 -0
- package/themes/og-measure/sample/assets/brand/og-logo.svg +12 -0
- package/themes/og-measure/sample/assets/data/measure.json +353 -0
- package/themes/og-measure/sample/beats.json +1 -0
- package/themes/og-measure/sample/index.html +11 -0
- package/themes/og-measure/sample/kf.json +66 -0
- package/themes/og-measure/sample/lib/data.ts +62 -0
- package/themes/og-measure/sample/main.ts +9 -0
- package/themes/og-measure/sample/measure.ts +78 -0
- package/themes/og-measure/sample/scenes/end.ts +47 -0
- package/themes/og-measure/sample/scenes/gather.ts +51 -0
- package/themes/og-measure/sample/scenes/models.ts +34 -0
- package/themes/og-measure/sample/scenes/one.ts +39 -0
- package/themes/og-measure/sample/scenes/price.ts +56 -0
- package/themes/og-measure/sample/style.json +97 -0
- package/themes/og-measure/slots.ts +88 -0
- package/themes/og-measure/still.png +0 -0
- package/themes/og-measure/style.json +97 -0
- package/themes/og-measure/theme.json +79 -0
- package/themes/riso-zine/THEME.md +43 -0
- package/themes/riso-zine/effects.ts +321 -0
- package/themes/riso-zine/sample/assets/og/logo.svg +12 -0
- package/themes/riso-zine/sample/beats.json +1 -0
- package/themes/riso-zine/sample/index.html +11 -0
- package/themes/riso-zine/sample/kf.json +59 -0
- package/themes/riso-zine/sample/lib/og.ts +29 -0
- package/themes/riso-zine/sample/lib/shot.ts +86 -0
- package/themes/riso-zine/sample/lib/zine.ts +42 -0
- package/themes/riso-zine/sample/main.ts +9 -0
- package/themes/riso-zine/sample/scenes/end.ts +61 -0
- package/themes/riso-zine/sample/scenes/gather.ts +62 -0
- package/themes/riso-zine/sample/scenes/models.ts +52 -0
- package/themes/riso-zine/sample/scenes/price.ts +82 -0
- package/themes/riso-zine/sample/style.json +99 -0
- package/themes/riso-zine/slots.ts +81 -0
- package/themes/riso-zine/still.png +0 -0
- package/themes/riso-zine/style.json +99 -0
- package/themes/riso-zine/theme.json +75 -0
- package/tools/beats/beats.py +372 -0
- package/tools/beats/requirements.txt +3 -0
- package/tools/lyrics/align.py +164 -0
- package/tools/lyrics/requirements.txt +9 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { clamp, emit, progress, toEase, type Ease, type EaseName, type Style, safeTime } from "@kasaframe/core";
|
|
2
|
+
|
|
3
|
+
export type TransitionName = "whip" | "zoomThrough" | "slide" | "matchCut" | "crossfade";
|
|
4
|
+
|
|
5
|
+
export const TRANSITIONS: readonly TransitionName[] = ["whip", "zoomThrough", "slide", "matchCut", "crossfade"];
|
|
6
|
+
|
|
7
|
+
export interface TransitionOpts {
|
|
8
|
+
/** 전환이 시작하는 시각. matchCut 은 이 순간 끊긴다 */
|
|
9
|
+
at: number;
|
|
10
|
+
dur?: number;
|
|
11
|
+
ease?: EaseName | Ease;
|
|
12
|
+
/** 화면 폭(px) — whip·slide 가 얼마나 밀지 */
|
|
13
|
+
width?: number;
|
|
14
|
+
dir?: "left" | "right" | "up" | "down";
|
|
15
|
+
source?: string;
|
|
16
|
+
scene?: string;
|
|
17
|
+
emit?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface TransitionState {
|
|
21
|
+
/** 나가는 장면 */
|
|
22
|
+
out: Style;
|
|
23
|
+
/** 들어오는 장면 */
|
|
24
|
+
in: Style;
|
|
25
|
+
p: number;
|
|
26
|
+
/** 두 장면을 다 그려야 하는가 */
|
|
27
|
+
both: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const DUR: Record<TransitionName, number> = { whip: 0.32, zoomThrough: 0.5, slide: 0.45, matchCut: 0.25, crossfade: 0.4 };
|
|
31
|
+
|
|
32
|
+
/** 0.5 를 지나며 0→1 로 넘어가는 짧은 계단 — 휙 지나가는 순간에 갈아 끼운다 */
|
|
33
|
+
const swap = (p: number, w = 0.12): number => clamp((p - 0.5 + w / 2) / w);
|
|
34
|
+
|
|
35
|
+
export function transition(name: TransitionName, t: number, opts: TransitionOpts): TransitionState {
|
|
36
|
+
t = safeTime(t);
|
|
37
|
+
if (!(TRANSITIONS as readonly string[]).includes(name)) throw new Error(`모르는 전환: ${name} (있는 것: ${TRANSITIONS.join(" · ")})`);
|
|
38
|
+
const dur = opts.dur ?? DUR[name];
|
|
39
|
+
const raw = progress(t, opts.at, dur);
|
|
40
|
+
const p = toEase(opts.ease, name === "slide" ? toEase("inOutCubic") : toEase("inOutQuart"))(raw);
|
|
41
|
+
const w = opts.width ?? 1920;
|
|
42
|
+
const dir = opts.dir ?? "left";
|
|
43
|
+
const sx = dir === "left" ? -1 : dir === "right" ? 1 : 0;
|
|
44
|
+
const sy = dir === "up" ? -1 : dir === "down" ? 1 : 0;
|
|
45
|
+
if (opts.emit !== false && raw > 0 && raw < 1) emit({ kind: "move", source: opts.source ?? name, scene: opts.scene, name: `transition:${name}`, value: 1 });
|
|
46
|
+
const both = raw > 0 && raw < 1;
|
|
47
|
+
switch (name) {
|
|
48
|
+
case "whip": {
|
|
49
|
+
const blur = Math.sin(Math.PI * p) * 28;
|
|
50
|
+
const k = swap(p);
|
|
51
|
+
return {
|
|
52
|
+
p,
|
|
53
|
+
both,
|
|
54
|
+
out: { x: sx * w * 0.5 * p, y: sy * w * 0.5 * p, blur, opacity: 1 - k },
|
|
55
|
+
in: { x: -sx * w * 0.5 * (1 - p), y: -sy * w * 0.5 * (1 - p), blur, opacity: k },
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
case "zoomThrough": {
|
|
59
|
+
const k = swap(p, 0.3);
|
|
60
|
+
return {
|
|
61
|
+
p,
|
|
62
|
+
both,
|
|
63
|
+
out: { scale: 1 + 2.2 * p, blur: 12 * p, opacity: 1 - k },
|
|
64
|
+
in: { scale: 0.7 + 0.3 * p, blur: 10 * (1 - p), opacity: k },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
case "slide":
|
|
68
|
+
return {
|
|
69
|
+
p,
|
|
70
|
+
both,
|
|
71
|
+
out: { x: sx * w * p, y: sy * w * p },
|
|
72
|
+
in: { x: -sx * w * (1 - p), y: -sy * w * (1 - p) },
|
|
73
|
+
};
|
|
74
|
+
case "matchCut": {
|
|
75
|
+
// 같은 자리·같은 크기로 이어 붙이고, 들어온 쪽만 살짝 가라앉혀 끊김을 감춘다
|
|
76
|
+
const cut = t >= opts.at;
|
|
77
|
+
return { p: raw, both: false, out: { opacity: cut ? 0 : 1 }, in: { opacity: cut ? 1 : 0, scale: cut ? 1.03 - 0.03 * p : 1.03 } };
|
|
78
|
+
}
|
|
79
|
+
case "crossfade":
|
|
80
|
+
return { p, both, out: { opacity: 1 - p }, in: { opacity: p } };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import { cardSlots, cardsRig, hump, rr } from "./rig.ts";
|
|
3
|
+
import { aim, disposeTree, dust, frameRing, gridPlane, imageMaterial, raw, sky, type Kit } from "./world.ts";
|
|
4
|
+
|
|
5
|
+
export function cards(k: Kit) {
|
|
6
|
+
const { aspect, pal, seed } = k;
|
|
7
|
+
const env = { aspect, seed };
|
|
8
|
+
const scene = new THREE.Scene();
|
|
9
|
+
scene.add(sky({ top: pal.deep, bottom: "#03050b", glow: pal.accent, dir: [0.3, 0.1, -1], amt: 0.45, pow: 5 }));
|
|
10
|
+
scene.add(gridPlane({ color: pal.accent, y: -2.2, width: 30, depth: 30, z: -2, focus: -2 }));
|
|
11
|
+
|
|
12
|
+
const slots = cardSlots(env);
|
|
13
|
+
const decor: { mesh: THREE.Group; base: THREE.Vector3; yaw: number; i: number }[] = [];
|
|
14
|
+
for (const [i, s] of slots.entries()) {
|
|
15
|
+
const g = new THREE.Group();
|
|
16
|
+
const w = aspect * s.scale;
|
|
17
|
+
const h = s.scale;
|
|
18
|
+
let mat;
|
|
19
|
+
if (s.img < 2) mat = imageMaterial(s.img === 0 ? k.from.texture : k.to.texture);
|
|
20
|
+
else {
|
|
21
|
+
// 다른 장면들 — 앞·뒤 그림을 흐리고 눌러 꾸밈으로만
|
|
22
|
+
mat = imageMaterial((s.img === 2 ? k.from : k.to).soft(), { double: true, back: pal.deep });
|
|
23
|
+
mat.uniforms.bias.value = 4;
|
|
24
|
+
mat.uniforms.dim.value = 0.3;
|
|
25
|
+
mat.uniforms.tint.value = raw(pal.deep);
|
|
26
|
+
mat.uniforms.tintAmt.value = 0.35;
|
|
27
|
+
}
|
|
28
|
+
g.add(new THREE.Mesh(new THREE.PlaneGeometry(w, h), mat));
|
|
29
|
+
const ring = new THREE.Mesh(
|
|
30
|
+
frameRing(w, h, 0.008 * Math.max(0.6, s.scale)),
|
|
31
|
+
new THREE.MeshBasicMaterial({ color: raw(s.img < 2 ? pal.glow : pal.accent), transparent: true, opacity: s.img < 2 ? 0.9 : 0.55, blending: THREE.AdditiveBlending, depthWrite: false, side: THREE.DoubleSide }),
|
|
32
|
+
);
|
|
33
|
+
g.add(ring);
|
|
34
|
+
g.position.set(...s.pos);
|
|
35
|
+
g.rotation.y = s.yaw;
|
|
36
|
+
scene.add(g);
|
|
37
|
+
if (s.img >= 2) decor.push({ mesh: g, base: g.position.clone(), yaw: s.yaw, i });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const n = 600;
|
|
41
|
+
const pts = new Float32Array(n * 3);
|
|
42
|
+
for (let i = 0; i < n; i++) {
|
|
43
|
+
pts[i * 3] = rr(seed, 600 + i, -9, 9);
|
|
44
|
+
pts[i * 3 + 1] = rr(seed, 1600 + i, -3, 4);
|
|
45
|
+
pts[i * 3 + 2] = rr(seed, 2600 + i, -12, -4);
|
|
46
|
+
}
|
|
47
|
+
scene.add(dust(pts, pal.glow, 0.02));
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
frame(u: number, t: number) {
|
|
51
|
+
aim(k.cam, cardsRig(u, env).cam);
|
|
52
|
+
// 꾸밈 판만 떠다닌다 — 앞·뒤 판은 경계 프레임을 지키려 제자리
|
|
53
|
+
const k2 = 0.4 + 0.6 * hump(u);
|
|
54
|
+
for (const d of decor) {
|
|
55
|
+
d.mesh.position.set(d.base.x, d.base.y + 0.07 * k2 * Math.sin(t * 1.1 + d.i * 1.7), d.base.z);
|
|
56
|
+
d.mesh.rotation.y = d.yaw + 0.06 * k2 * Math.sin(t * 0.8 + d.i);
|
|
57
|
+
}
|
|
58
|
+
k.renderer.clear(true, true, true);
|
|
59
|
+
k.renderer.render(scene, k.cam);
|
|
60
|
+
},
|
|
61
|
+
dispose() {
|
|
62
|
+
disposeTree(scene);
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import { CHAIN_GAP, FIT, chainRig, rr, type ChainOpts } from "./rig.ts";
|
|
3
|
+
import { aim, disposeTree, dust, frameRing, gridPlane, raw, screenMaterial, sky, type Kit } from "./world.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 화면 속 화면 — 화면들이 깊이 방향으로 늘어서 있고, 카메라가 화면마다 꽉 찬 순간(arrivals)을 지나며 가운데가 녹은 화면을 뚫고 다음 화면으로.
|
|
7
|
+
* 도착 순간은 그 화면 그림 그대로라 박마다 한 겹씩 맞춰도 경계가 이어진다.
|
|
8
|
+
*/
|
|
9
|
+
export function chain(k: Kit, o: ChainOpts) {
|
|
10
|
+
const { aspect, pal, seed } = k;
|
|
11
|
+
const env = { aspect, seed };
|
|
12
|
+
const gap = o.gap ?? CHAIN_GAP;
|
|
13
|
+
const n = k.screens.length;
|
|
14
|
+
const scene = new THREE.Scene();
|
|
15
|
+
const skyMesh = sky({ top: "#0a1024", bottom: "#020308", glow: pal.accent, dir: [0, 0, -1], amt: 0.7, pow: 14 });
|
|
16
|
+
scene.add(skyMesh);
|
|
17
|
+
const len = (n - 1) * gap + 4;
|
|
18
|
+
const grids = [-1.25, 1.25].map((y) => gridPlane({ color: pal.accent, y, width: 12, depth: len, z: -len / 2 + 2, focus: 0 }));
|
|
19
|
+
scene.add(...grids);
|
|
20
|
+
|
|
21
|
+
const mats = k.screens.map((s, i) => {
|
|
22
|
+
const m = screenMaterial(s.texture, pal.glow, aspect, rr(seed, 300 + i, 0, 100));
|
|
23
|
+
const plane = new THREE.Mesh(new THREE.PlaneGeometry(aspect, 1), m);
|
|
24
|
+
plane.position.z = -i * gap;
|
|
25
|
+
scene.add(plane);
|
|
26
|
+
// 화면 테두리 — 떠 있는 모니터. 꽉 찬 순간에는 시야 밖이다
|
|
27
|
+
const bezel = new THREE.Mesh(frameRing(aspect, 1, 0.05), new THREE.MeshBasicMaterial({ color: raw("#0d1018") }));
|
|
28
|
+
bezel.position.z = -i * gap - 0.001;
|
|
29
|
+
scene.add(bezel);
|
|
30
|
+
const halo = new THREE.Mesh(frameRing(aspect + 0.1, 1.1, 0.012), new THREE.MeshBasicMaterial({ color: raw(pal.glow), transparent: true, opacity: 0.85, blending: THREE.AdditiveBlending, depthWrite: false }));
|
|
31
|
+
halo.position.z = -i * gap;
|
|
32
|
+
scene.add(halo);
|
|
33
|
+
return m;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// 화면 사이 빛 고리·점 — 도착 카메라와 그 화면 사이(꽉 찬 순간의 시야)는 비운다
|
|
37
|
+
for (let g = 0; g < n - 1; g++) {
|
|
38
|
+
const near = -g * gap - 0.4;
|
|
39
|
+
const far = -(g + 1) * gap + FIT + 0.4;
|
|
40
|
+
for (let j = 0; j < 4; j++) {
|
|
41
|
+
const i = g * 4 + j;
|
|
42
|
+
const s = rr(seed, 100 + i, 1.15, 2.2);
|
|
43
|
+
const ring = new THREE.Mesh(
|
|
44
|
+
frameRing(aspect * s, s, rr(seed, 120 + i, 0.005, 0.013)),
|
|
45
|
+
new THREE.MeshBasicMaterial({ color: raw(j % 2 ? pal.accent : pal.glow), transparent: true, opacity: 0.7, blending: THREE.AdditiveBlending, depthWrite: false }),
|
|
46
|
+
);
|
|
47
|
+
ring.position.set(rr(seed, 140 + i, -0.15, 0.15), rr(seed, 160 + i, -0.1, 0.1), near + ((far - near) * (j + 0.5)) / 4);
|
|
48
|
+
ring.rotation.z = rr(seed, 180 + i, -0.4, 0.4);
|
|
49
|
+
scene.add(ring);
|
|
50
|
+
}
|
|
51
|
+
const m = 260;
|
|
52
|
+
const pts = new Float32Array(m * 3);
|
|
53
|
+
for (let i = 0; i < m; i++) {
|
|
54
|
+
let x = rr(seed, 400 + g * m + i, -4, 4);
|
|
55
|
+
const y = rr(seed, 2400 + g * m + i, -2.4, 2.4);
|
|
56
|
+
if (Math.abs(x) < 0.35 && Math.abs(y) < 0.3) x += x < 0 ? -0.4 : 0.4;
|
|
57
|
+
pts.set([x, y, rr(seed, 4400 + g * m + i, far, near)], i * 3);
|
|
58
|
+
}
|
|
59
|
+
scene.add(dust(pts, pal.glow, 0.014));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
frame(_u: number, t: number) {
|
|
64
|
+
const s = chainRig(t, env, o);
|
|
65
|
+
aim(k.cam, s.cam);
|
|
66
|
+
skyMesh.position.set(0, 0, s.cam.pos[2]);
|
|
67
|
+
for (const g of grids) (g.material as THREE.ShaderMaterial).uniforms.focus.value = s.cam.pos[2] - 4;
|
|
68
|
+
mats.forEach((m, i) => {
|
|
69
|
+
m.uniforms.dissolve.value = s.dissolve[i];
|
|
70
|
+
m.visible = s.dissolve[i] < 1;
|
|
71
|
+
});
|
|
72
|
+
k.renderer.clear(true, true, true);
|
|
73
|
+
k.renderer.render(scene, k.cam);
|
|
74
|
+
},
|
|
75
|
+
/** 이번 프레임에 보일 수 있는 화면 — 지나온 화면은 스냅숏하지 않는다 */
|
|
76
|
+
visible(t: number): boolean[] {
|
|
77
|
+
const s = chainRig(t, env, o);
|
|
78
|
+
return k.screens.map((_, i) => i >= s.index && i <= s.index + 2 && s.dissolve[i] < 1);
|
|
79
|
+
},
|
|
80
|
+
dispose() {
|
|
81
|
+
disposeTree(scene);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import { RoundedBoxGeometry } from "three/addons/geometries/RoundedBoxGeometry.js";
|
|
3
|
+
import { DIVE_DEPTH, FIT, diveRig, rr } from "./rig.ts";
|
|
4
|
+
import { aim, disposeTree, dust, frameRing, gridPlane, imageMaterial, raw, screenMaterial, sky, type Kit, type Palette } from "./world.ts";
|
|
5
|
+
|
|
6
|
+
function keyboardTexture(pal: Palette): THREE.CanvasTexture {
|
|
7
|
+
const c = document.createElement("canvas");
|
|
8
|
+
c.width = 1024;
|
|
9
|
+
c.height = 300;
|
|
10
|
+
const g = c.getContext("2d")!;
|
|
11
|
+
g.fillStyle = "#15171c";
|
|
12
|
+
g.fillRect(0, 0, c.width, c.height);
|
|
13
|
+
const rows = [14, 14, 13, 12, 9];
|
|
14
|
+
rows.forEach((n, r) => {
|
|
15
|
+
const kw = (c.width - 40) / 14.5;
|
|
16
|
+
let x = 20 + (14 - n) * kw * 0.5 * (r === 4 ? 0 : 1);
|
|
17
|
+
for (let i = 0; i < n; i++) {
|
|
18
|
+
const wide = r === 4 && i === 4 ? 5 : 1;
|
|
19
|
+
g.fillStyle = "#262a33";
|
|
20
|
+
g.beginPath();
|
|
21
|
+
g.roundRect(x + 3, 20 + r * 54 + 3, kw * wide - 6, 48, 6);
|
|
22
|
+
g.fill();
|
|
23
|
+
x += kw * wide;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
g.fillStyle = pal.accent;
|
|
27
|
+
g.globalAlpha = 0.35;
|
|
28
|
+
g.fillRect(0, c.height - 4, c.width, 4);
|
|
29
|
+
const t = new THREE.CanvasTexture(c);
|
|
30
|
+
t.colorSpace = THREE.SRGBColorSpace;
|
|
31
|
+
return t;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function dive(k: Kit) {
|
|
35
|
+
const { aspect, pal, seed } = k;
|
|
36
|
+
const env = { aspect, seed };
|
|
37
|
+
|
|
38
|
+
// 바깥 — 어두운 방, 책상 위 모니터
|
|
39
|
+
const outer = new THREE.Scene();
|
|
40
|
+
outer.add(sky({ top: pal.deep, bottom: "#05060a", glow: pal.accent, dir: [0, 0.2, -1], amt: 0.35 }));
|
|
41
|
+
const metal = new THREE.MeshStandardMaterial({ color: "#1b1e25", roughness: 0.42, metalness: 0.55 });
|
|
42
|
+
const bezel = new THREE.Mesh(new RoundedBoxGeometry(aspect + 0.12, 1.12, 0.08, 4, 0.03), metal);
|
|
43
|
+
bezel.position.z = -0.045;
|
|
44
|
+
outer.add(bezel);
|
|
45
|
+
const back = new THREE.Mesh(new RoundedBoxGeometry(aspect * 0.7, 0.62, 0.12, 4, 0.05), metal);
|
|
46
|
+
back.position.set(0, -0.04, -0.14);
|
|
47
|
+
outer.add(back);
|
|
48
|
+
const neck = new THREE.Mesh(new RoundedBoxGeometry(0.16, 0.5, 0.05, 2, 0.02), metal);
|
|
49
|
+
neck.position.set(0, -0.78, -0.16);
|
|
50
|
+
outer.add(neck);
|
|
51
|
+
const deskY = -1.03;
|
|
52
|
+
const foot = new THREE.Mesh(new RoundedBoxGeometry(0.74, 0.03, 0.4, 3, 0.012), metal);
|
|
53
|
+
foot.position.set(0, deskY + 0.015, -0.13);
|
|
54
|
+
outer.add(foot);
|
|
55
|
+
const desk = new THREE.Mesh(new THREE.PlaneGeometry(14, 8), new THREE.MeshStandardMaterial({ color: "#20232b", roughness: 0.7, metalness: 0.1 }));
|
|
56
|
+
desk.rotation.x = -Math.PI / 2;
|
|
57
|
+
desk.position.set(0, deskY, 1.5);
|
|
58
|
+
outer.add(desk);
|
|
59
|
+
const kbTex = keyboardTexture(pal);
|
|
60
|
+
const kb = new THREE.Mesh(
|
|
61
|
+
new RoundedBoxGeometry(1.25, 0.03, 0.38, 2, 0.01),
|
|
62
|
+
[metal, metal, new THREE.MeshStandardMaterial({ map: kbTex, roughness: 0.6 }), metal, metal, metal],
|
|
63
|
+
);
|
|
64
|
+
kb.position.set(-0.1, deskY + 0.015, 0.75);
|
|
65
|
+
kb.rotation.y = 0.04;
|
|
66
|
+
outer.add(kb);
|
|
67
|
+
outer.add(new THREE.HemisphereLight("#8fa4d8", "#08090c", 0.6));
|
|
68
|
+
const key = new THREE.DirectionalLight("#ffffff", 1.4);
|
|
69
|
+
key.position.set(-2, 3, 2.5);
|
|
70
|
+
outer.add(key);
|
|
71
|
+
const rim = new THREE.DirectionalLight(pal.accent, 1.2);
|
|
72
|
+
rim.position.set(2.5, 1.2, -2);
|
|
73
|
+
outer.add(rim);
|
|
74
|
+
const screenGlow = new THREE.PointLight(pal.glow, 2.2, 4, 1.4);
|
|
75
|
+
screenGlow.position.set(0, -0.2, 0.5);
|
|
76
|
+
outer.add(screenGlow);
|
|
77
|
+
|
|
78
|
+
// 화면 자리에 스텐실 1 — 안쪽 공간은 바깥에 있는 동안 여기로만 보인다
|
|
79
|
+
const mask = new THREE.Scene();
|
|
80
|
+
const maskMesh = new THREE.Mesh(
|
|
81
|
+
new THREE.PlaneGeometry(aspect, 1),
|
|
82
|
+
new THREE.MeshBasicMaterial({
|
|
83
|
+
colorWrite: false,
|
|
84
|
+
depthWrite: false,
|
|
85
|
+
depthTest: false,
|
|
86
|
+
stencilWrite: true,
|
|
87
|
+
stencilRef: 1,
|
|
88
|
+
stencilFunc: THREE.AlwaysStencilFunc,
|
|
89
|
+
stencilZPass: THREE.ReplaceStencilOp,
|
|
90
|
+
}),
|
|
91
|
+
);
|
|
92
|
+
mask.add(maskMesh);
|
|
93
|
+
|
|
94
|
+
// 안쪽 — 미지의 공간. 화면 모양 고리가 늘어선 통로 끝에 뒤 장면
|
|
95
|
+
const inner = new THREE.Scene();
|
|
96
|
+
inner.add(sky({ top: "#0a1024", bottom: "#020308", glow: pal.accent, dir: [0, 0, -1], amt: 0.7, pow: 14 }));
|
|
97
|
+
const rings: THREE.Mesh[] = [];
|
|
98
|
+
const nRing = 26;
|
|
99
|
+
for (let i = 0; i < nRing; i++) {
|
|
100
|
+
const z = -0.7 - (i / (nRing - 1)) * (DIVE_DEPTH - FIT - 1.6);
|
|
101
|
+
const s = rr(seed, 100 + i, 1.15, 2.2);
|
|
102
|
+
const m = new THREE.Mesh(
|
|
103
|
+
frameRing(aspect * s, s, rr(seed, 120 + i, 0.005, 0.013)),
|
|
104
|
+
new THREE.MeshBasicMaterial({ color: raw(i % 3 === 0 ? pal.glow : pal.accent), transparent: true, opacity: 0.75, blending: THREE.AdditiveBlending, depthWrite: false }),
|
|
105
|
+
);
|
|
106
|
+
m.position.set(rr(seed, 140 + i, -0.15, 0.15), rr(seed, 160 + i, -0.1, 0.1), z);
|
|
107
|
+
m.rotation.z = rr(seed, 180 + i, -0.35, 0.35);
|
|
108
|
+
inner.add(m);
|
|
109
|
+
rings.push(m);
|
|
110
|
+
}
|
|
111
|
+
for (const y of [-1.25, 1.25]) inner.add(gridPlane({ color: pal.accent, y, width: 12, depth: DIVE_DEPTH + 4, z: -DIVE_DEPTH / 2, focus: -4 }));
|
|
112
|
+
const n = 1800;
|
|
113
|
+
const pts = new Float32Array(n * 3);
|
|
114
|
+
for (let i = 0; i < n; i++) {
|
|
115
|
+
let x = rr(seed, 400 + i, -4, 4);
|
|
116
|
+
let y = rr(seed, 1400 + i, -2.4, 2.4);
|
|
117
|
+
// 카메라가 지나는 축 가까이는 비운다 — 코앞을 스치는 점이 큰 얼룩으로 찍힌다
|
|
118
|
+
if (Math.abs(x) < 0.35 && Math.abs(y) < 0.3) x += x < 0 ? -0.4 : 0.4;
|
|
119
|
+
pts[i * 3] = x;
|
|
120
|
+
pts[i * 3 + 1] = y;
|
|
121
|
+
pts[i * 3 + 2] = rr(seed, 2400 + i, -DIVE_DEPTH + FIT + 0.3, -0.2);
|
|
122
|
+
}
|
|
123
|
+
inner.add(dust(pts, pal.glow, 0.014));
|
|
124
|
+
const toMat = imageMaterial(k.to.texture);
|
|
125
|
+
const toPlane = new THREE.Mesh(new THREE.PlaneGeometry(aspect, 1), toMat);
|
|
126
|
+
toPlane.position.z = -DIVE_DEPTH;
|
|
127
|
+
inner.add(toPlane);
|
|
128
|
+
const halo = new THREE.Mesh(frameRing(aspect, 1, 0.03), new THREE.MeshBasicMaterial({ color: raw(pal.glow), transparent: true, blending: THREE.AdditiveBlending, depthWrite: false }));
|
|
129
|
+
halo.position.z = -DIVE_DEPTH;
|
|
130
|
+
inner.add(halo);
|
|
131
|
+
const innerMats: THREE.Material[] = [];
|
|
132
|
+
inner.traverse((o) => {
|
|
133
|
+
const m = (o as THREE.Mesh).material as THREE.Material | undefined;
|
|
134
|
+
if (m) innerMats.push(m);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// 위 — 앞 장면이 걸린 화면
|
|
138
|
+
const overlay = new THREE.Scene();
|
|
139
|
+
const screenMat = screenMaterial(k.from.texture, pal.glow, aspect, rr(seed, 0, 0, 100));
|
|
140
|
+
screenMat.depthTest = false;
|
|
141
|
+
screenMat.depthWrite = false;
|
|
142
|
+
overlay.add(new THREE.Mesh(new THREE.PlaneGeometry(aspect, 1), screenMat));
|
|
143
|
+
|
|
144
|
+
let wasInside: boolean | null = null;
|
|
145
|
+
return {
|
|
146
|
+
frame(u: number, t: number) {
|
|
147
|
+
const s = diveRig(u, env);
|
|
148
|
+
aim(k.cam, s.cam);
|
|
149
|
+
screenMat.uniforms.dissolve.value = s.dissolve;
|
|
150
|
+
screenMat.uniforms.glitch.value = s.glitch;
|
|
151
|
+
screenMat.uniforms.tick.value = Math.floor(t * 24);
|
|
152
|
+
screenMat.uniforms.sheen.value = 0.06 * Math.min(1, u / 0.3) * (1 - s.dissolve) * (u < 1 ? 1 : 0);
|
|
153
|
+
halo.material.opacity = 0.8 * (1 - Math.min(1, Math.max(0, (u - 0.8) / 0.18)));
|
|
154
|
+
if (wasInside !== s.inside) {
|
|
155
|
+
for (const m of innerMats) {
|
|
156
|
+
m.stencilWrite = !s.inside;
|
|
157
|
+
m.stencilRef = 1;
|
|
158
|
+
m.stencilFunc = THREE.EqualStencilFunc;
|
|
159
|
+
m.needsUpdate = true;
|
|
160
|
+
}
|
|
161
|
+
wasInside = s.inside;
|
|
162
|
+
}
|
|
163
|
+
const r = k.renderer;
|
|
164
|
+
r.clear(true, true, true);
|
|
165
|
+
if (s.inside) {
|
|
166
|
+
r.render(inner, k.cam);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
r.render(outer, k.cam);
|
|
170
|
+
r.clearDepth();
|
|
171
|
+
r.render(mask, k.cam);
|
|
172
|
+
r.render(inner, k.cam);
|
|
173
|
+
r.clearDepth();
|
|
174
|
+
r.render(overlay, k.cam);
|
|
175
|
+
},
|
|
176
|
+
dispose() {
|
|
177
|
+
for (const sc of [outer, mask, inner, overlay]) disposeTree(sc);
|
|
178
|
+
kbTex.dispose();
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import { foldRig, type FoldStyle } from "./rig.ts";
|
|
3
|
+
import { aim, disposeTree, imageMaterial, raw, sky, type Kit } from "./world.ts";
|
|
4
|
+
|
|
5
|
+
const STRIPS = 8;
|
|
6
|
+
|
|
7
|
+
// 아래에서 위로 말려 올라가는 종이. 말린 선(rollY) 아래쪽이 원통에 감기고, 감길수록 반지름이 조금씩 커져 겹이 안 겹친다
|
|
8
|
+
const ROLL_VERT = /* glsl */ `
|
|
9
|
+
uniform float rollY;
|
|
10
|
+
uniform float radius;
|
|
11
|
+
varying vec2 vUv;
|
|
12
|
+
varying float vShade;
|
|
13
|
+
void main() {
|
|
14
|
+
vUv = uv;
|
|
15
|
+
vec3 p = position;
|
|
16
|
+
float s = rollY - p.y;
|
|
17
|
+
vShade = 0.0;
|
|
18
|
+
if (s > 0.0) {
|
|
19
|
+
float r = radius + s * 0.012;
|
|
20
|
+
float a = s / r;
|
|
21
|
+
p.y = rollY - r * sin(a);
|
|
22
|
+
p.z = r - r * cos(a);
|
|
23
|
+
vShade = 0.5 - 0.5 * cos(a);
|
|
24
|
+
}
|
|
25
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 1.0);
|
|
26
|
+
}`;
|
|
27
|
+
|
|
28
|
+
const ROLL_FRAG = /* glsl */ `
|
|
29
|
+
uniform sampler2D map;
|
|
30
|
+
uniform float opacity;
|
|
31
|
+
uniform vec3 backColor;
|
|
32
|
+
varying vec2 vUv;
|
|
33
|
+
varying float vShade;
|
|
34
|
+
void main() {
|
|
35
|
+
vec3 c = texture2D(map, vUv).rgb;
|
|
36
|
+
if (!gl_FrontFacing) c = mix(c * 0.3, backColor, 0.6);
|
|
37
|
+
c *= 1.0 - 0.45 * vShade;
|
|
38
|
+
gl_FragColor = vec4(c, opacity);
|
|
39
|
+
}`;
|
|
40
|
+
|
|
41
|
+
export function fold(k: Kit, o: { fold?: FoldStyle }) {
|
|
42
|
+
const { aspect, pal, seed } = k;
|
|
43
|
+
const env = { aspect, seed };
|
|
44
|
+
const style = o.fold ?? "accordion";
|
|
45
|
+
const scene = new THREE.Scene();
|
|
46
|
+
scene.add(sky({ top: pal.deep, bottom: "#020308", glow: pal.accent, dir: [0, 0, -1], amt: 0.4, pow: 4 }));
|
|
47
|
+
// 뒤 장면은 바닥에 깔고 깊이를 안 쓴다 — 같은 면에 놓인 앞 장면 판과 깜빡이며 다투지 않게
|
|
48
|
+
const bMat = imageMaterial(k.to.texture);
|
|
49
|
+
bMat.depthWrite = false;
|
|
50
|
+
const bPlane = new THREE.Mesh(new THREE.PlaneGeometry(aspect, 1), bMat);
|
|
51
|
+
bPlane.renderOrder = -1;
|
|
52
|
+
scene.add(bPlane);
|
|
53
|
+
|
|
54
|
+
let apply: (amount: number, slide: number, alpha: number) => void;
|
|
55
|
+
if (style === "roll") {
|
|
56
|
+
const mat = new THREE.ShaderMaterial({
|
|
57
|
+
vertexShader: ROLL_VERT,
|
|
58
|
+
fragmentShader: ROLL_FRAG,
|
|
59
|
+
uniforms: { map: { value: k.from.texture }, rollY: { value: -1 }, radius: { value: 0.06 }, opacity: { value: 1 }, backColor: { value: raw(pal.glow) } },
|
|
60
|
+
side: THREE.DoubleSide,
|
|
61
|
+
transparent: true,
|
|
62
|
+
});
|
|
63
|
+
const sheet = new THREE.Mesh(new THREE.PlaneGeometry(aspect, 1, 1, 320), mat);
|
|
64
|
+
scene.add(sheet);
|
|
65
|
+
apply = (amount, _slide, alpha) => {
|
|
66
|
+
mat.uniforms.rollY.value = amount;
|
|
67
|
+
mat.uniforms.opacity.value = alpha;
|
|
68
|
+
sheet.visible = alpha > 0;
|
|
69
|
+
};
|
|
70
|
+
} else {
|
|
71
|
+
const sw = aspect / STRIPS;
|
|
72
|
+
const strips = Array.from({ length: STRIPS }, (_, i) => {
|
|
73
|
+
const geo = new THREE.PlaneGeometry(sw, 1);
|
|
74
|
+
geo.translate(sw / 2, 0, 0);
|
|
75
|
+
const uv = geo.attributes.uv as THREE.BufferAttribute;
|
|
76
|
+
for (let j = 0; j < uv.count; j++) uv.setX(j, (i + uv.getX(j)) / STRIPS);
|
|
77
|
+
const mat = imageMaterial(k.from.texture, { double: true, back: pal.accent });
|
|
78
|
+
const m = new THREE.Mesh(geo, mat);
|
|
79
|
+
scene.add(m);
|
|
80
|
+
return { m, mat };
|
|
81
|
+
});
|
|
82
|
+
apply = (theta, slide, alpha) => {
|
|
83
|
+
const c = Math.cos(theta);
|
|
84
|
+
const s = Math.sin(theta);
|
|
85
|
+
strips.forEach(({ m, mat }, i) => {
|
|
86
|
+
// 짝수 띠는 바닥에 붙고 홀수 띠는 카메라 쪽으로 — 병풍처럼 왼쪽으로 접힌다
|
|
87
|
+
const x = -aspect / 2 + i * sw * c - slide;
|
|
88
|
+
const z = i % 2 ? sw * s : 0;
|
|
89
|
+
m.position.set(x, 0, z);
|
|
90
|
+
m.rotation.y = i % 2 ? theta : -theta;
|
|
91
|
+
mat.uniforms.dim.value = (i % 2 ? 0.42 : 0.12) * s;
|
|
92
|
+
mat.uniforms.opacity.value = alpha;
|
|
93
|
+
m.visible = alpha > 0;
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
frame(u: number) {
|
|
100
|
+
const s = foldRig(u, env, style);
|
|
101
|
+
aim(k.cam, s.cam);
|
|
102
|
+
apply(s.amount, s.slide, s.alpha);
|
|
103
|
+
k.renderer.clear(true, true, true);
|
|
104
|
+
k.renderer.render(scene, k.cam);
|
|
105
|
+
},
|
|
106
|
+
dispose() {
|
|
107
|
+
disposeTree(scene);
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|