Skip to content

Instantly share code, notes, and snippets.

@RJ
Last active February 19, 2025 10:59
Show Gist options
  • Save RJ/74e598b84dd81feb40f72407dee4ad79 to your computer and use it in GitHub Desktop.
Save RJ/74e598b84dd81feb40f72407dee4ad79 to your computer and use it in GitHub Desktop.
half-baked avian quantization plugin
use crate::prelude::*;
use bevy::{
ecs::{intern::Interned, schedule::ScheduleLabel},
prelude::*,
};
use crate::{prepare::PrepareSet, sync::SyncSet};
/// Quantizes all the things
pub struct QuantizationPlugin {
schedule: Interned<dyn ScheduleLabel>,
}
impl QuantizationPlugin {
/// Creates a [`QuantizationPlugin`] using the given schedule
pub fn new(schedule: impl ScheduleLabel) -> Self {
Self {
schedule: schedule.intern(),
}
}
}
impl Plugin for QuantizationPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
self.schedule,
(
q_position,
q_linear_velocity,
q_rotation,
q_angular_velocity,
)
.in_set(PrepareSet::First),
);
app.add_systems(
self.schedule,
(
q_position,
q_linear_velocity,
q_rotation,
q_angular_velocity,
)
.in_set(SyncSet::First),
);
}
}
fn q_position(mut q: Query<&mut Position>) {
for mut v in q.iter_mut() {
v.0.quantize();
}
}
fn q_rotation(mut q: Query<&mut Rotation>) {
for mut v in q.iter_mut() {
#[cfg(feature = "2d")]
{
v.cos.quantize();
v.sin.quantize();
// normalize better here?
v.cos = v.cos.clamp(-1.0, 1.0);
v.sin = v.sin.clamp(-1.0, 1.0);
}
#[cfg(feature = "3d")]
{
v.0.quantize();
}
}
}
fn q_linear_velocity(mut q: Query<&mut LinearVelocity>) {
for mut v in q.iter_mut() {
v.0.quantize();
}
}
fn q_angular_velocity(mut q: Query<&mut AngularVelocity>) {
for mut v in q.iter_mut() {
let before = v.0;
v.0.quantize();
println!("a_angvel, before: {:?}, after: {:?}", before, v.0);
}
}
// --
/// Scale factor for quantization
/// 1/8192 ≈ 0.00122
pub const QUANTIZE_SCALE: f32 = 8192.0;
/// Trait extension for quantizing `f32`
pub trait QuantizableF32 {
fn quantize(&mut self);
}
impl QuantizableF32 for f32 {
fn quantize(&mut self) {
*self = ((*self * QUANTIZE_SCALE).round() as i32) as f32 / QUANTIZE_SCALE;
}
}
/// Trait extension for quantizing `Vec2`
pub trait QuantizableVec2 {
fn quantize(&mut self);
}
impl QuantizableVec2 for Vec2 {
fn quantize(&mut self) {
self.x.quantize();
self.y.quantize();
}
}
pub trait QuantizableVec3 {
fn quantize(&mut self);
}
impl QuantizableVec3 for Vec3 {
fn quantize(&mut self) {
self.x.quantize();
self.y.quantize();
self.z.quantize();
}
}
/// Trait extension for quantizing `Quat`
pub trait QuantizableQuat {
fn quantize(&mut self);
}
impl QuantizableQuat for Quat {
fn quantize(&mut self) {
self.x.quantize();
self.y.quantize();
self.z.quantize();
self.w.quantize();
*self = self.normalize();
}
}
@RJ
Copy link
Author

RJ commented Feb 19, 2025

i used 8192 because my physics values are up to 10k magnitude.

with a QUANTIZE_SCALE = 4096.0, the smallest step is 1/4096 (0.000244), and f32 can accurately distinguish steps of this size at values up to around 5000.0.

if your physics values are in the -1000 to 1000 range you could use a scale of 1000 (0.0001 accuracy).

depends on the game world. you could also think in meters/millimeters and set the quantization so you know exactly the minimum number of physics units you can represent (ie, 1mm of accuracy is enough).

(f32 having a 23 bit mantissa so the smallest step is ~ quantize_scale/(2^23))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment