updated_at: 2025-02-27 17:15

RAPIER

이곳의 설명은 angular + three.js + rapier 에 대한 예제입니다.

Install

npm install @dimforge/rapier3d-compat --save-dev

적용하는 방법

rapier를 사용하는 부분만 발췌하여 간단하게 설명드립니다.
cube를 클릭하면 위로 펄쩍 뛰었다가 다시 내려오는 프로그램중의 일부 입니다.

원본소스

import * as THREE from 'three';
import RAPIER from '@dimforge/rapier3d-compat';
..........
export class RapierComponent implements AfterViewInit {
..........
  constructor() {

  }
  ngAfterViewInit() {
    this.init();
  }

  private async init() { // async로 정의
    await RAPIER.init() // This line is only needed if using the compat version
    const gravity = new RAPIER.Vector3(0.0, -9.81, 0.0);
    this.world = new RAPIER.World(gravity);
    this.dynamicBodies = [];
    ..........

    this.setRenderer();
    this.update(); 

    this.createCubeMesh(); // cube 생성
    this.createFloorMesh(); // floor 생성
  }


  private createCubeMesh() {
    // Cuboid Collider
    this.cubeMesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshNormalMaterial());
    this.cubeMesh.castShadow = true;
    this.scene.add(this.cubeMesh);

    // 아래 부분이 RAPIER 적용하는 방식입니다.
    const rigidBody = this.world.createRigidBody(RAPIER.RigidBodyDesc.dynamic().setTranslation(0, 5, 0).setCanSleep(false));
    const colliderDesc = RAPIER.ColliderDesc.cuboid(0.5, 0.5, 0.5).setMass(1).setRestitution(1.1);
    this.world.createCollider(colliderDesc, rigidBody); // RAPIER.World  에 ColliderDesc(충돌체 모양) 과 cubeBody(출동체 Body 및 속성) 을 넣음으로서 실체 충돌체가 만들어 집니다.
    this.dynamicBodies.push([this.cubeMesh, rigidBody]);
  }

  private createFloorMesh() {
    const floorMesh = new THREE.Mesh(new THREE.BoxGeometry(100, 1, 100), new THREE.MeshPhongMaterial());
    floorMesh.receiveShadow = true;
    floorMesh.position.y = -1;
    this.scene.add(floorMesh);

    // 아래 부분이 RAPIER 적용하는 방식입니다.
    const rigidBody = this.world.createRigidBody(RAPIER.RigidBodyDesc.fixed().setTranslation(0, -1, 0)); // setTranslation 의 위치값은 floorMesh와 동일하게 적용
    const colliderDesc = RAPIER.ColliderDesc.cuboid(50, 0.5, 50); // floorMesh가 BoxGeometry  이므로 cuboid 사용하였고 크기는 작게 잡는다.
    this.world.createCollider(colliderDesc, rigidBody);
  }

   private setRenderer() {
    ..........

    this.renderer.domElement.addEventListener('click', (e) => {
        this.mouse.set(
          (e.clientX / this.renderer.domElement.clientWidth) * 2 - 1,
          -(e.clientY / this.renderer.domElement.clientHeight) * 2 + 1
        )
        this.raycaster.setFromCamera(this.mouse, this.camera)
      
        const intersects = this.raycaster.intersectObjects(
          [this.cubeMesh], // , sphereMesh, cylinderMesh, icosahedronMesh, torusKnotMesh
          false
        )
        if (intersects.length) {
          this.dynamicBodies.forEach((b) => {
            console.log('b:', b);
            b[0] === intersects[0].object && b[1].applyImpulse(new RAPIER.Vector3(0, 10, 0), true)
          })
        }
      })
  }


  private render() {
    const delta = this.clock.getDelta()
    this.world.timestep = Math.min(delta, 0.1)
    this.world.step()
  
    for (let i = 0, n = this.dynamicBodies.length; i < n; i++) {
      this.dynamicBodies[i][0].position.copy(this.dynamicBodies[i][1].translation())
      this.dynamicBodies[i][0].quaternion.copy(this.dynamicBodies[i][1].rotation())
    }
  
    this.renderer.render( this.scene, this.camera );
  }

  private update = () => {
    this.render();
    requestAnimationFrame(this.update); // request next update
  }

}

Collider vs RigidBody

Collider

Collider란, 해당 물체로의 접촉을 감지할 수 있도록 만들어진 기하학적 모양 혹은 형상(shape)을 의미한다. 이걸 설정하면 외부의 힘에 의해 RigidBody가 영향 받을 수 있게 한다.

  • RAPIER.ColliderDesc 를 이용하여 collider를 설정한다.
const cubeCollider = RAPIER.ColliderDesc.cuboid(0.5, 0.5, 0.5).setMass(1).setRestitution(1.1);
  • cuboid, ball 등를 통해 Collider의 모양을 육면체로 설정한다. 이 외에도 다양한 모양이 존재한다.
  • mass를 통해 질량을 설정한다.
  • restitution 메서드를 통해 얼마나 튕길지 그 정도를 결정한다.

RigidBody

