-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
dbcbb6f
commit 8f9a0e7
Showing
14 changed files
with
364 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,6 +54,7 @@ words: | |
- unhead | ||
- unocss | ||
- unplugin | ||
- vrma | ||
- vueuse | ||
- xsai | ||
ignoreWords: [] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import type { VRMAnimation } from '@pixiv/three-vrm-animation' | ||
import type { VRMCore } from '@pixiv/three-vrm-core' | ||
import { createVRMAnimationClip } from '@pixiv/three-vrm-animation' | ||
import { useVRMLoader } from './loader' | ||
|
||
export interface GLTFUserdata extends Record<string, any> { | ||
vrmAnimations: VRMAnimation[] | ||
} | ||
|
||
export async function loadVRMAnimation(url: string) { | ||
const loader = useVRMLoader() | ||
|
||
// load VRM Animation .vrma file | ||
const gltf = await loader.loadAsync(url) | ||
|
||
const userData = gltf.userData as GLTFUserdata | ||
if (!userData.vrmAnimations) { | ||
console.warn('No VRM animations found in the .vrma file') | ||
return | ||
} | ||
if (userData.vrmAnimations.length === 0) { | ||
console.warn('No VRM animations found in the .vrma file') | ||
return | ||
} | ||
|
||
return userData.vrmAnimations[0] | ||
} | ||
|
||
export async function clipFromVRMAnimation(vrm?: VRMCore, animation?: VRMAnimation) { | ||
if (!vrm) { | ||
console.warn('No VRM found') | ||
return | ||
} | ||
if (!animation) { | ||
return | ||
} | ||
|
||
// create animation clip | ||
return createVRMAnimationClip(animation, vrm) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import type { Object3D, Scene } from 'three' | ||
import { type VRMCore, VRMUtils } from '@pixiv/three-vrm' | ||
|
||
import { VRMLookAtQuaternionProxy } from '@pixiv/three-vrm-animation' | ||
import { useVRMLoader } from './loader' | ||
|
||
interface GLTFUserdata extends Record<string, any> { | ||
vrmCore?: VRMCore | ||
} | ||
|
||
export async function loadVrm(model: string, options?: { | ||
position?: [number, number, number] | ||
scene?: Scene | ||
lookAt?: boolean | ||
onProgress?: (progress: ProgressEvent<EventTarget>) => void | Promise<void> | ||
}): Promise<VRMCore | undefined> { | ||
const loader = useVRMLoader() | ||
const gltf = await loader.loadAsync(model, progress => options?.onProgress?.(progress)) | ||
|
||
const userData = gltf.userData as GLTFUserdata | ||
if (!userData.vrm) { | ||
return | ||
} | ||
|
||
const _vrm = userData.vrm | ||
|
||
// calling these functions greatly improves the performance | ||
VRMUtils.removeUnnecessaryVertices(_vrm.scene) | ||
VRMUtils.combineSkeletons(_vrm.scene) | ||
|
||
// Disable frustum culling | ||
_vrm.scene.traverse((object: Object3D) => { | ||
object.frustumCulled = false | ||
}) | ||
|
||
// Add look at quaternion proxy to the VRM; which is needed to play the look at animation | ||
if (options?.lookAt && _vrm.lookAt) { | ||
const lookAtQuatProxy = new VRMLookAtQuaternionProxy(_vrm.lookAt) | ||
lookAtQuatProxy.name = 'lookAtQuaternionProxy' | ||
_vrm.scene.add(lookAtQuatProxy) | ||
} | ||
|
||
// Add to scene | ||
if (options?.scene) | ||
options.scene.add(_vrm.scene) | ||
|
||
// Set position | ||
if (options?.position) | ||
_vrm.scene.position.set(...options.position) | ||
|
||
return _vrm | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { VRMLoaderPlugin } from '@pixiv/three-vrm' | ||
import { VRMAnimationLoaderPlugin } from '@pixiv/three-vrm-animation' | ||
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js' | ||
|
||
let loader: GLTFLoader | ||
|
||
export function useVRMLoader() { | ||
if (loader) { | ||
return loader | ||
} | ||
|
||
loader = new GLTFLoader() | ||
|
||
loader.crossOrigin = 'anonymous' | ||
loader.register(parser => new VRMLoaderPlugin(parser)) | ||
loader.register(parser => new VRMAnimationLoaderPlugin(parser)) | ||
|
||
return loader | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
export interface AudioManagerType { | ||
audioContext: AudioContext | ||
analyser: AnalyserNode | ||
dataBuffer: Float32Array | ||
frameId: number | null | ||
onVolumeChange?: (volume: number) => void | ||
} | ||
|
||
export function createAudioManager(): AudioManagerType { | ||
const audioContext = new AudioContext() | ||
const analyser = audioContext.createAnalyser() | ||
const dataBuffer = new Float32Array(2048) | ||
|
||
// Connect analyser to destination | ||
analyser.connect(audioContext.destination) | ||
|
||
return { | ||
audioContext, | ||
analyser, | ||
dataBuffer, | ||
frameId: null, | ||
onVolumeChange: undefined, | ||
} | ||
} | ||
|
||
function calculateVolume(manager: AudioManagerType): number { | ||
manager.analyser.getFloatTimeDomainData(manager.dataBuffer) | ||
|
||
// Find peak volume | ||
let volume = 0.0 | ||
for (let i = 0; i < manager.dataBuffer.length; i++) { | ||
volume = Math.max(volume, Math.abs(manager.dataBuffer[i])) | ||
} | ||
|
||
// Apply sigmoid normalization (from pixiv implementation) | ||
volume = 1 / (1 + Math.exp(-45 * volume + 5)) | ||
return volume < 0.1 ? 0 : volume | ||
} | ||
|
||
function updateFrame(manager: AudioManagerType) { | ||
if (manager.onVolumeChange) { | ||
manager.onVolumeChange(calculateVolume(manager)) | ||
} | ||
manager.frameId = requestAnimationFrame(() => updateFrame(manager)) | ||
} | ||
|
||
export async function playAudio(manager: AudioManagerType, source: ArrayBuffer | string): Promise<void> { | ||
try { | ||
const buffer = typeof source === 'string' | ||
? await (await fetch(source)).arrayBuffer() | ||
: source | ||
|
||
const audioBuffer = await manager.audioContext.decodeAudioData(buffer) | ||
const bufferSource = manager.audioContext.createBufferSource() | ||
|
||
bufferSource.buffer = audioBuffer | ||
bufferSource.connect(manager.analyser) | ||
bufferSource.start() | ||
|
||
return new Promise((resolve) => { | ||
bufferSource.onended = () => resolve() | ||
}) | ||
} | ||
catch (error) { | ||
console.error('Error playing audio:', error) | ||
throw error | ||
} | ||
} | ||
|
||
export function startVolumeTracking(manager: AudioManagerType, callback: (volume: number) => void) { | ||
manager.onVolumeChange = callback | ||
manager.audioContext.resume() | ||
updateFrame(manager) | ||
} | ||
|
||
export function stopVolumeTracking(manager: AudioManagerType) { | ||
if (manager.frameId) { | ||
cancelAnimationFrame(manager.frameId) | ||
manager.frameId = null | ||
} | ||
manager.onVolumeChange = undefined | ||
} | ||
|
||
export function disposeAudioManager(manager: AudioManagerType) { | ||
stopVolumeTracking(manager) | ||
manager.audioContext.close() | ||
} |
Oops, something went wrong.