inox2d/physics/
simple_physics.rs

1use std::f32::consts::PI;
2
3use glam::{vec2, vec4, Vec2};
4
5use super::{ParamMapMode, SimplePhysics, SimplePhysicsSystem};
6use crate::puppet::PuppetPhysics;
7use crate::render::NodeRenderCtx;
8
9impl SimplePhysics {
10	fn update_inputs(&mut self, node_render_ctx: &NodeRenderCtx) {
11		let anchor_pos = match self.local_only {
12			true => node_render_ctx.trans_offset.translation.extend(1.0),
13			false => node_render_ctx.trans * vec4(0.0, 0.0, 0.0, 1.0),
14		};
15
16		self.anchor = vec2(anchor_pos.x, anchor_pos.y);
17	}
18
19	fn calc_outputs(&self, node_render_ctx: &NodeRenderCtx) -> Vec2 {
20		let oscale = self.final_output_scale();
21
22		// "Okay, so this is confusing. We want to translate the angle back to local space, but not the coordinates."
23		// - Asahi Lina
24
25		// Transform the physics output back into local space.
26		// The origin here is the anchor. This gives us the local angle.
27		let local_pos4 = match self.local_only {
28			true => vec4(self.output.x, self.output.y, 0.0, 1.0),
29			false => node_render_ctx.trans.inverse() * vec4(self.output.x, self.output.y, 0.0, 1.0),
30		};
31
32		let local_angle = vec2(local_pos4.x, local_pos4.y).normalize();
33
34		// Figure out the relative length. We can work this out directly in global space.
35		let relative_length = self.output.distance(self.anchor) / self.final_length();
36
37		let param_value = match self.map_mode {
38			ParamMapMode::XY => {
39				let local_pos_norm = local_angle * relative_length;
40				let mut result = local_pos_norm - Vec2::Y;
41				result.y = -result.y; // Y goes up for params
42				result
43			}
44			ParamMapMode::AngleLength => {
45				let a = f32::atan2(-local_angle.x, local_angle.y) / PI;
46				vec2(a, relative_length)
47			}
48		};
49
50		param_value * oscale
51	}
52
53	pub fn update(&mut self, dt: f32, puppet_physics: PuppetPhysics, node_render_ctx: &NodeRenderCtx) -> Vec2 {
54		// Timestep is limited to 10 seconds.
55		// If you're getting 0.1 FPS, you have bigger issues to deal with.
56		let mut h = dt.min(10.);
57
58		self.update_inputs(node_render_ctx);
59
60		// Minimum physics timestep: 0.01s
61		while h > 0.01 {
62			self.tick(0.01, puppet_physics);
63			h -= 0.01;
64		}
65
66		self.tick(h, puppet_physics);
67
68		self.calc_outputs(node_render_ctx)
69	}
70
71	pub fn update_anchor(&mut self) {
72		let bob = self.anchor + vec2(0.0, self.final_length());
73
74		match &mut self.system {
75			SimplePhysicsSystem::RigidPendulum(system) => system.bob = bob,
76			SimplePhysicsSystem::SpringPendulum(system) => system.bob = bob,
77		}
78	}
79
80	pub fn final_gravity(&self, puppet_physics: &PuppetPhysics) -> f32 {
81		(self.props.gravity * self.offset_props.gravity) * puppet_physics.gravity * puppet_physics.pixels_per_meter
82	}
83
84	pub fn final_length(&self) -> f32 {
85		self.props.length * self.offset_props.length
86	}
87
88	pub fn final_frequency(&self) -> f32 {
89		self.props.frequency * self.offset_props.frequency
90	}
91
92	pub fn final_angle_damping(&self) -> f32 {
93		self.props.angle_damping * self.offset_props.angle_damping
94	}
95
96	pub fn final_length_damping(&self) -> f32 {
97		self.props.length_damping * self.offset_props.length_damping
98	}
99
100	pub fn final_output_scale(&self) -> Vec2 {
101		self.props.output_scale * self.offset_props.output_scale
102	}
103}