Skip to content

Commit

Permalink
feat: delegate responsibilities to sub-utils
Browse files Browse the repository at this point in the history
### Description

- Add `createRigiBody` util
  - Add `CreateRigidBodyProps` type
- `createRigidBodyDesc` need an object now
  - Add `CreateRigidBodyDescProps` type
- `createColliderDesc ` need an object now
  - Add `CreateColliderDescProps ` type
- Add `createCollider` util
  - Add `CreateColliderProps` type
- Add `createColliderFromChildren` util
  - Add `CreateColliderFromChildrenProps` type
- Map received children colliders and update their states
  • Loading branch information
Neosoulink committed Jun 18, 2024
1 parent 9441602 commit 8412712
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 76 deletions.
7 changes: 6 additions & 1 deletion playground/src/pages/basics/RigidBodyDemo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ const gl = {
<Suspense>
<Physics debug>
<RigidBody>
<TresMesh :position="[0, 4, 0]">
<TresMesh :position="[0, 8, 0]">
<TresTorusGeometry />
<TresMeshNormalMaterial />
</TresMesh>

<TresMesh :position="[0, 5, 0]">
<TresBoxGeometry />
<TresMeshNormalMaterial />
</TresMesh>
Expand Down
53 changes: 28 additions & 25 deletions src/components/RigidBody.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
<script setup lang="ts">
import { shallowRef, watch } from 'vue'
import type { Collider, RigidBody } from '@dimforge/rapier3d-compat'
import { type TresObject, useLoop } from '@tresjs/core'
import { useRapierContext } from '../composables/useRapier'
import { createRigidBodyDesc } from '../utils/rigid-body.util'
import { createColliderDesc } from '../utils/collider.util'
import { createRigiBody } from '../utils/rigid-body.util'
import { createColliderFromChildren } from '../utils/collider.util'
import type { RigidBodyProps } from '../types/rigid-body.type'
const props = withDefaults(defineProps<Partial<RigidBodyProps>>(), {
Expand All @@ -16,37 +15,41 @@ const props = withDefaults(defineProps<Partial<RigidBodyProps>>(), {
const { world } = await useRapierContext()
const { onBeforeRender } = useLoop()
const rigidRef = shallowRef<TresObject>()
const rigidBody = shallowRef<RigidBody>()
const collider = shallowRef<Collider>()
watch(rigidRef, (value) => {
if (!value) { return }
const rigidBodyDesc = createRigidBodyDesc(value.children[0], props.type)
const colliderDesc = createColliderDesc(value.children[0], props.collider)
if (!rigidBodyDesc || !colliderDesc) {
throw new Error('Invalid #RigidBody properties detected. Unable to construct the physics body')
}
rigidBody.value = world.createRigidBody(rigidBodyDesc)
collider.value = world.createCollider(colliderDesc, rigidBody.value)
const rigidObject = shallowRef<TresObject>()
const rigidBodyInfo = shallowRef<ReturnType<typeof createRigiBody>>()
const rigidColliders = shallowRef<ReturnType<typeof createColliderFromChildren>>()
watch(rigidObject, (object) => {
if (!object) { return }
rigidBodyInfo.value = createRigiBody({
object,
rigidBodyType: props.type,
world,
})
rigidColliders.value = createColliderFromChildren({
colliderShape: props.collider,
rigidBody: rigidBodyInfo.value.rigidBody,
object,
world,
})
})
onBeforeRender(() => {
if (!rigidBody.value) { return }
if (!rigidColliders.value) { return }
const position = rigidBody.value.translation()
rigidRef.value?.children[0].position.set(position.x, position.y, position.z)
rigidColliders.value.forEach((item) => {
const position = item.collider.translation()
item.child.position.set(position.x, position.y, position.z)
const rotation = rigidBody.value.rotation()
rigidRef.value?.children[0].quaternion.set(rotation.x, rotation.y, rotation.z, rotation.w)
const rotation = item.collider.rotation()
item.child.quaternion.set(rotation.x, rotation.y, rotation.z, rotation.w)
})
})
</script>

<template>
<TresGroup ref="rigidRef">
<TresGroup ref="rigidObject">
<slot></slot>
</TresGroup>
</template>
24 changes: 24 additions & 0 deletions src/types/collider.type.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import type { RigidBody, World } from '@dimforge/rapier3d-compat'
import type { TresObject } from '@tresjs/core'

/** @description Tres Rapier supported Collider shapes. */
export type ColliderShape =
| 'cuboid'
| 'ball'
Expand All @@ -6,3 +10,23 @@ export type ColliderShape =
| 'cylinder'
| 'trimesh'
| 'heightfield'

export interface CreateColliderDescProps {
/** @description The parent object. (@link TresObject}. */
object: TresObject
/** @description The `Collider` shape. {@link ColliderShape}. */
colliderShape?: ColliderShape
}

export interface CreateColliderProps extends CreateColliderDescProps {
/** @description The Collider shape. {@link ColliderShape}. */
rigidBody: RigidBody
/**
* @description The Rapier {@link World} context.
*
* @see https://rapier.rs/javascript3d/classes/World.html
*/
world: World
}

export interface CreateColliderFromChildrenProps extends CreateColliderProps {}
22 changes: 22 additions & 0 deletions src/types/rigid-body.type.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,34 @@
import type { TresObject } from '@tresjs/core'
import type { World } from '@dimforge/rapier3d-compat'

import type { ColliderShape } from './collider.type'

/** @description Tres Rapier supported `RigidBody` types. */
export type RigidBodyType =
| 'dynamic'
| 'kinematic'
| 'kinematicVelocity'
| 'fixed'

export interface RigidBodyProps {
/** @description Set the `RigidBody` type. */
type: RigidBodyType
/** @description Set the `RigidBody` collider shape. */
collider: ColliderShape
}

export interface CreateRigidBodyDescProps {
/** @description The parent object. (@link TresObject}. */
object: TresObject
/** @description The `rigidBody` type. {@link RigidBodyType}. */
rigidBodyType: RigidBodyType
}

export interface CreateRigidBodyProps extends CreateRigidBodyDescProps {
/**
* @description The Rapier {@link World} context.
*
* @see https://rapier.rs/javascript3d/classes/World.html
*/
world: World
}
128 changes: 87 additions & 41 deletions src/utils/collider.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,64 +2,110 @@ import { ColliderDesc } from '@dimforge/rapier3d-compat'
import { mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
import type { TresObject } from '@tresjs/core'

import type { ColliderShape } from '../types/collider.type'
import { VECTOR_ZERO } from '../constants/object.constant'
import type {
CreateColliderFromChildrenProps,
CreateColliderProps,
} from '../types/collider.type'
import type { CreateColliderDescProps } from './../types/collider.type'

/**
* @description Create a {@link ColliderDesc} shape based on the passed
* {@link TresObject} and {@link ColliderShape}
* @description Create a {@link ColliderDesc} shape based on the given
* {@link CreateColliderDescProps}
*
* @param object `TresObject` based on.
* @param props {@link CreateColliderDescProps}
*
* @param colliderShape The shape of the `ColliderDesc`
*
* @internal
* @see https://rapier.rs/javascript3d/classes/ColliderDesc.html
* @see https://rapier.rs/docs/user_guides/javascript/colliders
*/
export const createColliderDesc = (
object: TresObject,
colliderShape: ColliderShape,
export const createColliderDesc = (props: CreateColliderDescProps,
) => {
if (!object) {
return
}
if (!props.object.geometry) { return }

// Create a cuboid collider attached to the dynamic rigidBody.
object.geometry.computeBoundingBox()
// NOTE: Create a default `Cuboid` collider attached to the dynamic rigidBody.
props.object.geometry.computeBoundingBox()

const { boundingBox } = object.geometry
const { boundingBox } = props.object.geometry
const size = boundingBox!.getSize(VECTOR_ZERO)

let colliderDesc = ColliderDesc.cuboid(size.x / 2, size.y / 2, size.z / 2)

if (colliderShape === 'ball') {
colliderDesc = ColliderDesc.ball(size.x / 2)
}
else if (colliderShape === 'capsule') {
colliderDesc = ColliderDesc.capsule(size.x / 2, size.y / 2)
}
else if (colliderShape === 'cone') {
colliderDesc = ColliderDesc.cone(size.x / 2, size.y / 2)
}
else if (colliderShape === 'cylinder') {
colliderDesc = ColliderDesc.cylinder(size.x / 2, size.y / 2)
}
else if (colliderShape === 'trimesh') {
const clonedGeometry = mergeVertices(object.geometry)
const triMeshMap = clonedGeometry.attributes.position.array as Float32Array
const triMeshUnit = clonedGeometry.index?.array as Uint32Array
switch (props.colliderShape) {
case 'ball':
colliderDesc = ColliderDesc.ball(size.x / 2)
break
case 'capsule':
colliderDesc = ColliderDesc.capsule(size.x / 2, size.y / 2)
break
case 'cone':
colliderDesc = ColliderDesc.cone(size.x / 2, size.y / 2)
break
case 'cylinder':
colliderDesc = ColliderDesc.cylinder(size.x / 2, size.y / 2)
break
case 'trimesh': {
const clonedGeometry = mergeVertices(props.object.geometry)
const triMeshMap = clonedGeometry.attributes.position
.array as Float32Array
const triMeshUnit = clonedGeometry.index?.array as Uint32Array

colliderDesc = ColliderDesc.trimesh(triMeshMap, triMeshUnit)
colliderDesc = ColliderDesc.trimesh(triMeshMap, triMeshUnit)
break
}
}

// TODO: Unable to retrieve the subdivision number & the Matrix of the given object for heightfield
// else if (colliderShape === 'heightfield') {
// TODO: Unable to retrieve the subdivision number & the Matrix of the given object for #heightfield
// if (colliderShape === 'heightfield') {
// colliderDesc = ColliderDesc.heightfield(object.geometry)
// }
/* else if (colliderShape === 'trimesh') {
colliderDesc = ColliderDesc.trimesh(object.geometry)
}
else if (colliderShape === 'heightfield') {
colliderDesc = ColliderDesc.heightfield(object.geometry)
} */

colliderDesc
.setTranslation(props.object.position.x, props.object.position.y, props.object.position.z)
.setRotation(props.object.quaternion)

return colliderDesc
}

/**
* @description Create a {@link Collider} shape based on the given
* {@link CreateColliderProps}
*
* @param props {@link CreateColliderDescProps}
*
* @see https://rapier.rs/javascript3d/classes/Collider.html
* @see https://rapier.rs/docs/user_guides/javascript/colliders
*/
export const createCollider = (props: CreateColliderProps) => {
const { object, rigidBody, world } = props
const colliderDesc = createColliderDesc(props)

if (!colliderDesc) {
throw new Error(
`Invalid #ColliderDesc properties detected. Unable to create the collider for #${object?.name ?? 'object'}`,
)
}
const collider = world.createCollider(colliderDesc, rigidBody)

return {
colliderDesc,
collider,
}
}

/**
* @description Create a {@link Collider} shapes for each child in the object
* based on the given {@link CreateColliderFromChildrenProps}
*
* @param props {@link CreateColliderFromChildrenProps}
*
* @see https://rapier.rs/javascript3d/classes/Collider.html
* @see https://rapier.rs/docs/user_guides/javascript/colliders
*/
export const createColliderFromChildren = (props: CreateColliderFromChildrenProps) => {
return ((props?.object?.children ?? []) as TresObject[]).map((child) => {
return {
...createCollider({ ...props, object: child }),
child,
}
})
}
44 changes: 35 additions & 9 deletions src/utils/rigid-body.util.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { RigidBodyDesc } from '@dimforge/rapier3d-compat'
import type { TresObject } from '@tresjs/core'

import type { RigidBodyType } from '../types/rigid-body.type'
import type { CreateRigidBodyDescProps, CreateRigidBodyProps } from '../types/rigid-body.type'

/**
* @description Create a {@link RigidBodyDesc} based on the passed
* {@link TresObject} and the {@link RigidBodyType}
* @description Create a {@link RigidBodyDesc} based on the given
* {@link CreateRigidBodyDescProps}
*
* @param object `TresObject` based on.
* @param props {@link CreateRigidBodyDescProps}
*
* @param rigidBodyType The type of the `RigidBodyDesc`
*
* @internal
* @see https://rapier.rs/javascript3d/classes/RigidBodyDesc.html
* @see https://rapier.rs/docs/user_guides/javascript/rigid_bodies
*/
export const createRigidBodyDesc = (object: TresObject, rigidBodyType: RigidBodyType) => {
export const createRigidBodyDesc = (props: CreateRigidBodyDescProps) => {
const { object, rigidBodyType } = props

if (!object) { return }

const safeRigidBodyDescType: keyof typeof RigidBodyDesc = rigidBodyType === 'kinematic'
Expand All @@ -37,3 +37,29 @@ export const createRigidBodyDesc = (object: TresObject, rigidBodyType: RigidBody

return rigidBodyDesc
}

/**
* @description Create a {@link RigidBody} based on the given
* {@link CreateRigidBodyProps}
*
* @param props {@link CreateRigidBodyProps}
*
* @see https://rapier.rs/javascript3d/classes/RigidBody.html
* @see https://rapier.rs/docs/user_guides/javascript/rigid_bodies
*/
export const createRigiBody = (props: CreateRigidBodyProps) => {
const { object, world } = props
const rigidBodyDesc = createRigidBodyDesc(props)

if (!rigidBodyDesc) {
throw new Error(
`Invalid #ColliderDesc properties detected. Unable to create the rigid-body for #${object?.uuid ?? 'object'}`,
)
}
const rigidBody = world.createRigidBody(rigidBodyDesc)

return {
rigidBodyDesc,
rigidBody,
}
}

0 comments on commit 8412712

Please sign in to comment.