cannon.nodeOps.plugin.ts 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. import type { TresNodeOpsPlugin } from 'src/core/nodeOps'
  2. import {
  3. Body,
  4. Box,
  5. World,
  6. NaiveBroadphase,
  7. Vec3 as CannonVec3,
  8. } from 'cannon-es'
  9. import type { Object3D } from 'three'
  10. import type { TresContext } from '../composables'
  11. import { useRenderLoop } from '../composables'
  12. function setup() {
  13. const store = new Map()
  14. const world = new World()
  15. world.gravity.set(0, -9.8, 0)
  16. world.broadphase = new NaiveBroadphase()
  17. const loopAPI = useRenderLoop().onLoop(({ delta }) => {
  18. world.step(delta)
  19. for (const [body, object3D] of store) {
  20. object3D.position.copy(body.position)
  21. object3D.setRotationFromQuaternion(body.quaternion)
  22. }
  23. })
  24. const dispose = () => {
  25. world.bodies.forEach(b => world.removeBody(b))
  26. store.clear()
  27. loopAPI.off()
  28. }
  29. return { store, world, dispose }
  30. }
  31. export const plugin: TresNodeOpsPlugin<Body, Body, TresContext> = (
  32. context: TresContext,
  33. ) => {
  34. const { store, world, dispose } = setup()
  35. return {
  36. name: 'Cannon Physics plugin',
  37. filter: {
  38. tag: (tag: string) => tag === 'Collider',
  39. element: (a: any): a is Body => a && a.aabb,
  40. node: (a: any): a is Body => a && a.aabb,
  41. },
  42. createElement: (_: any, __: any, ___: any, props: Record<string, any>) => {
  43. props = props ?? {}
  44. const args = props.args ?? []
  45. const mass = props.mass ?? 1
  46. const angularVelocity = props['angular-velocity'] ?? [0, 0, 0]
  47. const velocity = props.velocity ?? [0, 0, 0]
  48. const shapeType = props.shape ?? 'Box'
  49. const shape = (() => {
  50. if ('Box' === shapeType) {
  51. const xyz: number[] = args.every((n: any) => typeof n === 'number') ? args : [1., 1., 1.]
  52. while (xyz.length < 3) xyz.push(1.0)
  53. return new Box(new CannonVec3(... xyz.map(n => 0.5 * n)))
  54. }
  55. return new Box(new CannonVec3(1, 1, 1))
  56. })()
  57. const body = new Body({ mass, shape })
  58. body.angularVelocity.set(
  59. angularVelocity[0],
  60. angularVelocity[1],
  61. angularVelocity[2],
  62. )
  63. body.velocity.set(velocity[0], velocity[1], velocity[2])
  64. return body
  65. },
  66. insert: (body: Body, parent) => {
  67. if (parent.isObject3D) {
  68. const p = (<THREE.Object3D>parent).position
  69. const r = (<THREE.Object3D>parent).quaternion
  70. body.position.set(p.x, p.y, p.z)
  71. body.quaternion.set(r.x, r.y, r.z, r.w)
  72. world.addBody(body)
  73. store.set(body, parent as Object3D)
  74. }
  75. },
  76. remove: (body: Body) => {
  77. world.removeBody(body)
  78. store.delete(body)
  79. },
  80. dispose,
  81. }
  82. }