Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: sensor support integration #141

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,8 @@
},
"[vue]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
}
}
92 changes: 73 additions & 19 deletions docs/components/collider.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,82 @@ Be aware that the event will be emitted by the `RigidBody` parent

## Props

| Prop | Description | Default |
| :-------------- | :------------------------------------------------------------------------------------------------------------ | --------- |
| **shape** | shape of the collider | `cuboid` |
| **args** | The half-sizes of the collider shapes | `[1,1,1]` |
| **object** | Required for certain shapes like `trimesh`, `hull`, `heightfield`. | |
| **friction** | The friction coefficient of this collider. (automatic-collider) | `0.5` |
| **mass** | Mass of the collider. (automatic-collider) | `1` |
| **density** | Restitution controls how elastic (aka. bouncy) a contact is. (automatic-collider) | `0` |
| **restitution** | The collider density. If non-zero the collider's mass and angular inertia will be added. (automatic-collider) | `1` |
| **activeCollision** | To set the collider receiver/emitter collision events | `false` |
| **activeCollisionTypes** | Type of the collision event. | `ActiveCollisionTypes.DEFAULT` |
| **collisionGroups** | To specify collision groups. | `undefined` |

:::info
You can access the [Collider](https://rapier.rs/docs/user_guides/javascript/colliders) instance
which offers full control over all the properties & methods available
by using [Template refs](https://vuejs.org/guide/essentials/template-refs.html#template-refs).
:::
| Prop | Description | Default |
| :----------------------- | :--------------------------- | :------- |
| **shape** | shape of the collider | `cuboid` |
| **args** | The half-sizes of the collider shapes | `[1,1,1]` |
| **object** | Required for certain shapes like `trimesh`, `hull`, `heightfield`. | |
| **friction** | The friction coefficient of this collider. (automatic-collider) | `0.5` |
| **mass** | Mass of the collider. (automatic-collider) | `1` |
| **density** | Restitution controls how elastic (aka. bouncy) a contact is. (automatic-collider) | `0` |
| **restitution** | The collider density. If non-zero the collider's mass and angular inertia will be added. (automatic-collider). | `1` |
| **activeCollision** | To set the collider receiver/emitter collision events | `false` |
| **activeCollisionTypes** | Type of the collision event. | `ActiveCollisionTypes.DEFAULT` |
| **collisionGroups** | To specify collision groups. | `undefined` |
| **sensor** | Set the collider as senor. More details [here](#sensor). | `undefined` |

## Expose object
## Events

The `Collider` component comes with a set of useful events allowing actions based on collisions or intersections (aka sensor).

### Sensor

The **Sensor** feature allows events to be triggered when there's an intersection or in other words, when the collider is traversed by another collider.

The traversed `Collider` (or the collider that will trigger events), is the sensor and should set the `activeCollision` and `sensor` properties to `true`.
By passing the above properties, the collider will no longer be affected by the physics law and will now start triggering the intersection events.

> ::: info
> **@intersection-enter**: When another collider starts to traverse the *sensor*
> **@intersection-exit**: When another collider leave the *sensor*
>
> ℹ️ Note that you can directly pass the events to the **`RigidBody`** for **auto-colliders**.
> :::

```vue
<RigidBody
type="fixed"
activeCollision
sensor
@intersection-enter="onIntersection2Enter"
@intersection-exit="onIntersectionExit"
>
<TresMesh :position="[0, 5, 0]">
<TresBoxGeometry :args="[10, 10, 0.5]" />
<TresMeshBasicMaterial color="#f4f4f4" />
</TresMesh>

<CuboidCollider
:args="[10, 3, 0.5]"
:position="[0, 3, 3]"
activeCollision
sensor
@intersection-enter="onIntersection1Enter"
@intersection-exit="onIntersectionExit"
/>

<CuboidCollider
:args="[10, 3, 0.5]"
:position="[0, 3, -3]"
activeCollision
sensor
@intersection-enter="onIntersection3Enter"
@intersection-exit="onIntersectionExit"
/>
</RigidBody>
```

<!-- TODO: Add the demo link -->

> ::: info
> You can access the [Collider](https://rapier.rs/docs/user_guides/javascript/colliders) instance
> which offers full control over all the properties & methods available
> by using [Template refs](https://vuejs.org/guide/essentials/template-refs.html#template-refs).
> :::

## Expose object

```md
{
instance,
colliderDesc,
Expand Down
120 changes: 120 additions & 0 deletions playground/src/pages/basics/SensorDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<script setup lang="ts">
import { OrbitControls } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'
// eslint-disable-next-line ts/ban-ts-comment
// @ts-ignore
import { CuboidCollider, Physics, RigidBody } from '@tresjs/rapier'
import { ACESFilmicToneMapping, MeshNormalMaterial, SRGBColorSpace } from 'three'
import { onMounted, shallowRef } from 'vue'
import type { Mesh } from 'three'

const gl = {
clearColor: '#82DBC5',
shadows: true,
alpha: false,
outputColorSpace: SRGBColorSpace,
toneMapping: ACESFilmicToneMapping,
}

const bodyContextRef = shallowRef()
const ballRef = shallowRef<Mesh>()

const onIntersection1Enter = () => {
if (ballRef.value?.material instanceof MeshNormalMaterial) {
ballRef.value.material.visible = false
}
}

const onIntersection2Enter = () => {
if (ballRef.value?.material instanceof MeshNormalMaterial) {
ballRef.value.material.colorWrite = false
}
}

const onIntersection3Enter = () => {
if (ballRef.value?.material instanceof MeshNormalMaterial) {
ballRef.value.material.wireframe = true
}
}

const onIntersectionExit = () => {
if (ballRef.value?.material instanceof MeshNormalMaterial) {
ballRef.value.material.visible = true
ballRef.value.material.colorWrite = true
ballRef.value.material.wireframe = false
}
}

const resetBall = () => {
bodyContextRef.value?.instance?.setAngvel({ x: -2, y: 0, z: 0 }, true)
bodyContextRef.value?.instance?.setLinvel({ x: 0, y: 0, z: -8 }, true)
bodyContextRef.value?.instance?.setRotation({ x: 0, y: 0, z: 0, w: 1 }, true)
bodyContextRef.value?.instance?.setTranslation({ x: 0, y: 0, z: 0 }, true)
}

onMounted(() => {
setTimeout(() => {
resetBall()

setInterval(() => {
resetBall()
}, 3000)
}, 100)
})
</script>

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

<Suspense>
<Physics debug>
<RigidBody ref="bodyContextRef" collider="ball">
<TresMesh ref="ballRef" :position="[0, 8, 8]">
<TresSphereGeometry />
<TresMeshNormalMaterial />
</TresMesh>
</RigidBody>

<RigidBody
type="fixed"
activeCollision
sensor
@intersection-enter="onIntersection2Enter"
@intersection-exit="onIntersectionExit"
>
<TresMesh :position="[0, 5, 0]">
<TresBoxGeometry :args="[10, 10, 0.5]" />
<TresMeshBasicMaterial color="#f4f4f4" />
</TresMesh>

<CuboidCollider
:args="[10, 3, 0.5]"
:position="[0, 3, 3]"
activeCollision
sensor
@intersection-enter="onIntersection1Enter"
@intersection-exit="onIntersectionExit"
/>

<CuboidCollider
:args="[10, 3, 0.5]"
:position="[0, 3, -3]"
activeCollision
sensor
@intersection-enter="onIntersection3Enter"
@intersection-exit="onIntersectionExit"
/>
</RigidBody>

<RigidBody type="fixed" name="fixedFloor" :restitution="0.2">
<TresMesh :position="[0, 0, 0]">
<TresPlaneGeometry :args="[20, 20, 1]" :rotate-x="-Math.PI / 2" />
<TresMeshBasicMaterial color="#f4f4f4" />
</TresMesh>
</RigidBody>
</Physics>
</Suspense>
</TresCanvas>
</template>
5 changes: 5 additions & 0 deletions playground/src/router/routes/basics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,9 @@ export const basicsRoutes = [
name: 'Collision',
component: () => import('../../pages/basics/CollisionDemo.vue'),
},
{
path: '/basics/sensor',
name: 'Sensor',
component: () => import('../../pages/basics/SensorDemo.vue'),
},
]
2 changes: 1 addition & 1 deletion src/components/Debug.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const { onBeforeRender } = useLoop()
const lineSegmentsRef = ref<LineSegments | null>(null)

onBeforeRender(() => {
if (!world || !lineSegmentsRef.value) { return }
if (!world || !lineSegmentsRef.value?.geometry?.boundingSphere) { return }

const buffers = world.debugRender()

Expand Down
32 changes: 23 additions & 9 deletions src/components/Physics.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ import { useLoop, useTresContext } from '@tresjs/core'
import { Vector3 } from 'three'
import { watch } from 'vue'
import type { VectorCoordinates } from '@tresjs/core'
import { useRapierContextProvider } from '../composables/useRapier'
import { GRAVITY } from '../constants/physics'
import { useRapierContextProvider } from '../composables'
import { GRAVITY } from '../constants'

import { collisionEmisor, get3DGroupFromSource, getSourceFromColliderHandle } from '../utils'
import {
collisionEmisor,
emitIntersection,
get3DGroupFromSource,
getSourceFromColliderHandle,
} from '../utils'
import Debug from './Debug.vue'
import type { PhysicsProps } from '../types'

Expand Down Expand Up @@ -57,13 +62,22 @@ onBeforeRender(() => {
const source2 = getSourceFromColliderHandle(world, handle2)
const group1 = get3DGroupFromSource(source1, scene)
const group2 = get3DGroupFromSource(source2, scene)
if (group1 && group2) {
collisionEmisor(
{ object: group1, context: source1 },
{ object: group2, context: source2 },
started,
)

if (!group1 || !group2) {
return
}

collisionEmisor(
{ object: group1, context: source1 },
{ object: group2, context: source2 },
started,
)

emitIntersection(
{ object: group2, context: source2 },
{ object: group1, context: source1 },
started && world.intersectionPair(source1.collider, source2.collider),
)
})
})
</script>
Expand Down
Loading