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
33 changes: 33 additions & 0 deletions app/components/cube-boy-dancefloor/CubeBoy.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<script setup lang="ts">
import { useAnimations, useGLTF } from '@tresjs/cientos'
import { computed } from 'vue'
import type { AnimationAction } from 'three'

const { state: model, nodes } = useGLTF('/models/cube-boy/cube-boy-dance.glb', {
draco: true,
})

const rig = computed(() => nodes.value.Rig)

const animations = computed(() => model.value?.animations || [])

watch(animations, (animations) => {
console.log('animations', animations)
}, { immediate: true })

const { actions } = useAnimations(animations, rig)
const currentAction = ref<AnimationAction>()


watch(actions, (actions) => {
if (Object.keys(actions || {}).length === 0) { return }

currentAction.value = actions.Wave
currentAction.value?.reset().play()
}, { immediate: true })

</script>

<template>
<primitive v-if="rig" :object="rig" />
</template>
36 changes: 36 additions & 0 deletions app/components/cube-boy-dancefloor/DanceFloor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<script setup lang="ts">

const cellColor = shallowRef('#000000')
const sectionColor = shallowRef('#000000')
const cellThickness = shallowRef(0)
const sectionThickness = shallowRef(0)
const cellSize = shallowRef(0)
const sectionSize = shallowRef(0)

let elapsed = 0
setInterval(() => {
elapsed += 1000 / 30
cellColor.value = Math.cos(elapsed * 0.01) > 0 ? '#FFFF00' : '#FF0000'
sectionColor.value = Math.sin(elapsed * 0.01) > 0 ? '#FF0000' : '#00FF00'
sectionThickness.value = Math.cos(elapsed * 0.003) + 1
cellThickness.value = Math.cos(elapsed * 0.001) + 1
cellSize.value = Math.sin(elapsed * 0.0001) + 2
sectionSize.value = Math.cos(elapsed * 0.0001) + 2
}, 1000 / 30)
</script>

<template>
<CubeBoyDancefloorGrid
:args="[10.5, 10.5]"
:cell-size="cellSize"
:cell-color="cellColor"
:cell-thickness="cellThickness"
:section-size="sectionSize"
:section-thickness="sectionThickness"
:section-color="sectionColor"
:infinite-grid="true"
:fade-from="0"
:fade-distance="12"
:fade-strength="1"
/>
</template>
183 changes: 183 additions & 0 deletions app/components/cube-boy-dancefloor/Grid.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<script setup lang="ts">
import { shaderMaterial } from './shaderMaterial'
import type { ColorRepresentation, PlaneGeometry, ShaderMaterial, Side, Uniform } from 'three'
import { BackSide, Color, Mesh, Plane, Vector3 } from 'three'
import { extend, useLoop } from '@tresjs/core'
import { shallowRef } from 'vue'

/**
Based on
https://github.com/Fyrestar/THREE.InfiniteGridHelper by https://github.com/Fyrestar
and https://github.com/threlte/threlte/blob/main/packages/extras/src/lib/components/Grid/Grid.svelte
by https://github.com/grischaerbe and https://github.com/jerzakm
*/

export interface GridMaterialType {
/** Cell size, default: 0.5 */
cellSize?: number
/** Cell thickness, default: 0.5 */
cellThickness?: number
/** Cell color, default: black */
cellColor?: ColorRepresentation
/** Section size, default: 1 */
sectionSize?: number
/** Section thickness, default: 1 */
sectionThickness?: number
/** Section color, default: #2080ff */
sectionColor?: ColorRepresentation
/** Follow camera, default: false */
followCamera?: boolean
/** Display the grid infinitely, default: false */
infiniteGrid?: boolean
/** Fade distance, default: 100 */
fadeDistance?: number
/** Fade strength, default: 1 */
fadeStrength?: number
/** Fade from camera (1) or origin (0), or somewhere in between, default: camera */
fadeFrom?: number
/** Material side, default: THREE.BackSide */
side?: Side
}

