feat: add 3d nodes

This commit is contained in:
dusk 2024-08-29 16:34:03 +03:00
parent ac91b9a53e
commit 0f67d318df
Signed by: dusk
SSH Key Fingerprint: SHA256:Abmvag+juovVufZTxyWY8KcVgrznxvBjQpJesv071Aw
4 changed files with 259 additions and 3 deletions

View File

@ -0,0 +1,101 @@
use super::*;
use godot::prelude::*;
use crate::{to_glam_vec, BoidProperties, Flock3D};
#[derive(GodotClass)]
#[class(init, base=Node3D)]
pub struct Boid3D {
#[export]
/// The properties of this boid.
/// Note: this cannot be changed in runtime, aside from removing and readding the node.
properties: Gd<BoidProperties>,
props: BoidProperties,
vel: Vec3,
flock_id: i64,
base: Base<Node3D>,
}
#[godot_api]
impl Boid3D {
#[func]
#[inline(always)]
/// Get the current velocity of this boid.
fn get_velocity(&self) -> Vector3 {
Vector3::new(self.vel.x, self.vel.y, self.vel.z)
}
#[func]
#[inline(always)]
/// Get the ID of this boid.
pub fn get_id(&self) -> i64 {
self.base().instance_id().to_i64()
}
#[func]
#[inline(always)]
/// Get the flock ID of this boid.
pub fn get_flock_id(&self) -> i64 {
self.flock_id
}
}
#[godot_api]
impl INode3D for Boid3D {
fn enter_tree(&mut self) {
let Some(mut flock) = self
.to_gd()
.get_parent()
.and_then(|gd| gd.try_cast::<Flock3D>().ok())
else {
let boid_id = self.get_id();
godot_error!("[Boid3D:{boid_id}] boids parent isn't a Flock3D, or has no parent");
return;
};
let mut flock = flock.bind_mut();
flock.register_boid(self.get_id());
self.flock_id = flock.get_id();
}
fn ready(&mut self) {
self.props = self.properties.bind().clone();
}
fn exit_tree(&mut self) {
let mut flock = godot::global::instance_from_id(self.get_flock_id())
.unwrap()
.cast::<Flock3D>();
flock.bind_mut().unregister_boid(self.get_id());
}
}
impl Boid for Boid3D {
#[inline(always)]
fn apply_force(&mut self, force: Vec3) {
self.vel += force;
let new_vel = self.vel.clamp_length_max(self.props.max_speed);
self.vel = new_vel;
self.base_mut()
.translate(Vector3::new(new_vel.x, new_vel.y, new_vel.z));
}
#[inline(always)]
fn get_boid_position(&self) -> Vec3 {
to_glam_vec(self.base().get_position())
}
#[inline(always)]
fn get_boid_velocity(&self) -> Vec3 {
self.vel
}
#[inline(always)]
fn get_boid_properties(&self) -> &BoidProperties {
&self.props
}
#[inline(always)]
fn get_flock_id(&self) -> i64 {
self.get_flock_id()
}
}

104
rust/src/flock/flock_3d.rs Normal file
View File

@ -0,0 +1,104 @@
use glam::*;
use godot::prelude::*;
use crate::{
get_singleton, to_glam_vec, Boid, Boid3D, BoidProperties, FlockProperties, FxIndexMap,
};
use super::Flock;
#[derive(GodotClass)]
#[class(init, base=Node3D)]
pub struct Flock3D {
#[export]
/// Properties of this flock.
/// Note: this cannot be changed in runtime, aside from removing and readding the node.
properties: Gd<FlockProperties>,
props: FlockProperties,
#[export]
/// A target node for the flock to follow.
target: Option<Gd<Node3D>>,
pub boids: FxIndexMap<i64, Gd<Boid3D>>,
base: Base<Node3D>,
}
impl Flock3D {
pub fn register_boid(&mut self, boid_id: i64) {
let boid: Gd<Boid3D> = godot::global::instance_from_id(boid_id).unwrap().cast();
self.boids.insert(boid_id, boid.clone());
get_singleton().bind_mut().register_boid_3d(boid_id, boid);
let flock_id = self.get_id();
godot_print!("[Flock3D:{flock_id}] boid {boid_id} registered");
}
pub fn unregister_boid(&mut self, boid_id: i64) {
self.boids.shift_remove(&boid_id);
get_singleton().bind_mut().unregister_boid_3d(boid_id);
let flock_id = self.get_id();
godot_print!("[Flock3D:{flock_id}] boid {boid_id} unregistered");
}
}
#[godot_api]
impl INode3D for Flock3D {
fn enter_tree(&mut self) {
get_singleton().bind_mut().register_flock_3d(self.get_id())
}
fn ready(&mut self) {
self.props = self.properties.bind().clone();
}
fn exit_tree(&mut self) {
get_singleton()
.bind_mut()
.unregister_flock_3d(self.get_id())
}
}
#[godot_api]
impl Flock3D {
#[func]
#[inline(always)]
pub fn get_id(&self) -> i64 {
self.base().instance_id().to_i64()
}
}
impl Flock for Flock3D {
#[inline(always)]
fn get_flock_properties(&self) -> &FlockProperties {
&self.props
}
#[inline(always)]
fn get_target_position(&self) -> Option<Vec3> {
self.target.as_ref().map(|t| to_glam_vec(t.get_position()))
}
#[inline(always)]
fn get_boids_posvel(&self) -> Vec<(Vec3, Vec3)> {
let boid_count = self.boids.len();
let mut result = Vec::with_capacity(boid_count);
result.extend(self.boids.values().map(|b| {
let b = b.bind();
(b.get_boid_position(), b.get_boid_velocity())
}));
result
}
#[inline(always)]
fn get_boids(&self) -> impl Iterator<Item = (&i64, (Vec3, Vec3, BoidProperties))> {
self.boids.iter().map(|(id, boid)| {
let boid = boid.bind();
(
id,
(
boid.get_boid_position(),
boid.get_boid_velocity(),
boid.get_boid_properties().clone(),
),
)
})
}
}

