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

Oxide engine/feat/physics particle #55

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions oxide_physics/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "oxide_physics"
version = "0.0.1"
authors = [
"Chris Ohk <[email protected]>",
"Changseo Jang <[email protected]>",
"Yongwook Choi <[email protected]>",
"Chaneun Yeo <[email protected]>",
"Seokwon Moon <[email protected]>",
"Oxide Engine"
]
edition = "2021"
description = "Physics library for Oxide"

repository = "https://github.com/OxideEngine/Oxide"

license = "MIT"

[lib]
name = "oxide_physics"
path = "src/lib.rs"

[dependencies]
oxide_math = {path = "../oxide_math", version = "0.0.1"}
num-traits = "0.2"
generational-arena = "0.2"
81 changes: 81 additions & 0 deletions oxide_physics/src/aabb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use crate::collide_broad_phase::{BoundingVolume, HasBoundingVolume};
use oxide_math::commons::vector3::Vector3;

pub struct AABB {
pub mins: Vector3,
pub maxs: Vector3,
}

pub fn aabb<S>(shape: &S, tv: Vector3) -> AABB
where
S: HasBoundingVolume<AABB>,
{
shape.bounding_volume(tv)
}

pub fn local_aabb<S>(shape: &S) -> AABB
where
S: HasBoundingVolume<AABB>,
{
shape.local_bounding_volume()
}

impl AABB {
pub fn new(mins: Vector3, maxs: Vector3) -> AABB {
AABB { mins, maxs }
}

pub fn mins(&self) -> Vector3 {
Vector3 {
x: self.mins.x,
y: self.mins.y,
z: self.mins.z,
}
}

pub fn maxs(&self) -> Vector3 {
Vector3 {
x: self.maxs.x,
y: self.maxs.y,
z: self.maxs.z,
}
}
}

impl BoundingVolume for AABB {
// check if the bounding volume 'bv' intersects with self
fn intersects(&self, other: &AABB) -> bool {
self.mins.x <= other.maxs.x
&& self.mins.y <= other.maxs.y
&& self.mins.z <= other.maxs.z
&& self.maxs.x >= other.mins.x
&& self.maxs.y >= other.mins.y
&& self.maxs.z >= other.mins.z
}

// check if self contains the 'bv'
fn contains(&self, other: &AABB) -> bool {
self.mins.x <= other.mins.x
&& self.mins.y <= other.mins.y
&& self.mins.z <= other.mins.z
&& self.maxs.x >= other.maxs.x
&& self.maxs.y >= other.maxs.y
&& self.maxs.z >= other.maxs.z
}

// merge this bounding volume with the other 'bv'
fn merged(&self, other: &AABB) -> AABB {
AABB {
mins: Vector3 {
x: self.mins.x.min(other.mins.x),
y: self.mins.y.min(other.mins.y),
z: self.mins.z.min(other.mins.z),
},
maxs: Vector3 {
x: self.maxs.x.max(other.maxs.x),
y: self.maxs.y.max(other.maxs.y),
z: self.maxs.z.max(other.maxs.z),
},
}
}
}
40 changes: 40 additions & 0 deletions oxide_physics/src/aabb_ball.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use crate::aabb::AABB;
use crate::collide_broad_phase::HasBoundingVolume;
use crate::shape::Ball;
use oxide_math::commons::vector3::Vector3;

pub fn ball_aabb(center: Vector3, radius: f32) -> AABB {
AABB::new(
Vector3 {
x: center.x - radius,
y: center.y - radius,
z: center.z - radius,
},
Vector3 {
x: center.x + radius,
y: center.y + radius,
z: center.z + radius,
},
)
}

pub fn local_ball_aabb(radius: f32) -> AABB {
ball_aabb(
Vector3 {
x: 0.0,
y: 0.0,
z: 0.0,
},
radius,
)
}

impl HasBoundingVolume<AABB> for Ball {
fn bounding_volume(&self, tv: Vector3) -> AABB {
ball_aabb(tv, self.radius())
}

fn local_bounding_volume(&self) -> AABB {
local_ball_aabb(self.radius())
}
}
23 changes: 23 additions & 0 deletions oxide_physics/src/aabb_cuboid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use crate::aabb::AABB;
use crate::collide_broad_phase::HasBoundingVolume;
use crate::shape::Cuboid;
use oxide_math::commons::vector3::Vector3;

impl HasBoundingVolume<AABB> for Cuboid {
fn bounding_volume(&self, tv: Vector3) -> AABB {
let tv2 = Vector3 {
x: tv.x,
y: tv.y,
z: tv.z,
};
AABB::new(self.mins() + tv, self.maxs() + tv2)
}

fn local_bounding_volume(&self) -> AABB {
self.bounding_volume(Vector3 {
x: 0.0,
y: 0.0,
z: 0.0,
})
}
}
26 changes: 26 additions & 0 deletions oxide_physics/src/collide_broad_phase.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use oxide_math::commons::vector3::Vector3;