export type GridProps = GridMaterialType & {
/** Default plane-geometry arguments */
args?: ConstructorParameters<typeof PlaneGeometry>
}

const props = withDefaults(defineProps<GridProps>(), {
cellColor: '#000000',
sectionColor: '#0000ff',
cellSize: 0.5,
sectionSize: 1,
followCamera: false,
infiniteGrid: false,
fadeDistance: 100,
fadeStrength: 1,
fadeFrom: 1,
cellThickness: 0.5,
sectionThickness: 1,
side: BackSide,
})

const GridMaterial = shaderMaterial(
{
cellSize: 0.5,
sectionSize: 1,
fadeDistance: 100,
fadeStrength: 1,
fadeFrom: 1,
cellThickness: 0.5,
sectionThickness: 1,
cellColor: new Color(),
sectionColor: new Color(),
infiniteGrid: false,
followCamera: false,
worldCamProjPosition: new Vector3(),
worldPlanePosition: new Vector3(),
},
/* glsl */ `
varying vec3 localPosition;
varying vec4 worldPosition;

uniform vec3 worldCamProjPosition;
uniform vec3 worldPlanePosition;
uniform float fadeDistance;
uniform bool infiniteGrid;
uniform bool followCamera;

void main() {
localPosition = position.xzy;
if (infiniteGrid) localPosition *= 1.0 + fadeDistance;

worldPosition = modelMatrix * vec4(localPosition, 1.0);
if (followCamera) {
worldPosition.xyz += (worldCamProjPosition - worldPlanePosition);
localPosition = (inverse(modelMatrix) * worldPosition).xyz;
}

gl_Position = projectionMatrix * viewMatrix * worldPosition;
}
`,
/* glsl */ `
varying vec3 localPosition;
varying vec4 worldPosition;

uniform vec3 worldCamProjPosition;
uniform float cellSize;
uniform float sectionSize;
uniform vec3 cellColor;
uniform vec3 sectionColor;
uniform float fadeDistance;
uniform float fadeStrength;
uniform float fadeFrom;
uniform float cellThickness;
uniform float sectionThickness;

float getGrid(float size, float thickness) {
vec2 r = localPosition.xz / size;
vec2 grid = abs(fract(r - 0.5) - 0.5) / fwidth(r);
float line = min(grid.x, grid.y) + 1.0 - thickness;
return 1.0 - min(line, 1.0);
}

void main() {
float g1 = getGrid(cellSize, cellThickness);
float g2 = getGrid(sectionSize, sectionThickness);

vec3 from = worldCamProjPosition*vec3(fadeFrom);
float dist = distance(from, worldPosition.xyz);
float d = 1.0 - min(dist / fadeDistance, 1.0);
vec3 color = mix(cellColor, sectionColor, min(1.0, sectionThickness * g2));

gl_FragColor = vec4(color, (g1 + g2) * pow(d, fadeStrength));
gl_FragColor.a = mix(0.75 * gl_FragColor.a, gl_FragColor.a, g2);
if (gl_FragColor.a <= 0.0) discard;

#include <tonemapping_fragment>
#include <colorspace_fragment>
}
`,
)
extend({ GridMaterial })

const ref = shallowRef<Mesh>(new Mesh())
const plane = new Plane()
const upVector = new Vector3(0, 1, 0)
const zeroVector = new Vector3(0, 0, 0)

const { onBeforeRender } = useLoop()

onBeforeRender((state) => {
if (!state.camera) { return }
plane.setFromNormalAndCoplanarPoint(upVector, zeroVector).applyMatrix4(ref.value.matrixWorld)

const gridMaterial = ref.value.material as ShaderMaterial
const worldCamProjPosition = gridMaterial.uniforms.worldCamProjPosition as Uniform<Vector3>
const worldPlanePosition = gridMaterial.uniforms.worldPlanePosition as Uniform<Vector3>

plane.projectPoint(state.camera.value!.position, worldCamProjPosition.value)
worldPlanePosition.value.set(0, 0, 0).applyMatrix4(ref.value.matrixWorld)
})
</script>

