npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ng-rapier-threejs

v0.176.1

Published

``` npm i ng-rapier-threejs ```

Readme

Angular + ThreeJs + Rapier

Install

npm i ng-rapier-threejs

How to Use

Sample Source

GitHub threejs-rapier-angular

SourceCode


import {World} from 'ng-rapier-threejs';
..........
export class RapierSample2Component implements AfterViewInit {
  @ViewChild('domContainer', {static: true}) domContainer!: ElementRef;

  public world:World

  constructor(world: World) {
    this.world = world;
  }

  ngAfterViewInit() {
    let interval = setInterval(() => {
      const domContainer = this.domContainer.nativeElement.offsetHeight;
      if (domContainer) {
        clearInterval(interval);
        this.init();
      }
    }, 10)
  }

  private async init() {
    this.world.clear()
      .setContainer(this.domContainer.nativeElement)
      .setScreen()
      .setCamera({fov:95, near: 0.1, far: 100, position: [0, 2, 5]})
      .setRenderer({antialias: true})
      .setLight({
        type: 'spot',
        intensity: Math.PI * 10,
        angle: Math.PI / 1.8,
        penumbra: 0.5,
        castShadow: true,
        shadow: {blurSamples: 10, radius: 5}
      }).setLight({
        type: 'spot',
        intensity: Math.PI * 10,
        position: [-2.5, 5, 5],
        angle: Math.PI / 1.8,
        penumbra: 0.5,
        castShadow: true,
        shadow: {blurSamples: 10, radius: 5}
      })
      .enableRapier(async (rapier: Rapier) => {
        this.rapier = rapier;
        rapier.init([0.0, -9.81,  0.0]);
        // await rapier.initRapier(0.0, -9.81, 0.0);
        rapier.enableRapierDebugRenderer();
      })
      .enableControls({damping: true, target:{x: 0, y: 1, z: 0}})
      .setGridHelper({position: {x: 0, y: -75, z: 0}})
      .update(); // requestAnimationFrame(this.update)
  }

다양한 mesh 생성

BoxGeometry

mesh 및 rapier collider를 각각 생성하여 결합하는 방식
const mesh = await new Mesh().create({
  geometry: {type: 'box', args: [50, 1, 50]}, // geometry 속성
  material: {type: 'phong'}, // material 속성
  mesh: {receiveShadow: true}
});

this.world.scene.add(mesh);

// Rapier 생성
const body: Body = new Body(this.rapier);
await body.create({
  body: {type: 'fixed', translation: new THREE.Vector3(0, -1, 0)},
  collider: {shape: 'cuboid', args:[25, 0.5, 25]},
  object3d: mesh // 위에서 생성한 ThreeJs의 mesh를 넣어주면 mesh의 속성(shape, postion, scale등등을 자동으로 처리합니다 )
});
mesh 및 rapier collider를 한번에 생성하는 방식

한번에 생성할 경우는 별도의 scene.add 이 필요없습니다.

await this.world.addObject({
  geometry: {type: 'box', args: [50, 1, 50]}, // geometry 속성
  material: {type: 'phong'}, // material 속성
  mesh: {receiveShadow: true},
  rapier: {
    body: {type: 'fixed', translation: new THREE.Vector3(0, -1, 0)},
    collider: {shape: 'cuboid', args:[25, 0.5, 25]},
  }
}, (mesh, body) => {

  });

SphereGeometry

await this.world.addObject({
  geometry: {type: 'sphere'}, // geometry 속성
  material: {type: 'normal'}, // material 속성
  mesh: {receiveShadow: true},
  rapier:{
    body: {type:'dynamic', translation: new THREE.Vector3(-2.5, 5, 0), canSleep: false},
    collider: {shape: 'ball', restitution: 0.5},
  }
});

CylinderGeometry

await this.world.addObject({
  geometry: {type: 'cylinder', args: [1, 1, 2, 16]}, // geometry 속성
  material: {type: 'normal'}, // material 속성
  mesh: {castShadow: true},
  rapier:{
    body: {type:'dynamic', translation: new THREE.Vector3(0, 5, 0), canSleep: false},
    collider: {shape: 'cylinder', mass:1, restitution: 0.5},
  }
});

IcosahedronGeometry

await this.world.addObject({
  geometry: {type: 'icosahedron', args: [1, 0]}, // geometry 속성
  material: {type: 'normal'}, // material 속성
  mesh: {receiveShadow: true},
  rapier:{
    body: {type:'dynamic', translation:new THREE.Vector3(2.5, 5, 0), canSleep: false},
    collider: {shape: 'convexHull', mass:1, restitution: 0.5}
  }
});

TorusKnotGeometry

await this.world.addObject({
  geometry: {type: 'torusknot'}, // geometry 속성
  material: {type: 'normal'}, // material 속성
  mesh: {receiveShadow: true},
  rapier:{
    body: {type:'dynamic', translation:new THREE.Vector3(5, 5, 0)},
    collider: {shape: 'trimesh', mass:1, restitution: 0.5},
  }
});

TextureLoader

texture loader 의 경우 material에 textureUrl 을 입력함으로서 자동으로 구현된다.

await this.world.addObject({
  material: { textureUrl: 'assets/images/ball.png'}, 
});

OBJLoader

await this.world.addObjectFromObjFile({
    material: {type: 'normal'}, // material 속성
    mesh: {url: '/assets/suzanne.obj', castShadow: true, name: 'Suzanne'},
    rapier:{
      body: {type:'dynamic', translation:new THREE.Vector3(-1, 10, 0)},
    collider: {shape: 'trimesh', mass:1, restitution: 0.5},
    }
  });
}

