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: Joints implementation #150

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,8 @@
},
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[markdown]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
}
}
72 changes: 72 additions & 0 deletions docs/components/joint.md
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Neosoulink you need to add the entry on docs/.vitepress/config so it's available on the side navigation

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Joints

Joints is an extension feature provided in [Rapier#Joint](https://rapier.rs/docs/user_guides/javascript/joints/). It lets us connect two or more bodies, restricting their movements according to each other.

In **TresJs** we can achieve such motion restriction by using one of the available components designed to handle joints:

- [GenericJoint](../../src/components/joints/index.ts#L41)
- [PrismaticJoint](../../src/components/joints/index.ts#L51)
- [RevoluteJoint](../../src/components/joints/index.ts#L60)
- [RopeJoint](../../src/components/joints/index.ts#L69)
- [SphericalJoint](../../src/components/joints/index.ts#L78)
- [SpringJoint](../../src/components/joints/index.ts#L86)

All of them extends [BaseJoint](../../src/components/joints/index.ts#L96).

## How to use

Here's a basic `Joints` implementation in **TresJs**:

```vue
<script setup>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Neosoulink examples should be in Typescript as much as possible:

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it

import { RigidBody, SphericalJoint } from '@tresjs/rapier'
import { shallowRef } from 'vue'

const bodyRefA = shallowRef(null)
const bodyRefB = shallowRef(null)
</script>

<template>
<RigidBody
ref="bodyRefA"
type="kinematic"
:position="[0, 0, 0]"
collider="ball"
>
<TresMesh>
<TresSphereGeometry />
<TresMeshNormalMaterial />
</TresMesh>
</RigidBody>

<RigidBody
ref="bodyRefB"
:position="[-2.2, 0, 0]"
collider="ball"
>
<TresMesh>
<TresSphereGeometry />
<TresMeshNormalMaterial />
</TresMesh>
</RigidBody>

<SphericalJoint
:bodies="[bodyRefA?.instance, bodyRefB?.instance]"
:params="[
[0, -1.1, 0],
[0, 2, 0],
]"
/>
</template>
```

> Preview
<img width="1141" alt="JointsDemo" src="https://github.com/user-attachments/assets/d3cacac3-8764-4906-886a-d0b7a764b7c0" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Neosoulink Why not using the normal markdown image notation for this?

![JointsDemo](https://github.com/user-attachments/assets/d3cacac3-8764-4906-886a-d0b7a764b7c0)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was automatically added from github, but let make it clean. I'm going to update it


### Explanation

In the above example, we created 2 `RigidBody` references, then, we implemented the `SphericalJoint` component, by placing our 2 `RigidBody` references in the `:bodies` property and specifying parameters, we created a `spherical-joint` between them.
Copy link
Member

@alvarosabu alvarosabu Jan 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we created two RigidBody references


:::info
To understand how each Joint type works, please take a look at the official [Rapier Joint Documentation](https://rapier.rs/docs/user_guides/javascript/joints).
:::
3 changes: 1 addition & 2 deletions playground/auto-imports.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
Expand Down Expand Up @@ -66,6 +65,6 @@ declare global {
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
import('vue')
}
81 changes: 81 additions & 0 deletions playground/src/pages/basics/JointsAdvancedDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<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 { type ExposedRigidBody, Physics, RigidBody, SphericalJoint } from '@tresjs/rapier'
import { ACESFilmicToneMapping, Quaternion, SRGBColorSpace } from 'three'
import { onMounted, onUnmounted, shallowRef } from 'vue'
import type { ShallowRef } from 'vue'

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

const yRotation = shallowRef(0)
const bodyRefs = shallowRef<ShallowRef[]>(
Array.from({ length: 10 }).map(() => shallowRef<ExposedRigidBody>(null)),
)
const currentInterval = shallowRef<NodeJS.Timeout | undefined>(undefined)

onMounted(() => {
currentInterval.value = setInterval(() => {
const body = bodyRefs.value[0].value?.[0]?.instance
if (!body) { return }

yRotation.value = yRotation.value + 1

body.setNextKinematicRotation(new Quaternion(0, Math.sin(yRotation.value) * 1.5, 0, 1))
}, 1000)
})

onUnmounted(() =>
clearInterval(currentInterval.value),
)
</script>

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

<Suspense>
<Physics debug>
<RigidBody
v-for="(ref, i) in bodyRefs"
:key="i"
:ref="ref"
:type="i === 0 ? 'kinematic' : 'dynamic'"
:position="[i * 1.5, 0, 0]"
collider="ball"
>
<TresMesh>
<TresSphereGeometry />
<TresMeshNormalMaterial />
</TresMesh>
</RigidBody>

<SphericalJoint
v-for="(ref, i) in bodyRefs"
:key="i"
:bodies="[ref.value?.[0]?.instance, bodyRefs[i - 1]?.value?.[0]?.instance]"
:params="[
[-1.1, 0, 0],
[1.1, 0, 0],
]"
/>

<RigidBody type="fixed">
<TresMesh :position="[0, -10, 0]">
<TresPlaneGeometry :args="[40, 40, 20]" :rotate-x="-Math.PI / 2" />
<TresMeshBasicMaterial color="#f4f4f4" />
</TresMesh>
</RigidBody>
</Physics>
</Suspense>
</TresCanvas>
</template>
70 changes: 70 additions & 0 deletions playground/src/pages/basics/JointsDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<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 { type ExposedRigidBody, Physics, RigidBody, SphericalJoint } from '@tresjs/rapier'
import { ACESFilmicToneMapping, SRGBColorSpace } from 'three'
import { shallowRef } from 'vue'
import type { ShallowRef } from 'vue'

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

const bodyRefA: ShallowRef<ExposedRigidBody> = shallowRef(null)
const bodyRefB: ShallowRef<ExposedRigidBody> = shallowRef(null)
</script>

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

<Suspense>
<Physics debug>
<RigidBody
ref="bodyRefA"
type="kinematic"
:position="[0, 0, 0]"
collider="ball"
>
<TresMesh>
<TresSphereGeometry />
<TresMeshNormalMaterial />
</TresMesh>
</RigidBody>

<RigidBody
ref="bodyRefB"
:position="[-2.2, 0, 0]"
collider="ball"
>
<TresMesh>
<TresSphereGeometry />
<TresMeshNormalMaterial />
</TresMesh>
</RigidBody>

<SphericalJoint
:bodies="[bodyRefA?.instance, bodyRefB?.instance]"
:params="[
[0, -1.1, 0],
[0, 2, 0],
]"
/>

<RigidBody type="fixed">
<TresMesh :position="[0, -8, 0]">
<TresPlaneGeometry :args="[40, 40, 20]" :rotate-x="-Math.PI / 2" />
<TresMeshBasicMaterial color="#f4f4f4" />
</TresMesh>
</RigidBody>
</Physics>
</Suspense>
</TresCanvas>
</template>
10 changes: 10 additions & 0 deletions playground/src/router/routes/basics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,14 @@ export const basicsRoutes = [
name: 'Sensor',
component: () => import('../../pages/basics/SensorDemo.vue'),
},
{
path: '/basics/joints',
name: 'Joints',
component: () => import('../../pages/basics/JointsDemo.vue'),
},
{
path: '/basics/joints-advanced',
name: 'Advanced Joints',
component: () => import('../../pages/basics/JointsAdvancedDemo.vue'),
},
]
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './colliders'
export { default as InstancedRigidBody } from './InstancedRigidBody.vue'
export * from './joints'
export { default as Physics } from './Physics.vue'
export { default as RigidBody } from './RigidBody.vue'
Loading
Loading