물리 엔진의 기본 요소로, 중력과 충돌 같은 물리 법칙이 적용되는 객체를 생성한다
collier가 모양을 만들었다고 하면 rigidbody 모양에 대한 위치, 회전 등을 나타내고 어떻게 물리 법칙이 적용될 것인지(fixed, dynamic..)를 정의한다.

User Guides

Steps

1. RigidBodyDesc 생성

const rigidBodyDesc = RAPIER.RigidBodyDesc.fixed().setTranslation(0, -1, 0)

2. RigidBody 생성

RigidBodyDesc 를 이용하여 RigidBody 생성

const rigidBody = world.createRigidBody(rigidBodyDesc)

3. ColliderDesc 생성

const colliderDesc = RAPIER.ColliderDesc.cuboid(50, 0.5, 50);
  • activeCollisionTypes: ActiveCollisionTypes;
  • activeEvents: ActiveEvents;
  • activeHooks: ActiveHooks;
  • angularInertiaLocalFrame: Rotation;
  • centerOfMass: Vector;
  • collisionGroups: InteractionGroups;
  • contactForceEventThreshold: number;
  • contactSkin: number;
  • density: number;

밀도

  • enabled: boolean; => setEnabled(), isEnabled()
  • friction: number; => setFriction()
  • frictionCombineRule: CoefficientCombineRule; => setFrictionCombineRule(rule: CoefficientCombineRule): void;
  • isSensor: boolean; => setSensor(isSensor: boolean): void;
  • mass: number;

질량, 질량이 없는 rigid-body는 중력의 영향을 받지 않습니다. 따라서 rigid-body가 떨어지지 않으면 질량을 명시하거나 밀도(density)가 0이 아닌 충돌체를 하나 이상 놓아 두세요.

  • massPropsMode: MassPropsMode;
  • principalAngularInertia: Vector;
  • restitution: number; => setRestitution()

restitution이 없을 경우 리바운드 하지 않습니다.(지면에 떨어졌을때 그대로 멈춤)

  • restitutionCombineRule: CoefficientCombineRule;
  • rotation: Rotation;
  • shape: Shape; => get shape(): Shape;
  • solverGroups: InteractionGroups;
  • translation: Vector;

4. 충돌체(Collider) 생성

colliderDesc와 rigidBody를 이용하여 최종적인 충돌체를 생성한다.

world.createCollider(colliderDesc, rigidBody);

RigidBodyDesc

  • additionalSolverIterations: number;
  • angularDamping: number;
  • angularInertiaLocalFrame: Rotation;
  • angvel: Vector;
  • canSleep: boolean;
  • ccdEnabled: boolean;
  • centerOfMass: Vector;
  • dominanceGroup: number;
  • enabled: boolean;
  • gravityScale: number;
  • linearDamping: number;
  • linvel: Vector;
  • mass: number;
  • massOnly: boolean;
  • principalAngularInertia: Vector;
  • rotation: Rotation;
  • rotationsEnabledX: boolean;
  • rotationsEnabledY: boolean;
  • rotationsEnabledZ: boolean;
  • sleeping: boolean;
  • softCcdPrediction: number;
  • status: RigidBodyType;
  • translation: Vector;
  • translationsEnabledX: boolean;
  • translationsEnabledY: boolean;
  • translationsEnabledZ: boolean;
  • userData?: unknown;

type

RAPIER.RigidBodyDesc.fixed()
RAPIER.RigidBodyDesc.dynamic()
RAPIER.RigidBodyDesc.kinematicPositionBased()
RAPIER.RigidBodyDesc.kinematicVelocityBased()
  • Dynamic: body가 외부의 힘과 접촉에 의해 영향을 받는다는 것을 나타냅니다. 움직이는 body에 사용
  • Fixed: body가 움직일 수 없음을 나타냅니다. 마치 무한한 질량을 가지고 있고 어떤 힘에도 영향을 받지 않는 것처럼 행동합니다. dynamic body와는 계속 충돌하지만 fixed body나 kinematic(운동학적) body와는 충돌하지 않습니다. 이것은 일반적으로 지면이나 body를 일시적으로 동결하는 데 사용됩니다.
  • KinematicPositionBased: body의 포지션이 물리 엔진에 의해 변경되어서는 안 됨을 나타냅니다. 사용자는 다음 위치를 자유롭게 설정할 수 있으며, body 속도는 각 업데이트에서 그에 따라 추론되어 접촉하는 dynamic body의 현실적인 동작을 보장합니다. 이는 일반적으로 이동 플랫폼, 엘리베이터 등에 사용됩니다.
  • KinematicVelocityBased: 신체 속도가 물리 엔진에 의해 변경되어서는 안 됨을 나타냅니다. 사용자는 속도를 자유롭게 설정할 수 있으며, 각 업데이트에서 다음 신체 위치가 추론되어 접촉하는 동적 신체의 현실적인 동작을 보장합니다. 이는 일반적으로 이동 플랫폼, 엘리베이터 등에 사용됩니다.

Method

