feat: idle animation

This commit is contained in:
Neko Ayaka
2024-12-06 19:26:30 +08:00
parent dbcbb6f6cf
commit 8f9a0e76cd
14 changed files with 364 additions and 104 deletions
+5
View File
@@ -15,3 +15,8 @@ pnpm i
```shell
pnpm dev
```
## Acknowledgements
- [pixiv/ChatVRM](https://github.com/pixiv/ChatVRM)
- [josephrocca/ChatVRM-js: A JS conversion/adaptation of parts of the ChatVRM (TypeScript) code for standalone use in OpenCharacters and elsewhere](https://github.com/josephrocca/ChatVRM-js)
+5
View File
@@ -15,3 +15,8 @@ pnpm i
```shell
pnpm dev
```
## Acknowledgements
- [pixiv/ChatVRM](https://github.com/pixiv/ChatVRM)
- [josephrocca/ChatVRM-js: A JS conversion/adaptation of parts of the ChatVRM (TypeScript) code for standalone use in OpenCharacters and elsewhere](https://github.com/josephrocca/ChatVRM-js)
+1
View File
@@ -54,6 +54,7 @@ words:
- unhead
- unocss
- unplugin
- vrma
- vueuse
- xsai
ignoreWords: []
+3 -1
View File
@@ -31,7 +31,9 @@
"@pixi/sprite": "6",
"@pixi/ticker": "^6.5.10",
"@pixi/utils": "6",
"@pixiv/three-vrm": "^3.2.0",
"@pixiv/three-vrm": "^3.3.0",
"@pixiv/three-vrm-animation": "^3.3.0",
"@pixiv/three-vrm-core": "^3.3.0",
"@tresjs/cientos": "^4.0.3",
"@tresjs/core": "^4.3.1",
"@types/yauzl": "^2.10.3",
+2 -1
View File
@@ -324,7 +324,8 @@ onUnmounted(() => {
/>
<ThreeDScene
v-else-if="stageView === '3d'"
model="/assets/vrm/models/AvatarSample-A/AvatarSample_A.vrm"
model="/assets/vrm/models/AvatarSample-B/AvatarSample_B.vrm"
idle-animation="/assets/vrm/animations/idle_loop.vrma"
w="50%" min-w="50% <lg:full" min-h="100 sm:100" h-full flex-1
@error="console.error"
/>
@@ -8,6 +8,7 @@ import VRMModel from './VRMModel.vue'
const props = defineProps<{
model: string
idleAnimation: string
}>()
const emit = defineEmits<{
@@ -15,12 +16,12 @@ const emit = defineEmits<{
(e: 'error', value: unknown): void
}>()
const cameraPositionX = ref(0)
const cameraPositionY = ref(0.1)
const cameraPositionZ = ref(-1.5)
const vrmModelPositionX = ref(0)
const cameraPositionX = ref(-0.17)
const cameraPositionY = ref(0)
const cameraPositionZ = ref(-1)
const vrmModelPositionX = ref(-0.18)
const vrmModelPositionY = ref(-1.4)
const vrmModelPositionZ = ref(-0.3)
const vrmModelPositionZ = ref(-0.24)
</script>
<template>
@@ -102,14 +103,16 @@ const vrmModelPositionZ = ref(-0.3)
</div>
<TresCanvas :alpha="true" :antialias="true" :width="canvasWidth" :height="canvasHeight">
<TresPerspectiveCamera :position="[cameraPositionX, cameraPositionY, cameraPositionZ]" />
<TresDirectionalLight :color="0xFFFFFF" :intensity="0.6" :position="[1, 1, 1]" />
<OrbitControls />
<VRMModel
:model="props.model"
:idle-animation="props.idleAnimation"
:position="[vrmModelPositionX, vrmModelPositionY, vrmModelPositionZ]"
@load-model-progress="(val) => emit('loadModelProgress', val)"
@error="(val) => emit('error', val)"
/>
<TresAmbientLight :intensity="1" />
<TresAmbientLight :color="0xFFFFFF" :intensity="0.4" />
</TresCanvas>
</Screen>
</template>
+48 -27
View File
@@ -1,11 +1,14 @@
<script setup lang="ts">
import type { VRMCore } from '@pixiv/three-vrm'
import { VRMLoaderPlugin } from '@pixiv/three-vrm'
import { useTresContext } from '@tresjs/core'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import type { VRMCore } from '@pixiv/three-vrm-core'
import { useLoop, useTresContext } from '@tresjs/core'
import { AnimationMixer } from 'three'
import { clipFromVRMAnimation, loadVRMAnimation } from '~/composables/vrm/animation'
import { loadVrm } from '~/composables/vrm/core'
const props = defineProps<{
model: string
idleAnimation: string
loadAnimations?: string[]
position: [number, number, number]
}>()
@@ -14,43 +17,61 @@ const emit = defineEmits<{
(e: 'error', value: unknown): void
}>()
let vrm: VRMCore
const vrm = ref<VRMCore>()
const vrmAnimationMixer = ref<AnimationMixer>()
const { scene } = useTresContext()
const { onBeforeRender } = useLoop()
interface GLTFUserdata extends Record<string, any> {
vrm: VRMCore
}
onMounted(() => {
onMounted(async () => {
if (!scene.value) {
return
}
const loader = new GLTFLoader()
loader.register(parser => new VRMLoaderPlugin(parser))
loader.load(
props.model,
(gltf) => {
const userData = gltf.userData as GLTFUserdata
vrm = userData.vrm as VRMCore
scene.value.add(vrm.scene)
vrm.scene.position.set(...props.position)
},
progress => emit('loadModelProgress', Number.parseFloat((100.0 * (progress.loaded / progress.total)).toFixed(2))),
error => emit('error', error),
)
try {
const _vrm = await loadVrm(props.model, {
scene: scene.value,
lookAt: true,
position: props.position,
onProgress: progress => emit('loadModelProgress', Number.parseFloat((100.0 * (progress.loaded / progress.total)).toFixed(2))),
})
if (!_vrm) {
console.warn('No VRM model loaded')
return
}
const animation = await loadVRMAnimation(props.idleAnimation)
const clip = await clipFromVRMAnimation(_vrm, animation)
if (!clip) {
console.warn('No VRM animation loaded')
return
}
// play animation
vrmAnimationMixer.value = new AnimationMixer(_vrm.scene)
vrmAnimationMixer.value.clipAction(clip).play()
onBeforeRender(({ delta }) => {
vrmAnimationMixer.value?.update(delta)
vrm.value?.update(delta)
})
vrm.value = _vrm
}
catch (err) {
emit('error', err)
}
})
onUnmounted(() => {
if (vrm) {
if (vrm.value) {
const { scene } = useTresContext()
scene.value.remove(vrm.scene)
scene.value.remove(vrm.value.scene)
}
})
watch(() => props.position, ([x, y, z]) => {
if (vrm) {
vrm.scene.position.set(x, y, z)
if (vrm.value) {
vrm.value.scene.position.set(x, y, z)
}
})
</script>
@@ -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)
}
@@ -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
}
@@ -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
}
+87
View File
@@ -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()
}
+2 -8
View File
@@ -20,10 +20,7 @@ export const useLLM = defineStore('llm', () => {
async function models(apiUrl: string, apiKey: string) {
if (apiUrl === '') {
return {
data: [],
object: '',
}
return []
}
try {
@@ -34,10 +31,7 @@ export const useLLM = defineStore('llm', () => {
}
catch (err) {
if (String(err).includes(`Failed to construct 'URL': Invalid URL`)) {
return {
data: [],
object: '',
}
return []
}
throw err
+91 -61
View File
@@ -142,8 +142,14 @@ importers:
specifier: '6'
version: 6.5.10(@pixi/constants@6.5.10)(@pixi/settings@6.5.10(@pixi/constants@6.5.10))
'@pixiv/three-vrm':
specifier: ^3.2.0
version: 3.2.0(three@0.171.0)
specifier: ^3.3.0
version: 3.3.0(three@0.171.0)
'@pixiv/three-vrm-animation':
specifier: ^3.3.0
version: 3.3.0(three@0.171.0)
'@pixiv/three-vrm-core':
specifier: ^3.3.0
version: 3.3.0(three@0.171.0)
'@tresjs/cientos':
specifier: ^4.0.3
version: 4.0.3(@tresjs/core@4.3.1(three@0.171.0)(vue@3.5.13(typescript@5.7.2)))(@types/three@0.170.0)(react@18.3.1)(three@0.171.0)(vue@3.5.13(typescript@5.7.2))
@@ -1898,61 +1904,72 @@ packages:
'@pixi/constants': 6.5.10
'@pixi/settings': 6.5.10
'@pixiv/three-vrm-core@3.2.0':
resolution: {integrity: sha512-S/vEMSouVsbS7yv+PToK+r6UOWm0uR6d3djPOVSeGZKXbXjoNWZc5btjVWL+FAE1/rtdxXO9cMw0kBvI43Rujw==}
'@pixiv/three-vrm-animation@3.3.0':
resolution: {integrity: sha512-hv+Wq+4BVYZMw4g1IIOPqMyVewi+5gt9BO5T8STdd/M93dJnrwV1TTRhWhhRbwWVWYpKKX91dDrmjXJK5tKcJQ==}
peerDependencies:
three: '>=0.137'
'@pixiv/three-vrm-materials-hdr-emissive-multiplier@3.2.0':
resolution: {integrity: sha512-3Ai1ENdNiVnsxfzzXSKDCyT+w6awY7sHjoX6fKKnQ9tn2WGhxY596VUGDp/WyGDY2BM4NkSCUVd6qSGNEeEX6g==}
'@pixiv/three-vrm-core@3.3.0':
resolution: {integrity: sha512-AeeuErYF+jxhqJvoBuuc6pqqcegZMSgkLLE+huMHGHasMuHjQt2RAslcANLfpHLn65WEN4GmCtqBw2IWrxFiBQ==}
peerDependencies:
three: '>=0.137'
'@pixiv/three-vrm-materials-mtoon@3.2.0':
resolution: {integrity: sha512-wZollYD+cnTCdCJyrSuIGC1zVqXrZl2zWEzzwZ0+955/+UUltxYAHdFmSXpoChYNX89FFiIzIdfoARdYHMqNzw==}
'@pixiv/three-vrm-materials-hdr-emissive-multiplier@3.3.0':
resolution: {integrity: sha512-aiCAkkZtT4/RGBO75DNEqeIWjdbEg//UUU+uYls2cnNucGa6td9zFXMkQ2S7yj+38/DFmKAvOSBWKyI6Q6e7EQ==}
peerDependencies:
three: '>=0.137'
'@pixiv/three-vrm-materials-v0compat@3.2.0':
resolution: {integrity: sha512-z1qjQUjqTWWE7f/7vm2QzluL7e8fcSi/q6RMjpmsXQurfXdH06j43IWn5Lp8E9O16ZHbKw9HqBN8YkBv1qpgmg==}
'@pixiv/three-vrm-materials-mtoon@3.3.0':
resolution: {integrity: sha512-DvUeFidvY74fau/L+Lhe5sxvC5ri5eHK7SF700eYosJIWvDY/KgVkCjc3jCNqrAOp0OBGGCYjcvRDh23eVUO0Q==}
peerDependencies:
three: '>=0.137'
'@pixiv/three-vrm-node-constraint@3.2.0':
resolution: {integrity: sha512-NQMD3KQJx2i6CEGnabcOdeS8/m0KUrvOp4N4iEdFM0LRd/34zmasHBqLROlhFMG7tGan858wZ1HqyWnKOIc6Rw==}
'@pixiv/three-vrm-materials-v0compat@3.3.0':
resolution: {integrity: sha512-Ot0sj5Hfacw4258TDBFXOlWvI35B9YLW1wJ6WnqUZdlQd9t7dzD0o0NommOofVn40bRmIuI5Bxd5N9j0WxBxzg==}
peerDependencies:
three: '>=0.137'
'@pixiv/three-vrm-springbone@3.2.0':
resolution: {integrity: sha512-kMdHQLgHvhwNj/5WAPh6n+Q/Vva+9OgMFAzBtILoMKxfusS/LyDc7Du2KUaHU8huyB/ucvHSEan0rTEH2mz1dQ==}
'@pixiv/three-vrm-node-constraint@3.3.0':
resolution: {integrity: sha512-XvbS1sFJsuCfnboxEKhV2NltT929k3/VSYkQLRvw+uz6oVPUJHZ3w6cK8E6pvD9WKuBefDf+AQZQpn5PCRO25w==}
peerDependencies:
three: '>=0.137'
'@pixiv/three-vrm@3.2.0':
resolution: {integrity: sha512-M/t9xRK3v0SYsY1G4S3MMcQimovrQQBn4rcM+Ffh3z/gZTOGeivfRuGWhjZhxWY+ACoZ9X8HcpdvGbNsTVPrjQ==}
'@pixiv/three-vrm-springbone@3.3.0':
resolution: {integrity: sha512-aLzRXJukck7G8g/lm2Asob1N0QPjgYGoGU4YA9IgpdBFIz07S3CNDww/Lv/wmLJahyZqQCdJvghSr2X5yG8EtQ==}
peerDependencies:
three: '>=0.137'
'@pixiv/types-vrm-0.0@3.2.0':
resolution: {integrity: sha512-hxj3Z1V8KqEfeg11+6Q0groz7Rdpg/WL0PsoEFvPY1U06QD2SZFIdxLsSh2yRfMiwL1DhZJi614PoaHMaMiVhQ==}
'@pixiv/three-vrm@3.3.0':
resolution: {integrity: sha512-zPr3di6BYeZzhMf3WOTR5uG/o88ZtLzUOar6n2VtTukY8IKX05rbBIoAwcjqgxXA7kn+CEverKN1tQXuYQAHKw==}
peerDependencies:
three: '>=0.137'
'@pixiv/types-vrmc-materials-hdr-emissive-multiplier-1.0@3.2.0':
resolution: {integrity: sha512-xWiWtUP7IdVAT4JFjSD797TaX1fD38TwBucahYjT8Yq2b20frRQJ+/FrCG1cJ83Ja02PDQGtLY1VKy4lZ6c4Xg==}
'@pixiv/types-vrm-0.0@3.3.0':
resolution: {integrity: sha512-3AC+hR24O9yEMxoKkGiEVvj8zydh/poy5ZR6FieyvbRsKTJ/+Z8WJgTKnO7hJHkvPOntYznhsWJqjj/YG9kTLw==}
'@pixiv/types-vrmc-materials-mtoon-1.0@3.2.0':
resolution: {integrity: sha512-p6sWf6uzce3cSc8KiStSyB33GPwDEYPFjRxDKNpepe64i9wuXVqdtJNvh0WOWrb85VLxOfq78XAWeJIcdUF2Ww==}
'@pixiv/types-vrmc-materials-hdr-emissive-multiplier-1.0@3.3.0':
resolution: {integrity: sha512-Q6PCgnt0Kl5v+DI8VAueJ4qyTgQjf186SpWa91m7ZTPIX7hsdOeXly7cO8n64UBnEgihcgWoBS8mhMOLbHBxVg==}
'@pixiv/types-vrmc-node-constraint-1.0@3.2.0':
resolution: {integrity: sha512-wZT86mIHDBV7GefnsdsEGwT6IDxZISNHhbaCAjmoyZNZz+mO/J65WOrdbiMSlTgFXxyiBVge7h4WplLMsZCuhA==}
'@pixiv/types-vrmc-materials-mtoon-1.0@3.3.0':
resolution: {integrity: sha512-I0tJIMbs6yaHoMu6cw/bZxABk1kieWKv0Gu6JhECfN8IAo+WM0sYPOYMfzEkbxulpDUA8JIo0Kfn5s1B2Maf2A==}
'@pixiv/types-vrmc-springbone-1.0@3.2.0':
resolution: {integrity: sha512-ifnnbtGQiVTJF51MRhNPGfpdZMePBlEKDpOJe83n0S0XmVtHahbspWelGxiecDbPfYUMElqpIBhnDZmai4fT5Q==}
'@pixiv/types-vrmc-node-constraint-1.0@3.3.0':
resolution: {integrity: sha512-y6Ae2OFZQcY/ymxJqNT6lAT8Jo5kdDVfDtY2UvjtTlQ5tE0IbUv7HaBXVIX8CHurUWCy0kyV51iwSQ1Sdbw0bA==}
'@pixiv/types-vrmc-springbone-extended-collider-1.0@3.2.0':
resolution: {integrity: sha512-tImS5zKDMT9mliAyAaUmjs9susVj+mJiV8ZK2G2EOcchkt0SlD+kcrfpJPFxng5zfux/EFpIHUbXRFL9wqUOrQ==}
'@pixiv/types-vrmc-springbone-1.0@3.3.0':
resolution: {integrity: sha512-IjyV9QyxcyohIji5AegC4OS2VMsSKEDA+1ddq39KBPtTPtjvSjGU7ppvNBrWXYhyX0s0OCOsdqM0SmTCZVp/kw==}
'@pixiv/types-vrmc-vrm-1.0@3.2.0':
resolution: {integrity: sha512-XvqfRK90DIQl+6PxqIi0svEmjilFzpOOOdzGR6zh0tNV0yBV4cemLSs9v6BouDMtPt4aZ69dQR2ApA8VMbaV4w==}
'@pixiv/types-vrmc-springbone-extended-collider-1.0@3.3.0':
resolution: {integrity: sha512-5iBqq3S1MsOtKj5XVVbmx0z4UKO2muUSkamzI+Y2pErQsJyssu9SZBewPyC+aFG8XPk7wUbenQ5MxWHWOPFnFw==}
'@pixiv/types-vrmc-vrm-1.0@2.0.3':
resolution: {integrity: sha512-RMP34Bk1qLFQv/CRB1Zqvn2qMFfWQfP2Hms5QrrfoBsW9XroZdEe0zPLNbGmUPYh4F7VtBb+B7+RCuymRtpehA==}
'@pixiv/types-vrmc-vrm-1.0@3.3.0':
resolution: {integrity: sha512-4JJizAfGFpyyL+IGMv22z5ORqJ2M3sFgYK0ZRF/ZjOmG8aaQHqh9zFx5KYXdudbT3N95Qo+Nl8/nLN5pwMFEoA==}
'@pixiv/types-vrmc-vrm-animation-1.0@3.3.0':
resolution: {integrity: sha512-jgmiBPldGmrWbn0EUMt51bb8cSZqBw43bUYHcQfTar70OyVC1h25aV1B34nTNzl1IDPX2QnV2FdOPASpRtRYAA==}
'@pkgr/core@0.1.0':
resolution: {integrity: sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==}
@@ -8350,64 +8367,77 @@ snapshots:
eventemitter3: 3.1.2
url: 0.11.4
'@pixiv/three-vrm-core@3.2.0(three@0.171.0)':
'@pixiv/three-vrm-animation@3.3.0(three@0.171.0)':
dependencies:
'@pixiv/types-vrm-0.0': 3.2.0
'@pixiv/types-vrmc-vrm-1.0': 3.2.0
'@pixiv/three-vrm-core': 3.3.0(three@0.171.0)
'@pixiv/types-vrmc-vrm-1.0': 3.3.0
'@pixiv/types-vrmc-vrm-animation-1.0': 3.3.0
three: 0.171.0
'@pixiv/three-vrm-materials-hdr-emissive-multiplier@3.2.0(three@0.171.0)':
'@pixiv/three-vrm-core@3.3.0(three@0.171.0)':
dependencies:
'@pixiv/types-vrmc-materials-hdr-emissive-multiplier-1.0': 3.2.0
'@pixiv/types-vrm-0.0': 3.3.0
'@pixiv/types-vrmc-vrm-1.0': 3.3.0
three: 0.171.0
'@pixiv/three-vrm-materials-mtoon@3.2.0(three@0.171.0)':
'@pixiv/three-vrm-materials-hdr-emissive-multiplier@3.3.0(three@0.171.0)':
dependencies:
'@pixiv/types-vrm-0.0': 3.2.0
'@pixiv/types-vrmc-materials-mtoon-1.0': 3.2.0
'@pixiv/types-vrmc-materials-hdr-emissive-multiplier-1.0': 3.3.0
three: 0.171.0
'@pixiv/three-vrm-materials-v0compat@3.2.0(three@0.171.0)':
'@pixiv/three-vrm-materials-mtoon@3.3.0(three@0.171.0)':
dependencies:
'@pixiv/types-vrm-0.0': 3.2.0
'@pixiv/types-vrmc-materials-mtoon-1.0': 3.2.0
'@pixiv/types-vrm-0.0': 3.3.0
'@pixiv/types-vrmc-materials-mtoon-1.0': 3.3.0
three: 0.171.0
'@pixiv/three-vrm-node-constraint@3.2.0(three@0.171.0)':
'@pixiv/three-vrm-materials-v0compat@3.3.0(three@0.171.0)':
dependencies:
'@pixiv/types-vrmc-node-constraint-1.0': 3.2.0
'@pixiv/types-vrm-0.0': 3.3.0
'@pixiv/types-vrmc-materials-mtoon-1.0': 3.3.0
three: 0.171.0
'@pixiv/three-vrm-springbone@3.2.0(three@0.171.0)':
'@pixiv/three-vrm-node-constraint@3.3.0(three@0.171.0)':
dependencies:
'@pixiv/types-vrm-0.0': 3.2.0
'@pixiv/types-vrmc-springbone-1.0': 3.2.0
'@pixiv/types-vrmc-springbone-extended-collider-1.0': 3.2.0
'@pixiv/types-vrmc-node-constraint-1.0': 3.3.0
three: 0.171.0
'@pixiv/three-vrm@3.2.0(three@0.171.0)':
'@pixiv/three-vrm-springbone@3.3.0(three@0.171.0)':
dependencies:
'@pixiv/three-vrm-core': 3.2.0(three@0.171.0)
'@pixiv/three-vrm-materials-hdr-emissive-multiplier': 3.2.0(three@0.171.0)
'@pixiv/three-vrm-materials-mtoon': 3.2.0(three@0.171.0)
'@pixiv/three-vrm-materials-v0compat': 3.2.0(three@0.171.0)
'@pixiv/three-vrm-node-constraint': 3.2.0(three@0.171.0)
'@pixiv/three-vrm-springbone': 3.2.0(three@0.171.0)
'@pixiv/types-vrm-0.0': 3.3.0
'@pixiv/types-vrmc-springbone-1.0': 3.3.0
'@pixiv/types-vrmc-springbone-extended-collider-1.0': 3.3.0
three: 0.171.0
'@pixiv/types-vrm-0.0@3.2.0': {}
'@pixiv/three-vrm@3.3.0(three@0.171.0)':
dependencies:
'@pixiv/three-vrm-core': 3.3.0(three@0.171.0)
'@pixiv/three-vrm-materials-hdr-emissive-multiplier': 3.3.0(three@0.171.0)
'@pixiv/three-vrm-materials-mtoon': 3.3.0(three@0.171.0)
'@pixiv/three-vrm-materials-v0compat': 3.3.0(three@0.171.0)
'@pixiv/three-vrm-node-constraint': 3.3.0(three@0.171.0)
'@pixiv/three-vrm-springbone': 3.3.0(three@0.171.0)
three: 0.171.0
'@pixiv/types-vrmc-materials-hdr-emissive-multiplier-1.0@3.2.0': {}
'@pixiv/types-vrm-0.0@3.3.0': {}
'@pixiv/types-vrmc-materials-mtoon-1.0@3.2.0': {}
'@pixiv/types-vrmc-materials-hdr-emissive-multiplier-1.0@3.3.0': {}
'@pixiv/types-vrmc-node-constraint-1.0@3.2.0': {}
'@pixiv/types-vrmc-materials-mtoon-1.0@3.3.0': {}
'@pixiv/types-vrmc-springbone-1.0@3.2.0': {}
'@pixiv/types-vrmc-node-constraint-1.0@3.3.0': {}
'@pixiv/types-vrmc-springbone-extended-collider-1.0@3.2.0': {}
'@pixiv/types-vrmc-springbone-1.0@3.3.0': {}
'@pixiv/types-vrmc-vrm-1.0@3.2.0': {}
'@pixiv/types-vrmc-springbone-extended-collider-1.0@3.3.0': {}
'@pixiv/types-vrmc-vrm-1.0@2.0.3': {}
'@pixiv/types-vrmc-vrm-1.0@3.3.0': {}
'@pixiv/types-vrmc-vrm-animation-1.0@3.3.0':
dependencies:
'@pixiv/types-vrmc-vrm-1.0': 2.0.3
'@pkgr/core@0.1.0': {}