<template>
<TresMesh ref="ref" :frustum-culled="false">
<TresGridMaterial
:transparent="true"
:extensions-derivatives="true"
:side="props.side"
:cell-size="props.cellSize"
:section-size="props.sectionSize"
:cell-color="props.cellColor"
:section-color="props.sectionColor"
:cell-thickness="props.cellThickness"
:section-thickness="props.sectionThickness"
:fade-distance="props.fadeDistance"
:fade-strength="props.fadeStrength"
:fade-from="props.fadeFrom"
:infinite-grid="props.infiniteGrid"
:follow-camera="props.followCamera"
/>
<TresPlaneGeometry :args="props.args" />
</TresMesh>
</template>
18 changes: 18 additions & 0 deletions app/components/cube-boy-dancefloor/Lighting.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script setup lang="ts">

</script>
<template>
<TresAmbientLight :intensity="0.4" />
<TresDirectionalLight
:position="[5, 5, 5]"
:intensity="1"
color="red"
cast-shadow
/>
<TresDirectionalLight
:position="[-5, 5, -5]"
:intensity="1"
color="blue"
cast-shadow
/>
</template>
71 changes: 71 additions & 0 deletions app/components/cube-boy-dancefloor/MusicPlayer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<script lang="ts" setup>
import { MathUtils } from 'three'

const playing = shallowRef(false)

function onClick() {
playing.value = !playing.value
}

const attribution = 'Just Enough by Yarin Primak from Artlist.io'
const attributionLength = shallowRef(0)
const attributionDisplay = computed(() => attribution.slice(0, Math.min(attribution.length, attributionLength.value)))
const typing = shallowRef(false)
let attributionLengthTarget = 0
let timeout: ReturnType<typeof setTimeout> = setTimeout(() => { }, 0)

function nextLetter() {
attributionLength.value = MathUtils.clamp(
attributionLength.value + Math.sign(attributionLengthTarget - attributionLength.value),
0,
attribution.length,
)
typing.value = attributionLengthTarget != attributionLength.value
if (typing.value) {
clearTimeout(timeout)
timeout = setTimeout(nextLetter, 25)
}
}

watch(playing, (p) => {
if (p) {
attributionLengthTarget = attribution.length
}
else {
attributionLengthTarget = 0
}
nextLetter()
})
</script>

<template>
<div
:class="[
'flex justify-end items-center gap-1 fixed right-0 bottom-0 z-10',
'my-3 mb-24 pr-3 border border-r-0 border-white text-white font-mono',
'whitespace-pre-wrap cursor-pointer transition-colors duration-500',
typing ? 'text-cyan-400 bg-indigo-950' : 'bg-purple-900 hover:bg-purple-800'
]"
>
<UButton
:icon="playing ? 'i-lucide-volume-2' : 'i-lucide-volume-x'"
size="lg"
variant="ghost"
color="white"
class=" hover:text-yellow-400 transition-colors duration-500"
@click="onClick"
/>
<audio v-if="playing" autoplay loop
src="/music/yarin-primak-just-enough.mp3">
<a href="https://artlist.io/royalty-free-music/song/just-enough/137412"> Download audio </a>
</audio>
<ULink
to="https://artlist.io/royalty-free-music/song/just-enough/137412"
class="text-white hover:text-yellow-400 transition-colors duration-500"
external
>
{{ attributionDisplay }}
</ULink>
</div>
</template>

16 changes: 16 additions & 0 deletions app/components/cube-boy-dancefloor/index.global.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup lang="ts">

</script>

<template>
<TresCanvas window-size :clear-color="'#000'">
<!-- Experiment content will go here -->
<TresPerspectiveCamera :position="[9,9,9]" :fov="50" />
<OrbitControls />
<CubeBoyDancefloorCubeBoy />
<CubeBoyDancefloorDanceFloor />
<CubeBoyDancefloorLighting />
<TheScreenshot />
</TresCanvas>
<CubeBoyDancefloorMusicPlayer />
</template>
Loading