Start integrating asset loading and animations

This commit is contained in:
2026-08-26 16:33:00 +02:00
parent 73855ea354
commit 4ff388f9c0
23 changed files with 724 additions and 301 deletions
+1
View File
@@ -3,3 +3,4 @@ use bevy::math::{Vec2, primitives::Segment2d};
use fixed::prelude::*;
pub type FGi32 = fixed::FixedI32<fixed::types::extra::U16>;
pub type FGWide = fixed::FixedI128<fixed::types::extra::U32>;
+57 -15
View File
@@ -3,9 +3,10 @@ use bevy::math::{Vec2, primitives::Segment2d};
use bevy::prelude::*;
use fixed::prelude::*;
use crate::math::vec::FGVec2;
type FGi32 = fixed::FixedI32<fixed::types::extra::U16>;
use crate::math::{
int::{FGWide, FGi32},
vec::FGVec2,
};
#[derive(Clone, Copy, PartialEq, Reflect)]
pub struct FGSegment2d {
@@ -81,7 +82,14 @@ impl FGSegment2d {
// Point lies somewhere in the middle, we compute the closest point by finding the parameter along the line.
let t = projection_scaled / length_squared;
self.point1 + segment_vector * t
FGVec2::new(
FGi32::from_num(
FGWide::from_num(self.point1.x) + FGWide::from_num(segment_vector.x) * t,
),
FGi32::from_num(
FGWide::from_num(self.point1.y) + FGWide::from_num(segment_vector.y) * t,
),
)
}
/// Compute the normalized counterclockwise normal on the left-hand side of the line segment.
@@ -100,22 +108,34 @@ impl FGSegment2d {
let p1 = self.point2();
let p2 = rhs.point1();
let p3 = rhs.point2();
let s1_x = p1.x - p0.x;
let s1_y = p1.y - p0.y;
let s2_x = p3.x - p2.x;
let s2_y = p3.y - p2.y;
let p0_x = FGWide::from_num(p0.x);
let p0_y = FGWide::from_num(p0.y);
let p1_x = FGWide::from_num(p1.x);
let p1_y = FGWide::from_num(p1.y);
let p2_x = FGWide::from_num(p2.x);
let p2_y = FGWide::from_num(p2.y);
let p3_x = FGWide::from_num(p3.x);
let p3_y = FGWide::from_num(p3.y);
let s1_x = p1_x - p0_x;
let s1_y = p1_y - p0_y;
let s2_x = p3_x - p2_x;
let s2_y = p3_y - p2_y;
let denom = -s2_x * s1_y + s1_x * s2_y;
if denom == FGi32::ZERO {
if denom == FGWide::ZERO {
return None; // Parallel or collinear
}
info!("{p0}, {p1}, {p2}, {p3}");
let s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / denom;
let t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / denom;
let s = (-s1_y * (p0.x - p2.x) + s1_x * (p0.y - p2.y)) / denom;
let t = ( s2_x * (p0.y - p2.y) - s2_y * (p0.x - p2.x)) / denom;
if s >= FGi32::ZERO && s <= FGi32::ONE && t >= FGi32::ZERO && t <= FGi32::ONE {
Some(FGVec2::new(p0.x + (t * s1_x), p0.y + (t * s1_y)))
if s >= FGWide::ZERO && s <= FGWide::ONE && t >= FGWide::ZERO && t <= FGWide::ONE {
Some(FGVec2::new(
FGi32::from_num(p0_x + t * s1_x),
FGi32::from_num(p0_y + t * s1_y),
))
} else {
None
}
@@ -134,4 +154,26 @@ mod tests {
assert_eq!(segment.closest_point(FGVec2::lit("-6", "1")), segment.point1());
assert_eq!(segment.closest_point(FGVec2::lit("6", "1")), segment.point2());
}
#[test]
fn segment_intersection_avoids_cross_product_overflow() {
let segment = FGSegment2d::new(FGVec2::lit("56", "-3.5"), FGVec2::lit("56", "-200"));
let rhs = FGSegment2d::new(
FGVec2::lit("-111.26334", "-130.22586"),
FGVec2::lit("-112.36334", "-133.02586"),
);
assert_eq!(segment.segment_intersection(&rhs), None);
}
#[test]
fn segment_intersection_handles_large_crossing_segments() {
let horizontal = FGSegment2d::new(FGVec2::lit("-200", "0"), FGVec2::lit("200", "0"));
let vertical = FGSegment2d::new(FGVec2::lit("0", "-200"), FGVec2::lit("0", "200"));
assert_eq!(
horizontal.segment_intersection(&vertical),
Some(FGVec2::ZERO)
);
}
}
+29 -10
View File
@@ -4,7 +4,7 @@ use fixed::types::I32F32;
use serde::{Deserialize, Serialize};
use std::ops::{AddAssign, Add, Div, Mul, Sub};
use super::int::FGi32;
use super::int::{FGWide, FGi32};
#[derive(Clone, Copy, PartialEq, Default, Debug, Reflect, Serialize, Deserialize)]
#[reflect(opaque)]
@@ -41,13 +41,15 @@ impl FGVec2 {
}
#[inline]
pub fn length_squared(&self) -> FGi32 {
self.x * self.x + self.y * self.y
pub fn length_squared(&self) -> FGWide {
let x = FGWide::from_num(self.x);
let y = FGWide::from_num(self.y);
x * x + y * y
}
#[inline]
pub fn length(self) -> FGi32 {
(self.x * self.x + self.y * self.y).sqrt()
FGi32::from_num(self.length_squared().sqrt())
}
/// Returns the vector projection of `self` onto `rhs`.
@@ -62,7 +64,11 @@ impl FGVec2 {
pub fn project_onto_normalized(self, rhs: Self) -> Self {
assert!(rhs.is_normalized());
rhs * self.dot(rhs)
let scale = self.dot(rhs);
Self::new(
FGi32::from_num(FGWide::from_num(rhs.x) * scale),
FGi32::from_num(FGWide::from_num(rhs.y) * scale),
)
}
const EPS: FGi32 = FGi32::from_bits(6);
@@ -76,16 +82,21 @@ impl FGVec2 {
#[inline]
#[must_use]
pub fn dot(self, rhs: Self) -> FGi32 {
(self.x * rhs.x) + (self.y * rhs.y)
pub fn dot(self, rhs: Self) -> FGWide {
FGWide::from_num(self.x) * FGWide::from_num(rhs.x)
+ FGWide::from_num(self.y) * FGWide::from_num(rhs.y)
}
#[inline]
#[must_use]
pub fn project_onto(self, rhs: Self) -> Self {
let other_len_sq = rhs.dot(rhs);
assert!(other_len_sq != FGi32::ZERO);
rhs * (self.dot(rhs) / other_len_sq)
assert!(other_len_sq != FGWide::ZERO);
let scale = self.dot(rhs) / other_len_sq;
Self::new(
FGi32::from_num(FGWide::from_num(rhs.x) * scale),
FGi32::from_num(FGWide::from_num(rhs.y) * scale),
)
}
#[inline]
@@ -246,7 +257,7 @@ mod is_normalized_tests {
let v = FGVec2 { x: fx, y: fy };
if let Some(normalized) = v.normalize() {
let ls = normalized.length_squared();
let ls = FGi32::from_num(normalized.length_squared());
let diff_bits = ls.to_bits() - FGi32::ONE.to_bits();
let abs_diff_bits = diff_bits.abs();
@@ -310,4 +321,12 @@ mod is_normalized_tests {
let non_unit_small = FGVec2 { x: FGi32::from_num(0.5), y: FGi32::from_num(0.0) };
assert!(!non_unit_small.is_normalized());
}
#[test]
fn dot_and_length_squared_use_wide_intermediates() {
let vector = FGVec2::lit("200", "200");
assert_eq!(vector.dot(vector), FGWide::from_num(80_000));
assert_eq!(vector.length_squared(), FGWide::from_num(80_000));
}
}