GLTFLoader

class Car {
  dynamicBodies: any = []
  private game: RapierSample2Component;
  private translation:number[];
  constructor(game: any, translation:number[] = [0, 0, 0]) {
    this.game = game;
    this.translation = translation;
    this.create();

  }

  private async create(){
    
    await new Mesh().loadGLTF({
      url: '/assets/sedanSports.glb',
    }, (gltf:any)=>{
      const carMesh:any = gltf.getObjectByName('body');
      carMesh.position.set(0, 0, 0)
      carMesh.traverse((o: any) => {
        if(o.type === 'Mesh' ) {
          o.castShadow = true;
        }
      })
  
      const wheelBLMesh: any = gltf.getObjectByName('wheel_backLeft')
      const wheelBRMesh: any = gltf.getObjectByName('wheel_backRight')
      const wheelFLMesh: any = gltf.getObjectByName('wheel_frontLeft')
      const wheelFRMesh: any = gltf.getObjectByName('wheel_frontRight')

      this.game.world.scene.add(carMesh, wheelBLMesh,  wheelBRMesh, wheelFLMesh, wheelFRMesh);

      const position = new THREE.Vector3(this.translation[0], this.translation[1], this.translation[2]);
      const carBody: Body = new Body(this.game.rapier);
      carBody.create({ // convexHull trimmesh
        body : {type:'dynamic', translation: position.clone(), canSleep: false},
        collider: { shape: 'convexHull', restitution: 0.5},
        object3d: carMesh // 위에서 생성한 ThreeJs의 mesh를 넣어주면 mesh의 속성(shape, postion, scale등등을 자동으로 처리합니다 )
      });

      const wheelBLBody: Body = new Body(this.game.rapier);
      wheelBLBody.create({ // shape: 'cylinder',
        body : {type:'dynamic', translation: position.clone().add(new THREE.Vector3(-1, 1, 1)), canSleep: false},
        collider: {shape: 'cylinder', args: [0.1, 0.3], restitution: 0.5,
          //  translation: position.clone().add(new THREE.Vector3(-1, 1, 1)),
          translation: new THREE.Vector3(-0.2, 0, 0),
          rotation: new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 0, 1), -Math.PI / 2)
        },
        object3d: wheelBLMesh
      });

      const wheelBRBody: Body = new Body(this.game.rapier);
      wheelBRBody.create({ // shape: 'cylinder',
        body : {type:'dynamic', translation: position.clone().add(new THREE.Vector3(1, 1, 1)), canSleep: false},
        collider: {shape: 'cylinder', args: [0.1, 0.3], restitution: 0.5,
          translation: new THREE.Vector3(0.2, 0, 0),
          rotation: new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI / 2)
        },
        object3d: wheelBRMesh
      });

