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

Merged
merged 17 commits into from
Dec 12, 2024
Merged
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
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/bug.report.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Bug report \U0001F41B"
name: Bug report 🐛
description: Report an issue with Tres Rapier
labels: [pending triage]
body:
Expand Down Expand Up @@ -61,4 +61,4 @@ body:
- label: Check that there isn't [already an issue](https://github.com/Tresjs/rapier/issues) that reports the same bug to avoid creating a duplicate.
required: true
- label: The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.
required: true
required: true
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
blank_issues_enabled: false
blank_issues_enabled: false
12 changes: 6 additions & 6 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: "New feature proposal \U0001FA90"
description: Propose a new feature to be added to Tres Rapier
labels: ["enhancement"]
name: New feature proposal 🪐
description: Propose a new feature to be added to Tres Rapier
labels: [enhancement]
body:
- type: markdown
attributes:
Expand All @@ -10,15 +10,15 @@ body:
id: feature-description
attributes:
label: Description
description: "Clear and concise description of the problem. Please make the reason and usecases as detailed as possible. If you intend to submit a PR for this issue, tell us in the description. Thanks!"
description: 'Clear and concise description of the problem. Please make the reason and usecases as detailed as possible. If you intend to submit a PR for this issue, tell us in the description. Thanks!'
placeholder: As a developer using TresJS I want [goal / wish] so that [benefit].
validations:
required: true
- type: textarea
id: suggested-solution
attributes:
label: Suggested solution
description: "In module [xy] we could provide following implementation..."
description: 'In module [xy] we could provide following implementation...'
validations:
required: true
- type: textarea
Expand All @@ -44,4 +44,4 @@ body:
- label: Read the [docs](https://rapier.tresjs.org/guide).
required: true
- label: Check that there isn't [already an issue](https://github.com/tresjs/rapier/issues) that reports the same bug to avoid creating a duplicate.
required: true
required: true
2 changes: 1 addition & 1 deletion .github/workflows/lint-pr.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: 'Lint PR'
name: Lint PR

on:
pull_request_target:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Run Lint
Expand Down
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"
}
}
93 changes: 74 additions & 19 deletions docs/components/collider.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,93 @@ Similar to [how collisions work in rigid-bodies](./rigid-body.md#collisions) you

Be aware that the event will be emitted by the `RigidBody` parent

```html
...
<RigidBody @collision-enter="onCollisionEnter" @collision-exit="onCollisionExit">
```vue { 2,3}
<RigidBody
@collision-enter="onCollisionEnter"
@collision-exit="onCollisionExit"
>
<BallCollider activeCollision />
</RigidBody>
...
```

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

## 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:

- **@intersection-enter**: When another collider starts to traverse the *sensor*
- **@intersection-exit**: When another collider leave the *sensor*

::: info
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
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { tresLintConfig } from '@tresjs/eslint-config'

export default tresLintConfig({
ignores: ['dist', 'node_modules', 'public', '.github', 'docs/blog'],
ignores: ['**/node_modules/**', 'public', 'README.md', 'docs/blog'],
}, {
rules: {
'style/max-statements-per-line': 'off',
Expand Down
122 changes: 122 additions & 0 deletions playground/src/pages/basics/SensorDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<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 = (ev) => {
// eslint-disable-next-line no-console
console.log('Intersection 1 enter', ev)
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]" name="ball">
<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
Loading