pub trait BoundingVolume {
// check if the bounding volume 'bv' intersects with self
fn intersects(&self, bv: &Self) -> bool;

// check if self contains the 'bv'
fn contains(&self, bv: &Self) -> bool;

// merge this bounding volume with the other 'bv'
fn merged(&self, bv: &Self) -> Self;
}

pub trait HasBoundingVolume<BV> {
// TBD: rotation by 4x4 matrix
// bounding volume of 'self' translated by 'tv'
fn bounding_volume(&self, tv: Vector3) -> BV;

fn local_bounding_volume(&self) -> BV {
self.bounding_volume(Vector3 {
x: 0.0,
y: 0.0,
z: 0.0,
})
}
}
18 changes: 18 additions & 0 deletions oxide_physics/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
pub mod particle;
pub mod pfgen;

pub mod shape;

pub mod aabb;
mod aabb_ball;
mod aabb_cuboid;
pub mod collide_broad_phase;

#[cfg(test)]
mod tests {
#[test]
fn aabb_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
}
150 changes: 150 additions & 0 deletions oxide_physics/src/particle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#![allow(dead_code)]
#![allow(unused_variables)]

use num_traits::pow;
use oxide_math::commons::vector::*;
use oxide_math::commons::vector3::Vector3;

extern crate generational_arena;
Copy link
Member

Choose a reason for hiding this comment

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

extern crate is no longer needed.

use generational_arena::Arena;

pub struct Particle {
inverse_mass: f32,
damping: f32,
position: Vector3,
pub velocity: Vector3,
force_accum: Vector3,
acceleration: Vector3,
}

impl Particle {
// returns integrated velocity
fn integrate(&mut self, duration: f32) -> Result<Vector3, &str> {
// not to integrate things with infinite mass
if self.inverse_mass <= 0.0f32 {
return Ok(
Vector3 {
x: self.velocity.x,
y: self.velocity.y,
z: self.velocity.z,
});
}
if duration <= 0.0 {
return Err("Cannot integrate with zero duration")
}

// update linear position
self.position.x += self.velocity.scale(duration).x;
self.position.y += self.velocity.scale(duration).y;
self.position.z += self.velocity.scale(duration).z;

// work out the acceleration from the force
let delta = self.force_accum.scale(self.inverse_mass);
let resulting_acc = Vector3 {
x: self.acceleration.x + delta.x,
y: self.acceleration.y + delta.y,
z: self.acceleration.z + delta.z,
};

// update linear velocity from the acceleration
self.velocity.x += resulting_acc.scale(duration).x;
self.velocity.y += resulting_acc.scale(duration).y;
self.velocity.z += resulting_acc.scale(duration).z;

// impose drag
self.velocity = self.velocity.scale(pow(self.damping, duration as usize));

Particle::clear_accumulator(self);

Ok(
Vector3 {
x: self.velocity.x,
y: self.velocity.y,
z: self.velocity.z,
})
}

// Returns inverse of mass
fn set_mass(&mut self, mass: f32) -> Result<f32, &str> {
if mass == 0.0f32 {
return Err("Cannot set zero mass")
}
self.inverse_mass = (1.0f32) / mass;
Ok(self.inverse_mass)
}

// Returns mass of the particle
pub fn get_mass(&self) -> f32 {
if self.inverse_mass == 0.0f32 {
f32::MAX
} else {
1.0f32 / self.inverse_mass
}
}

// Returns the velocity of the particle
pub fn get_velocity(&self) -> Vector3 {
Vector3 {
x: self.velocity.x,
y: self.velocity.y,
z: self.velocity.z,
}
}

pub fn has_finite_mass(&self) -> bool {
self.inverse_mass > 0.0f32
}

fn clear_accumulator(&mut self) {
self.force_accum = Vector3 {
x: 0.0f32,
y: 0.0f32,
z: 0.0f32,
};
}

pub fn add_force(&mut self, force: &Vector3) {
self.force_accum = Vector3 {
x: self.force_accum.x + force.x,
y: self.force_accum.y + force.y,
z: self.force_accum.z + force.z,
};
}
}

// The default particle set containing all the particles added to the world
// Uses arena to avoid ABA problem
pub struct DefaultParticleSet {
particles: Arena<Box<Particle>>,
removed: Vec<DefaultParticleHandle>,
}

impl DefaultParticleSet {
// Creates an empty set
pub fn new() -> Self {
DefaultParticleSet {
particles: Arena::new(),
removed: Vec::new(),
}
}

// Adds a particle to this set
pub fn insert(&mut self, particle: Particle) -> DefaultParticleHandle {
self.particles.insert(Box::new(particle))
}

// Removes a particle from this set
pub fn remove(&mut self, particle_handle: DefaultParticleHandle) -> Option<Box<Particle>> {
let result = self.particles.remove(particle_handle)?;
self.removed.push(particle_handle);
Some(result)
}
}

impl Default for DefaultParticleSet {
fn default() -> Self {
Self::new()
}
}

pub type DefaultParticleHandle = generational_arena::Index;
Loading