1
- import { BufferGeometry , Euler , Group , Quaternion , Vector3 } from "three" ;
1
+ import {
2
+ BufferGeometry ,
3
+ Euler ,
4
+ Group ,
5
+ Matrix4 ,
6
+ Quaternion ,
7
+ Vector3 ,
8
+ } from "three" ;
2
9
import { useMemo , useRef , useState } from "react" ;
3
- import { useLimiter } from "../../../../logic/limiter" ;
4
- import { useFrame } from "@react-three/fiber" ;
10
+ import { useLimitedFrame } from "../../../../logic/limiter" ;
5
11
import { useTrimeshCollision } from "../utils/trimesh" ;
6
12
7
13
type TrimeshColliderProps = {
@@ -13,33 +19,51 @@ export default function TrimeshCollider(props: TrimeshColliderProps) {
13
19
14
20
const group = useRef < Group > ( null ) ;
15
21
16
- const dummyPos = useMemo ( ( ) => new Vector3 ( ) , [ ] ) ;
17
- const dummyQuat = useMemo ( ( ) => new Quaternion ( ) , [ ] ) ;
18
- const dummyEuler = useMemo ( ( ) => new Euler ( ) , [ ] ) ;
19
- const dummyScale = useMemo ( ( ) => new Vector3 ( 1 , 1 , 1 ) , [ ] ) ;
20
- const [ scale , setScale ] = useState ( new Vector3 ( 1 , 1 , 1 ) ) ;
22
+ const [ pos ] = useState ( ( ) => new Vector3 ( ) ) ;
23
+ const [ quat ] = useState ( ( ) => new Quaternion ( ) ) ;
24
+ const [ scale ] = useState ( ( ) => new Vector3 ( ) ) ;
25
+ const [ euler ] = useState ( ( ) => new Euler ( ) ) ;
26
+ const [ curScale , setCurScale ] = useState ( new Vector3 ( 1 , 1 , 1 ) ) ;
21
27
22
28
const geometry = useMemo ( ( ) => {
23
- const g = geo . clone ( ) . scale ( scale . x , scale . y , scale . z ) ;
29
+ const g = geo . clone ( ) . scale ( curScale . x , curScale . y , curScale . z ) ;
24
30
g . computeVertexNormals ( ) ;
25
31
return g ;
26
- } , [ geo , scale ] ) ;
32
+ } , [ geo , curScale ] ) ;
27
33
28
34
const [ , api ] = useTrimeshCollision ( geometry ) ;
29
35
30
- const limiter = useLimiter ( 5 ) ;
31
- useFrame ( ( { clock } ) => {
32
- if ( ! limiter . isReady ( clock ) || ! group . current ) return ;
36
+ // there's some state update that causes the api not to receive an out of sync position
37
+ // i think it's whenever the api gets recreated. for now, just re-apply transforms every 2 seconds
38
+ const needsUpdate = useRef ( false ) ;
39
+ useLimitedFrame ( 1 / 2 , ( ) => {
40
+ needsUpdate . current = true ;
41
+ } ) ;
42
+
43
+ const lastUpdatedMatrix = useRef < Matrix4 > ( new Matrix4 ( ) ) ;
44
+ useLimitedFrame ( 8 , ( ) => {
45
+ if ( ! group . current ) return ;
33
46
34
- group . current . getWorldPosition ( dummyPos ) ;
35
- group . current . getWorldQuaternion ( dummyQuat ) ;
36
- group . current . getWorldScale ( dummyScale ) ;
47
+ // get global position, rotation, scale
48
+ group . current . updateWorldMatrix ( true , false ) ;
49
+ group . current . matrixWorld . decompose ( pos , quat , scale ) ;
37
50
38
- api . position . copy ( dummyPos ) ;
39
- api . rotation . copy ( dummyEuler . setFromQuaternion ( dummyQuat ) ) ;
40
- if ( ! dummyScale . equals ( scale ) ) {
41
- setScale ( dummyScale . clone ( ) ) ;
51
+ // no need to update if nothing changed
52
+ if (
53
+ lastUpdatedMatrix . current . equals ( group . current . matrixWorld ) &&
54
+ ! needsUpdate . current
55
+ ) {
56
+ return ;
42
57
}
58
+
59
+ // update last values
60
+ lastUpdatedMatrix . current . copy ( group . current . matrixWorld ) ;
61
+ needsUpdate . current = false ;
62
+
63
+ // if a change was found, update collider
64
+ api . position . copy ( pos ) ;
65
+ api . rotation . copy ( euler . setFromQuaternion ( quat ) ) ;
66
+ if ( ! scale . equals ( curScale ) ) setCurScale ( scale . clone ( ) ) ;
43
67
} ) ;
44
68
45
69
return < group ref = { group } /> ;
0 commit comments