feat: add 3d nodes
This commit is contained in:
parent
2d11c9c9ec
commit
6b602b5731
@ -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
104
rust/src/flock/flock_3d.rs
Normal 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(),
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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)]
|
||||
|
Loading…
Reference in New Issue
Block a user