feat: 集成Rust WASM渲染引擎与TypeScript ECS框架 (#228)

* feat: 集成Rust WASM渲染引擎与TypeScript ECS框架

* feat: 增强编辑器UI功能与跨平台支持

* fix: 修复CI测试和类型检查问题

* fix: 修复CI问题并提高测试覆盖率

* fix: 修复CI问题并提高测试覆盖率
This commit is contained in:
YHH
2025-11-21 10:03:18 +08:00
committed by GitHub
parent 8b9616837d
commit a768b890fd
107 changed files with 10221 additions and 477 deletions

View File

@@ -0,0 +1,184 @@
//! Color utilities.
//! 颜色工具。
use bytemuck::{Pod, Zeroable};
/// RGBA color representation.
/// RGBA颜色表示。
///
/// Colors are stored as normalized floats (0.0-1.0) and can be converted
/// to packed u32 format for efficient GPU transfer.
/// 颜色以归一化浮点数0.0-1.0存储可转换为打包的u32格式以高效传输到GPU。
///
/// # Examples | 示例
/// ```rust
/// let red = Color::RED;
/// let custom = Color::new(0.5, 0.7, 0.3, 1.0);
/// let packed = custom.to_packed(); // For GPU
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Pod, Zeroable)]
#[repr(C)]
pub struct Color {
/// Red component (0.0-1.0).
/// 红色分量。
pub r: f32,
/// Green component (0.0-1.0).
/// 绿色分量。
pub g: f32,
/// Blue component (0.0-1.0).
/// 蓝色分量。
pub b: f32,
/// Alpha component (0.0-1.0).
/// 透明度分量。
pub a: f32,
}
impl Color {
/// White (1, 1, 1, 1).
/// 白色。
pub const WHITE: Self = Self { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
/// Black (0, 0, 0, 1).
/// 黑色。
pub const BLACK: Self = Self { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
/// Red (1, 0, 0, 1).
/// 红色。
pub const RED: Self = Self { r: 1.0, g: 0.0, b: 0.0, a: 1.0 };
/// Green (0, 1, 0, 1).
/// 绿色。
pub const GREEN: Self = Self { r: 0.0, g: 1.0, b: 0.0, a: 1.0 };
/// Blue (0, 0, 1, 1).
/// 蓝色。
pub const BLUE: Self = Self { r: 0.0, g: 0.0, b: 1.0, a: 1.0 };
/// Transparent (0, 0, 0, 0).
/// 透明。
pub const TRANSPARENT: Self = Self { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
/// Create a new color.
/// 创建新颜色。
#[inline]
pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
Self { r, g, b, a }
}
/// Create a color from RGB values (alpha = 1.0).
/// 从RGB值创建颜色alpha = 1.0)。
#[inline]
pub const fn rgb(r: f32, g: f32, b: f32) -> Self {
Self { r, g, b, a: 1.0 }
}
/// Create from u8 values (0-255).
/// 从u8值创建0-255
#[inline]
pub fn from_rgba8(r: u8, g: u8, b: u8, a: u8) -> Self {
Self {
r: r as f32 / 255.0,
g: g as f32 / 255.0,
b: b as f32 / 255.0,
a: a as f32 / 255.0,
}
}
/// Create from hex value (0xRRGGBB or 0xRRGGBBAA).
/// 从十六进制值创建。
#[inline]
pub fn from_hex(hex: u32) -> Self {
if hex > 0xFFFFFF {
// 0xRRGGBBAA format
Self::from_rgba8(
((hex >> 24) & 0xFF) as u8,
((hex >> 16) & 0xFF) as u8,
((hex >> 8) & 0xFF) as u8,
(hex & 0xFF) as u8,
)
} else {
// 0xRRGGBB format
Self::from_rgba8(
((hex >> 16) & 0xFF) as u8,
((hex >> 8) & 0xFF) as u8,
(hex & 0xFF) as u8,
255,
)
}
}
/// Convert to packed u32 (ABGR format for WebGL).
/// 转换为打包的u32WebGL的ABGR格式
#[inline]
pub fn to_packed(&self) -> u32 {
let r = (self.r.clamp(0.0, 1.0) * 255.0) as u32;
let g = (self.g.clamp(0.0, 1.0) * 255.0) as u32;
let b = (self.b.clamp(0.0, 1.0) * 255.0) as u32;
let a = (self.a.clamp(0.0, 1.0) * 255.0) as u32;
(a << 24) | (b << 16) | (g << 8) | r
}
/// Create from packed u32 (ABGR format).
/// 从打包的u32创建ABGR格式
#[inline]
pub fn from_packed(packed: u32) -> Self {
Self::from_rgba8(
(packed & 0xFF) as u8,
((packed >> 8) & 0xFF) as u8,
((packed >> 16) & 0xFF) as u8,
((packed >> 24) & 0xFF) as u8,
)
}
/// Linear interpolation between two colors.
/// 两个颜色之间的线性插值。
#[inline]
pub fn lerp(&self, other: &Self, t: f32) -> Self {
Self {
r: self.r + (other.r - self.r) * t,
g: self.g + (other.g - self.g) * t,
b: self.b + (other.b - self.b) * t,
a: self.a + (other.a - self.a) * t,
}
}
/// Multiply color by alpha (premultiplied alpha).
/// 颜色乘以alpha预乘alpha
#[inline]
pub fn premultiply(&self) -> Self {
Self {
r: self.r * self.a,
g: self.g * self.a,
b: self.b * self.a,
a: self.a,
}
}
/// Set the alpha value.
/// 设置alpha值。
#[inline]
pub fn with_alpha(self, a: f32) -> Self {
Self { a, ..self }
}
}
impl Default for Color {
fn default() -> Self {
Self::WHITE
}
}
impl From<[f32; 4]> for Color {
#[inline]
fn from([r, g, b, a]: [f32; 4]) -> Self {
Self { r, g, b, a }
}
}
impl From<Color> for [f32; 4] {
#[inline]
fn from(c: Color) -> Self {
[c.r, c.g, c.b, c.a]
}
}

View File

@@ -0,0 +1,19 @@
//! Mathematical primitives for 2D game development.
//! 用于2D游戏开发的数学基元。
//!
//! This module provides wrappers around `glam` types with additional
//! game-specific functionality.
//! 此模块提供对`glam`类型的封装,并添加游戏特定的功能。
mod vec2;
mod transform;
mod rect;
mod color;
pub use vec2::Vec2;
pub use transform::Transform2D;
pub use rect::Rect;
pub use color::Color;
// Re-export glam types for internal use | 重新导出glam类型供内部使用
pub use glam::{Mat3, Mat4, Vec3, Vec4};

View File

@@ -0,0 +1,148 @@
//! Rectangle implementation.
//! 矩形实现。
use super::Vec2;
/// Axis-aligned rectangle.
/// 轴对齐矩形。
///
/// # Examples | 示例
/// ```rust
/// let rect = Rect::new(10.0, 20.0, 100.0, 50.0);
/// let point = Vec2::new(50.0, 40.0);
/// assert!(rect.contains_point(point));
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Rect {
/// X position (left edge).
/// X位置左边缘
pub x: f32,
/// Y position (top edge).
/// Y位置上边缘
pub y: f32,
/// Width.
/// 宽度。
pub width: f32,
/// Height.
/// 高度。
pub height: f32,
}
impl Rect {
/// Create a new rectangle.
/// 创建新矩形。
#[inline]
pub const fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
Self { x, y, width, height }
}
/// Create a rectangle from two corner points.
/// 从两个角点创建矩形。
#[inline]
pub fn from_corners(min: Vec2, max: Vec2) -> Self {
Self {
x: min.x,
y: min.y,
width: max.x - min.x,
height: max.y - min.y,
}
}
/// Create a rectangle centered at a point.
/// 创建以某点为中心的矩形。
#[inline]
pub fn from_center(center: Vec2, width: f32, height: f32) -> Self {
Self {
x: center.x - width * 0.5,
y: center.y - height * 0.5,
width,
height,
}
}
/// Get the minimum (top-left) corner.
/// 获取最小(左上)角点。
#[inline]
pub fn min(&self) -> Vec2 {
Vec2::new(self.x, self.y)
}
/// Get the maximum (bottom-right) corner.
/// 获取最大(右下)角点。
#[inline]
pub fn max(&self) -> Vec2 {
Vec2::new(self.x + self.width, self.y + self.height)
}
/// Get the center point.
/// 获取中心点。
#[inline]
pub fn center(&self) -> Vec2 {
Vec2::new(self.x + self.width * 0.5, self.y + self.height * 0.5)
}
/// Get the size as a vector.
/// 获取尺寸向量。
#[inline]
pub fn size(&self) -> Vec2 {
Vec2::new(self.width, self.height)
}
/// Check if the rectangle contains a point.
/// 检查矩形是否包含某点。
#[inline]
pub fn contains_point(&self, point: Vec2) -> bool {
point.x >= self.x
&& point.x <= self.x + self.width
&& point.y >= self.y
&& point.y <= self.y + self.height
}
/// Check if this rectangle intersects with another.
/// 检查此矩形是否与另一个相交。
#[inline]
pub fn intersects(&self, other: &Rect) -> bool {
self.x < other.x + other.width
&& self.x + self.width > other.x
&& self.y < other.y + other.height
&& self.y + self.height > other.y
}
/// Get the intersection of two rectangles.
/// 获取两个矩形的交集。
pub fn intersection(&self, other: &Rect) -> Option<Rect> {
let x = self.x.max(other.x);
let y = self.y.max(other.y);
let right = (self.x + self.width).min(other.x + other.width);
let bottom = (self.y + self.height).min(other.y + other.height);
if right > x && bottom > y {
Some(Rect::new(x, y, right - x, bottom - y))
} else {
None
}
}
/// Get the union of two rectangles (bounding box).
/// 获取两个矩形的并集(包围盒)。
pub fn union(&self, other: &Rect) -> Rect {
let x = self.x.min(other.x);
let y = self.y.min(other.y);
let right = (self.x + self.width).max(other.x + other.width);
let bottom = (self.y + self.height).max(other.y + other.height);
Rect::new(x, y, right - x, bottom - y)
}
/// Expand the rectangle by a margin.
/// 按边距扩展矩形。
#[inline]
pub fn expand(&self, margin: f32) -> Rect {
Rect::new(
self.x - margin,
self.y - margin,
self.width + margin * 2.0,
self.height + margin * 2.0,
)
}
}

View File

@@ -0,0 +1,164 @@
//! 2D transform implementation.
//! 2D变换实现。
use super::Vec2;
use glam::Mat3;
/// 2D transformation combining position, rotation, and scale.
/// 组合位置、旋转和缩放的2D变换。
///
/// # Examples | 示例
/// ```rust
/// let mut transform = Transform2D::new();
/// transform.position = Vec2::new(100.0, 200.0);
/// transform.rotation = std::f32::consts::PI / 4.0; // 45 degrees
/// transform.scale = Vec2::new(2.0, 2.0);
///
/// let matrix = transform.to_matrix();
/// ```
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Transform2D {
/// Position in world space.
/// 世界空间中的位置。
pub position: Vec2,
/// Rotation in radians.
/// 旋转角度(弧度)。
pub rotation: f32,
/// Scale factor.
/// 缩放因子。
pub scale: Vec2,
/// Origin point for rotation and scaling (0-1 range, relative to size).
/// 旋转和缩放的原点0-1范围相对于尺寸
pub origin: Vec2,
}
impl Default for Transform2D {
fn default() -> Self {
Self {
position: Vec2::ZERO,
rotation: 0.0,
scale: Vec2::new(1.0, 1.0),
origin: Vec2::new(0.5, 0.5), // Center by default | 默认居中
}
}
}
impl Transform2D {
/// Create a new transform with default values.
/// 使用默认值创建新变换。
#[inline]
pub fn new() -> Self {
Self::default()
}
/// Create a transform with specified position.
/// 使用指定位置创建变换。
#[inline]
pub fn from_position(x: f32, y: f32) -> Self {
Self {
position: Vec2::new(x, y),
..Default::default()
}
}
/// Create a transform with position, rotation, and scale.
/// 使用位置、旋转和缩放创建变换。
#[inline]
pub fn from_pos_rot_scale(position: Vec2, rotation: f32, scale: Vec2) -> Self {
Self {
position,
rotation,
scale,
..Default::default()
}
}
/// Convert to a 3x3 transformation matrix.
/// 转换为3x3变换矩阵。
///
/// The matrix is constructed as: T * R * S (translate, rotate, scale).
/// 矩阵构造顺序为T * R * S平移、旋转、缩放
pub fn to_matrix(&self) -> Mat3 {
let cos = self.rotation.cos();
let sin = self.rotation.sin();
// Construct TRS matrix directly for performance
// 直接构造TRS矩阵以提高性能
Mat3::from_cols(
glam::Vec3::new(cos * self.scale.x, sin * self.scale.x, 0.0),
glam::Vec3::new(-sin * self.scale.y, cos * self.scale.y, 0.0),
glam::Vec3::new(self.position.x, self.position.y, 1.0),
)
}
/// Convert to a 3x3 matrix with origin offset applied.
/// 转换为应用原点偏移的3x3矩阵。
///
/// # Arguments | 参数
/// * `width` - Sprite width | 精灵宽度
/// * `height` - Sprite height | 精灵高度
pub fn to_matrix_with_origin(&self, width: f32, height: f32) -> Mat3 {
let ox = -self.origin.x * width * self.scale.x;
let oy = -self.origin.y * height * self.scale.y;
let cos = self.rotation.cos();
let sin = self.rotation.sin();
// Apply origin offset after rotation
// 在旋转后应用原点偏移
let tx = self.position.x + ox * cos - oy * sin;
let ty = self.position.y + ox * sin + oy * cos;
Mat3::from_cols(
glam::Vec3::new(cos * self.scale.x, sin * self.scale.x, 0.0),
glam::Vec3::new(-sin * self.scale.y, cos * self.scale.y, 0.0),
glam::Vec3::new(tx, ty, 1.0),
)
}
/// Transform a local point to world space.
/// 将局部点变换到世界空间。
#[inline]
pub fn transform_point(&self, point: Vec2) -> Vec2 {
let rotated = point.rotate(self.rotation);
Vec2::new(
rotated.x * self.scale.x + self.position.x,
rotated.y * self.scale.y + self.position.y,
)
}
/// Inverse transform a world point to local space.
/// 将世界点反变换到局部空间。
#[inline]
pub fn inverse_transform_point(&self, point: Vec2) -> Vec2 {
let local = Vec2::new(
(point.x - self.position.x) / self.scale.x,
(point.y - self.position.y) / self.scale.y,
);
local.rotate(-self.rotation)
}
/// Translate the transform by a delta.
/// 按增量平移变换。
#[inline]
pub fn translate(&mut self, delta: Vec2) {
self.position = self.position + delta;
}
/// Rotate the transform by an angle (in radians).
/// 按角度旋转变换(弧度)。
#[inline]
pub fn rotate(&mut self, angle: f32) {
self.rotation += angle;
}
/// Scale the transform by a factor.
/// 按因子缩放变换。
#[inline]
pub fn scale_by(&mut self, factor: Vec2) {
self.scale = Vec2::new(self.scale.x * factor.x, self.scale.y * factor.y);
}
}

View File

@@ -0,0 +1,214 @@
//! 2D vector implementation.
//! 2D向量实现。
use bytemuck::{Pod, Zeroable};
/// 2D vector for positions, velocities, and directions.
/// 用于位置、速度和方向的2D向量。
///
/// # Examples | 示例
/// ```rust
/// let pos = Vec2::new(100.0, 200.0);
/// let velocity = Vec2::new(1.0, 0.0);
/// let new_pos = pos + velocity * 16.0;
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Default, Pod, Zeroable)]
#[repr(C)]
pub struct Vec2 {
/// X component.
/// X分量。
pub x: f32,
/// Y component.
/// Y分量。
pub y: f32,
}
impl Vec2 {
/// Zero vector (0, 0).
/// 零向量。
pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
/// Unit vector pointing right (1, 0).
/// 指向右的单位向量。
pub const RIGHT: Self = Self { x: 1.0, y: 0.0 };
/// Unit vector pointing up (0, 1).
/// 指向上的单位向量。
pub const UP: Self = Self { x: 0.0, y: 1.0 };
/// Create a new 2D vector.
/// 创建新的2D向量。
#[inline]
pub const fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
/// Create a vector with both components set to the same value.
/// 创建两个分量相同的向量。
#[inline]
pub const fn splat(v: f32) -> Self {
Self { x: v, y: v }
}
/// Calculate the length (magnitude) of the vector.
/// 计算向量的长度(模)。
#[inline]
pub fn length(&self) -> f32 {
(self.x * self.x + self.y * self.y).sqrt()
}
/// Calculate the squared length (avoids sqrt).
/// 计算长度的平方(避免开方运算)。
#[inline]
pub fn length_squared(&self) -> f32 {
self.x * self.x + self.y * self.y
}
/// Normalize the vector (make it unit length).
/// 归一化向量(使其成为单位长度)。
#[inline]
pub fn normalize(&self) -> Self {
let len = self.length();
if len > 0.0 {
Self {
x: self.x / len,
y: self.y / len,
}
} else {
Self::ZERO
}
}
/// Calculate dot product with another vector.
/// 计算与另一个向量的点积。
#[inline]
pub fn dot(&self, other: &Self) -> f32 {
self.x * other.x + self.y * other.y
}
/// Calculate cross product (returns scalar for 2D).
/// 计算叉积2D返回标量
#[inline]
pub fn cross(&self, other: &Self) -> f32 {
self.x * other.y - self.y * other.x
}
/// Calculate distance to another point.
/// 计算到另一点的距离。
#[inline]
pub fn distance(&self, other: &Self) -> f32 {
(*self - *other).length()
}
/// Linear interpolation between two vectors.
/// 两个向量之间的线性插值。
#[inline]
pub fn lerp(&self, other: &Self, t: f32) -> Self {
Self {
x: self.x + (other.x - self.x) * t,
y: self.y + (other.y - self.y) * t,
}
}
/// Rotate the vector by an angle (in radians).
/// 按角度旋转向量(弧度)。
#[inline]
pub fn rotate(&self, angle: f32) -> Self {
let cos = angle.cos();
let sin = angle.sin();
Self {
x: self.x * cos - self.y * sin,
y: self.x * sin + self.y * cos,
}
}
/// Convert to glam Vec2.
/// 转换为glam Vec2。
#[inline]
pub fn to_glam(&self) -> glam::Vec2 {
glam::Vec2::new(self.x, self.y)
}
/// Create from glam Vec2.
/// 从glam Vec2创建。
#[inline]
pub fn from_glam(v: glam::Vec2) -> Self {
Self { x: v.x, y: v.y }
}
}
// Operator implementations | 运算符实现
impl std::ops::Add for Vec2 {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl std::ops::Sub for Vec2 {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
}
}
}
impl std::ops::Mul<f32> for Vec2 {
type Output = Self;
#[inline]
fn mul(self, rhs: f32) -> Self::Output {
Self {
x: self.x * rhs,
y: self.y * rhs,
}
}
}
impl std::ops::Div<f32> for Vec2 {
type Output = Self;
#[inline]
fn div(self, rhs: f32) -> Self::Output {
Self {
x: self.x / rhs,
y: self.y / rhs,
}
}
}
impl std::ops::Neg for Vec2 {
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
Self {
x: -self.x,
y: -self.y,
}
}
}
impl From<(f32, f32)> for Vec2 {
#[inline]
fn from((x, y): (f32, f32)) -> Self {
Self { x, y }
}
}
impl From<[f32; 2]> for Vec2 {
#[inline]
fn from([x, y]: [f32; 2]) -> Self {
Self { x, y }
}
}