      const wheelFLBody: Body = new Body(this.game.rapier);
      wheelFLBody.create({ // shape: 'cylinder',
        body : {type:'dynamic', translation: position.clone().add(new THREE.Vector3(-1, 1, -1)), canSleep: false},
        collider: {shape: 'cylinder', args: [0.1, 0.3], restitution: 0.5,
          translation: new THREE.Vector3(-0.2, 0, 0),
          rotation: new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI / 2)
        },
        object3d: wheelFLMesh
      });

      const wheelFRBody: Body = new Body(this.game.rapier);
      wheelFRBody.create({
        body: {type:'dynamic', translation: position.clone().add(new THREE.Vector3(1, 1, -1)), canSleep: false},
        collider: {shape: 'cylinder', args: [0.1, 0.3], restitution: 0.5,
          translation: new THREE.Vector3(0.2, 0, 0),
          rotation: new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI / 2),
        },
        
        object3d: wheelFRMesh
      });

      this.game.rapier.world.createImpulseJoint(
        RAPIER.JointData.revolute(new RAPIER.Vector3(-0.55, 0, 0.63), new RAPIER.Vector3(0, 0, 0), new RAPIER.Vector3(-1, 0, 0)),
        carBody.rigidBody,
        wheelBLBody.rigidBody,
        true
      )

      this.game.rapier.world.createImpulseJoint(
        RAPIER.JointData.revolute(new RAPIER.Vector3(0.55, 0, 0.63), new RAPIER.Vector3(0, 0, 0), new RAPIER.Vector3(1, 0, 0)),
        carBody.rigidBody,
        wheelBRBody.rigidBody,
        true
      )

      this.game.rapier.world.createImpulseJoint(
        RAPIER.JointData.revolute(new RAPIER.Vector3(-0.55, 0, -0.63), new RAPIER.Vector3(0, 0, 0), new RAPIER.Vector3(-1, 0, 0)),
        carBody.rigidBody,
        wheelFLBody.rigidBody,
        true
      )

      this.game.rapier.world.createImpulseJoint(
        RAPIER.JointData.revolute(new RAPIER.Vector3(0.55, 0, -0.63), new RAPIER.Vector3(0, 0, 0), new RAPIER.Vector3(1, 0, 0)),
        carBody.rigidBody,
        wheelFRBody.rigidBody,
        true
      )
    });

  }
}

addRapierBody

mesh 없이 rapier body 만 생성할때 사용

await this.world.addRapierBody({
  body: {type: 'fixed', additionalMass: 0, translation: [-1, 0.5, 1]},
  collider: {shape: 'cuboid', args:[1 / 2, 1 / 2, 1 / 2]},
});

mesh

geometry

  • type
    • box : BoxGeometry

material

  • type
    • normal : MeshNormalMaterial
    • phong: MeshPhongMaterial
    • standard: MeshStandardMaterial

Animation

Three World

  • this.world.update(); : three.js 의 world 를 업데이트 합니다.
  public render() {
    const clock = {delta: this.clock.getDelta(), elapsedTime:this.clock.getElapsedTime()}
    if(this.controls) {
      this.controls.update();
    }
    this.updates.forEach((fnc:(clock: ClockProps) => void)=>{
      fnc(clock);
    })
    this.renderer.render( this.scene, this.camera );
  }
  • 추가적인 update를 원할 경우 this.world.updates.push((clock:any)=>{this.update(clock)}); 처럼 사용하면됩니다.

rapier update

rapier에서는 생성된 dynamic Body를 자동적으로 업데이트 합니다.

private update(clock: ClockProps) {
  ..........

  for (let i = 0, n = this.dynamicBodies.length; i < n; i++) {
    this.dynamicBodies[i].update(clock);
  }
}

Additional Animation

자동적으로 주어지는 물리적 animation외에 body에 별도의 animation을 적용하기 위해서는 아래와 같이 처리한다. useFrame 을 활용한 animation 처리하기

await this.world.addObject(obstacle2, (mesh:any, body:any) => {
  if(body) {
    body.useFrame = (clock: any) => {
      const time = clock.elapsedTime;
      const y = -0.3 * Math.sin(1.5 * difficulty * time + timeOffset) + 1.3;
      body.rigidBody.setNextKinematicTranslation({
        x: position[0],
        y: position[1] + y- 0.8,
        z: position[2],
      });
    };
  };
});

drainCollisionEvents

await this.world.addObject({
  rapier: {
    collider: {
      onCollisionEnter:()=>{}
    },
  }
});

Lensflare

import {LensFlare} from 'ng-rapier-threejs'; 
const lensflare = (await new LensFlare()
  .addElement({ texture: { url: 'assets/images/lensflare.png' }, size: 700, distance: 0, color: 0xe61a19 }))
  .get();