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

Draft PR for review : Add physics module to Nazara (physics engine: nphysics2d) #8

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ members = [
"nazara_core",
"nazara_gfx",
"nazara_net",
"nazara_physics2d",
]
12 changes: 12 additions & 0 deletions nazara_physics2d/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "nazara_physics2d"
version = "0.1.0"
authors = ["Faymoon"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
Copy link
Contributor

Choose a reason for hiding this comment

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

Are minor versions very useful here? (thanks Lynix)

Copy link
Author

Choose a reason for hiding this comment

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

I juste left default cargo.toml

nphysics2d = "0.13.0"
nalgebra = "0.19.0"
ncollide2d = "0.21.0"
40 changes: 40 additions & 0 deletions nazara_physics2d/examples/ball_fall2d.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
extern crate nazara_physics2d;
extern crate nalgebra;
extern crate ncollide2d;
Copy link
Contributor

Choose a reason for hiding this comment

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

The goal is not to avoid the use of the lib ncollide and to go through Nazara2d?

Copy link
Author

Choose a reason for hiding this comment

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

nphysics2d requires ncollide2d


use std::time::Instant;

use nazara_physics2d::physworld::PhysWorld;
use nazara_physics2d::rigidbody::RigidBodyBuilder;
//use nazara_physics2d::rigidbody::RigidBody;
use nazara_physics2d::collider::Collider;
use nazara_physics2d::material::Material;

use nalgebra::Vector2;
use nalgebra::Point2;
Copy link
Contributor

Choose a reason for hiding this comment

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

Same thing for nalgebra I think.

Copy link
Author

@Faymoon Faymoon Dec 11, 2019

Choose a reason for hiding this comment

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

Same nphysics2d requires nalgebra


fn main()
{
let mut world = PhysWorld::new(Vector2::y() * -9.81);

let material = Material::new(0.1, 0.0, None);
let collider = Collider::Circle{ radius: 4.0, offset: None };
let body = RigidBodyBuilder::new().mass(1.2) // simply set the mass of the body
.collider(&collider, Some(&material)) // set a collider and optionally a material, material and collider are linked so they must be set together
.build(&mut world); // simply build the rigidbody into the world

let ground_collider = Collider::Box{ size: Vector2::new(20.0, 5.0), offset: Some(Point2::new(-10.0, -30.0)) }; // the offset is an internal translation, and it won't change the body's position, here (0, 0)
let ground = RigidBodyBuilder::new().make_static() // make the body static, so it won't move and it won't need a mass
.collider(&ground_collider, None) // set the collider and no material
.build(&mut world); // mwork as the other body

let mut instant = Instant::now(); // record the actual instant

loop
{
world.step((instant.elapsed().as_nanos() as f64) / 1_000_000_000.0); // tell the physical world to simulate the elapsed time since last step
instant = Instant::now(); // record the instant

println!("{:?}", body.get_position().unwrap()); // print the ball position
}
}
62 changes: 62 additions & 0 deletions nazara_physics2d/src/collider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use nalgebra::Vector2;
use nalgebra::geometry::Point2;
use nphysics2d::object::ColliderDesc;
use nphysics2d::material::MaterialHandle;
use ncollide2d::shape;

use crate::material::Material;
use crate::number::Number;

pub enum Collider<T: Number>
{
Box{ size: Vector2<T>, offset: Option<Point2<T>> },
Circle{ radius: T, offset: Option<Point2<T>> },
Capsule{ radius: T, half_height: T },
//Compound(colliders: Vec<Collider<T>> ),
Segment{ first_point: Point2<T> , second_point: Point2<T> },
//Triangle(first_point: Point2<T>, second_point: Point2<T>, third_point: Point2<T>), because shape::Triangle doesn't impl shape trait for no reason
}

impl<T: Number> Collider<T>
{
pub(crate) fn create_desc(&self, material: Option<&Material<T>>) -> ColliderDesc<T>
{
let mut description = match self
{
&Collider::Box{ size, offset } =>
{
let mut desc = ColliderDesc::new(shape::ShapeHandle::new(shape::Cuboid::new(size.unscale((2.0).into())))); // /2 because Cuboid expect half size
if let Some(offset) = offset
{
desc.set_translation(offset.coords);
}
desc
}
&Collider::Circle{ radius, offset } =>
{
let mut desc = ColliderDesc::new(shape::ShapeHandle::new(shape::Ball::new(radius)));
if let Some(offset) = offset
{
desc.set_translation(offset.coords);
}
desc
},
&Collider::Capsule{ radius, half_height } =>
ColliderDesc::new(shape::ShapeHandle::new(shape::Capsule::new(half_height, radius))),
//&Compound(colliders) =>
&Collider::Segment{ first_point, second_point } =>
ColliderDesc::new(shape::ShapeHandle::new(shape::Segment::new(first_point, second_point))),
//&Triangle(first_point, second_point, third_point) =>
// ColliderDesc::new(shape::ShapeHandle::new(shape::Triangle::new(first_point, second_point, third_point))),
};
if let Some(material) = material
{
if let Some(density) = material.density
{
description.set_density(density);
}
description.set_material(MaterialHandle::new(material.material));
}
description
}
}
9 changes: 9 additions & 0 deletions nazara_physics2d/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pub mod rigidbody;
pub mod physworld;
pub mod collider;
pub mod material;
pub mod number;

extern crate nphysics2d;
extern crate nalgebra;
extern crate ncollide2d;
21 changes: 21 additions & 0 deletions nazara_physics2d/src/material.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use nphysics2d::material::BasicMaterial;

use crate::number::Number;

pub struct Material<T: Number>
{
pub(crate) material: BasicMaterial<T>,
pub(crate) density: Option<T>,
}

impl<T: Number> Material<T>
{
pub fn new(restitution: T, friction: T, density: Option<T>) -> Self
{
Material
{
material: BasicMaterial::new(restitution, friction),
density,
}
}
}
6 changes: 6 additions & 0 deletions nazara_physics2d/src/number.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use std::convert::From;

use nalgebra::RealField;

pub trait Number : From<f64> + RealField {}
impl<T> Number for T where T: From<f64> + RealField {}
42 changes: 42 additions & 0 deletions nazara_physics2d/src/physworld.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use std::cell::RefCell;
use std::rc::Rc;

use nalgebra::Vector2;
use nphysics2d::object::{DefaultBodySet, DefaultColliderSet};
use nphysics2d::force_generator::DefaultForceGeneratorSet;
use nphysics2d::joint::DefaultJointConstraintSet;
use nphysics2d::world::{DefaultMechanicalWorld, DefaultGeometricalWorld};

use crate::number::Number;

pub struct PhysWorld<T: Number>
{
mechanical_world: DefaultMechanicalWorld<T>,
geometric_world: DefaultGeometricalWorld<T>,
pub(crate) body_set: Rc<RefCell<DefaultBodySet<T>>>,
pub(crate) collider_set: DefaultColliderSet<T>,
joint_set: DefaultJointConstraintSet<T>,
force_generator_set: DefaultForceGeneratorSet<T>,
}

impl<T: Number> PhysWorld<T>
{
pub fn new(gravity: Vector2<T>) -> PhysWorld<T>
{
PhysWorld
{
mechanical_world: DefaultMechanicalWorld::new(gravity),
geometric_world: DefaultGeometricalWorld::new(),
body_set: Rc::new(RefCell::new(DefaultBodySet::new())),
collider_set: DefaultColliderSet::new(),
joint_set: DefaultJointConstraintSet::new(),
force_generator_set: DefaultForceGeneratorSet::new(),
}
}

pub fn step(&mut self, timestep: T)
{
self.mechanical_world.set_timestep(timestep);
self.mechanical_world.step(&mut self.geometric_world, &mut (*self.body_set.borrow_mut()), &mut self.collider_set, &mut self.joint_set, &mut self.force_generator_set);
}
}
85 changes: 85 additions & 0 deletions nazara_physics2d/src/rigidbody.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use std::cell::RefCell;
use std::rc::{Rc, Weak};

use nalgebra::Vector2;
use nphysics2d::object::{RigidBodyDesc, BodyStatus, DefaultBodyHandle, BodyPartHandle, DefaultBodySet};

use crate::physworld::PhysWorld;
use crate::collider::Collider;
use crate::material::Material;
use crate::number::Number;

pub enum RigidBodyStatus
{
Static,
Dynamic,
}

pub struct RigidBodyBuilder<'a, T: Number>
{
desc: RigidBodyDesc<T>,
collider: Option<&'a Collider<T>>,
material: Option<&'a Material<T>>,
}

impl<'a, T: Number> RigidBodyBuilder<'a, T>
{
pub fn new() -> Self
{
RigidBodyBuilder
{
desc: RigidBodyDesc::new(),
collider: None,
material: None,
}
}

pub fn mass(mut self, mass: T) -> Self
{
self.desc.set_mass(mass);
self
}

pub fn make_static(mut self) -> Self
{
self.desc.set_status(BodyStatus::Static);
self
}

pub fn collider(mut self, col: &'a Collider<T>, mat: Option<&'a Material<T>>) -> Self
{
self.collider = Some(col);
self.material = mat;
self
}

pub fn build(&self, world: &mut PhysWorld<T>) -> RigidBody<T>
{
let handle = world.body_set.borrow_mut().insert(self.desc.build());

if let Some(c) = self.collider
{
world.collider_set.insert(c.create_desc(self.material).build(BodyPartHandle(handle, 0)));
}

RigidBody
{
body: handle,
body_set: Rc::downgrade(&world.body_set),
}
}
}

pub struct RigidBody<T: Number>
{
body: DefaultBodyHandle,
body_set: Weak<RefCell<DefaultBodySet<T>>>,
}

impl<T: Number> RigidBody<T>
{
pub fn get_position(&self) -> Option<Vector2<T>>
{
Some(self.body_set.upgrade()?.borrow().rigid_body(self.body)?.position().translation.vector)
}
}