RigidBody 생성시 사용가능한 메쏘드

  • .translation(vector![0.0, 5.0, 1.0]) // The rigid body translation. (Default: zero vector.)

  • .rotation(vector![0.0, 0.0, 5.0]) // The rigid body rotation. (Default: no rotation.) // The rigid body position. Will override .translation(...) and .rotation(...). // Default: the identity isometry.

  • .position(Isometry::new( vector![1.0, 3.0, 2.0], vector![0.0, 0.0, 0.4], ))

  • .linvel(vector![1.0, 3.0, 4.0]) // The linear velocity of this body.(Default: zero velocity.)

  • .angvel(vector![3.0, 0.0, 1.0]) // The angular velocity of this body. (Default: zero velocity.)

  • .gravity_scale(0.5) // The scaling factor applied to the gravity affecting the rigid-body. (Default: 1.0)

  • .can_sleep(true) // Whether or not this body can sleep. (Default: true)

  • .ccd_enabled(false) // Whether or not CCD is enabled for this rigid-body. (Default: false)

  • .build(); // All done, actually build the rigid-body.

RigidBody 생성후(after build) 사용가능한 메쏘드

/* Set the position after the rigid-body creation. */
let rigid_body = rigid_body_set.get_mut(rigid_body_handle).unwrap();
// The `true` argument makes sure the rigid-body is awake.
rigid_body.set_translation(vector![0.0, 5.0, 1.0], true);
rigid_body.set_rotation(Rotation::from_scaled_axis(vector![0.2, 0.0, 0.0]), true);
assert_eq!(*rigid_body.translation(), vector![0.0, 5.0, 1.0]);
assert_eq!(rigid_body.rotation().scaled_axis(), vector![0.2, 0.0, 0.0]);

rigid_body.set_position(
    Isometry::new(vector![1.0, 2.0, 3.0], vector![0.0, 0.4, 0.0]),
    true,
);
assert_eq!(
    *rigid_body.position(),
    Isometry::new(vector![1.0, 2.0, 3.0], vector![0.0, 0.4, 0.0])
);

Coliders

충돌체(Coliders)는 접촉할 때 접촉과 충돌 이벤트를 생성하는 기하학적 모양을 나타냅니다. rigid body에 하나 이상의 Coliders를 부착하면 rigid body가 접촉력의 영향을 받을 수 있습니다.

let colliderDesc = new RAPIER.ColliderDesc(new RAPIER.Ball(0.5))
    // The collider translation wrt. the body it is attached to.
    // Default: the zero vector.
    .setTranslation(1.0, 2.0)
    // The collider rotation wrt. the body it is attached to.
    // Default: the identity rotation.
    .setRotation(3.14)
    // The collider density. If non-zero the collider's mass and angular inertia will be added
    // to the inertial properties of the body it is attached to.
    // Default: 1.0
    .setDensity(1.3)
    // Whether this collider is a sensor.
    // Default: false
    .setSensor(true);

Shape

ball

ball 모양의 충돌체

ColliderBuilder::ball(0.5);
RAPIER.ColliderDesc.ball(0.5);
cuboid

3d에서는 box 모양, 2d에서는 사각형을 나타낸다.

new Cuboid(hx: number, hy: number, hz: number): Cuboid
.cuboid(0.5, 0.5, 0.5)
trimesh

Builder for a triangle-mesh-shaped collider.

ColliderBuilder::trimesh(vertices, indices);

// Builder for a cuboid-shaped collider.
let _ = ColliderBuilder::cuboid(0.5, 0.2, 0.1);
// Builder for a capsule-shaped collider. The capsule principal axis is the `x` coordinate axis.
let _ = ColliderBuilder::capsule_x(0.5, 0.2);
// Builder for a capsule-shaped collider. The capsule principal axis is the `y` coordinate axis.
let _ = ColliderBuilder::capsule_y(0.5, 0.2);
// Builder for a capsule-shaped collider. The capsule principal axis is the `z` coordinate axis.
let _ = ColliderBuilder::capsule_z(0.5, 0.2);

// Builder for a heightfield-shaped collider.
let _ = ColliderBuilder::heightfield(heights, scale);
// Builder for a collider with the given shape.
let collider = ColliderBuilder::new(SharedShape::ball(0.5))

setFriction

The friction coefficient of this collider. Default: 0.5

.setFriction(0.8)

World

castRay

ray와 physics world 사이에서 가장 가까운 인터섹션을 찾는다.

castRay(ray: Ray, maxToi: number, solid: boolean, filterFlags?: QueryFilterFlags, filterGroups?: InteractionGroups, filterExcludeCollider?: Collider, filterExcludeRigidBody?: RigidBody, filterPredicate?: (collider: Collider) => boolean): RayColliderHit | null;

parameter

  • ray : The ray to cast.
  • maxToi : The maximum time-of-impact that can be reported by this cast. This effectively
    • limits the length of the ray to ray.dir.norm() * maxToi.
  • solid : If false then the ray will attempt to hit the boundary of a shape, even if its
    • origin already lies inside of a shape. In other terms, true implies that all shapes are plain,
    • whereas false implies that all shapes are hollow for this ray-cast.
  • groups - Used to filter the colliders that can or cannot be hit by the ray.
평점을 남겨주세요
평점 : 5.0
총 투표수 : 1

질문 및 답글