View File

@ -3,6 +3,7 @@ use crate::{BoidProperties, FlockProperties};
use glam::*;
pub mod flock_2d;
pub mod flock_3d;
pub trait Flock {
fn get_flock_properties(&self) -> &FlockProperties;

View File

@ -16,7 +16,7 @@ mod flock_properties;
pub use boid::{boid_2d::*, boid_3d::*, Boid};
pub use boid_properties::BoidProperties;
pub use flock::{flock_2d::*, Flock};
pub use flock::{flock_2d::*, flock_3d::*, Flock};
pub use flock_properties::FlockProperties;
use rustc_hash::FxBuildHasher;
@ -115,8 +115,13 @@ impl INode for BoidsProcess {
#[inline(always)]
fn physics_process(&mut self, _: f64) {
if self.get_engine_singleton().get_physics_frames() % (self.process_per_tick as u64) == 0 {
if self.process_2d {
self.get_boids_singleton().bind_mut().process_boids_2d();
let (process_2d, process_3d) = (self.process_2d, self.process_3d);
let mut s = self.get_boids_singleton().bind_mut();
if process_2d {
s.process_boids_2d();
}
if process_3d {
s.process_boids_3d();
}
}
}
@ -128,6 +133,8 @@ impl INode for BoidsProcess {
struct Boids {
flocks2d: FxIndexMap<i64, Gd<Flock2D>>,
boids2d: FxIndexMap<i64, Gd<Boid2D>>,
flocks3d: FxIndexMap<i64, Gd<Flock3D>>,
boids3d: FxIndexMap<i64, Gd<Boid3D>>,
base: Base<Object>,
}
@ -152,6 +159,27 @@ impl Boids {
fn unregister_boid_2d(&mut self, boid_id: i64) {
self.boids2d.shift_remove(&boid_id);
}
fn register_flock_3d(&mut self, flock_id: i64) {
let flock = godot::global::instance_from_id(flock_id).unwrap().cast();
self.flocks3d.insert(flock_id, flock);
godot_print!("[Boids] flock {flock_id} registered");
}
fn unregister_flock_3d(&mut self, flock_id: i64) {
self.flocks3d.shift_remove(&flock_id);
godot_print!("[Boids] flock {flock_id} unregistered");
}
#[inline(always)]
fn register_boid_3d(&mut self, boid_id: i64, boid: Gd<Boid3D>) {
self.boids3d.insert(boid_id, boid);
}
#[inline(always)]
fn unregister_boid_3d(&mut self, boid_id: i64) {
self.boids3d.shift_remove(&boid_id);
}
}
#[godot_api]
@ -164,6 +192,14 @@ impl Boids {
process_boids(&mut self.boids2d, &self.flocks2d)
}
#[func]
#[inline(always)]
/// Process all 3D boids once.
/// NOTE: This function is not intended to be manually called. Prefer using `BoidsProcess` as an autoload singleton where possible.
fn process_boids_3d(&mut self) {
process_boids(&mut self.boids3d, &self.flocks3d)
}
#[func]
#[inline(always)]
/// Gets the total 2D boid count.
@ -177,6 +213,20 @@ impl Boids {
fn get_total_flock_2d_count(&self) -> i64 {
self.flocks2d.len() as i64
}
#[func]
#[inline(always)]
/// Gets the total 3D boid count.
fn get_total_boid_3d_count(&self) -> i64 {
self.boids3d.len() as i64
}
#[func]
#[inline(always)]
/// Gets the total 3D flock count.
fn get_total_flock_3d_count(&self) -> i64 {
self.flocks3d.len() as i64
}
}
#[inline(always)]