Skip to content

Commit 2f95276

Browse files
feat(app): 110 Applying methods to RigidBodies (#112)
* feat(app): 110 Applying methods to RigidBodies * docs: update rigidBody docs
1 parent 7deb26f commit 2f95276

File tree

7 files changed

+290
-115
lines changed

7 files changed

+290
-115
lines changed

docs/components/rigid-body.md

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ To use a `RigidBody` component, the best case is to import it from `@tresjs/rapi
2323

2424
## Types
2525

26-
We can specify what kind of `RigidBody` [type](https://rapier.rs/docs/user_guides/javascript/rigid_bodies#rigid-body-type). `Dynamic` is the default.
26+
We can specify what kind of `RigidBody` type. `Dynamic` is the default.
2727

2828
A basic floor example with type fixed:
2929
```html
@@ -35,34 +35,78 @@ A basic floor example with type fixed:
3535
</RigidBody>
3636
```
3737

38-
## Inner Colliders
38+
### Available types
3939

40-
In addition to the [Colliders components](/components/collider), you can specify a set of pre-defined colliders in order to fit the mesh with the best shape possible.
40+
| Prop | Description |
41+
| :--------------- | :--------------------------------------------------- |
42+
| `Dynamic` | Indicates that the body is affected by external forces and contacts. |
43+
| `Fixed` | Indicates the body cannot move. It acts as if it has an infinite mass and will not be affected by any force. |
44+
| `KinematicPositionBased` | Indicates that the body position must not be altered by the physics engine. |
45+
| `KinematicVelocityBased` | Indicates that the body velocity must not be altered by the physics engine.|
46+
47+
:::info
48+
Both position-based and velocity-based kinematic bodies are mostly the same. Choosing between both is mostly a matter of preference between position-based control and velocity-based control.
49+
:::
50+
51+
More info at [Rigid-body type](https://rapier.rs/docs/user_guides/javascript/rigid_bodies#rigid-body-type)
52+
53+
## Automatic Colliders
54+
55+
`RigidBody` comes with automatic colliders, if you need a custom Collider please check [Colliders components](/components/collider), you can specify a set of pre-defined colliders in order to fit the mesh with the best shape possible. `cuboid` is the default.
4156

4257
A basic example, a ball falling down:
43-
```html
58+
```html{1}
4459
<RigidBody collider="ball">
4560
<TresMesh :position="[0,7, 0]">
4661
<TresSphereGeometry />
4762
<TresMeshNormalMaterial />
4863
</TresMesh>
49-
</RigidBody>
50-
```
51-
52-
## InstanceMesh
64+
</RigidBody>
5365
54-
You can use `RigidBody` with `TresInstancedMesh` too.
55-
56-
A basic example, with TresInstancedMesh:
57-
```html
58-
<RigidBody instanced collider="hull">
59-
<TresInstancedMesh ref="torusInstancedMesh" :args="[torusKnots, torusKnotsMaterial, 3]" />
60-
</RigidBody>
6166
```
67+
### Available Automatic Colliders
6268

6369
## Applying forces
6470

65-
SOON
71+
To use methods (like applying forces or impulses) you first need to access the element using [template ref](https://vuejs.org/guide/essentials/template-refs.html#template-refs). Then access to the `instance`
72+
73+
Basic example, making the cube jump with one click:
74+
75+
```vue
76+
<script setup lang="ts">
77+
import { TresCanvas } from '@tresjs/core'
78+
import { Physics, RigidBody } from '@tresjs/rapier'
79+
import { shallowRef } from 'vue'
80+
81+
const rigidCubeRef = shallowRef(null)
82+
83+
const jumpCube = () => {
84+
if (rigidCubeRef.value) {
85+
// if you mass is 1 your object will not move
86+
rigidCubeRef.value.rigidBodyInfos.rigidBodyDesc.mass = 5
87+
rigidCubeRef.value.instance.applyImpulse({ x: 0, y: 15, z: 0 }, true)
88+
}
89+
}
90+
</script>
91+
92+
<template>
93+
<TresCanvas window-size>
94+
<TresPerspectiveCamera :position="[11, 11, 11]" :look-at="[0, 0, 0]" />
95+
<Suspense>
96+
<Physics debug>
97+
<RigidBody ref="rigidCubeRef">
98+
<TresMesh :position="[0, 5, 0]" @click="jumpCube">
99+
<TresBoxGeometry />
100+
<TresMeshNormalMaterial />
101+
</TresMesh>
102+
</RigidBody>
103+
</Physics>
104+
</Suspense>
105+
</TresCanvas>
106+
</template>
107+
```
108+
109+
More info [Forces and Impulses](https://rapier.rs/docs/user_guides/javascript/rigid_bodies#forces-and-impulses)
66110

67111
## Collisions
68112

@@ -71,3 +115,15 @@ SOON
71115
## Events
72116

73117
SOON
118+
119+
## Props
120+
121+
## Expose object
122+
```
123+
{
124+
instance: rigidBodyInstance,
125+
rigidBodyInfos,
126+
collider: colliderInfos,
127+
group: parentObject,
128+
}
129+
```
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<script setup lang="ts">
2+
import { OrbitControls } from '@tresjs/cientos'
3+
import { TresCanvas } from '@tresjs/core'
4+
import { Physics, RigidBody } from '@tresjs/rapier'
5+
import { ACESFilmicToneMapping, SRGBColorSpace } from 'three'
6+
import { shallowRef } from 'vue'
7+
8+
const gl = {
9+
clearColor: '#82DBC5',
10+
shadows: true,
11+
alpha: false,
12+
outputColorSpace: SRGBColorSpace,
13+
toneMapping: ACESFilmicToneMapping,
14+
}
15+
16+
const rigidCubeRef = shallowRef(null)
17+
const rigidSphereRef = shallowRef(null)
18+
19+
const jumpCube = () => {
20+
if (rigidCubeRef.value) {
21+
rigidCubeRef.value.rigidBodyInfos.rigidBodyDesc.mass = 5
22+
rigidCubeRef.value.instance.applyImpulse({ x: 0, y: 15, z: 0 }, true)
23+
}
24+
}
25+
const windSphere = () => {
26+
if (rigidSphereRef.value) {
27+
rigidSphereRef.value.rigidBodyInfos.rigidBodyDesc.mass = 5
28+
rigidSphereRef.value.instance.applyImpulse({ x: 5, y: 0, z: 0 }, true)
29+
}
30+
}
31+
</script>
32+
33+
<template>
34+
<TresCanvas v-bind="gl" window-size>
35+
<TresPerspectiveCamera :position="[11, 11, 11]" :look-at="[0, 0, 0]" />
36+
<OrbitControls />
37+
38+
<Suspense>
39+
<Physics debug>
40+
<RigidBody ref="rigidCubeRef">
41+
<TresMesh :position="[0, 5, 0]" @click="jumpCube">
42+
<TresBoxGeometry />
43+
<TresMeshNormalMaterial />
44+
</TresMesh>
45+
</RigidBody>
46+
47+
<RigidBody ref="rigidSphereRef" collider="ball">
48+
<TresMesh :position="[Math.random() * 2, Math.random() * 2 + 8, Math.random() * 2]" @click="windSphere">
49+
<TresSphereGeometry />
50+
<TresMeshNormalMaterial />
51+
</TresMesh>
52+
</RigidBody>
53+
54+
<RigidBody type="fixed">
55+
<TresMesh :position="[0, 0, 0]">
56+
<TresPlaneGeometry :args="[20, 20, 20]" :rotate-x="-Math.PI / 2" />
57+
<TresMeshBasicMaterial color="#f4f4f4" />
58+
</TresMesh>
59+
</RigidBody>
60+
</Physics>
61+
</Suspense>
62+
</TresCanvas>
63+
</template>

playground/src/pages/basics/RigidBodyDemo.vue

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup lang="ts">
22
import { OrbitControls } from '@tresjs/cientos'
33
import { TresCanvas } from '@tresjs/core'
4-
import { Physics, RigidBody } from '@tresjs/rapier'
4+
import { InstanceRigidBody, Physics, RigidBody } from '@tresjs/rapier'
55
import { ACESFilmicToneMapping, DynamicDrawUsage, Matrix4, MeshNormalMaterial, SRGBColorSpace, TorusKnotGeometry } from 'three'
66
import { shallowRef, watch } from 'vue'
77
import type { InstancedMesh } from 'three'
@@ -14,6 +14,13 @@ const gl = {
1414
toneMapping: ACESFilmicToneMapping,
1515
}
1616
17+
const instanceRBRef = shallowRef<InstancedMesh>()
18+
19+
watch(instanceRBRef, (IRB) => {
20+
// eslint-disable-next-line no-console
21+
console.log('jaime ~ IRB:', IRB)
22+
})
23+
1724
const torusInstancedMesh = shallowRef<InstancedMesh>()
1825
1926
const torusKnots = new TorusKnotGeometry()
@@ -45,9 +52,9 @@ watch(torusInstancedMesh, (mesh) => {
4552

4653
<Suspense>
4754
<Physics debug>
48-
<RigidBody instanced collider="hull">
55+
<InstanceRigidBody ref="instanceRBRef" collider="hull">
4956
<TresInstancedMesh ref="torusInstancedMesh" :args="[torusKnots, torusKnotsMaterial, 3]" />
50-
</RigidBody>
57+
</InstanceRigidBody>
5158

5259
<RigidBody>
5360
<TresMesh :position="[0, 8, 0]">

playground/src/router/routes/basics.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ export const basicsRoutes = [
44
name: 'Rigid Body',
55
component: () => import('../../pages/basics/RigidBodyDemo.vue'),
66
},
7+
{
8+
path: '/basics/applying-forces',
9+
name: 'Applying Forces',
10+
component: () => import('../../pages/basics/ApplyingForcesDemo.vue'),
11+
},
712
{
813
path: '/basics/gravity',
914
name: 'Gravity',

src/components/InstanceRigidBody.vue

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<script setup lang="ts">
2+
import { type TresObject, useLoop } from '@tresjs/core'
3+
import { InstancedMesh } from 'three'
4+
5+
import { shallowRef, watch } from 'vue'
6+
import { useRapierContext } from '../composables/useRapier'
7+
import { MATRIX_ZERO, QUATERNION_ZERO, VECTOR_ZERO } from '../constants/object'
8+
import { createCollider } from '../utils/collider'
9+
import { createRigidBody } from '../utils/rigid-body'
10+
import type { CreateColliderReturnType } from '../types/collider'
11+
import type { CreateRigidBodyReturnType, RigidBodyProps } from '../types/rigid-body'
12+
13+
const props = withDefaults(defineProps<Partial<RigidBodyProps>>(), {
14+
type: 'dynamic',
15+
collider: 'cuboid',
16+
})
17+
18+
const parentObject = shallowRef<TresObject>()
19+
const rigidBodyInfos = shallowRef<CreateRigidBodyReturnType[]>()
20+
const colliderInfos = shallowRef<CreateColliderReturnType[]>()
21+
const rigidBodyInstance = shallowRef()
22+
23+
defineExpose({
24+
instance: rigidBodyInstance,
25+
rigidBodyInfos,
26+
collider: colliderInfos,
27+
group: parentObject,
28+
})
29+
30+
const { world } = await useRapierContext()
31+
const { onBeforeRender } = useLoop()
32+
33+
watch(parentObject, (object?: TresObject) => {
34+
if (!object) { return }
35+
36+
const child = object?.children[0]
37+
if (
38+
!(child instanceof InstancedMesh)
39+
|| typeof parentObject.value?.children?.length !== 'number'
40+
|| parentObject.value.children.length > 1
41+
) {
42+
throw new Error('Incorrect data assignment detected! #RigidBody support only one #InstancedMesh')
43+
}
44+
45+
const instanceArray = child.instanceMatrix.array
46+
const rigidBodies: CreateRigidBodyReturnType[] = []
47+
const colliders: CreateColliderReturnType[] = []
48+
49+
for (let i = 0; i < child.count; i++) {
50+
const matrix = MATRIX_ZERO.fromArray(instanceArray, i * 16)
51+
const position = VECTOR_ZERO.clone()
52+
const quaternion = QUATERNION_ZERO.clone()
53+
const scale = VECTOR_ZERO.clone()
54+
matrix.decompose(position, quaternion, scale)
55+
56+
const rigidBodyInfo = createRigidBody({
57+
object: child,
58+
rigidBodyType: props.type,
59+
world,
60+
})
61+
rigidBodyInfo.rigidBody.setTranslation(position, true)
62+
rigidBodyInfo.rigidBody.setRotation(quaternion, true)
63+
64+
const colliderInfo = createCollider({
65+
object: child,
66+
colliderShape: props.collider,
67+
rigidBody: rigidBodyInfo.rigidBody,
68+
world,
69+
})
70+
71+
rigidBodies.push(rigidBodyInfo)
72+
colliders.push(colliderInfo)
73+
}
74+
75+
rigidBodyInfos.value = rigidBodies
76+
colliderInfos.value = colliders
77+
rigidBodyInstance.value = rigidBodyInfos.value.map(rigidBodyInfo => rigidBodyInfo.rigidBody)
78+
})
79+
80+
onBeforeRender(() => {
81+
if (!colliderInfos.value || !rigidBodyInfos.value) { return }
82+
83+
const child: InstancedMesh = parentObject.value?.children[0]
84+
const array = child.instanceMatrix.array
85+
86+
for (let i = 0; i < child.count; i++) {
87+
let position = VECTOR_ZERO.clone()
88+
let quaternion = QUATERNION_ZERO.clone()
89+
let scale = VECTOR_ZERO.clone()
90+
91+
child.getMatrixAt(i, MATRIX_ZERO)
92+
MATRIX_ZERO.decompose(position, quaternion, scale)
93+
94+
position = position.copy(
95+
rigidBodyInfos.value[i].rigidBody.translation(),
96+
)
97+
quaternion = quaternion.copy(
98+
rigidBodyInfos.value[i].rigidBody.rotation(),
99+
)
100+
scale = scale.copy(scale)
101+
102+
MATRIX_ZERO
103+
.compose(position, quaternion, scale)
104+
.toArray(array, i * 16)
105+
}
106+
107+
child.instanceMatrix.needsUpdate = true
108+
child.computeBoundingSphere()
109+
})
110+
</script>
111+
112+
<template>
113+
<TresGroup ref="parentObject">
114+
<slot></slot>
115+
</TresGroup>
116+
</template>

0 commit comments

Comments
 (0)