Skip to content

Commit

Permalink
feat(app): 110 Applying methods to RigidBodies (#112)
Browse files Browse the repository at this point in the history
* feat(app): 110 Applying methods to RigidBodies

* docs: update rigidBody docs
  • Loading branch information
JaimeTorrealba authored Sep 12, 2024
1 parent 7deb26f commit 2f95276
Show file tree
Hide file tree
Showing 7 changed files with 290 additions and 115 deletions.
88 changes: 72 additions & 16 deletions docs/components/rigid-body.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ To use a `RigidBody` component, the best case is to import it from `@tresjs/rapi

## Types

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

A basic floor example with type fixed:
```html
Expand All @@ -35,34 +35,78 @@ A basic floor example with type fixed:
</RigidBody>
```

## Inner Colliders
### Available types

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.
| Prop | Description |
| :--------------- | :--------------------------------------------------- |
| `Dynamic` | Indicates that the body is affected by external forces and contacts. |
| `Fixed` | Indicates the body cannot move. It acts as if it has an infinite mass and will not be affected by any force. |
| `KinematicPositionBased` | Indicates that the body position must not be altered by the physics engine. |
| `KinematicVelocityBased` | Indicates that the body velocity must not be altered by the physics engine.|

:::info
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.
:::

More info at [Rigid-body type](https://rapier.rs/docs/user_guides/javascript/rigid_bodies#rigid-body-type)

## Automatic Colliders

`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.

A basic example, a ball falling down:
```html
```html{1}
<RigidBody collider="ball">
<TresMesh :position="[0,7, 0]">
<TresSphereGeometry />
<TresMeshNormalMaterial />
</TresMesh>
</RigidBody>
```

## InstanceMesh
</RigidBody>
You can use `RigidBody` with `TresInstancedMesh` too.

A basic example, with TresInstancedMesh:
```html
<RigidBody instanced collider="hull">
<TresInstancedMesh ref="torusInstancedMesh" :args="[torusKnots, torusKnotsMaterial, 3]" />
</RigidBody>
```
### Available Automatic Colliders

## Applying forces

SOON
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`

Basic example, making the cube jump with one click:

```vue
<script setup lang="ts">
import { TresCanvas } from '@tresjs/core'
import { Physics, RigidBody } from '@tresjs/rapier'
import { shallowRef } from 'vue'
const rigidCubeRef = shallowRef(null)
const jumpCube = () => {
if (rigidCubeRef.value) {
// if you mass is 1 your object will not move
rigidCubeRef.value.rigidBodyInfos.rigidBodyDesc.mass = 5
rigidCubeRef.value.instance.applyImpulse({ x: 0, y: 15, z: 0 }, true)
}
}
</script>
<template>
<TresCanvas window-size>
<TresPerspectiveCamera :position="[11, 11, 11]" :look-at="[0, 0, 0]" />
<Suspense>
<Physics debug>
<RigidBody ref="rigidCubeRef">
<TresMesh :position="[0, 5, 0]" @click="jumpCube">
<TresBoxGeometry />
<TresMeshNormalMaterial />
</TresMesh>
</RigidBody>
</Physics>
</Suspense>
</TresCanvas>
</template>
```

More info [Forces and Impulses](https://rapier.rs/docs/user_guides/javascript/rigid_bodies#forces-and-impulses)

## Collisions

Expand All @@ -71,3 +115,15 @@ SOON
## Events

SOON

## Props

## Expose object
```
{
instance: rigidBodyInstance,
rigidBodyInfos,
collider: colliderInfos,
group: parentObject,
}
```
Expand Down
63 changes: 63 additions & 0 deletions playground/src/pages/basics/ApplyingForcesDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<script setup lang="ts">
import { OrbitControls } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'
import { Physics, RigidBody } from '@tresjs/rapier'
import { ACESFilmicToneMapping, SRGBColorSpace } from 'three'
import { shallowRef } from 'vue'
const gl = {
clearColor: '#82DBC5',
shadows: true,
alpha: false,
outputColorSpace: SRGBColorSpace,
toneMapping: ACESFilmicToneMapping,
}
const rigidCubeRef = shallowRef(null)
const rigidSphereRef = shallowRef(null)
const jumpCube = () => {
if (rigidCubeRef.value) {
rigidCubeRef.value.rigidBodyInfos.rigidBodyDesc.mass = 5
rigidCubeRef.value.instance.applyImpulse({ x: 0, y: 15, z: 0 }, true)
}
}
const windSphere = () => {
if (rigidSphereRef.value) {
rigidSphereRef.value.rigidBodyInfos.rigidBodyDesc.mass = 5
rigidSphereRef.value.instance.applyImpulse({ x: 5, y: 0, z: 0 }, true)
}
}
</script>

<template>
<TresCanvas v-bind="gl" window-size>
<TresPerspectiveCamera :position="[11, 11, 11]" :look-at="[0, 0, 0]" />
<OrbitControls />

<Suspense>
<Physics debug>
<RigidBody ref="rigidCubeRef">
<TresMesh :position="[0, 5, 0]" @click="jumpCube">
<TresBoxGeometry />
<TresMeshNormalMaterial />
</TresMesh>
</RigidBody>

<RigidBody ref="rigidSphereRef" collider="ball">
<TresMesh :position="[Math.random() * 2, Math.random() * 2 + 8, Math.random() * 2]" @click="windSphere">
<TresSphereGeometry />
<TresMeshNormalMaterial />
</TresMesh>
</RigidBody>

<RigidBody type="fixed">
<TresMesh :position="[0, 0, 0]">
<TresPlaneGeometry :args="[20, 20, 20]" :rotate-x="-Math.PI / 2" />
<TresMeshBasicMaterial color="#f4f4f4" />
</TresMesh>
</RigidBody>
</Physics>
</Suspense>
</TresCanvas>
</template>
13 changes: 10 additions & 3 deletions playground/src/pages/basics/RigidBodyDemo.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { OrbitControls } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'
import { Physics, RigidBody } from '@tresjs/rapier'
import { InstanceRigidBody, Physics, RigidBody } from '@tresjs/rapier'
import { ACESFilmicToneMapping, DynamicDrawUsage, Matrix4, MeshNormalMaterial, SRGBColorSpace, TorusKnotGeometry } from 'three'
import { shallowRef, watch } from 'vue'
import type { InstancedMesh } from 'three'
Expand All @@ -14,6 +14,13 @@ const gl = {
toneMapping: ACESFilmicToneMapping,
}
const instanceRBRef = shallowRef<InstancedMesh>()
watch(instanceRBRef, (IRB) => {
// eslint-disable-next-line no-console
console.log('jaime ~ IRB:', IRB)
})
const torusInstancedMesh = shallowRef<InstancedMesh>()
const torusKnots = new TorusKnotGeometry()
Expand Down Expand Up @@ -45,9 +52,9 @@ watch(torusInstancedMesh, (mesh) => {

<Suspense>
<Physics debug>
<RigidBody instanced collider="hull">
<InstanceRigidBody ref="instanceRBRef" collider="hull">
<TresInstancedMesh ref="torusInstancedMesh" :args="[torusKnots, torusKnotsMaterial, 3]" />
</RigidBody>
</InstanceRigidBody>

<RigidBody>
<TresMesh :position="[0, 8, 0]">
Expand Down
5 changes: 5 additions & 0 deletions playground/src/router/routes/basics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ export const basicsRoutes = [
name: 'Rigid Body',
component: () => import('../../pages/basics/RigidBodyDemo.vue'),
},
{
path: '/basics/applying-forces',
name: 'Applying Forces',
component: () => import('../../pages/basics/ApplyingForcesDemo.vue'),
},
{
path: '/basics/gravity',
name: 'Gravity',
Expand Down
116 changes: 116 additions & 0 deletions src/components/InstanceRigidBody.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<script setup lang="ts">
import { type TresObject, useLoop } from '@tresjs/core'
import { InstancedMesh } from 'three'
import { shallowRef, watch } from 'vue'
import { useRapierContext } from '../composables/useRapier'
import { MATRIX_ZERO, QUATERNION_ZERO, VECTOR_ZERO } from '../constants/object'
import { createCollider } from '../utils/collider'
import { createRigidBody } from '../utils/rigid-body'
import type { CreateColliderReturnType } from '../types/collider'
import type { CreateRigidBodyReturnType, RigidBodyProps } from '../types/rigid-body'
const props = withDefaults(defineProps<Partial<RigidBodyProps>>(), {
type: 'dynamic',
collider: 'cuboid',
})
const parentObject = shallowRef<TresObject>()
const rigidBodyInfos = shallowRef<CreateRigidBodyReturnType[]>()
const colliderInfos = shallowRef<CreateColliderReturnType[]>()
const rigidBodyInstance = shallowRef()
defineExpose({
instance: rigidBodyInstance,
rigidBodyInfos,
collider: colliderInfos,
group: parentObject,
})
const { world } = await useRapierContext()
const { onBeforeRender } = useLoop()
watch(parentObject, (object?: TresObject) => {
if (!object) { return }
const child = object?.children[0]
if (
!(child instanceof InstancedMesh)
|| typeof parentObject.value?.children?.length !== 'number'
|| parentObject.value.children.length > 1
) {
throw new Error('Incorrect data assignment detected! #RigidBody support only one #InstancedMesh')
}
const instanceArray = child.instanceMatrix.array
const rigidBodies: CreateRigidBodyReturnType[] = []
const colliders: CreateColliderReturnType[] = []
for (let i = 0; i < child.count; i++) {
const matrix = MATRIX_ZERO.fromArray(instanceArray, i * 16)
const position = VECTOR_ZERO.clone()
const quaternion = QUATERNION_ZERO.clone()
const scale = VECTOR_ZERO.clone()
matrix.decompose(position, quaternion, scale)
const rigidBodyInfo = createRigidBody({
object: child,
rigidBodyType: props.type,
world,
})
rigidBodyInfo.rigidBody.setTranslation(position, true)
rigidBodyInfo.rigidBody.setRotation(quaternion, true)
const colliderInfo = createCollider({
object: child,
colliderShape: props.collider,
rigidBody: rigidBodyInfo.rigidBody,
world,
})
rigidBodies.push(rigidBodyInfo)
colliders.push(colliderInfo)
}
rigidBodyInfos.value = rigidBodies
colliderInfos.value = colliders
rigidBodyInstance.value = rigidBodyInfos.value.map(rigidBodyInfo => rigidBodyInfo.rigidBody)
})
onBeforeRender(() => {
if (!colliderInfos.value || !rigidBodyInfos.value) { return }
const child: InstancedMesh = parentObject.value?.children[0]
const array = child.instanceMatrix.array
for (let i = 0; i < child.count; i++) {
let position = VECTOR_ZERO.clone()
let quaternion = QUATERNION_ZERO.clone()
let scale = VECTOR_ZERO.clone()
child.getMatrixAt(i, MATRIX_ZERO)
MATRIX_ZERO.decompose(position, quaternion, scale)
position = position.copy(
rigidBodyInfos.value[i].rigidBody.translation(),
)
quaternion = quaternion.copy(
rigidBodyInfos.value[i].rigidBody.rotation(),
)
scale = scale.copy(scale)
MATRIX_ZERO
.compose(position, quaternion, scale)
.toArray(array, i * 16)
}
child.instanceMatrix.needsUpdate = true
child.computeBoundingSphere()
})
</script>

<template>
<TresGroup ref="parentObject">
<slot></slot>
</TresGroup>
</template>
Loading

0 comments on commit 2f95276

Please sign in to comment.