@react-three/viverse 0.1.20 → 0.2.1
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/dist/animation.d.ts +27 -0
- package/dist/animation.js +44 -0
- package/dist/bone.d.ts +10 -0
- package/dist/bone.js +18 -0
- package/dist/gamepad.d.ts +1 -2
- package/dist/gamepad.js +22 -33
- package/dist/index.d.ts +7 -2
- package/dist/index.js +8 -3
- package/dist/model.d.ts +7 -0
- package/dist/model.js +13 -0
- package/dist/physics.d.ts +36 -0
- package/dist/physics.js +78 -0
- package/dist/simple.d.ts +7 -0
- package/dist/simple.js +68 -0
- package/dist/utils.d.ts +14 -0
- package/dist/utils.js +79 -0
- package/package.json +3 -2
- package/dist/character.d.ts +0 -51
- package/dist/character.js +0 -167
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CharacterAnimationOptions, StartAnimationOptions } from '@pmndrs/viverse';
|
|
2
|
+
import { RootState } from '@react-three/fiber';
|
|
3
|
+
import { ActionParams } from '@react-three/timeline';
|
|
4
|
+
import { ReactNode } from 'react';
|
|
5
|
+
import { AnimationAction, AnimationActionLoopStyles, AnimationClip } from 'three';
|
|
6
|
+
export type AdditiveCharacterAnimationActionProps = Omit<CharacterAnimationActionProps, 'additiveToClip'> & {
|
|
7
|
+
referenceClip: CharacterAnimationOptions;
|
|
8
|
+
};
|
|
9
|
+
export declare const AdditiveCharacterAnimationAction: import("react").ForwardRefExoticComponent<Omit<CharacterAnimationActionProps, "additiveToClip"> & {
|
|
10
|
+
referenceClip: CharacterAnimationOptions;
|
|
11
|
+
} & import("react").RefAttributes<AnimationAction>>;
|
|
12
|
+
export type CharacterAnimationActionProps = {
|
|
13
|
+
dependencies?: Array<unknown>;
|
|
14
|
+
until?: () => Promise<unknown>;
|
|
15
|
+
loop?: AnimationActionLoopStyles;
|
|
16
|
+
additiveReferenceClip?: AnimationClip;
|
|
17
|
+
} & Omit<ActionParams<RootState>, 'until'> & CharacterAnimationOptions & StartAnimationOptions;
|
|
18
|
+
export declare function CharacterAnimationLayer({ name, children }: {
|
|
19
|
+
name: string;
|
|
20
|
+
children?: ReactNode;
|
|
21
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export declare const CharacterAnimationAction: import("react").ForwardRefExoticComponent<{
|
|
23
|
+
dependencies?: Array<unknown>;
|
|
24
|
+
until?: () => Promise<unknown>;
|
|
25
|
+
loop?: AnimationActionLoopStyles;
|
|
26
|
+
additiveReferenceClip?: AnimationClip;
|
|
27
|
+
} & Omit<ActionParams<RootState>, "until"> & CharacterAnimationOptions & StartAnimationOptions & import("react").RefAttributes<AnimationAction>>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { startAnimation } from '@pmndrs/viverse';
|
|
3
|
+
import { Action, animationFinished } from '@react-three/timeline';
|
|
4
|
+
import { createContext, forwardRef, useContext, useImperativeHandle, useMemo } from 'react';
|
|
5
|
+
import { LoopRepeat } from 'three';
|
|
6
|
+
import { makeClipAdditive } from 'three/src/animation/AnimationUtils.js';
|
|
7
|
+
import { useCharacterModel } from './model.js';
|
|
8
|
+
import { useCharacterAnimationLoader } from './utils.js';
|
|
9
|
+
export const AdditiveCharacterAnimationAction = forwardRef(({ referenceClip: referenceClipOptions, ...props }, ref) => {
|
|
10
|
+
const model = useCharacterModel();
|
|
11
|
+
const referenceClip = useCharacterAnimationLoader(model, { ...props, ...referenceClipOptions });
|
|
12
|
+
return _jsx(CharacterAnimationAction, { ref: ref, additiveReferenceClip: referenceClip, ...props });
|
|
13
|
+
});
|
|
14
|
+
const CharacterAnimationLayerContext = createContext(undefined);
|
|
15
|
+
export function CharacterAnimationLayer({ name, children }) {
|
|
16
|
+
return _jsx(CharacterAnimationLayerContext.Provider, { value: name, children: children });
|
|
17
|
+
}
|
|
18
|
+
export const CharacterAnimationAction = forwardRef(({ additiveReferenceClip, until, init, dependencies, update, fadeDuration, paused, loop, sync, crossFade, layer, ...animationOptions }, ref) => {
|
|
19
|
+
const layerFromContext = useContext(CharacterAnimationLayerContext);
|
|
20
|
+
layer ??= layerFromContext;
|
|
21
|
+
const model = useCharacterModel();
|
|
22
|
+
const srcClip = useCharacterAnimationLoader(model, animationOptions);
|
|
23
|
+
const clip = useMemo(() => additiveReferenceClip == null ? srcClip : makeClipAdditive(srcClip.clone(), undefined, additiveReferenceClip), [srcClip, additiveReferenceClip]);
|
|
24
|
+
const animation = useMemo(() => model.mixer.clipAction(clip), [clip, model]);
|
|
25
|
+
animation.clampWhenFinished = true;
|
|
26
|
+
animation.loop = loop ?? LoopRepeat;
|
|
27
|
+
useImperativeHandle(ref, () => animation, [animation]);
|
|
28
|
+
return (_jsx(Action, { init: () => {
|
|
29
|
+
const cleanupAnimation = startAnimation(animation, model.currentAnimations, {
|
|
30
|
+
layer,
|
|
31
|
+
fadeDuration,
|
|
32
|
+
paused,
|
|
33
|
+
sync,
|
|
34
|
+
crossFade,
|
|
35
|
+
});
|
|
36
|
+
const cleanupInit = init?.();
|
|
37
|
+
return () => {
|
|
38
|
+
cleanupInit?.();
|
|
39
|
+
cleanupAnimation?.();
|
|
40
|
+
};
|
|
41
|
+
}, until: until ?? (() => animationFinished(animation)), update: update, dependencies: dependencies != null
|
|
42
|
+
? [...dependencies, animationOptions.mask, fadeDuration, paused, sync, crossFade, animation, model, layer]
|
|
43
|
+
: undefined }));
|
|
44
|
+
});
|
package/dist/bone.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { VRMHumanBoneName } from '@pixiv/three-vrm';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
export declare function CharacterModelBone({ bone, children }: {
|
|
4
|
+
bone: VRMHumanBoneName;
|
|
5
|
+
children?: ReactNode;
|
|
6
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated use CharacterModelBone instead
|
|
9
|
+
*/
|
|
10
|
+
export declare const VrmCharacterModelBone: typeof CharacterModelBone;
|
package/dist/bone.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { VRM } from '@pixiv/three-vrm';
|
|
3
|
+
import { createPortal } from '@react-three/fiber';
|
|
4
|
+
import { useMemo } from 'react';
|
|
5
|
+
import { Fragment } from 'react/jsx-runtime';
|
|
6
|
+
import { useCharacterModel } from './model.js';
|
|
7
|
+
export function CharacterModelBone({ bone, children }) {
|
|
8
|
+
const model = useCharacterModel();
|
|
9
|
+
const boneObject = useMemo(() => (model instanceof VRM ? model.humanoid.getRawBoneNode(bone) : model.scene.getObjectByName(bone)), [model, bone]);
|
|
10
|
+
if (boneObject == null) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return (_jsx(Fragment, { children: createPortal(_jsx("group", { quaternion: model.boneRotationOffset, children: children }), boneObject) }, boneObject.id));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* @deprecated use CharacterModelBone instead
|
|
17
|
+
*/
|
|
18
|
+
export const VrmCharacterModelBone = CharacterModelBone;
|
package/dist/gamepad.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export declare function useXRControllerInput(): Input;
|
|
1
|
+
export declare function useXRControllerInput(): void;
|
package/dist/gamepad.js
CHANGED
|
@@ -1,38 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { JumpAction, MoveBackwardAction, MoveForwardAction, MoveLeftAction, MoveRightAction, } from '@pmndrs/viverse';
|
|
2
|
+
import { useFrame } from '@react-three/fiber';
|
|
2
3
|
import { useXRControllerButtonEvent, useXRInputSourceState } from '@react-three/xr';
|
|
3
|
-
import {
|
|
4
|
+
import { useEffect, useRef } from 'react';
|
|
4
5
|
export function useXRControllerInput() {
|
|
5
6
|
const leftController = useXRInputSourceState('controller', 'left');
|
|
6
|
-
const lastAPressed = useRef(null);
|
|
7
7
|
const rightController = useXRInputSourceState('controller', 'right');
|
|
8
|
-
useXRControllerButtonEvent(rightController, 'a-button', (state) => state === 'pressed' &&
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
return field === MoveLeftField ? Math.max(0, -thumbstickXAxis) : Math.max(0, thumbstickXAxis);
|
|
29
|
-
}
|
|
30
|
-
case LastTimeJumpPressedField:
|
|
31
|
-
return lastAPressed.current;
|
|
32
|
-
case RunField:
|
|
33
|
-
return (leftController?.gamepad?.['xr-standard-trigger']?.state === 'pressed');
|
|
34
|
-
}
|
|
35
|
-
return undefined;
|
|
36
|
-
},
|
|
37
|
-
}), [leftController]);
|
|
8
|
+
useXRControllerButtonEvent(rightController, 'a-button', (state) => state === 'pressed' && JumpAction.emit());
|
|
9
|
+
const forwardWriterRef = useRef(undefined);
|
|
10
|
+
const backwardWriterRef = useRef(undefined);
|
|
11
|
+
const leftWriterRef = useRef(undefined);
|
|
12
|
+
const rightWriterRef = useRef(undefined);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const abortController = new AbortController();
|
|
15
|
+
forwardWriterRef.current = MoveForwardAction.createWriter(abortController.signal);
|
|
16
|
+
backwardWriterRef.current = MoveBackwardAction.createWriter(abortController.signal);
|
|
17
|
+
leftWriterRef.current = MoveLeftAction.createWriter(abortController.signal);
|
|
18
|
+
rightWriterRef.current = MoveRightAction.createWriter(abortController.signal);
|
|
19
|
+
return () => abortController.abort();
|
|
20
|
+
}, [leftController]);
|
|
21
|
+
useFrame(() => {
|
|
22
|
+
forwardWriterRef.current?.write(-Math.min(0, leftController?.gamepad?.['xr-standard-thumbstick']?.yAxis ?? 0));
|
|
23
|
+
backwardWriterRef.current?.write(Math.max(0, leftController?.gamepad?.['xr-standard-thumbstick']?.yAxis ?? 0));
|
|
24
|
+
leftWriterRef.current?.write(-Math.min(0, leftController?.gamepad?.['xr-standard-thumbstick']?.xAxis ?? 0));
|
|
25
|
+
rightWriterRef.current?.write(Math.max(0, leftController?.gamepad?.['xr-standard-thumbstick']?.xAxis ?? 0));
|
|
26
|
+
});
|
|
38
27
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -67,10 +67,15 @@ export declare function useViversePublicAvatarList(): Awaited<ReturnType<AvatarC
|
|
|
67
67
|
* Uses React Suspense for data fetching.
|
|
68
68
|
*/
|
|
69
69
|
export declare function useViversePublicAvatarByID(id: string): Awaited<ReturnType<AvatarClient['getPublicAvatarByID']>> | undefined;
|
|
70
|
-
export * from './character.js';
|
|
71
70
|
export * from './material.js';
|
|
72
|
-
export
|
|
71
|
+
export * as Vanilla from '@pmndrs/viverse';
|
|
73
72
|
export * from '@viverse/sdk';
|
|
74
73
|
export * from '@viverse/sdk/avatar-client';
|
|
75
74
|
export * from './gamepad.js';
|
|
76
75
|
export * from './mobile.js';
|
|
76
|
+
export * from './physics.js';
|
|
77
|
+
export * from './utils.js';
|
|
78
|
+
export * from './model.js';
|
|
79
|
+
export * from './animation.js';
|
|
80
|
+
export * from './bone.js';
|
|
81
|
+
export * from './simple.js';
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Client } from '@viverse/sdk';
|
|
|
3
3
|
import AvatarClient from '@viverse/sdk/avatar-client';
|
|
4
4
|
import { createContext, useCallback, useContext } from 'react';
|
|
5
5
|
import { suspend, clear } from 'suspend-react';
|
|
6
|
-
import { BvhPhysicsWorld } from './
|
|
6
|
+
import { BvhPhysicsWorld } from './physics.js';
|
|
7
7
|
// auth
|
|
8
8
|
const viverseCheckAuthSymbol = Symbol('viverse-check-auth');
|
|
9
9
|
const authSuspenseKeys = [];
|
|
@@ -152,10 +152,15 @@ export function useViversePublicAvatarByID(id) {
|
|
|
152
152
|
const avatarClient = useViverseAvatarClient();
|
|
153
153
|
return suspend(async () => avatarClient?.getPublicAvatarByID(id), [viversePublicAvatarByIDSymbol, avatarClient, id]);
|
|
154
154
|
}
|
|
155
|
-
export * from './character.js';
|
|
156
155
|
export * from './material.js';
|
|
157
|
-
export
|
|
156
|
+
export * as Vanilla from '@pmndrs/viverse';
|
|
158
157
|
export * from '@viverse/sdk';
|
|
159
158
|
export * from '@viverse/sdk/avatar-client';
|
|
160
159
|
export * from './gamepad.js';
|
|
161
160
|
export * from './mobile.js';
|
|
161
|
+
export * from './physics.js';
|
|
162
|
+
export * from './utils.js';
|
|
163
|
+
export * from './model.js';
|
|
164
|
+
export * from './animation.js';
|
|
165
|
+
export * from './bone.js';
|
|
166
|
+
export * from './simple.js';
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { CharacterModel } from '@pmndrs/viverse';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
export declare function CharacterModelProvider({ model, children }: {
|
|
4
|
+
children?: ReactNode;
|
|
5
|
+
model: CharacterModel;
|
|
6
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function useCharacterModel(): CharacterModel;
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from 'react';
|
|
3
|
+
const CharacterModelContext = createContext(undefined);
|
|
4
|
+
export function CharacterModelProvider({ model, children }) {
|
|
5
|
+
return _jsx(CharacterModelContext.Provider, { value: model, children: children });
|
|
6
|
+
}
|
|
7
|
+
export function useCharacterModel() {
|
|
8
|
+
const model = useContext(CharacterModelContext);
|
|
9
|
+
if (model == null) {
|
|
10
|
+
throw new Error(`useCharacterModel can only be used inside a CharacterModelProvider`);
|
|
11
|
+
}
|
|
12
|
+
return model;
|
|
13
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { BvhCharacterPhysics, BvhCharacterPhysicsOptions, BvhPhysicsWorld as VanillaBvhPhysicsWorld } from '@pmndrs/viverse';
|
|
2
|
+
import { ReactNode, RefObject } from 'react';
|
|
3
|
+
import { Object3D } from 'three';
|
|
4
|
+
/**
|
|
5
|
+
* provides the bvh physics world context
|
|
6
|
+
*/
|
|
7
|
+
export declare function BvhPhysicsWorld({ children }: {
|
|
8
|
+
children?: ReactNode;
|
|
9
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare function useBvhPhysicsWorld(): VanillaBvhPhysicsWorld;
|
|
11
|
+
export declare function useBvhCharacterPhysics(model: Object3D | RefObject<Object3D | null>, options?: BvhCharacterPhysicsOptions): BvhCharacterPhysics;
|
|
12
|
+
/**
|
|
13
|
+
* @deprecated use <BvhPhysicsBody kinematic={false} /> instead (kinematic={false} can be skipped as its the default)
|
|
14
|
+
*/
|
|
15
|
+
export declare const FixedBvhPhysicsBody: import("react").ForwardRefExoticComponent<{
|
|
16
|
+
children?: ReactNode;
|
|
17
|
+
} & import("react").RefAttributes<Object3D<import("three").Object3DEventMap>>>;
|
|
18
|
+
/**
|
|
19
|
+
* allows to add all children as static (non-moving) objects as sensors to the bvh physics world
|
|
20
|
+
* @requires that the structure of the inner content is not changing or has a suspense boundary
|
|
21
|
+
* do not wrap the content inside in a suspense!
|
|
22
|
+
*/
|
|
23
|
+
export declare const BvhPhysicsSensor: import("react").ForwardRefExoticComponent<{
|
|
24
|
+
children?: ReactNode;
|
|
25
|
+
isStatic?: boolean;
|
|
26
|
+
onIntersectedChanged?: (intersected: boolean) => void;
|
|
27
|
+
} & import("react").RefAttributes<Object3D<import("three").Object3DEventMap>>>;
|
|
28
|
+
/**
|
|
29
|
+
* allows to add all children as static (non-moving) or kinematic (moving) objects as obstacles to the bvh physics world
|
|
30
|
+
* @requires that the structure of the inner content is not changing or has a suspense boundary
|
|
31
|
+
* do not wrap the content inside in a suspense!
|
|
32
|
+
*/
|
|
33
|
+
export declare const BvhPhysicsBody: import("react").ForwardRefExoticComponent<{
|
|
34
|
+
children?: ReactNode;
|
|
35
|
+
kinematic?: boolean;
|
|
36
|
+
} & import("react").RefAttributes<Object3D<import("three").Object3DEventMap>>>;
|
package/dist/physics.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { BvhCharacterPhysics, BvhPhysicsWorld as VanillaBvhPhysicsWorld, } from '@pmndrs/viverse';
|
|
3
|
+
import { useFrame } from '@react-three/fiber';
|
|
4
|
+
import { createContext, forwardRef, useContext, useEffect, useImperativeHandle, useMemo, useRef, } from 'react';
|
|
5
|
+
import { Object3D } from 'three';
|
|
6
|
+
const BvhPhyiscsWorldContext = createContext(undefined);
|
|
7
|
+
/**
|
|
8
|
+
* provides the bvh physics world context
|
|
9
|
+
*/
|
|
10
|
+
export function BvhPhysicsWorld({ children }) {
|
|
11
|
+
const world = useMemo(() => new VanillaBvhPhysicsWorld(), []);
|
|
12
|
+
return _jsx(BvhPhyiscsWorldContext.Provider, { value: world, children: children });
|
|
13
|
+
}
|
|
14
|
+
export function useBvhPhysicsWorld() {
|
|
15
|
+
const world = useContext(BvhPhyiscsWorldContext);
|
|
16
|
+
if (world == null) {
|
|
17
|
+
throw new Error('useBvhPhysicsWorld must be used within a BvhPhysicsWorld component');
|
|
18
|
+
}
|
|
19
|
+
return world;
|
|
20
|
+
}
|
|
21
|
+
export function useBvhCharacterPhysics(model, options) {
|
|
22
|
+
const world = useBvhPhysicsWorld();
|
|
23
|
+
const characterPhysics = useMemo(() => new BvhCharacterPhysics(world), [world]);
|
|
24
|
+
useFrame((_, delta) => {
|
|
25
|
+
const resolvedModel = model instanceof Object3D ? model : model.current;
|
|
26
|
+
if (resolvedModel == null) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
characterPhysics.update(resolvedModel, delta, options);
|
|
30
|
+
});
|
|
31
|
+
return characterPhysics;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* @deprecated use <BvhPhysicsBody kinematic={false} /> instead (kinematic={false} can be skipped as its the default)
|
|
35
|
+
*/
|
|
36
|
+
export const FixedBvhPhysicsBody = forwardRef(({ children }, ref) => {
|
|
37
|
+
return _jsxs(BvhPhysicsBody, { children: [" ", children] });
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* allows to add all children as static (non-moving) objects as sensors to the bvh physics world
|
|
41
|
+
* @requires that the structure of the inner content is not changing or has a suspense boundary
|
|
42
|
+
* do not wrap the content inside in a suspense!
|
|
43
|
+
*/
|
|
44
|
+
export const BvhPhysicsSensor = forwardRef(({ children, isStatic = true, onIntersectedChanged }, ref) => {
|
|
45
|
+
const world = useBvhPhysicsWorld();
|
|
46
|
+
const internalRef = useRef(null);
|
|
47
|
+
const listenerRef = useRef(onIntersectedChanged);
|
|
48
|
+
listenerRef.current = onIntersectedChanged;
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const body = internalRef.current;
|
|
51
|
+
if (body == null) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
world.addSensor(body, isStatic, (intersected) => listenerRef.current?.(intersected));
|
|
55
|
+
return () => world.removeSensor(body);
|
|
56
|
+
}, [world, isStatic]);
|
|
57
|
+
useImperativeHandle(ref, () => internalRef.current, []);
|
|
58
|
+
return _jsx("group", { ref: internalRef, children: children });
|
|
59
|
+
});
|
|
60
|
+
/**
|
|
61
|
+
* allows to add all children as static (non-moving) or kinematic (moving) objects as obstacles to the bvh physics world
|
|
62
|
+
* @requires that the structure of the inner content is not changing or has a suspense boundary
|
|
63
|
+
* do not wrap the content inside in a suspense!
|
|
64
|
+
*/
|
|
65
|
+
export const BvhPhysicsBody = forwardRef(({ children, kinematic = false }, ref) => {
|
|
66
|
+
const world = useBvhPhysicsWorld();
|
|
67
|
+
const internalRef = useRef(null);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
const body = internalRef.current;
|
|
70
|
+
if (body == null) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
world.addBody(body, kinematic);
|
|
74
|
+
return () => world.removeBody(body);
|
|
75
|
+
}, [world, kinematic]);
|
|
76
|
+
useImperativeHandle(ref, () => internalRef.current, []);
|
|
77
|
+
return _jsx("group", { ref: internalRef, children: children });
|
|
78
|
+
});
|
package/dist/simple.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SimpleCharacterOptions } from '@pmndrs/viverse';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { Group, Object3D } from 'three';
|
|
4
|
+
export declare const SimpleCharacter: import("react").ForwardRefExoticComponent<Omit<{
|
|
5
|
+
children?: ReactNode;
|
|
6
|
+
useViverseAvatar?: boolean;
|
|
7
|
+
} & SimpleCharacterOptions & import("@react-three/fiber/dist/declarations/src/core/utils.js").Mutable<import("@react-three/fiber/dist/declarations/src/core/utils.js").Overwrite<Partial<import("@react-three/fiber/dist/declarations/src/core/utils.js").Overwrite<Group<import("three").Object3DEventMap>, import("@react-three/fiber").MathProps<Group<import("three").Object3DEventMap>> & import("@react-three/fiber").ReactProps<Group<import("three").Object3DEventMap>> & Partial<import("@react-three/fiber").EventHandlers>>>, Omit<import("@react-three/fiber").InstanceProps<Group<import("three").Object3DEventMap>, typeof Group>, "object">>>, "ref"> & import("react").RefAttributes<Object3D<import("three").Object3DEventMap>>>;
|
package/dist/simple.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { RunAction, shouldJump, updateSimpleCharacterInputVelocity, updateSimpleCharacterRotation, } from '@pmndrs/viverse';
|
|
3
|
+
import { IdleAnimationUrl, JumpDownAnimationUrl, JumpForwardAnimationUrl, JumpLoopAnimationUrl, JumpUpAnimationUrl, RunAnimationUrl, WalkAnimationUrl, } from '@pmndrs/viverse/src/animation/default.js';
|
|
4
|
+
import { useFrame } from '@react-three/fiber';
|
|
5
|
+
import { Graph, GrapthState, Parallel, RunTimeline, Switch, SwitchCase, timePassed } from '@react-three/timeline';
|
|
6
|
+
import { forwardRef, Suspense, useImperativeHandle, useRef } from 'react';
|
|
7
|
+
import { LoopOnce, Vector3 } from 'three';
|
|
8
|
+
import { CharacterAnimationAction } from './animation.js';
|
|
9
|
+
import { useViverseActiveAvatar } from './index.js';
|
|
10
|
+
import { CharacterModelProvider } from './model.js';
|
|
11
|
+
import { useBvhCharacterPhysics } from './physics.js';
|
|
12
|
+
import { useCharacterCameraBehavior, useCharacterModelLoader, useSimpleCharacterInputs } from './utils.js';
|
|
13
|
+
export const SimpleCharacter = forwardRef(({ input, inputOptions, cameraBehavior, children, movement, physics: physicsOptions, model, useViverseAvatar, animation, ...props }, ref) => {
|
|
14
|
+
useSimpleCharacterInputs(input, inputOptions);
|
|
15
|
+
const internalRef = useRef(null);
|
|
16
|
+
useCharacterCameraBehavior(internalRef, cameraBehavior);
|
|
17
|
+
const physics = useBvhCharacterPhysics(internalRef, physicsOptions);
|
|
18
|
+
const lastJumpTimeRef = useRef(-Infinity);
|
|
19
|
+
useFrame((state) => updateSimpleCharacterInputVelocity(state.camera, physics, movement));
|
|
20
|
+
useFrame(() => {
|
|
21
|
+
if (model != false || movement?.jump === false) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const bufferTime = movement?.jump === true ? undefined : movement?.jump?.bufferTime;
|
|
25
|
+
if (shouldJump(physics, lastJumpTimeRef.current, bufferTime)) {
|
|
26
|
+
lastJumpTimeRef.current = performance.now() / 1000;
|
|
27
|
+
physics.applyVelocity(
|
|
28
|
+
// eslint-disable-next-line @react-three/no-new-in-loop
|
|
29
|
+
new Vector3(0, (typeof movement?.jump === 'object' ? movement?.jump.speed : undefined) ?? 8, 0));
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
useImperativeHandle(ref, () => internalRef.current, []);
|
|
33
|
+
return (_jsx("group", { ...props, ref: internalRef, children: model == false ? (children) : (_jsx(SimpleCharacterModel, { animation: animation, physics: physics, model: model == true ? undefined : model, movement: movement, useViverseAvatar: useViverseAvatar, children: children })) }));
|
|
34
|
+
});
|
|
35
|
+
function SimpleCharacterModel({ children, model: modelOptions, movement, physics, useViverseAvatar = true, animation, }) {
|
|
36
|
+
const avatar = useViverseActiveAvatar();
|
|
37
|
+
const model = useCharacterModelLoader(avatar != null && useViverseAvatar
|
|
38
|
+
? {
|
|
39
|
+
type: 'vrm',
|
|
40
|
+
url: avatar?.vrmUrl,
|
|
41
|
+
...modelOptions,
|
|
42
|
+
}
|
|
43
|
+
: modelOptions);
|
|
44
|
+
const lastJumpTimeRef = useRef(-Infinity);
|
|
45
|
+
useFrame((state, delta) => updateSimpleCharacterRotation(delta, physics, state.camera, model, animation));
|
|
46
|
+
return (_jsx(_Fragment, { children: _jsxs(CharacterModelProvider, { model: model, children: [_jsx(RunTimeline, { children: _jsxs(Graph, { enterState: "move", children: [_jsx(GrapthState, { name: "move", transitionTo: {
|
|
47
|
+
jumpStart: {
|
|
48
|
+
whenUpdate: () => shouldJump(physics, lastJumpTimeRef.current),
|
|
49
|
+
},
|
|
50
|
+
jumpLoop: { whenUpdate: () => !physics.isGrounded },
|
|
51
|
+
}, children: _jsxs(Switch, { children: [_jsx(SwitchCase, { index: 0, condition: () => physics.inputVelocity.lengthSq() === 0, children: _jsx(Suspense, { fallback: null, children: _jsx(CharacterAnimationAction, { ...animation?.idle, fadeDuration: animation?.crossFadeDuration, url: IdleAnimationUrl }) }) }), movement?.run != false && (_jsx(SwitchCase, { index: 1, condition: () => RunAction.get(), children: _jsx(Suspense, { fallback: null, children: _jsx(CharacterAnimationAction, { ...animation?.run, fadeDuration: animation?.crossFadeDuration, scaleTime: 0.8, url: RunAnimationUrl }) }) })), movement?.walk != false && (_jsx(SwitchCase, { index: 2, children: _jsx(Suspense, { fallback: null, children: _jsx(CharacterAnimationAction, { ...animation?.walk, fadeDuration: animation?.crossFadeDuration, scaleTime: 0.5, url: WalkAnimationUrl }) }) })), _jsx(SwitchCase, { index: 3, children: _jsx(Suspense, { fallback: null, children: _jsx(CharacterAnimationAction, { ...animation?.idle, fadeDuration: animation?.crossFadeDuration, url: IdleAnimationUrl }) }) })] }) }), _jsx(GrapthState, { name: "jumpStart", transitionTo: {
|
|
52
|
+
jumpDown: { whenUpdate: () => !physics.isGrounded },
|
|
53
|
+
finally: () => (RunAction.get() ? 'jumpForward' : 'jumpUp'),
|
|
54
|
+
}, children: _jsx(Parallel, { type: "race", children: _jsxs(Suspense, { fallback: null, children: [_jsx(CharacterAnimationAction, { ...animation?.jumpUp, fadeDuration: animation?.crossFadeDuration ?? 0.1, until: () => timePassed(0.2, 'seconds'), update: () => void physics.inputVelocity.multiplyScalar(0.3), paused: true, url: JumpUpAnimationUrl }), _jsx(CharacterAnimationAction, { ...animation?.jumpForward, fadeDuration: animation?.crossFadeDuration ?? 0.1, paused: true, url: JumpForwardAnimationUrl })] }) }) }), _jsx(GrapthState, { name: "jumpLoop", transitionTo: {
|
|
55
|
+
jumpDown: { whenUpdate: () => physics.isGrounded },
|
|
56
|
+
}, children: _jsx(Suspense, { fallback: null, children: _jsx(CharacterAnimationAction, { ...animation?.jumpLoop, fadeDuration: animation?.crossFadeDuration, url: JumpLoopAnimationUrl }) }) }), _jsx(GrapthState, { name: "jumpUp", transitionTo: {
|
|
57
|
+
jumpDown: {
|
|
58
|
+
whenUpdate: (_, _clock, actionTime) => actionTime > 0.3 && physics.isGrounded,
|
|
59
|
+
},
|
|
60
|
+
finally: 'jumpLoop',
|
|
61
|
+
}, children: _jsx(Suspense, { fallback: null, children: _jsx(CharacterAnimationAction, { ...animation?.jumpUp, fadeDuration: animation?.crossFadeDuration, loop: LoopOnce, init: () => {
|
|
62
|
+
lastJumpTimeRef.current = performance.now() / 1000;
|
|
63
|
+
physics.applyVelocity(new Vector3(0, (typeof movement?.jump === 'object' ? movement?.jump.speed : undefined) ?? 8, 0));
|
|
64
|
+
}, url: JumpUpAnimationUrl }) }) }), _jsx(GrapthState, { name: "jumpForward", transitionTo: { finally: () => (physics.isGrounded ? 'move' : 'jumpLoop') }, children: _jsx(Suspense, { fallback: null, children: _jsx(CharacterAnimationAction, { ...animation?.jumpForward, fadeDuration: animation?.crossFadeDuration, scaleTime: 0.9, init: () => {
|
|
65
|
+
lastJumpTimeRef.current = performance.now() / 1000;
|
|
66
|
+
physics.applyVelocity(new Vector3(0, 8, 0));
|
|
67
|
+
}, loop: LoopOnce, url: JumpForwardAnimationUrl }) }) }), _jsx(GrapthState, { name: "jumpDown", transitionTo: { finally: 'move' }, children: _jsx(Suspense, { fallback: null, children: _jsx(CharacterAnimationAction, { ...animation?.jumpDown, fadeDuration: animation?.crossFadeDuration, until: () => timePassed(150, 'milliseconds'), loop: LoopOnce, url: JumpDownAnimationUrl }) }) })] }) }), _jsx("primitive", { object: model.scene }), children] }) }));
|
|
68
|
+
}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CharacterCameraBehavior, CharacterAnimationOptions, CharacterModelOptions, SimpleCharacterCameraBehaviorOptions, CharacterModel, SimpleCharacterInputOptions } from '@pmndrs/viverse';
|
|
2
|
+
import { RefObject } from 'react';
|
|
3
|
+
import { Object3D } from 'three';
|
|
4
|
+
export declare function useCharacterCameraBehavior(model: Object3D | RefObject<Object3D | null>, options?: SimpleCharacterCameraBehaviorOptions): RefObject<CharacterCameraBehavior | undefined>;
|
|
5
|
+
export declare function useCharacterModelLoader(options?: CharacterModelOptions): CharacterModel;
|
|
6
|
+
export declare function useCharacterAnimationLoader(model: CharacterModel, options: CharacterAnimationOptions): import("three").AnimationClip;
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated use inputs directly
|
|
9
|
+
*/
|
|
10
|
+
export declare function useSimpleCharacterInputs(inputsClasses?: ReadonlyArray<{
|
|
11
|
+
new (domElement: HTMLElement): {
|
|
12
|
+
dispose(): void;
|
|
13
|
+
};
|
|
14
|
+
}>, options?: SimpleCharacterInputOptions): void;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { VRM } from '@pixiv/three-vrm';
|
|
2
|
+
import { CharacterCameraBehavior, flattenCharacterAnimationOptions, flattenCharacterModelOptions, loadCharacterModel, loadCharacterAnimation, ScreenJoystickInput, ScreenJumpButtonInput, PointerCaptureInput, LocomotionKeyboardInput, applySimpleCharacterInputOptions, } from '@pmndrs/viverse';
|
|
3
|
+
import { useFrame, useThree } from '@react-three/fiber';
|
|
4
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
5
|
+
import { suspend } from 'suspend-react';
|
|
6
|
+
import { Object3D } from 'three';
|
|
7
|
+
import { useBvhPhysicsWorld } from './physics.js';
|
|
8
|
+
export function useCharacterCameraBehavior(model, options) {
|
|
9
|
+
const behaviorRef = useRef(undefined);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
const behavior = new CharacterCameraBehavior();
|
|
12
|
+
behaviorRef.current = behavior;
|
|
13
|
+
return () => {
|
|
14
|
+
behaviorRef.current = undefined;
|
|
15
|
+
behavior.dispose();
|
|
16
|
+
};
|
|
17
|
+
}, []);
|
|
18
|
+
const world = useBvhPhysicsWorld();
|
|
19
|
+
const raycast = useMemo(() => world.raycast.bind(world), [world]);
|
|
20
|
+
useFrame((state, delta) => {
|
|
21
|
+
const resolvedModel = model instanceof Object3D ? model : model.current;
|
|
22
|
+
if (resolvedModel == null) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
behaviorRef.current?.update(state.camera, resolvedModel, delta, raycast, options);
|
|
26
|
+
});
|
|
27
|
+
return behaviorRef;
|
|
28
|
+
}
|
|
29
|
+
const loadCharacterModelSymbol = Symbol('loadCharacterModel');
|
|
30
|
+
export function useCharacterModelLoader(options) {
|
|
31
|
+
const model = suspend((_, ...params) => loadCharacterModel(...params), [loadCharacterModelSymbol, ...flattenCharacterModelOptions(options)]);
|
|
32
|
+
useFrame((_, delta) => {
|
|
33
|
+
if (model instanceof VRM) {
|
|
34
|
+
model.update(delta);
|
|
35
|
+
}
|
|
36
|
+
model.mixer.update(delta);
|
|
37
|
+
});
|
|
38
|
+
return model;
|
|
39
|
+
}
|
|
40
|
+
const loadCharacterAnimationSymbol = Symbol('loadCharacterAnimation');
|
|
41
|
+
export function useCharacterAnimationLoader(model, options) {
|
|
42
|
+
return suspend((_, ...params) => loadCharacterAnimation(...params), [loadCharacterAnimationSymbol, model, ...flattenCharacterAnimationOptions(options)]);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* @deprecated use inputs directly
|
|
46
|
+
*/
|
|
47
|
+
export function useSimpleCharacterInputs(inputsClasses = [
|
|
48
|
+
ScreenJoystickInput,
|
|
49
|
+
ScreenJumpButtonInput,
|
|
50
|
+
PointerCaptureInput,
|
|
51
|
+
LocomotionKeyboardInput,
|
|
52
|
+
], options) {
|
|
53
|
+
const dom = useThree((s) => s.gl.domElement);
|
|
54
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
55
|
+
const inputs = useMemo(() => [], [dom]);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const removedInputs = new Set(inputs);
|
|
58
|
+
for (const inputClass of inputsClasses) {
|
|
59
|
+
const existingInput = inputs.find((existingInput) => existingInput instanceof inputClass);
|
|
60
|
+
if (existingInput != null) {
|
|
61
|
+
removedInputs.delete(existingInput);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
inputs.push(new inputClass(dom));
|
|
65
|
+
}
|
|
66
|
+
for (const removedInput of removedInputs) {
|
|
67
|
+
removedInput.dispose();
|
|
68
|
+
const index = inputs.indexOf(removedInput);
|
|
69
|
+
if (index != -1) {
|
|
70
|
+
inputs.splice(index, 1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
applySimpleCharacterInputOptions(inputs, options);
|
|
74
|
+
});
|
|
75
|
+
useEffect(() => () => {
|
|
76
|
+
inputs.forEach((input) => input.dispose());
|
|
77
|
+
inputs.length = 0;
|
|
78
|
+
}, [inputs]);
|
|
79
|
+
}
|
package/package.json
CHANGED
|
@@ -24,11 +24,12 @@
|
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@viverse/sdk": "^1.2.10-alpha.0",
|
|
27
|
+
"@react-three/timeline": "^0.3.7",
|
|
27
28
|
"suspend-react": "^0.1.3",
|
|
28
29
|
"@pixiv/three-vrm": "^3.4.2",
|
|
29
30
|
"zustand": "^5.0.6",
|
|
30
31
|
"@react-three/xr": "^6.6.20",
|
|
31
|
-
"@pmndrs/viverse": "^0.1
|
|
32
|
+
"@pmndrs/viverse": "^0.2.1"
|
|
32
33
|
},
|
|
33
34
|
"peerDependencies": {
|
|
34
35
|
"@react-three/fiber": "*"
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
"@types/react": "^19.1.8",
|
|
40
41
|
"react": "^19.1.0"
|
|
41
42
|
},
|
|
42
|
-
"version": "0.1
|
|
43
|
+
"version": "0.2.1",
|
|
43
44
|
"scripts": {
|
|
44
45
|
"build": "tsc",
|
|
45
46
|
"check:prettier": "prettier --check src",
|
package/dist/character.d.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { SimpleCharacterOptions, SimpleCharacter as SimpleCharacterImpl, VRMHumanBoneName } from '@pmndrs/viverse';
|
|
2
|
-
import { ThreeElement } from '@react-three/fiber';
|
|
3
|
-
import { ReactNode } from 'react';
|
|
4
|
-
import { Group, Object3D } from 'three';
|
|
5
|
-
declare module '@react-three/fiber' {
|
|
6
|
-
interface ThreeElements {
|
|
7
|
-
simpleCharacterImpl: ThreeElement<typeof SimpleCharacterImpl>;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* provides the bvh physics world context
|
|
12
|
-
*/
|
|
13
|
-
export declare function BvhPhysicsWorld({ children }: {
|
|
14
|
-
children?: ReactNode;
|
|
15
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
16
|
-
/**
|
|
17
|
-
* creates a simple character controller supporting running, walking, and jumping with a default avatar and animations with can be configutred
|
|
18
|
-
*/
|
|
19
|
-
export declare const SimpleCharacter: import("react").ForwardRefExoticComponent<Omit<SimpleCharacterOptions & {
|
|
20
|
-
useViverseAvatar?: boolean;
|
|
21
|
-
children?: ReactNode;
|
|
22
|
-
} & import("@react-three/fiber/dist/declarations/src/core/utils.js").Mutable<import("@react-three/fiber/dist/declarations/src/core/utils.js").Overwrite<Partial<import("@react-three/fiber/dist/declarations/src/core/utils.js").Overwrite<Group<import("three").Object3DEventMap>, import("@react-three/fiber").MathProps<Group<import("three").Object3DEventMap>> & import("@react-three/fiber").ReactProps<Group<import("three").Object3DEventMap>> & Partial<import("@react-three/fiber").EventHandlers>>>, Omit<import("@react-three/fiber").InstanceProps<Group<import("three").Object3DEventMap>, typeof Group>, "object">>>, "ref"> & import("react").RefAttributes<Group<import("three").Object3DEventMap>>>;
|
|
23
|
-
/**
|
|
24
|
-
* @deprecated use <BvhPhysicsBody kinematic={false} /> instead (kinematic={false} can be skipped as its the default)
|
|
25
|
-
*/
|
|
26
|
-
export declare const FixedBvhPhysicsBody: import("react").ForwardRefExoticComponent<{
|
|
27
|
-
children?: ReactNode;
|
|
28
|
-
} & import("react").RefAttributes<Object3D<import("three").Object3DEventMap>>>;
|
|
29
|
-
/**
|
|
30
|
-
* allows to add all children as static (non-moving) objects as sensors to the bvh physics world
|
|
31
|
-
* @requires that the structure of the inner content is not changing or has a suspense boundary
|
|
32
|
-
* do not wrap the content inside in a suspense!
|
|
33
|
-
*/
|
|
34
|
-
export declare const BvhPhysicsSensor: import("react").ForwardRefExoticComponent<{
|
|
35
|
-
children?: ReactNode;
|
|
36
|
-
isStatic?: boolean;
|
|
37
|
-
onIntersectedChanged?: (intersected: boolean) => void;
|
|
38
|
-
} & import("react").RefAttributes<Object3D<import("three").Object3DEventMap>>>;
|
|
39
|
-
/**
|
|
40
|
-
* allows to add all children as static (non-moving) or kinematic (moving) objects as obstacles to the bvh physics world
|
|
41
|
-
* @requires that the structure of the inner content is not changing or has a suspense boundary
|
|
42
|
-
* do not wrap the content inside in a suspense!
|
|
43
|
-
*/
|
|
44
|
-
export declare const BvhPhysicsBody: import("react").ForwardRefExoticComponent<{
|
|
45
|
-
children?: ReactNode;
|
|
46
|
-
kinematic?: boolean;
|
|
47
|
-
} & import("react").RefAttributes<Object3D<import("three").Object3DEventMap>>>;
|
|
48
|
-
export declare function CharacterModelBone({ bone, children }: {
|
|
49
|
-
bone: VRMHumanBoneName;
|
|
50
|
-
children?: ReactNode;
|
|
51
|
-
}): import("react/jsx-runtime").JSX.Element | null;
|
package/dist/character.js
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { VRM } from '@pixiv/three-vrm';
|
|
3
|
-
import { BvhPhysicsWorld as BvhPhysicsWorldImpl, SimpleCharacter as SimpleCharacterImpl, preloadSimpleCharacterAssets, simpleCharacterAnimationNames, InputSystem, LocomotionKeyboardInput, PointerCaptureInput, ScreenJoystickInput, ScreenJumpButtonInput, extractProxy, } from '@pmndrs/viverse';
|
|
4
|
-
import { useFrame, useThree, extend, createPortal, useStore } from '@react-three/fiber';
|
|
5
|
-
import { createContext, forwardRef, Fragment, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
|
|
6
|
-
import { clear, suspend } from 'suspend-react';
|
|
7
|
-
import { create } from 'zustand';
|
|
8
|
-
import { useViverseActiveAvatar } from './index.js';
|
|
9
|
-
const BvhPhyiscsWorldContext = createContext(undefined);
|
|
10
|
-
extend({ SimpleCharacterImpl });
|
|
11
|
-
const CharacterModelStoreContext = createContext(undefined);
|
|
12
|
-
/**
|
|
13
|
-
* provides the bvh physics world context
|
|
14
|
-
*/
|
|
15
|
-
export function BvhPhysicsWorld({ children }) {
|
|
16
|
-
const world = useMemo(() => new BvhPhysicsWorldImpl(), []);
|
|
17
|
-
return _jsx(BvhPhyiscsWorldContext.Provider, { value: world, children: children });
|
|
18
|
-
}
|
|
19
|
-
const PreloadSimpleCharacterAssetsSymbol = Symbol('preload-simple-character-assets');
|
|
20
|
-
/**
|
|
21
|
-
* creates a simple character controller supporting running, walking, and jumping with a default avatar and animations with can be configutred
|
|
22
|
-
*/
|
|
23
|
-
export const SimpleCharacter = forwardRef(({ children, useViverseAvatar = true, input, movement, model, physics, cameraBehavior, animation, inputOptions, ...groupProps }, ref) => {
|
|
24
|
-
const avatar = useViverseActiveAvatar();
|
|
25
|
-
const world = useContext(BvhPhyiscsWorldContext);
|
|
26
|
-
if (world == null) {
|
|
27
|
-
throw new Error('SimpleCharacter must be used within a BvhPhysicsWorld component');
|
|
28
|
-
}
|
|
29
|
-
const domElement = useThree((s) => s.gl.domElement);
|
|
30
|
-
const newOptions = {
|
|
31
|
-
inputOptions,
|
|
32
|
-
movement,
|
|
33
|
-
physics,
|
|
34
|
-
cameraBehavior,
|
|
35
|
-
animation,
|
|
36
|
-
model: model != false && avatar != null && useViverseAvatar
|
|
37
|
-
? {
|
|
38
|
-
type: avatar.vrmUrl != null ? 'vrm' : undefined,
|
|
39
|
-
url: avatar?.vrmUrl,
|
|
40
|
-
...(model === true ? undefined : model),
|
|
41
|
-
}
|
|
42
|
-
: model,
|
|
43
|
-
};
|
|
44
|
-
const preloadSimpleCharacterAssetsKeys = [
|
|
45
|
-
JSON.stringify(newOptions.model),
|
|
46
|
-
...simpleCharacterAnimationNames.map((name) => JSON.stringify(newOptions.animation?.[name])),
|
|
47
|
-
];
|
|
48
|
-
suspend(async () => {
|
|
49
|
-
const result = await preloadSimpleCharacterAssets(newOptions);
|
|
50
|
-
result.model?.scene.addEventListener('dispose', () => clear([PreloadSimpleCharacterAssetsSymbol, ...preloadSimpleCharacterAssetsKeys]));
|
|
51
|
-
return result;
|
|
52
|
-
}, [PreloadSimpleCharacterAssetsSymbol, ...preloadSimpleCharacterAssetsKeys]);
|
|
53
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
54
|
-
const currentOptions = useMemo(() => ({}), preloadSimpleCharacterAssetsKeys);
|
|
55
|
-
Object.assign(currentOptions, newOptions);
|
|
56
|
-
const internalRef = useRef(null);
|
|
57
|
-
const store = useMemo(() => create(() => ({ model: undefined })), []);
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
if (internalRef.current == null) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
internalRef.current.inputSystem.dispose();
|
|
63
|
-
if (input == null || 'length' in input) {
|
|
64
|
-
internalRef.current.inputSystem = new InputSystem(domElement, input ?? [ScreenJoystickInput, ScreenJumpButtonInput, PointerCaptureInput, LocomotionKeyboardInput], extractProxy(currentOptions, 'inputOptions'));
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
internalRef.current.inputSystem = input;
|
|
68
|
-
},
|
|
69
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
70
|
-
Array.isArray(input) ? [...input, domElement] : [input, domElement]);
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
const simpleCharacter = internalRef.current;
|
|
73
|
-
if (simpleCharacter == null) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
simpleCharacter.addEventListener('loaded', () => {
|
|
77
|
-
store.setState({ model: simpleCharacter.model });
|
|
78
|
-
});
|
|
79
|
-
if (simpleCharacter.model != null) {
|
|
80
|
-
store.setState({ model: simpleCharacter.model });
|
|
81
|
-
}
|
|
82
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
83
|
-
}, [world, domElement, currentOptions]);
|
|
84
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
85
|
-
useImperativeHandle(ref, () => internalRef.current, [world, domElement, currentOptions]);
|
|
86
|
-
useFrame((_, delta) => internalRef.current?.update(delta));
|
|
87
|
-
const r3fStore = useStore();
|
|
88
|
-
const getCamera = useCallback(() => r3fStore.getState().camera, [r3fStore]);
|
|
89
|
-
return (_jsx("simpleCharacterImpl", { ...groupProps, args: [getCamera, world, domElement, currentOptions], ref: internalRef, children: _jsx(CharacterModelStoreContext.Provider, { value: store, children: children }) }));
|
|
90
|
-
});
|
|
91
|
-
/**
|
|
92
|
-
* @deprecated use <BvhPhysicsBody kinematic={false} /> instead (kinematic={false} can be skipped as its the default)
|
|
93
|
-
*/
|
|
94
|
-
export const FixedBvhPhysicsBody = forwardRef(({ children }, ref) => {
|
|
95
|
-
return _jsxs(BvhPhysicsBody, { children: [" ", children] });
|
|
96
|
-
});
|
|
97
|
-
/**
|
|
98
|
-
* allows to add all children as static (non-moving) objects as sensors to the bvh physics world
|
|
99
|
-
* @requires that the structure of the inner content is not changing or has a suspense boundary
|
|
100
|
-
* do not wrap the content inside in a suspense!
|
|
101
|
-
*/
|
|
102
|
-
export const BvhPhysicsSensor = forwardRef(({ children, isStatic = true, onIntersectedChanged }, ref) => {
|
|
103
|
-
const world = useContext(BvhPhyiscsWorldContext);
|
|
104
|
-
if (world == null) {
|
|
105
|
-
throw new Error('BvhPhysicsSensor must be used within a BvhPhysicsWorld component');
|
|
106
|
-
}
|
|
107
|
-
const internalRef = useRef(null);
|
|
108
|
-
const listenerRef = useRef(onIntersectedChanged);
|
|
109
|
-
listenerRef.current = onIntersectedChanged;
|
|
110
|
-
useEffect(() => {
|
|
111
|
-
const body = internalRef.current;
|
|
112
|
-
if (body == null) {
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
world.addSensor(body, isStatic, (intersected) => listenerRef.current?.(intersected));
|
|
116
|
-
return () => world.removeSensor(body);
|
|
117
|
-
}, [world, isStatic]);
|
|
118
|
-
useImperativeHandle(ref, () => internalRef.current, []);
|
|
119
|
-
return _jsx("group", { ref: internalRef, children: children });
|
|
120
|
-
});
|
|
121
|
-
/**
|
|
122
|
-
* allows to add all children as static (non-moving) or kinematic (moving) objects as obstacles to the bvh physics world
|
|
123
|
-
* @requires that the structure of the inner content is not changing or has a suspense boundary
|
|
124
|
-
* do not wrap the content inside in a suspense!
|
|
125
|
-
*/
|
|
126
|
-
export const BvhPhysicsBody = forwardRef(({ children, kinematic = false }, ref) => {
|
|
127
|
-
const world = useContext(BvhPhyiscsWorldContext);
|
|
128
|
-
if (world == null) {
|
|
129
|
-
throw new Error('FixedPhysicsBody must be used within a BvhPhysicsWorld component');
|
|
130
|
-
}
|
|
131
|
-
const internalRef = useRef(null);
|
|
132
|
-
useEffect(() => {
|
|
133
|
-
const body = internalRef.current;
|
|
134
|
-
if (body == null) {
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
world.addBody(body, kinematic);
|
|
138
|
-
return () => world.removeBody(body);
|
|
139
|
-
}, [world, kinematic]);
|
|
140
|
-
useImperativeHandle(ref, () => internalRef.current, []);
|
|
141
|
-
return _jsx("group", { ref: internalRef, children: children });
|
|
142
|
-
});
|
|
143
|
-
export function CharacterModelBone({ bone, children }) {
|
|
144
|
-
const [state, setState] = useState(undefined);
|
|
145
|
-
const store = useContext(CharacterModelStoreContext);
|
|
146
|
-
useEffect(() => {
|
|
147
|
-
if (store == null) {
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
const updateContainer = ({ model }) => {
|
|
151
|
-
if (model == null) {
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
const boneObject = model instanceof VRM ? model.humanoid.getRawBoneNode(bone) : model.scene.getObjectByName(bone);
|
|
155
|
-
if (boneObject == null) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
setState({ boneObject: boneObject, boneRotationOffset: model.boneRotationOffset });
|
|
159
|
-
};
|
|
160
|
-
updateContainer(store.getState());
|
|
161
|
-
return store.subscribe(updateContainer);
|
|
162
|
-
}, [bone, store]);
|
|
163
|
-
if (state == null) {
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
return (_jsx(Fragment, { children: createPortal(_jsx("group", { quaternion: state.boneRotationOffset, children: children }), state.boneObject) }, state.boneObject.id));
|
|
167
|
-
}
|