refactor: reorganize package structure and decouple framework packages (#338)
* refactor: reorganize package structure and decouple framework packages ## Package Structure Reorganization - Reorganized 55 packages into categorized subdirectories: - packages/framework/ - Generic framework (Laya/Cocos compatible) - packages/engine/ - ESEngine core modules - packages/rendering/ - Rendering modules (WASM dependent) - packages/physics/ - Physics modules - packages/streaming/ - World streaming - packages/network-ext/ - Network extensions - packages/editor/ - Editor framework and plugins - packages/rust/ - Rust WASM engine - packages/tools/ - Build tools and SDK ## Framework Package Decoupling - Decoupled behavior-tree and blueprint packages from ESEngine dependencies - Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent) - ESEngine-specific code moved to esengine/ subpath exports - Framework packages now usable with Cocos/Laya without ESEngine ## CI Configuration - Updated CI to only type-check and lint framework packages - Added type-check:framework and lint:framework scripts ## Breaking Changes - Package import paths changed due to directory reorganization - ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine') * fix: update es-engine file path after directory reorganization * docs: update README to focus on framework over engine * ci: only build framework packages, remove Rust/WASM dependencies * fix: remove esengine subpath from behavior-tree and blueprint builds ESEngine integration code will only be available in full engine builds. Framework packages are now purely engine-agnostic. * fix: move network-protocols to framework, build both in CI * fix: update workflow paths from packages/core to packages/framework/core * fix: exclude esengine folder from type-check in behavior-tree and blueprint * fix: update network tsconfig references to new paths * fix: add test:ci:framework to only test framework packages in CI * fix: only build core and math npm packages in CI * fix: exclude test files from CodeQL and fix string escaping security issue
This commit is contained in:
7
packages/rust/engine-shared/src/batch/mod.rs
Normal file
7
packages/rust/engine-shared/src/batch/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
//! 批处理数据结构
|
||||
//!
|
||||
//! Batch data structures.
|
||||
|
||||
mod sprite_data;
|
||||
|
||||
pub use sprite_data::*;
|
||||
417
packages/rust/engine-shared/src/batch/sprite_data.rs
Normal file
417
packages/rust/engine-shared/src/batch/sprite_data.rs
Normal file
@@ -0,0 +1,417 @@
|
||||
//! 精灵批处理数据结构
|
||||
//!
|
||||
//! Sprite batch data structures.
|
||||
//!
|
||||
//! 本模块提供纯数据结构,不包含任何渲染调用。
|
||||
//! This module provides pure data structures without any rendering calls.
|
||||
|
||||
use crate::types::vertex::SpriteVertex;
|
||||
|
||||
/// 批处理键
|
||||
///
|
||||
/// 用于区分不同批次(按材质和纹理分组)。
|
||||
///
|
||||
/// Batch key.
|
||||
/// Used to distinguish different batches (grouped by material and texture).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct BatchKey {
|
||||
/// 材质 ID | Material ID
|
||||
pub material_id: u32,
|
||||
/// 纹理 ID | Texture ID
|
||||
pub texture_id: u32,
|
||||
}
|
||||
|
||||
impl BatchKey {
|
||||
/// 创建新的批处理键
|
||||
///
|
||||
/// Create new batch key.
|
||||
pub const fn new(material_id: u32, texture_id: u32) -> Self {
|
||||
Self { material_id, texture_id }
|
||||
}
|
||||
|
||||
/// 默认批处理键(默认材质和纹理)
|
||||
///
|
||||
/// Default batch key (default material and texture).
|
||||
pub const fn default_key() -> Self {
|
||||
Self::new(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BatchKey {
|
||||
fn default() -> Self {
|
||||
Self::default_key()
|
||||
}
|
||||
}
|
||||
|
||||
/// 精灵批处理数据(纯数据,无渲染调用)
|
||||
///
|
||||
/// 预分配的数组用于存储精灵顶点数据,避免每帧分配。
|
||||
///
|
||||
/// Sprite batch data (pure data, no rendering calls).
|
||||
/// Pre-allocated arrays for storing sprite vertex data, avoiding per-frame allocation.
|
||||
#[derive(Debug)]
|
||||
pub struct SpriteBatchBuffer {
|
||||
/// 顶点数据 | Vertex data
|
||||
vertices: Vec<SpriteVertex>,
|
||||
|
||||
/// 索引数据 | Index data
|
||||
indices: Vec<u16>,
|
||||
|
||||
/// 批次列表:(BatchKey, 起始索引, 索引数量) | Batch list: (BatchKey, start index, index count)
|
||||
batches: Vec<(BatchKey, u32, u32)>,
|
||||
|
||||
/// 最大精灵数 | Max sprite count
|
||||
max_sprites: usize,
|
||||
|
||||
/// 当前精灵数 | Current sprite count
|
||||
sprite_count: usize,
|
||||
|
||||
/// 上一个批处理键 | Last batch key
|
||||
last_batch_key: Option<BatchKey>,
|
||||
}
|
||||
|
||||
impl SpriteBatchBuffer {
|
||||
/// 每个精灵的顶点数 | Vertices per sprite
|
||||
pub const VERTICES_PER_SPRITE: usize = 4;
|
||||
|
||||
/// 每个精灵的索引数 | Indices per sprite
|
||||
pub const INDICES_PER_SPRITE: usize = 6;
|
||||
|
||||
/// 创建新的批处理缓冲区
|
||||
///
|
||||
/// Create new batch buffer.
|
||||
pub fn new(max_sprites: usize) -> Self {
|
||||
let max_vertices = max_sprites * Self::VERTICES_PER_SPRITE;
|
||||
let max_indices = max_sprites * Self::INDICES_PER_SPRITE;
|
||||
|
||||
// 预生成索引
|
||||
let mut indices = Vec::with_capacity(max_indices);
|
||||
for i in 0..max_sprites {
|
||||
let base = (i * Self::VERTICES_PER_SPRITE) as u16;
|
||||
indices.extend_from_slice(&[
|
||||
base,
|
||||
base + 1,
|
||||
base + 2,
|
||||
base + 2,
|
||||
base + 3,
|
||||
base,
|
||||
]);
|
||||
}
|
||||
|
||||
Self {
|
||||
vertices: Vec::with_capacity(max_vertices),
|
||||
indices,
|
||||
batches: Vec::with_capacity(64),
|
||||
max_sprites,
|
||||
sprite_count: 0,
|
||||
last_batch_key: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 清空缓冲区(为下一帧准备)
|
||||
///
|
||||
/// Clear buffer (prepare for next frame).
|
||||
pub fn clear(&mut self) {
|
||||
self.vertices.clear();
|
||||
self.batches.clear();
|
||||
self.sprite_count = 0;
|
||||
self.last_batch_key = None;
|
||||
}
|
||||
|
||||
/// 添加精灵
|
||||
///
|
||||
/// Add sprite.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `x`, `y`: 位置 | Position
|
||||
/// - `width`, `height`: 尺寸 | Size
|
||||
/// - `rotation`: 旋转(弧度)| Rotation (radians)
|
||||
/// - `origin_x`, `origin_y`: 原点(0-1)| Origin (0-1)
|
||||
/// - `u0`, `v0`, `u1`, `v1`: UV 坐标 | UV coordinates
|
||||
/// - `color`: 打包的 RGBA 颜色 | Packed RGBA color
|
||||
/// - `texture_id`: 纹理 ID | Texture ID
|
||||
/// - `material_id`: 材质 ID | Material ID
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn add_sprite(
|
||||
&mut self,
|
||||
x: f32,
|
||||
y: f32,
|
||||
width: f32,
|
||||
height: f32,
|
||||
rotation: f32,
|
||||
origin_x: f32,
|
||||
origin_y: f32,
|
||||
u0: f32,
|
||||
v0: f32,
|
||||
u1: f32,
|
||||
v1: f32,
|
||||
color: u32,
|
||||
texture_id: u32,
|
||||
material_id: u32,
|
||||
) -> bool {
|
||||
if self.sprite_count >= self.max_sprites {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 解包颜色
|
||||
let r = ((color >> 24) & 0xFF) as f32 / 255.0;
|
||||
let g = ((color >> 16) & 0xFF) as f32 / 255.0;
|
||||
let b = ((color >> 8) & 0xFF) as f32 / 255.0;
|
||||
let a = (color & 0xFF) as f32 / 255.0;
|
||||
let color_arr = [r, g, b, a];
|
||||
|
||||
// 计算宽高比
|
||||
let aspect = if height != 0.0 { width / height } else { 1.0 };
|
||||
|
||||
// 计算顶点位置(考虑原点和旋转)
|
||||
let ox = origin_x * width;
|
||||
let oy = origin_y * height;
|
||||
|
||||
let cos_r = rotation.cos();
|
||||
let sin_r = rotation.sin();
|
||||
|
||||
// 四个角的局部坐标
|
||||
let corners = [
|
||||
(-ox, -oy), // 左上
|
||||
(width - ox, -oy), // 右上
|
||||
(width - ox, height - oy), // 右下
|
||||
(-ox, height - oy), // 左下
|
||||
];
|
||||
|
||||
// UV 坐标
|
||||
let uvs = [
|
||||
[u0, v0], // 左上
|
||||
[u1, v0], // 右上
|
||||
[u1, v1], // 右下
|
||||
[u0, v1], // 左下
|
||||
];
|
||||
|
||||
// 添加四个顶点
|
||||
for i in 0..4 {
|
||||
let (lx, ly) = corners[i];
|
||||
let rx = lx * cos_r - ly * sin_r + x;
|
||||
let ry = lx * sin_r + ly * cos_r + y;
|
||||
|
||||
self.vertices.push(SpriteVertex {
|
||||
position: [rx, ry],
|
||||
texcoord: uvs[i],
|
||||
color: color_arr,
|
||||
aspect,
|
||||
});
|
||||
}
|
||||
|
||||
// 更新批次
|
||||
let key = BatchKey::new(material_id, texture_id);
|
||||
if self.last_batch_key != Some(key) {
|
||||
// 开始新批次
|
||||
let start_index = (self.sprite_count * Self::INDICES_PER_SPRITE) as u32;
|
||||
self.batches.push((key, start_index, Self::INDICES_PER_SPRITE as u32));
|
||||
self.last_batch_key = Some(key);
|
||||
} else {
|
||||
// 扩展当前批次
|
||||
if let Some((_, _, count)) = self.batches.last_mut() {
|
||||
*count += Self::INDICES_PER_SPRITE as u32;
|
||||
}
|
||||
}
|
||||
|
||||
self.sprite_count += 1;
|
||||
true
|
||||
}
|
||||
|
||||
/// 从 SoA 数据添加精灵(与现有 API 兼容)
|
||||
///
|
||||
/// Add sprites from SoA data (compatible with existing API).
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `transforms`: [x, y, rotation, scaleX, scaleY, originX, originY] per sprite
|
||||
/// - `texture_ids`: 纹理 ID | Texture IDs
|
||||
/// - `uvs`: [u0, v0, u1, v1] per sprite
|
||||
/// - `colors`: 打包的 RGBA 颜色 | Packed RGBA colors
|
||||
/// - `material_ids`: 材质 ID | Material IDs
|
||||
/// - `texture_sizes`: 纹理尺寸映射函数 | Texture size lookup function
|
||||
pub fn add_sprites_soa<F>(
|
||||
&mut self,
|
||||
transforms: &[f32],
|
||||
texture_ids: &[u32],
|
||||
uvs: &[f32],
|
||||
colors: &[u32],
|
||||
material_ids: &[u32],
|
||||
texture_sizes: F,
|
||||
) -> usize
|
||||
where
|
||||
F: Fn(u32) -> (f32, f32),
|
||||
{
|
||||
let count = texture_ids.len();
|
||||
let mut added = 0;
|
||||
|
||||
for i in 0..count {
|
||||
let t_offset = i * 7;
|
||||
let uv_offset = i * 4;
|
||||
|
||||
if t_offset + 6 >= transforms.len() || uv_offset + 3 >= uvs.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
let x = transforms[t_offset];
|
||||
let y = transforms[t_offset + 1];
|
||||
let rotation = transforms[t_offset + 2];
|
||||
let scale_x = transforms[t_offset + 3];
|
||||
let scale_y = transforms[t_offset + 4];
|
||||
let origin_x = transforms[t_offset + 5];
|
||||
let origin_y = transforms[t_offset + 6];
|
||||
|
||||
let texture_id = texture_ids[i];
|
||||
let (tex_width, tex_height) = texture_sizes(texture_id);
|
||||
|
||||
let width = tex_width * scale_x;
|
||||
let height = tex_height * scale_y;
|
||||
|
||||
let u0 = uvs[uv_offset];
|
||||
let v0 = uvs[uv_offset + 1];
|
||||
let u1 = uvs[uv_offset + 2];
|
||||
let v1 = uvs[uv_offset + 3];
|
||||
|
||||
let color = colors[i];
|
||||
let material_id = material_ids[i];
|
||||
|
||||
if self.add_sprite(
|
||||
x, y, width, height, rotation, origin_x, origin_y,
|
||||
u0, v0, u1, v1, color, texture_id, material_id,
|
||||
) {
|
||||
added += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
added
|
||||
}
|
||||
|
||||
/// 获取顶点数据
|
||||
///
|
||||
/// Get vertex data.
|
||||
pub fn vertices(&self) -> &[SpriteVertex] {
|
||||
&self.vertices
|
||||
}
|
||||
|
||||
/// 获取顶点数据(字节)
|
||||
///
|
||||
/// Get vertex data as bytes.
|
||||
pub fn vertices_as_bytes(&self) -> &[u8] {
|
||||
bytemuck::cast_slice(&self.vertices)
|
||||
}
|
||||
|
||||
/// 获取索引数据
|
||||
///
|
||||
/// Get index data.
|
||||
pub fn indices(&self) -> &[u16] {
|
||||
&self.indices[..self.sprite_count * Self::INDICES_PER_SPRITE]
|
||||
}
|
||||
|
||||
/// 获取批次列表
|
||||
///
|
||||
/// Get batch list.
|
||||
pub fn batches(&self) -> &[(BatchKey, u32, u32)] {
|
||||
&self.batches
|
||||
}
|
||||
|
||||
/// 获取精灵数量
|
||||
///
|
||||
/// Get sprite count.
|
||||
pub fn sprite_count(&self) -> usize {
|
||||
self.sprite_count
|
||||
}
|
||||
|
||||
/// 获取最大精灵数
|
||||
///
|
||||
/// Get max sprite count.
|
||||
pub fn max_sprites(&self) -> usize {
|
||||
self.max_sprites
|
||||
}
|
||||
|
||||
/// 是否为空
|
||||
///
|
||||
/// Check if empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.sprite_count == 0
|
||||
}
|
||||
|
||||
/// 是否已满
|
||||
///
|
||||
/// Check if full.
|
||||
pub fn is_full(&self) -> bool {
|
||||
self.sprite_count >= self.max_sprites
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SpriteBatchBuffer {
|
||||
fn default() -> Self {
|
||||
Self::new(10000)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_batch_buffer_creation() {
|
||||
let buffer = SpriteBatchBuffer::new(100);
|
||||
assert_eq!(buffer.max_sprites(), 100);
|
||||
assert_eq!(buffer.sprite_count(), 0);
|
||||
assert!(buffer.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_sprite() {
|
||||
let mut buffer = SpriteBatchBuffer::new(100);
|
||||
|
||||
let result = buffer.add_sprite(
|
||||
100.0, 100.0, // position
|
||||
64.0, 64.0, // size
|
||||
0.0, // rotation
|
||||
0.5, 0.5, // origin
|
||||
0.0, 0.0, 1.0, 1.0, // uvs
|
||||
0xFFFFFFFF, // color (white)
|
||||
1, // texture_id
|
||||
0, // material_id
|
||||
);
|
||||
|
||||
assert!(result);
|
||||
assert_eq!(buffer.sprite_count(), 1);
|
||||
assert_eq!(buffer.vertices().len(), 4);
|
||||
assert_eq!(buffer.batches().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_batch_grouping() {
|
||||
let mut buffer = SpriteBatchBuffer::new(100);
|
||||
|
||||
// 添加两个相同纹理/材质的精灵(应该合并到一个批次)
|
||||
buffer.add_sprite(0.0, 0.0, 32.0, 32.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0xFFFFFFFF, 1, 0);
|
||||
buffer.add_sprite(50.0, 0.0, 32.0, 32.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0xFFFFFFFF, 1, 0);
|
||||
|
||||
assert_eq!(buffer.batches().len(), 1);
|
||||
assert_eq!(buffer.batches()[0].2, 12); // 2 sprites * 6 indices
|
||||
|
||||
// 添加不同纹理的精灵(应该创建新批次)
|
||||
buffer.add_sprite(100.0, 0.0, 32.0, 32.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0xFFFFFFFF, 2, 0);
|
||||
|
||||
assert_eq!(buffer.batches().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clear() {
|
||||
let mut buffer = SpriteBatchBuffer::new(100);
|
||||
|
||||
buffer.add_sprite(0.0, 0.0, 32.0, 32.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0xFFFFFFFF, 1, 0);
|
||||
assert_eq!(buffer.sprite_count(), 1);
|
||||
|
||||
buffer.clear();
|
||||
assert_eq!(buffer.sprite_count(), 0);
|
||||
assert!(buffer.is_empty());
|
||||
assert!(buffer.batches().is_empty());
|
||||
}
|
||||
}
|
||||
421
packages/rust/engine-shared/src/camera.rs
Normal file
421
packages/rust/engine-shared/src/camera.rs
Normal file
@@ -0,0 +1,421 @@
|
||||
//! 2D 相机(纯数学实现)
|
||||
//!
|
||||
//! 2D camera (pure math implementation).
|
||||
|
||||
use glam::{Mat3, Vec2};
|
||||
|
||||
/// 2D 相机
|
||||
///
|
||||
/// 提供正交投影、坐标转换等功能。
|
||||
/// 纯数学实现,不依赖任何图形 API。
|
||||
///
|
||||
/// 2D camera.
|
||||
/// Provides orthographic projection, coordinate conversion, etc.
|
||||
/// Pure math implementation, no graphics API dependencies.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Camera2D {
|
||||
/// 相机位置(世界坐标)| Camera position (world coordinates)
|
||||
position: Vec2,
|
||||
|
||||
/// 旋转角度(弧度,顺时针为正)| Rotation (radians, clockwise positive)
|
||||
rotation: f32,
|
||||
|
||||
/// 缩放级别 | Zoom level
|
||||
zoom: f32,
|
||||
|
||||
/// 视口宽度 | Viewport width
|
||||
width: f32,
|
||||
|
||||
/// 视口高度 | Viewport height
|
||||
height: f32,
|
||||
}
|
||||
|
||||
impl Default for Camera2D {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
position: Vec2::ZERO,
|
||||
rotation: 0.0,
|
||||
zoom: 1.0,
|
||||
width: 800.0,
|
||||
height: 600.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Camera2D {
|
||||
/// 创建新相机
|
||||
///
|
||||
/// Create new camera.
|
||||
pub fn new(width: f32, height: f32) -> Self {
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Builder Pattern ====================
|
||||
|
||||
/// 设置位置
|
||||
///
|
||||
/// Set position.
|
||||
pub fn with_position(mut self, x: f32, y: f32) -> Self {
|
||||
self.position = Vec2::new(x, y);
|
||||
self
|
||||
}
|
||||
|
||||
/// 设置缩放
|
||||
///
|
||||
/// Set zoom.
|
||||
pub fn with_zoom(mut self, zoom: f32) -> Self {
|
||||
self.zoom = zoom.max(0.001);
|
||||
self
|
||||
}
|
||||
|
||||
/// 设置旋转
|
||||
///
|
||||
/// Set rotation.
|
||||
pub fn with_rotation(mut self, rotation: f32) -> Self {
|
||||
self.rotation = rotation;
|
||||
self
|
||||
}
|
||||
|
||||
// ==================== Getters ====================
|
||||
|
||||
/// 获取位置
|
||||
///
|
||||
/// Get position.
|
||||
pub fn position(&self) -> Vec2 {
|
||||
self.position
|
||||
}
|
||||
|
||||
/// 获取 X 坐标
|
||||
///
|
||||
/// Get X coordinate.
|
||||
pub fn x(&self) -> f32 {
|
||||
self.position.x
|
||||
}
|
||||
|
||||
/// 获取 Y 坐标
|
||||
///
|
||||
/// Get Y coordinate.
|
||||
pub fn y(&self) -> f32 {
|
||||
self.position.y
|
||||
}
|
||||
|
||||
/// 获取旋转
|
||||
///
|
||||
/// Get rotation.
|
||||
pub fn rotation(&self) -> f32 {
|
||||
self.rotation
|
||||
}
|
||||
|
||||
/// 获取缩放
|
||||
///
|
||||
/// Get zoom.
|
||||
pub fn zoom(&self) -> f32 {
|
||||
self.zoom
|
||||
}
|
||||
|
||||
/// 获取视口宽度
|
||||
///
|
||||
/// Get viewport width.
|
||||
pub fn width(&self) -> f32 {
|
||||
self.width
|
||||
}
|
||||
|
||||
/// 获取视口高度
|
||||
///
|
||||
/// Get viewport height.
|
||||
pub fn height(&self) -> f32 {
|
||||
self.height
|
||||
}
|
||||
|
||||
// ==================== Setters ====================
|
||||
|
||||
/// 设置位置
|
||||
///
|
||||
/// Set position.
|
||||
pub fn set_position(&mut self, x: f32, y: f32) {
|
||||
self.position = Vec2::new(x, y);
|
||||
}
|
||||
|
||||
/// 设置旋转
|
||||
///
|
||||
/// Set rotation.
|
||||
pub fn set_rotation(&mut self, rotation: f32) {
|
||||
self.rotation = rotation;
|
||||
}
|
||||
|
||||
/// 设置缩放
|
||||
///
|
||||
/// Set zoom.
|
||||
pub fn set_zoom(&mut self, zoom: f32) {
|
||||
self.zoom = zoom.max(0.001);
|
||||
}
|
||||
|
||||
/// 调整视口大小
|
||||
///
|
||||
/// Resize viewport.
|
||||
pub fn resize(&mut self, width: f32, height: f32) {
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
}
|
||||
|
||||
// ==================== Transform Methods ====================
|
||||
|
||||
/// 移动相机
|
||||
///
|
||||
/// Move camera.
|
||||
pub fn translate(&mut self, dx: f32, dy: f32) {
|
||||
self.position.x += dx;
|
||||
self.position.y += dy;
|
||||
}
|
||||
|
||||
/// 旋转相机
|
||||
///
|
||||
/// Rotate camera.
|
||||
pub fn rotate(&mut self, delta: f32) {
|
||||
self.rotation += delta;
|
||||
}
|
||||
|
||||
/// 缩放相机
|
||||
///
|
||||
/// Zoom camera.
|
||||
pub fn zoom_by(&mut self, factor: f32) {
|
||||
self.zoom = (self.zoom * factor).max(0.001);
|
||||
}
|
||||
|
||||
// ==================== Matrix Generation ====================
|
||||
|
||||
/// 获取投影矩阵
|
||||
///
|
||||
/// 将世界坐标转换为 NDC(-1 到 1)。
|
||||
///
|
||||
/// Get projection matrix.
|
||||
/// Transforms world coordinates to NDC (-1 to 1).
|
||||
pub fn projection_matrix(&self) -> Mat3 {
|
||||
// 计算缩放
|
||||
let scale_x = 2.0 / self.width * self.zoom;
|
||||
let scale_y = 2.0 / self.height * self.zoom;
|
||||
|
||||
// 计算旋转
|
||||
let cos_r = self.rotation.cos();
|
||||
let sin_r = self.rotation.sin();
|
||||
|
||||
// 计算平移(相机位置取反)
|
||||
let tx = -self.position.x;
|
||||
let ty = -self.position.y;
|
||||
|
||||
// 构建变换矩阵:Scale * Rotate * Translate
|
||||
// 先平移,再旋转,最后缩放
|
||||
Mat3::from_cols_array(&[
|
||||
scale_x * cos_r,
|
||||
scale_y * sin_r,
|
||||
0.0,
|
||||
-scale_x * sin_r,
|
||||
scale_y * cos_r,
|
||||
0.0,
|
||||
scale_x * (tx * cos_r - ty * sin_r),
|
||||
scale_y * (tx * sin_r + ty * cos_r),
|
||||
1.0,
|
||||
])
|
||||
}
|
||||
|
||||
/// 获取视图矩阵
|
||||
///
|
||||
/// Get view matrix.
|
||||
pub fn view_matrix(&self) -> Mat3 {
|
||||
let cos_r = self.rotation.cos();
|
||||
let sin_r = self.rotation.sin();
|
||||
|
||||
let tx = -self.position.x;
|
||||
let ty = -self.position.y;
|
||||
|
||||
Mat3::from_cols_array(&[
|
||||
cos_r,
|
||||
sin_r,
|
||||
0.0,
|
||||
-sin_r,
|
||||
cos_r,
|
||||
0.0,
|
||||
tx * cos_r - ty * sin_r,
|
||||
tx * sin_r + ty * cos_r,
|
||||
1.0,
|
||||
])
|
||||
}
|
||||
|
||||
/// 获取逆投影矩阵
|
||||
///
|
||||
/// Get inverse projection matrix.
|
||||
pub fn inverse_projection_matrix(&self) -> Mat3 {
|
||||
self.projection_matrix().inverse()
|
||||
}
|
||||
|
||||
// ==================== Coordinate Conversion ====================
|
||||
|
||||
/// 屏幕坐标转世界坐标
|
||||
///
|
||||
/// Screen to world coordinates.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `screen_pos`: 屏幕坐标(像素,左上角为原点)| Screen coordinates (pixels, origin at top-left)
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// 世界坐标 | World coordinates
|
||||
pub fn screen_to_world(&self, screen_pos: Vec2) -> Vec2 {
|
||||
// 屏幕坐标转 NDC
|
||||
let ndc_x = (screen_pos.x / self.width) * 2.0 - 1.0;
|
||||
let ndc_y = 1.0 - (screen_pos.y / self.height) * 2.0; // Y 轴翻转
|
||||
|
||||
// NDC 转世界坐标
|
||||
let inv_proj = self.inverse_projection_matrix();
|
||||
let world = inv_proj * glam::Vec3::new(ndc_x, ndc_y, 1.0);
|
||||
|
||||
Vec2::new(world.x, world.y)
|
||||
}
|
||||
|
||||
/// 世界坐标转屏幕坐标
|
||||
///
|
||||
/// World to screen coordinates.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `world_pos`: 世界坐标 | World coordinates
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// 屏幕坐标(像素,左上角为原点)| Screen coordinates (pixels, origin at top-left)
|
||||
pub fn world_to_screen(&self, world_pos: Vec2) -> Vec2 {
|
||||
// 世界坐标转 NDC
|
||||
let proj = self.projection_matrix();
|
||||
let ndc = proj * glam::Vec3::new(world_pos.x, world_pos.y, 1.0);
|
||||
|
||||
// NDC 转屏幕坐标
|
||||
let screen_x = (ndc.x + 1.0) * 0.5 * self.width;
|
||||
let screen_y = (1.0 - ndc.y) * 0.5 * self.height; // Y 轴翻转
|
||||
|
||||
Vec2::new(screen_x, screen_y)
|
||||
}
|
||||
|
||||
/// 获取可见区域(世界坐标 AABB)
|
||||
///
|
||||
/// Get visible bounds (world coordinate AABB).
|
||||
pub fn visible_bounds(&self) -> (Vec2, Vec2) {
|
||||
// 四个角的屏幕坐标
|
||||
let corners = [
|
||||
Vec2::new(0.0, 0.0),
|
||||
Vec2::new(self.width, 0.0),
|
||||
Vec2::new(self.width, self.height),
|
||||
Vec2::new(0.0, self.height),
|
||||
];
|
||||
|
||||
// 转换为世界坐标
|
||||
let world_corners: Vec<Vec2> = corners.iter().map(|c| self.screen_to_world(*c)).collect();
|
||||
|
||||
// 计算 AABB
|
||||
let mut min = world_corners[0];
|
||||
let mut max = world_corners[0];
|
||||
|
||||
for corner in &world_corners[1..] {
|
||||
min = min.min(*corner);
|
||||
max = max.max(*corner);
|
||||
}
|
||||
|
||||
(min, max)
|
||||
}
|
||||
|
||||
/// 检查点是否在可见区域内
|
||||
///
|
||||
/// Check if point is visible.
|
||||
pub fn is_point_visible(&self, world_pos: Vec2) -> bool {
|
||||
let (min, max) = self.visible_bounds();
|
||||
world_pos.x >= min.x && world_pos.x <= max.x && world_pos.y >= min.y && world_pos.y <= max.y
|
||||
}
|
||||
|
||||
/// 检查矩形是否与可见区域相交
|
||||
///
|
||||
/// Check if rectangle intersects visible area.
|
||||
pub fn is_rect_visible(&self, pos: Vec2, size: Vec2) -> bool {
|
||||
let (min, max) = self.visible_bounds();
|
||||
let rect_max = pos + size;
|
||||
|
||||
pos.x <= max.x && rect_max.x >= min.x && pos.y <= max.y && rect_max.y >= min.y
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_camera_creation() {
|
||||
let camera = Camera2D::new(800.0, 600.0);
|
||||
assert_eq!(camera.width(), 800.0);
|
||||
assert_eq!(camera.height(), 600.0);
|
||||
assert_eq!(camera.position(), Vec2::ZERO);
|
||||
assert_eq!(camera.zoom(), 1.0);
|
||||
assert_eq!(camera.rotation(), 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_camera_builder() {
|
||||
let camera = Camera2D::new(800.0, 600.0)
|
||||
.with_position(100.0, 50.0)
|
||||
.with_zoom(2.0)
|
||||
.with_rotation(std::f32::consts::PI / 4.0);
|
||||
|
||||
assert_eq!(camera.position(), Vec2::new(100.0, 50.0));
|
||||
assert_eq!(camera.zoom(), 2.0);
|
||||
assert!((camera.rotation() - std::f32::consts::PI / 4.0).abs() < 0.0001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_screen_to_world_identity() {
|
||||
let camera = Camera2D::new(800.0, 600.0);
|
||||
|
||||
// 屏幕中心应该对应世界原点
|
||||
let center = camera.screen_to_world(Vec2::new(400.0, 300.0));
|
||||
assert!((center.x).abs() < 0.001);
|
||||
assert!((center.y).abs() < 0.001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_world_to_screen_identity() {
|
||||
let camera = Camera2D::new(800.0, 600.0);
|
||||
|
||||
// 世界原点应该对应屏幕中心
|
||||
let center = camera.world_to_screen(Vec2::ZERO);
|
||||
assert!((center.x - 400.0).abs() < 0.001);
|
||||
assert!((center.y - 300.0).abs() < 0.001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_coordinate_roundtrip() {
|
||||
let camera = Camera2D::new(800.0, 600.0)
|
||||
.with_position(100.0, 50.0)
|
||||
.with_zoom(1.5)
|
||||
.with_rotation(0.3);
|
||||
|
||||
let world_pos = Vec2::new(200.0, 150.0);
|
||||
let screen_pos = camera.world_to_screen(world_pos);
|
||||
let back_to_world = camera.screen_to_world(screen_pos);
|
||||
|
||||
assert!((back_to_world.x - world_pos.x).abs() < 0.01);
|
||||
assert!((back_to_world.y - world_pos.y).abs() < 0.01);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_visible_bounds() {
|
||||
let camera = Camera2D::new(800.0, 600.0);
|
||||
let (min, max) = camera.visible_bounds();
|
||||
|
||||
// 默认相机应该看到 -400 到 400(水平),-300 到 300(垂直)
|
||||
assert!((min.x - (-400.0)).abs() < 0.01);
|
||||
assert!((max.x - 400.0).abs() < 0.01);
|
||||
assert!((min.y - (-300.0)).abs() < 0.01);
|
||||
assert!((max.y - 300.0).abs() < 0.01);
|
||||
}
|
||||
}
|
||||
40
packages/rust/engine-shared/src/lib.rs
Normal file
40
packages/rust/engine-shared/src/lib.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
//! ESEngine 图形后端共享库
|
||||
//!
|
||||
//! 本库提供跨平台图形后端抽象层,包括:
|
||||
//! - 类型安全的资源句柄
|
||||
//! - 图形后端 trait 定义
|
||||
//! - 平台抽象 trait
|
||||
//! - 共享数据结构
|
||||
//!
|
||||
//! ESEngine graphics backend shared library.
|
||||
//! Provides cross-platform graphics backend abstraction including:
|
||||
//! - Type-safe resource handles
|
||||
//! - Graphics backend trait definitions
|
||||
//! - Platform abstraction traits
|
||||
//! - Shared data structures
|
||||
|
||||
pub mod types;
|
||||
pub mod traits;
|
||||
pub mod batch;
|
||||
pub mod camera;
|
||||
|
||||
// Re-export commonly used items | 重新导出常用项
|
||||
pub use types::{
|
||||
handle::*,
|
||||
vertex::*,
|
||||
blend::*,
|
||||
uniform::*,
|
||||
texture::*,
|
||||
};
|
||||
|
||||
pub use traits::{
|
||||
backend::*,
|
||||
platform::*,
|
||||
renderer::*,
|
||||
};
|
||||
|
||||
pub use batch::*;
|
||||
pub use camera::*;
|
||||
|
||||
// Re-export glam for convenience | 方便使用,重新导出 glam
|
||||
pub use glam::{Vec2, Vec3, Vec4, Mat3, Mat4};
|
||||
519
packages/rust/engine-shared/src/traits/backend.rs
Normal file
519
packages/rust/engine-shared/src/traits/backend.rs
Normal file
@@ -0,0 +1,519 @@
|
||||
//! 图形后端主 trait
|
||||
//!
|
||||
//! Main graphics backend trait.
|
||||
|
||||
use crate::types::{
|
||||
handle::*,
|
||||
vertex::*,
|
||||
blend::*,
|
||||
texture::*,
|
||||
uniform::UniformValue,
|
||||
};
|
||||
use glam::{Vec2, Vec3, Vec4, Mat3, Mat4};
|
||||
use thiserror::Error;
|
||||
|
||||
// ==================== 错误类型 | Error Types ====================
|
||||
|
||||
/// 图形后端错误
|
||||
///
|
||||
/// Graphics backend error.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum GraphicsError {
|
||||
/// 着色器编译失败 | Shader compilation failed
|
||||
#[error("Shader compilation failed: {0}")]
|
||||
ShaderCompilation(String),
|
||||
|
||||
/// 着色器链接失败 | Shader linking failed
|
||||
#[error("Shader linking failed: {0}")]
|
||||
ShaderLinking(String),
|
||||
|
||||
/// 纹理创建失败 | Texture creation failed
|
||||
#[error("Texture creation failed: {0}")]
|
||||
TextureCreation(String),
|
||||
|
||||
/// 缓冲区创建失败 | Buffer creation failed
|
||||
#[error("Buffer creation failed: {0}")]
|
||||
BufferCreation(String),
|
||||
|
||||
/// 无效句柄 | Invalid handle
|
||||
#[error("Invalid handle: {0}")]
|
||||
InvalidHandle(String),
|
||||
|
||||
/// 上下文丢失 | Context lost
|
||||
#[error("Context lost")]
|
||||
ContextLost,
|
||||
|
||||
/// 不支持的操作 | Unsupported operation
|
||||
#[error("Unsupported operation: {0}")]
|
||||
Unsupported(String),
|
||||
|
||||
/// 后端错误 | Backend error
|
||||
#[error("Backend error: {0}")]
|
||||
Backend(String),
|
||||
|
||||
/// 资源不存在 | Resource not found
|
||||
#[error("Resource not found: {0}")]
|
||||
ResourceNotFound(String),
|
||||
|
||||
/// 数据大小不匹配 | Data size mismatch
|
||||
#[error("Data size mismatch: expected {expected}, got {actual}")]
|
||||
DataSizeMismatch { expected: usize, actual: usize },
|
||||
}
|
||||
|
||||
/// 图形操作结果
|
||||
///
|
||||
/// Graphics operation result.
|
||||
pub type GraphicsResult<T> = Result<T, GraphicsError>;
|
||||
|
||||
// ==================== 缓冲区用途 | Buffer Usage ====================
|
||||
|
||||
/// 缓冲区用途
|
||||
///
|
||||
/// Buffer usage.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
pub enum BufferUsage {
|
||||
/// 静态数据(不常更新)| Static data (rarely updated)
|
||||
#[default]
|
||||
Static,
|
||||
/// 动态数据(经常更新)| Dynamic data (frequently updated)
|
||||
Dynamic,
|
||||
/// 流式数据(每帧更新)| Streaming data (updated every frame)
|
||||
Stream,
|
||||
}
|
||||
|
||||
// ==================== 图形功能 | Graphics Features ====================
|
||||
|
||||
/// 图形功能
|
||||
///
|
||||
/// Graphics feature.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum GraphicsFeature {
|
||||
/// 各向异性过滤 | Anisotropic filtering
|
||||
AnisotropicFiltering,
|
||||
/// 实例化渲染 | Instanced rendering
|
||||
Instancing,
|
||||
/// 计算着色器 | Compute shaders
|
||||
ComputeShaders,
|
||||
/// 多渲染目标 | Multiple render targets
|
||||
MultipleRenderTargets,
|
||||
/// 浮点纹理 | Float textures
|
||||
FloatTextures,
|
||||
/// WebGPU | WebGPU support
|
||||
WebGPU,
|
||||
}
|
||||
|
||||
// ==================== 图形后端 Trait | Graphics Backend Trait ====================
|
||||
|
||||
/// 图形后端主 trait
|
||||
///
|
||||
/// 定义所有图形操作的抽象接口,由具体后端(WebGL2、WGPU 等)实现。
|
||||
///
|
||||
/// Main graphics backend trait.
|
||||
/// Defines abstract interface for all graphics operations,
|
||||
/// implemented by concrete backends (WebGL2, WGPU, etc.).
|
||||
pub trait GraphicsBackend: Sized {
|
||||
// ==================== 基本信息 | Basic Info ====================
|
||||
|
||||
/// 后端名称
|
||||
///
|
||||
/// Backend name.
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// 后端版本
|
||||
///
|
||||
/// Backend version.
|
||||
fn version(&self) -> &str;
|
||||
|
||||
// ==================== 生命周期 | Lifecycle ====================
|
||||
|
||||
/// 调整视口大小
|
||||
///
|
||||
/// Resize viewport.
|
||||
fn resize(&mut self, width: u32, height: u32);
|
||||
|
||||
/// 获取当前宽度
|
||||
///
|
||||
/// Get current width.
|
||||
fn width(&self) -> u32;
|
||||
|
||||
/// 获取当前高度
|
||||
///
|
||||
/// Get current height.
|
||||
fn height(&self) -> u32;
|
||||
|
||||
// ==================== 帧控制 | Frame Control ====================
|
||||
|
||||
/// 开始新帧
|
||||
///
|
||||
/// Begin new frame.
|
||||
fn begin_frame(&mut self);
|
||||
|
||||
/// 结束当前帧
|
||||
///
|
||||
/// End current frame.
|
||||
fn end_frame(&mut self);
|
||||
|
||||
/// 清屏
|
||||
///
|
||||
/// Clear screen.
|
||||
fn clear(&mut self, r: f32, g: f32, b: f32, a: f32);
|
||||
|
||||
/// 设置视口
|
||||
///
|
||||
/// Set viewport.
|
||||
fn set_viewport(&mut self, x: i32, y: i32, width: u32, height: u32);
|
||||
|
||||
// ==================== 缓冲区操作 | Buffer Operations ====================
|
||||
|
||||
/// 创建顶点缓冲区
|
||||
///
|
||||
/// Create vertex buffer.
|
||||
fn create_vertex_buffer(
|
||||
&mut self,
|
||||
data: &[u8],
|
||||
usage: BufferUsage,
|
||||
) -> GraphicsResult<BufferHandle>;
|
||||
|
||||
/// 创建指定大小的顶点缓冲区(预分配)
|
||||
///
|
||||
/// Create vertex buffer with specified size (pre-allocate).
|
||||
fn create_vertex_buffer_sized(
|
||||
&mut self,
|
||||
size: usize,
|
||||
usage: BufferUsage,
|
||||
) -> GraphicsResult<BufferHandle>;
|
||||
|
||||
/// 创建索引缓冲区
|
||||
///
|
||||
/// Create index buffer.
|
||||
fn create_index_buffer(
|
||||
&mut self,
|
||||
data: &[u16],
|
||||
usage: BufferUsage,
|
||||
) -> GraphicsResult<BufferHandle>;
|
||||
|
||||
/// 创建索引缓冲区(u32)
|
||||
///
|
||||
/// Create index buffer (u32).
|
||||
fn create_index_buffer_u32(
|
||||
&mut self,
|
||||
data: &[u32],
|
||||
usage: BufferUsage,
|
||||
) -> GraphicsResult<BufferHandle>;
|
||||
|
||||
/// 更新缓冲区数据
|
||||
///
|
||||
/// Update buffer data.
|
||||
fn update_buffer(
|
||||
&mut self,
|
||||
handle: BufferHandle,
|
||||
offset: usize,
|
||||
data: &[u8],
|
||||
) -> GraphicsResult<()>;
|
||||
|
||||
/// 销毁缓冲区
|
||||
///
|
||||
/// Destroy buffer.
|
||||
fn destroy_buffer(&mut self, handle: BufferHandle);
|
||||
|
||||
/// 创建顶点数组对象
|
||||
///
|
||||
/// Create vertex array object.
|
||||
fn create_vertex_array(
|
||||
&mut self,
|
||||
vertex_buffer: BufferHandle,
|
||||
index_buffer: Option<BufferHandle>,
|
||||
layout: &VertexLayout,
|
||||
) -> GraphicsResult<VertexArrayHandle>;
|
||||
|
||||
/// 销毁顶点数组对象
|
||||
///
|
||||
/// Destroy vertex array object.
|
||||
fn destroy_vertex_array(&mut self, handle: VertexArrayHandle);
|
||||
|
||||
// ==================== 着色器操作 | Shader Operations ====================
|
||||
|
||||
/// 编译着色器程序
|
||||
///
|
||||
/// Compile shader program.
|
||||
fn compile_shader(
|
||||
&mut self,
|
||||
vertex_src: &str,
|
||||
fragment_src: &str,
|
||||
) -> GraphicsResult<ShaderHandle>;
|
||||
|
||||
/// 销毁着色器
|
||||
///
|
||||
/// Destroy shader.
|
||||
fn destroy_shader(&mut self, handle: ShaderHandle);
|
||||
|
||||
/// 绑定着色器
|
||||
///
|
||||
/// Bind shader.
|
||||
fn bind_shader(&mut self, handle: ShaderHandle) -> GraphicsResult<()>;
|
||||
|
||||
/// 设置 Uniform(float)
|
||||
///
|
||||
/// Set uniform (float).
|
||||
fn set_uniform_f32(&mut self, name: &str, value: f32) -> GraphicsResult<()>;
|
||||
|
||||
/// 设置 Uniform(vec2)
|
||||
///
|
||||
/// Set uniform (vec2).
|
||||
fn set_uniform_vec2(&mut self, name: &str, value: Vec2) -> GraphicsResult<()>;
|
||||
|
||||
/// 设置 Uniform(vec3)
|
||||
///
|
||||
/// Set uniform (vec3).
|
||||
fn set_uniform_vec3(&mut self, name: &str, value: Vec3) -> GraphicsResult<()>;
|
||||
|
||||
/// 设置 Uniform(vec4)
|
||||
///
|
||||
/// Set uniform (vec4).
|
||||
fn set_uniform_vec4(&mut self, name: &str, value: Vec4) -> GraphicsResult<()>;
|
||||
|
||||
/// 设置 Uniform(mat3)
|
||||
///
|
||||
/// Set uniform (mat3).
|
||||
fn set_uniform_mat3(&mut self, name: &str, value: &Mat3) -> GraphicsResult<()>;
|
||||
|
||||
/// 设置 Uniform(mat4)
|
||||
///
|
||||
/// Set uniform (mat4).
|
||||
fn set_uniform_mat4(&mut self, name: &str, value: &Mat4) -> GraphicsResult<()>;
|
||||
|
||||
/// 设置 Uniform(int/sampler)
|
||||
///
|
||||
/// Set uniform (int/sampler).
|
||||
fn set_uniform_i32(&mut self, name: &str, value: i32) -> GraphicsResult<()>;
|
||||
|
||||
/// 设置 Uniform(通用)
|
||||
///
|
||||
/// Set uniform (generic).
|
||||
fn set_uniform(&mut self, name: &str, value: &UniformValue) -> GraphicsResult<()> {
|
||||
match value {
|
||||
UniformValue::Float(v) => self.set_uniform_f32(name, *v),
|
||||
UniformValue::Float2(v) => self.set_uniform_vec2(name, *v),
|
||||
UniformValue::Float3(v) => self.set_uniform_vec3(name, *v),
|
||||
UniformValue::Float4(v) => self.set_uniform_vec4(name, *v),
|
||||
UniformValue::Int(v) => self.set_uniform_i32(name, *v),
|
||||
UniformValue::Mat3(v) => self.set_uniform_mat3(name, v),
|
||||
UniformValue::Mat4(v) => self.set_uniform_mat4(name, v),
|
||||
UniformValue::Texture(unit) => self.set_uniform_i32(name, *unit as i32),
|
||||
_ => Err(GraphicsError::Unsupported(format!(
|
||||
"Uniform type {} not supported",
|
||||
value.type_name()
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 纹理操作 | Texture Operations ====================
|
||||
|
||||
/// 创建纹理
|
||||
///
|
||||
/// Create texture.
|
||||
fn create_texture(&mut self, desc: &TextureDescriptor) -> GraphicsResult<TextureHandle>;
|
||||
|
||||
/// 创建空白纹理(用于动态图集)
|
||||
///
|
||||
/// Create blank texture (for dynamic atlas).
|
||||
fn create_blank_texture(&mut self, width: u32, height: u32) -> GraphicsResult<TextureHandle>;
|
||||
|
||||
/// 上传纹理数据
|
||||
///
|
||||
/// Upload texture data.
|
||||
fn upload_texture_data(
|
||||
&mut self,
|
||||
handle: TextureHandle,
|
||||
data: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> GraphicsResult<()>;
|
||||
|
||||
/// 更新纹理区域
|
||||
///
|
||||
/// Update texture region.
|
||||
fn update_texture_region(
|
||||
&mut self,
|
||||
handle: TextureHandle,
|
||||
x: u32,
|
||||
y: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
data: &[u8],
|
||||
) -> GraphicsResult<()>;
|
||||
|
||||
/// 销毁纹理
|
||||
///
|
||||
/// Destroy texture.
|
||||
fn destroy_texture(&mut self, handle: TextureHandle);
|
||||
|
||||
/// 绑定纹理到纹理单元
|
||||
///
|
||||
/// Bind texture to texture unit.
|
||||
fn bind_texture(&mut self, handle: TextureHandle, unit: u32) -> GraphicsResult<()>;
|
||||
|
||||
/// 获取纹理尺寸
|
||||
///
|
||||
/// Get texture dimensions.
|
||||
fn get_texture_size(&self, handle: TextureHandle) -> Option<(u32, u32)>;
|
||||
|
||||
// ==================== 渲染状态 | Render State ====================
|
||||
|
||||
/// 应用渲染状态
|
||||
///
|
||||
/// Apply render state.
|
||||
fn apply_render_state(&mut self, state: &RenderState);
|
||||
|
||||
/// 设置混合模式
|
||||
///
|
||||
/// Set blend mode.
|
||||
fn set_blend_mode(&mut self, mode: BlendMode);
|
||||
|
||||
/// 设置裁剪矩形
|
||||
///
|
||||
/// Set scissor rectangle.
|
||||
fn set_scissor(&mut self, rect: Option<ScissorRect>);
|
||||
|
||||
// ==================== 绘制命令 | Draw Commands ====================
|
||||
|
||||
/// 绘制(索引,u16)
|
||||
///
|
||||
/// Draw indexed (u16).
|
||||
fn draw_indexed(
|
||||
&mut self,
|
||||
vao: VertexArrayHandle,
|
||||
index_count: u32,
|
||||
index_offset: u32,
|
||||
) -> GraphicsResult<()>;
|
||||
|
||||
/// 绘制(索引,u32)
|
||||
///
|
||||
/// Draw indexed (u32).
|
||||
fn draw_indexed_u32(
|
||||
&mut self,
|
||||
vao: VertexArrayHandle,
|
||||
index_count: u32,
|
||||
index_offset: u32,
|
||||
) -> GraphicsResult<()> {
|
||||
// 默认实现使用 u16 版本,后端可覆盖
|
||||
self.draw_indexed(vao, index_count, index_offset)
|
||||
}
|
||||
|
||||
/// 绘制(非索引)
|
||||
///
|
||||
/// Draw non-indexed.
|
||||
fn draw(
|
||||
&mut self,
|
||||
vao: VertexArrayHandle,
|
||||
vertex_count: u32,
|
||||
vertex_offset: u32,
|
||||
) -> GraphicsResult<()>;
|
||||
|
||||
/// 绘制线段
|
||||
///
|
||||
/// Draw lines.
|
||||
fn draw_lines(
|
||||
&mut self,
|
||||
vao: VertexArrayHandle,
|
||||
vertex_count: u32,
|
||||
vertex_offset: u32,
|
||||
) -> GraphicsResult<()>;
|
||||
|
||||
/// 绘制闭合线条
|
||||
///
|
||||
/// Draw line loop.
|
||||
fn draw_line_loop(
|
||||
&mut self,
|
||||
vao: VertexArrayHandle,
|
||||
vertex_count: u32,
|
||||
vertex_offset: u32,
|
||||
) -> GraphicsResult<()>;
|
||||
|
||||
/// 绘制连续线条
|
||||
///
|
||||
/// Draw line strip.
|
||||
fn draw_line_strip(
|
||||
&mut self,
|
||||
vao: VertexArrayHandle,
|
||||
vertex_count: u32,
|
||||
vertex_offset: u32,
|
||||
) -> GraphicsResult<()>;
|
||||
|
||||
// ==================== 查询 | Queries ====================
|
||||
|
||||
/// 获取最大纹理尺寸
|
||||
///
|
||||
/// Get max texture size.
|
||||
fn max_texture_size(&self) -> u32;
|
||||
|
||||
/// 是否支持某功能
|
||||
///
|
||||
/// Check feature support.
|
||||
fn supports_feature(&self, feature: GraphicsFeature) -> bool;
|
||||
|
||||
/// 获取最大纹理单元数
|
||||
///
|
||||
/// Get max texture units.
|
||||
fn max_texture_units(&self) -> u32 {
|
||||
16 // 默认值,后端可覆盖
|
||||
}
|
||||
|
||||
/// 获取最大顶点属性数
|
||||
///
|
||||
/// Get max vertex attributes.
|
||||
fn max_vertex_attributes(&self) -> u32 {
|
||||
16 // 默认值,后端可覆盖
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 扩展 Trait | Extension Traits ====================
|
||||
|
||||
/// 帧缓冲区操作扩展
|
||||
///
|
||||
/// Framebuffer operations extension.
|
||||
pub trait FramebufferExt: GraphicsBackend {
|
||||
/// 创建帧缓冲区
|
||||
///
|
||||
/// Create framebuffer.
|
||||
fn create_framebuffer(
|
||||
&mut self,
|
||||
color_attachment: TextureHandle,
|
||||
depth_attachment: Option<TextureHandle>,
|
||||
) -> GraphicsResult<FramebufferHandle>;
|
||||
|
||||
/// 销毁帧缓冲区
|
||||
///
|
||||
/// Destroy framebuffer.
|
||||
fn destroy_framebuffer(&mut self, handle: FramebufferHandle);
|
||||
|
||||
/// 绑定帧缓冲区
|
||||
///
|
||||
/// Bind framebuffer.
|
||||
fn bind_framebuffer(&mut self, handle: Option<FramebufferHandle>) -> GraphicsResult<()>;
|
||||
}
|
||||
|
||||
/// 实例化渲染扩展
|
||||
///
|
||||
/// Instanced rendering extension.
|
||||
pub trait InstancingExt: GraphicsBackend {
|
||||
/// 绘制实例化(索引)
|
||||
///
|
||||
/// Draw instanced (indexed).
|
||||
fn draw_indexed_instanced(
|
||||
&mut self,
|
||||
vao: VertexArrayHandle,
|
||||
index_count: u32,
|
||||
instance_count: u32,
|
||||
) -> GraphicsResult<()>;
|
||||
|
||||
/// 绘制实例化(非索引)
|
||||
///
|
||||
/// Draw instanced (non-indexed).
|
||||
fn draw_instanced(
|
||||
&mut self,
|
||||
vao: VertexArrayHandle,
|
||||
vertex_count: u32,
|
||||
instance_count: u32,
|
||||
) -> GraphicsResult<()>;
|
||||
}
|
||||
7
packages/rust/engine-shared/src/traits/mod.rs
Normal file
7
packages/rust/engine-shared/src/traits/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
//! 图形后端 trait 定义
|
||||
//!
|
||||
//! Graphics backend trait definitions.
|
||||
|
||||
pub mod backend;
|
||||
pub mod platform;
|
||||
pub mod renderer;
|
||||
441
packages/rust/engine-shared/src/traits/platform.rs
Normal file
441
packages/rust/engine-shared/src/traits/platform.rs
Normal file
@@ -0,0 +1,441 @@
|
||||
//! 平台抽象 trait
|
||||
//!
|
||||
//! Platform abstraction trait.
|
||||
|
||||
use super::backend::{GraphicsBackend, GraphicsResult};
|
||||
use crate::types::texture::ImageData;
|
||||
use std::future::Future;
|
||||
|
||||
// ==================== 后端配置 | Backend Configuration ====================
|
||||
|
||||
/// 后端配置
|
||||
///
|
||||
/// Backend configuration.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BackendConfig {
|
||||
/// 画布/窗口 ID
|
||||
///
|
||||
/// Canvas/Window ID.
|
||||
pub canvas_id: Option<String>,
|
||||
|
||||
/// 初始宽度
|
||||
///
|
||||
/// Initial width.
|
||||
pub width: u32,
|
||||
|
||||
/// 初始高度
|
||||
///
|
||||
/// Initial height.
|
||||
pub height: u32,
|
||||
|
||||
/// 是否启用抗锯齿
|
||||
///
|
||||
/// Enable antialiasing.
|
||||
pub antialias: bool,
|
||||
|
||||
/// 是否使用高 DPI
|
||||
///
|
||||
/// Use high DPI.
|
||||
pub high_dpi: bool,
|
||||
|
||||
/// 电源偏好
|
||||
///
|
||||
/// Power preference.
|
||||
pub power_preference: PowerPreference,
|
||||
|
||||
/// 是否保留绘制缓冲区
|
||||
///
|
||||
/// Preserve drawing buffer.
|
||||
pub preserve_drawing_buffer: bool,
|
||||
|
||||
/// Alpha 模式
|
||||
///
|
||||
/// Alpha mode.
|
||||
pub alpha: bool,
|
||||
|
||||
/// 深度缓冲区大小(0 表示禁用)
|
||||
///
|
||||
/// Depth buffer size (0 to disable).
|
||||
pub depth_size: u8,
|
||||
|
||||
/// 模板缓冲区大小(0 表示禁用)
|
||||
///
|
||||
/// Stencil buffer size (0 to disable).
|
||||
pub stencil_size: u8,
|
||||
}
|
||||
|
||||
impl Default for BackendConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
canvas_id: None,
|
||||
width: 800,
|
||||
height: 600,
|
||||
antialias: false,
|
||||
high_dpi: true,
|
||||
power_preference: PowerPreference::HighPerformance,
|
||||
preserve_drawing_buffer: false,
|
||||
alpha: true,
|
||||
depth_size: 0,
|
||||
stencil_size: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BackendConfig {
|
||||
/// 创建新配置
|
||||
///
|
||||
/// Create new configuration.
|
||||
pub fn new(width: u32, height: u32) -> Self {
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置画布 ID
|
||||
///
|
||||
/// Set canvas ID.
|
||||
pub fn with_canvas(mut self, canvas_id: impl Into<String>) -> Self {
|
||||
self.canvas_id = Some(canvas_id.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// 设置抗锯齿
|
||||
///
|
||||
/// Set antialiasing.
|
||||
pub fn with_antialias(mut self, antialias: bool) -> Self {
|
||||
self.antialias = antialias;
|
||||
self
|
||||
}
|
||||
|
||||
/// 设置高 DPI
|
||||
///
|
||||
/// Set high DPI.
|
||||
pub fn with_high_dpi(mut self, high_dpi: bool) -> Self {
|
||||
self.high_dpi = high_dpi;
|
||||
self
|
||||
}
|
||||
|
||||
/// 设置电源偏好
|
||||
///
|
||||
/// Set power preference.
|
||||
pub fn with_power_preference(mut self, preference: PowerPreference) -> Self {
|
||||
self.power_preference = preference;
|
||||
self
|
||||
}
|
||||
|
||||
/// 启用深度缓冲区
|
||||
///
|
||||
/// Enable depth buffer.
|
||||
pub fn with_depth(mut self, bits: u8) -> Self {
|
||||
self.depth_size = bits;
|
||||
self
|
||||
}
|
||||
|
||||
/// 启用模板缓冲区
|
||||
///
|
||||
/// Enable stencil buffer.
|
||||
pub fn with_stencil(mut self, bits: u8) -> Self {
|
||||
self.stencil_size = bits;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// 电源偏好
|
||||
///
|
||||
/// Power preference.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum PowerPreference {
|
||||
/// 低功耗(集成显卡)
|
||||
///
|
||||
/// Low power (integrated GPU).
|
||||
LowPower,
|
||||
|
||||
/// 高性能(独立显卡,默认)
|
||||
///
|
||||
/// High performance (discrete GPU, default).
|
||||
#[default]
|
||||
HighPerformance,
|
||||
}
|
||||
|
||||
// ==================== 资产加载器 | Asset Loader ====================
|
||||
|
||||
/// 资产加载错误
|
||||
///
|
||||
/// Asset loading error.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AssetError {
|
||||
/// 错误消息
|
||||
///
|
||||
/// Error message.
|
||||
pub message: String,
|
||||
|
||||
/// 资产路径
|
||||
///
|
||||
/// Asset path.
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AssetError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Failed to load '{}': {}", self.path, self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for AssetError {}
|
||||
|
||||
/// 资产加载结果
|
||||
///
|
||||
/// Asset loading result.
|
||||
pub type AssetResult<T> = Result<T, AssetError>;
|
||||
|
||||
/// 资产加载器 trait
|
||||
///
|
||||
/// Asset loader trait.
|
||||
pub trait AssetLoader {
|
||||
/// 加载二进制数据
|
||||
///
|
||||
/// Load binary data.
|
||||
fn load_bytes(&self, path: &str) -> impl Future<Output = AssetResult<Vec<u8>>> + Send;
|
||||
|
||||
/// 加载文本
|
||||
///
|
||||
/// Load text.
|
||||
fn load_text(&self, path: &str) -> impl Future<Output = AssetResult<String>> + Send;
|
||||
|
||||
/// 加载图片(返回 RGBA 数据)
|
||||
///
|
||||
/// Load image (returns RGBA data).
|
||||
fn load_image(&self, path: &str) -> impl Future<Output = AssetResult<ImageData>> + Send;
|
||||
}
|
||||
|
||||
// ==================== 输入系统 | Input System ====================
|
||||
|
||||
/// 鼠标按钮
|
||||
///
|
||||
/// Mouse button.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum MouseButton {
|
||||
/// 左键 | Left button
|
||||
Left,
|
||||
/// 中键 | Middle button
|
||||
Middle,
|
||||
/// 右键 | Right button
|
||||
Right,
|
||||
/// 其他按钮 | Other button
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
/// 键盘键码(常用键)
|
||||
///
|
||||
/// Keyboard key code (common keys).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum KeyCode {
|
||||
// 字母键
|
||||
A, B, C, D, E, F, G, H, I, J, K, L, M,
|
||||
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
||||
|
||||
// 数字键
|
||||
Key0, Key1, Key2, Key3, Key4,
|
||||
Key5, Key6, Key7, Key8, Key9,
|
||||
|
||||
// 功能键
|
||||
F1, F2, F3, F4, F5, F6,
|
||||
F7, F8, F9, F10, F11, F12,
|
||||
|
||||
// 控制键
|
||||
Escape, Tab, CapsLock, Shift, Control, Alt,
|
||||
Space, Enter, Backspace, Delete, Insert,
|
||||
Home, End, PageUp, PageDown,
|
||||
|
||||
// 方向键
|
||||
Up, Down, Left, Right,
|
||||
|
||||
// 符号键
|
||||
Minus, Equal, BracketLeft, BracketRight,
|
||||
Backslash, Semicolon, Quote, Comma, Period, Slash,
|
||||
Backquote,
|
||||
|
||||
// 其他
|
||||
Unknown(u32),
|
||||
}
|
||||
|
||||
/// 输入状态 trait
|
||||
///
|
||||
/// Input state trait.
|
||||
pub trait InputState {
|
||||
/// 检查按键是否按下
|
||||
///
|
||||
/// Check if key is pressed.
|
||||
fn is_key_down(&self, key: KeyCode) -> bool;
|
||||
|
||||
/// 检查按键是否刚按下(本帧)
|
||||
///
|
||||
/// Check if key was just pressed (this frame).
|
||||
fn is_key_just_pressed(&self, key: KeyCode) -> bool;
|
||||
|
||||
/// 检查按键是否刚释放(本帧)
|
||||
///
|
||||
/// Check if key was just released (this frame).
|
||||
fn is_key_just_released(&self, key: KeyCode) -> bool;
|
||||
|
||||
/// 检查鼠标按钮是否按下
|
||||
///
|
||||
/// Check if mouse button is pressed.
|
||||
fn is_mouse_button_down(&self, button: MouseButton) -> bool;
|
||||
|
||||
/// 获取鼠标位置
|
||||
///
|
||||
/// Get mouse position.
|
||||
fn mouse_position(&self) -> (f32, f32);
|
||||
|
||||
/// 获取鼠标滚轮增量
|
||||
///
|
||||
/// Get mouse wheel delta.
|
||||
fn mouse_wheel_delta(&self) -> f32;
|
||||
|
||||
/// 更新输入状态(每帧调用)
|
||||
///
|
||||
/// Update input state (call every frame).
|
||||
fn update(&mut self);
|
||||
}
|
||||
|
||||
// ==================== 平台 Trait | Platform Trait ====================
|
||||
|
||||
/// 平台抽象 trait
|
||||
///
|
||||
/// 定义平台相关操作的抽象接口。
|
||||
///
|
||||
/// Platform abstraction trait.
|
||||
/// Defines abstract interface for platform-specific operations.
|
||||
pub trait Platform {
|
||||
/// 后端类型
|
||||
///
|
||||
/// Backend type.
|
||||
type Backend: GraphicsBackend;
|
||||
|
||||
/// 资产加载器类型
|
||||
///
|
||||
/// Asset loader type.
|
||||
type AssetLoader: AssetLoader;
|
||||
|
||||
/// 输入状态类型
|
||||
///
|
||||
/// Input state type.
|
||||
type Input: InputState;
|
||||
|
||||
/// 创建图形后端
|
||||
///
|
||||
/// Create graphics backend.
|
||||
fn create_backend(&self, config: BackendConfig) -> GraphicsResult<Self::Backend>;
|
||||
|
||||
/// 获取资产加载器
|
||||
///
|
||||
/// Get asset loader.
|
||||
fn asset_loader(&self) -> &Self::AssetLoader;
|
||||
|
||||
/// 获取输入状态
|
||||
///
|
||||
/// Get input state.
|
||||
fn input(&self) -> &Self::Input;
|
||||
|
||||
/// 获取输入状态(可变)
|
||||
///
|
||||
/// Get input state (mutable).
|
||||
fn input_mut(&mut self) -> &mut Self::Input;
|
||||
|
||||
/// 获取屏幕尺寸
|
||||
///
|
||||
/// Get screen size.
|
||||
fn screen_size(&self) -> (u32, u32);
|
||||
|
||||
/// 获取设备像素比
|
||||
///
|
||||
/// Get device pixel ratio.
|
||||
fn device_pixel_ratio(&self) -> f32;
|
||||
|
||||
/// 获取当前时间(秒)
|
||||
///
|
||||
/// Get current time (seconds).
|
||||
fn time(&self) -> f64;
|
||||
|
||||
/// 请求下一帧
|
||||
///
|
||||
/// Request next frame.
|
||||
fn request_animation_frame(&self, callback: impl FnOnce(f64) + 'static);
|
||||
}
|
||||
|
||||
// ==================== 平台类型枚举 | Platform Type Enum ====================
|
||||
|
||||
/// 平台类型
|
||||
///
|
||||
/// Platform type.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum PlatformType {
|
||||
/// Web 浏览器
|
||||
///
|
||||
/// Web browser.
|
||||
Web,
|
||||
|
||||
/// 微信小游戏
|
||||
///
|
||||
/// WeChat Mini Game.
|
||||
WeChatMiniGame,
|
||||
|
||||
/// Windows 桌面
|
||||
///
|
||||
/// Windows desktop.
|
||||
Windows,
|
||||
|
||||
/// macOS 桌面
|
||||
///
|
||||
/// macOS desktop.
|
||||
MacOS,
|
||||
|
||||
/// Linux 桌面
|
||||
///
|
||||
/// Linux desktop.
|
||||
Linux,
|
||||
|
||||
/// Android
|
||||
Android,
|
||||
|
||||
/// iOS
|
||||
IOS,
|
||||
|
||||
/// 未知平台
|
||||
///
|
||||
/// Unknown platform.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl PlatformType {
|
||||
/// 是否为桌面平台
|
||||
///
|
||||
/// Check if desktop platform.
|
||||
pub const fn is_desktop(&self) -> bool {
|
||||
matches!(self, Self::Windows | Self::MacOS | Self::Linux)
|
||||
}
|
||||
|
||||
/// 是否为移动平台
|
||||
///
|
||||
/// Check if mobile platform.
|
||||
pub const fn is_mobile(&self) -> bool {
|
||||
matches!(self, Self::Android | Self::IOS)
|
||||
}
|
||||
|
||||
/// 是否为 Web 平台
|
||||
///
|
||||
/// Check if web platform.
|
||||
pub const fn is_web(&self) -> bool {
|
||||
matches!(self, Self::Web | Self::WeChatMiniGame)
|
||||
}
|
||||
|
||||
/// 是否为原生平台
|
||||
///
|
||||
/// Check if native platform.
|
||||
pub const fn is_native(&self) -> bool {
|
||||
!self.is_web()
|
||||
}
|
||||
}
|
||||
293
packages/rust/engine-shared/src/traits/renderer.rs
Normal file
293
packages/rust/engine-shared/src/traits/renderer.rs
Normal file
@@ -0,0 +1,293 @@
|
||||
//! 高级渲染器 trait
|
||||
//!
|
||||
//! High-level renderer trait.
|
||||
|
||||
use super::backend::{GraphicsBackend, GraphicsResult};
|
||||
use crate::camera::Camera2D;
|
||||
|
||||
// ==================== 精灵批处理数据 | Sprite Batch Data ====================
|
||||
|
||||
/// 精灵批处理数据
|
||||
///
|
||||
/// 与现有 TypeScript 层的 RenderBatcher 数据格式兼容。
|
||||
///
|
||||
/// Sprite batch data.
|
||||
/// Compatible with existing TypeScript RenderBatcher data format.
|
||||
#[derive(Debug)]
|
||||
pub struct SpriteBatchData<'a> {
|
||||
/// 变换数据:[x, y, rotation, scaleX, scaleY, originX, originY] per sprite
|
||||
///
|
||||
/// Transform data: [x, y, rotation, scaleX, scaleY, originX, originY] per sprite.
|
||||
pub transforms: &'a [f32],
|
||||
|
||||
/// 纹理 ID(每个精灵一个)
|
||||
///
|
||||
/// Texture ID (one per sprite).
|
||||
pub texture_ids: &'a [u32],
|
||||
|
||||
/// UV 坐标:[u0, v0, u1, v1] per sprite
|
||||
///
|
||||
/// UV coordinates: [u0, v0, u1, v1] per sprite.
|
||||
pub uvs: &'a [f32],
|
||||
|
||||
/// 打包的 RGBA 颜色(每个精灵一个)
|
||||
///
|
||||
/// Packed RGBA color (one per sprite).
|
||||
pub colors: &'a [u32],
|
||||
|
||||
/// 材质 ID(每个精灵一个,0 = 默认)
|
||||
///
|
||||
/// Material ID (one per sprite, 0 = default).
|
||||
pub material_ids: &'a [u32],
|
||||
}
|
||||
|
||||
impl<'a> SpriteBatchData<'a> {
|
||||
/// 获取精灵数量
|
||||
///
|
||||
/// Get sprite count.
|
||||
pub fn sprite_count(&self) -> usize {
|
||||
self.texture_ids.len()
|
||||
}
|
||||
|
||||
/// 验证数据一致性
|
||||
///
|
||||
/// Validate data consistency.
|
||||
pub fn validate(&self) -> Result<(), &'static str> {
|
||||
let count = self.sprite_count();
|
||||
|
||||
if self.transforms.len() != count * 7 {
|
||||
return Err("transforms length mismatch (expected count * 7)");
|
||||
}
|
||||
if self.uvs.len() != count * 4 {
|
||||
return Err("uvs length mismatch (expected count * 4)");
|
||||
}
|
||||
if self.colors.len() != count {
|
||||
return Err("colors length mismatch");
|
||||
}
|
||||
if self.material_ids.len() != count {
|
||||
return Err("material_ids length mismatch");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// 相机状态
|
||||
///
|
||||
/// Camera state.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct CameraState {
|
||||
/// X 坐标 | X coordinate
|
||||
pub x: f32,
|
||||
/// Y 坐标 | Y coordinate
|
||||
pub y: f32,
|
||||
/// 缩放 | Zoom
|
||||
pub zoom: f32,
|
||||
/// 旋转(弧度)| Rotation (radians)
|
||||
pub rotation: f32,
|
||||
}
|
||||
|
||||
impl CameraState {
|
||||
/// 创建新的相机状态
|
||||
///
|
||||
/// Create new camera state.
|
||||
pub const fn new(x: f32, y: f32, zoom: f32, rotation: f32) -> Self {
|
||||
Self { x, y, zoom, rotation }
|
||||
}
|
||||
|
||||
/// 创建默认相机状态
|
||||
///
|
||||
/// Create default camera state.
|
||||
pub const fn identity() -> Self {
|
||||
Self {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
zoom: 1.0,
|
||||
rotation: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// 转换为 Camera2D
|
||||
///
|
||||
/// Convert to Camera2D.
|
||||
pub fn to_camera2d(&self, width: f32, height: f32) -> Camera2D {
|
||||
Camera2D::new(width, height)
|
||||
.with_position(self.x, self.y)
|
||||
.with_zoom(self.zoom)
|
||||
.with_rotation(self.rotation)
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 2D 渲染器 Trait | 2D Renderer Trait ====================
|
||||
|
||||
/// 2D 渲染器 trait
|
||||
///
|
||||
/// 在 GraphicsBackend 基础上提供更高级的 2D 渲染 API。
|
||||
///
|
||||
/// 2D renderer trait.
|
||||
/// Provides higher-level 2D rendering API built on top of GraphicsBackend.
|
||||
pub trait Renderer2D {
|
||||
/// 后端类型
|
||||
///
|
||||
/// Backend type.
|
||||
type Backend: GraphicsBackend;
|
||||
|
||||
/// 获取底层后端
|
||||
///
|
||||
/// Get underlying backend.
|
||||
fn backend(&self) -> &Self::Backend;
|
||||
|
||||
/// 获取底层后端(可变)
|
||||
///
|
||||
/// Get underlying backend (mutable).
|
||||
fn backend_mut(&mut self) -> &mut Self::Backend;
|
||||
|
||||
/// 提交精灵批次
|
||||
///
|
||||
/// Submit sprite batch.
|
||||
fn submit_sprite_batch(&mut self, data: SpriteBatchData) -> GraphicsResult<()>;
|
||||
|
||||
/// 设置相机
|
||||
///
|
||||
/// Set camera.
|
||||
fn set_camera(&mut self, state: CameraState);
|
||||
|
||||
/// 获取相机状态
|
||||
///
|
||||
/// Get camera state.
|
||||
fn camera(&self) -> CameraState;
|
||||
|
||||
/// 渲染当前帧
|
||||
///
|
||||
/// Render current frame.
|
||||
fn render(&mut self) -> GraphicsResult<()>;
|
||||
|
||||
/// 清屏
|
||||
///
|
||||
/// Clear screen.
|
||||
fn clear(&mut self, r: f32, g: f32, b: f32, a: f32) {
|
||||
self.backend_mut().clear(r, g, b, a);
|
||||
}
|
||||
|
||||
/// 屏幕坐标转世界坐标
|
||||
///
|
||||
/// Screen to world coordinates.
|
||||
fn screen_to_world(&self, screen_x: f32, screen_y: f32) -> (f32, f32);
|
||||
|
||||
/// 世界坐标转屏幕坐标
|
||||
///
|
||||
/// World to screen coordinates.
|
||||
fn world_to_screen(&self, world_x: f32, world_y: f32) -> (f32, f32);
|
||||
}
|
||||
|
||||
// ==================== Gizmo 渲染器 Trait | Gizmo Renderer Trait ====================
|
||||
|
||||
/// Gizmo 类型
|
||||
///
|
||||
/// Gizmo type.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GizmoShape {
|
||||
/// 矩形
|
||||
///
|
||||
/// Rectangle.
|
||||
Rect {
|
||||
x: f32,
|
||||
y: f32,
|
||||
width: f32,
|
||||
height: f32,
|
||||
rotation: f32,
|
||||
origin_x: f32,
|
||||
origin_y: f32,
|
||||
show_handles: bool,
|
||||
},
|
||||
|
||||
/// 圆形
|
||||
///
|
||||
/// Circle.
|
||||
Circle {
|
||||
x: f32,
|
||||
y: f32,
|
||||
radius: f32,
|
||||
},
|
||||
|
||||
/// 线段/多边形
|
||||
///
|
||||
/// Line/polygon.
|
||||
Line {
|
||||
points: Vec<(f32, f32)>,
|
||||
closed: bool,
|
||||
},
|
||||
|
||||
/// 胶囊体
|
||||
///
|
||||
/// Capsule.
|
||||
Capsule {
|
||||
x: f32,
|
||||
y: f32,
|
||||
radius: f32,
|
||||
half_height: f32,
|
||||
rotation: f32,
|
||||
},
|
||||
}
|
||||
|
||||
/// Gizmo 渲染器 trait(编辑器功能)
|
||||
///
|
||||
/// Gizmo renderer trait (editor feature).
|
||||
pub trait GizmoRenderer {
|
||||
/// 添加 Gizmo
|
||||
///
|
||||
/// Add gizmo.
|
||||
fn add_gizmo(&mut self, shape: GizmoShape, r: f32, g: f32, b: f32, a: f32);
|
||||
|
||||
/// 清空 Gizmo
|
||||
///
|
||||
/// Clear gizmos.
|
||||
fn clear_gizmos(&mut self);
|
||||
|
||||
/// 渲染 Gizmo
|
||||
///
|
||||
/// Render gizmos.
|
||||
fn render_gizmos(&mut self) -> GraphicsResult<()>;
|
||||
|
||||
/// 是否显示 Gizmo
|
||||
///
|
||||
/// Check if gizmos are visible.
|
||||
fn gizmos_visible(&self) -> bool;
|
||||
|
||||
/// 设置 Gizmo 可见性
|
||||
///
|
||||
/// Set gizmo visibility.
|
||||
fn set_gizmos_visible(&mut self, visible: bool);
|
||||
}
|
||||
|
||||
// ==================== 网格渲染器 Trait | Grid Renderer Trait ====================
|
||||
|
||||
/// 网格渲染器 trait(编辑器功能)
|
||||
///
|
||||
/// Grid renderer trait (editor feature).
|
||||
pub trait GridRenderer {
|
||||
/// 是否显示网格
|
||||
///
|
||||
/// Check if grid is visible.
|
||||
fn grid_visible(&self) -> bool;
|
||||
|
||||
/// 设置网格可见性
|
||||
///
|
||||
/// Set grid visibility.
|
||||
fn set_grid_visible(&mut self, visible: bool);
|
||||
|
||||
/// 渲染网格
|
||||
///
|
||||
/// Render grid.
|
||||
fn render_grid(&mut self) -> GraphicsResult<()>;
|
||||
|
||||
/// 设置网格大小
|
||||
///
|
||||
/// Set grid size.
|
||||
fn set_grid_size(&mut self, size: f32);
|
||||
|
||||
/// 设置网格颜色
|
||||
///
|
||||
/// Set grid color.
|
||||
fn set_grid_color(&mut self, r: f32, g: f32, b: f32, a: f32);
|
||||
}
|
||||
333
packages/rust/engine-shared/src/types/blend.rs
Normal file
333
packages/rust/engine-shared/src/types/blend.rs
Normal file
@@ -0,0 +1,333 @@
|
||||
//! 混合模式与渲染状态
|
||||
//!
|
||||
//! Blend modes and render state.
|
||||
|
||||
/// 混合模式
|
||||
///
|
||||
/// Blend mode.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum BlendMode {
|
||||
/// 无混合(禁用 alpha 混合)
|
||||
///
|
||||
/// No blending (disable alpha blending).
|
||||
None,
|
||||
|
||||
/// Alpha 混合(默认)
|
||||
///
|
||||
/// srcRGB * srcAlpha + dstRGB * (1 - srcAlpha)
|
||||
///
|
||||
/// Alpha blending (default).
|
||||
#[default]
|
||||
Alpha,
|
||||
|
||||
/// 加法混合(发光效果)
|
||||
///
|
||||
/// srcRGB + dstRGB
|
||||
///
|
||||
/// Additive blending (glow effects).
|
||||
Additive,
|
||||
|
||||
/// 乘法混合(阴影效果)
|
||||
///
|
||||
/// srcRGB * dstRGB
|
||||
///
|
||||
/// Multiply blending (shadow effects).
|
||||
Multiply,
|
||||
|
||||
/// 屏幕混合(提亮效果)
|
||||
///
|
||||
/// 1 - (1 - srcRGB) * (1 - dstRGB)
|
||||
///
|
||||
/// Screen blending (lighten effects).
|
||||
Screen,
|
||||
|
||||
/// 预乘 Alpha
|
||||
///
|
||||
/// srcRGB + dstRGB * (1 - srcAlpha)
|
||||
///
|
||||
/// Premultiplied alpha.
|
||||
PremultipliedAlpha,
|
||||
}
|
||||
|
||||
impl BlendMode {
|
||||
/// 获取所有混合模式
|
||||
///
|
||||
/// Get all blend modes.
|
||||
pub const fn all() -> &'static [BlendMode] {
|
||||
&[
|
||||
BlendMode::None,
|
||||
BlendMode::Alpha,
|
||||
BlendMode::Additive,
|
||||
BlendMode::Multiply,
|
||||
BlendMode::Screen,
|
||||
BlendMode::PremultipliedAlpha,
|
||||
]
|
||||
}
|
||||
|
||||
/// 获取混合模式名称
|
||||
///
|
||||
/// Get blend mode name.
|
||||
pub const fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::None => "None",
|
||||
Self::Alpha => "Alpha",
|
||||
Self::Additive => "Additive",
|
||||
Self::Multiply => "Multiply",
|
||||
Self::Screen => "Screen",
|
||||
Self::PremultipliedAlpha => "PremultipliedAlpha",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 裁剪模式(背面剔除)
|
||||
///
|
||||
/// Cull mode (backface culling).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum CullMode {
|
||||
/// 无裁剪(双面渲染)
|
||||
///
|
||||
/// No culling (double-sided rendering).
|
||||
#[default]
|
||||
None,
|
||||
|
||||
/// 裁剪正面
|
||||
///
|
||||
/// Cull front faces.
|
||||
Front,
|
||||
|
||||
/// 裁剪背面
|
||||
///
|
||||
/// Cull back faces.
|
||||
Back,
|
||||
}
|
||||
|
||||
/// 比较函数(深度/模板测试)
|
||||
///
|
||||
/// Comparison function (depth/stencil test).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum CompareFunc {
|
||||
/// 永不通过 | Never pass
|
||||
Never,
|
||||
/// 小于时通过 | Pass if less
|
||||
Less,
|
||||
/// 等于时通过 | Pass if equal
|
||||
Equal,
|
||||
/// 小于等于时通过 | Pass if less or equal
|
||||
LessEqual,
|
||||
/// 大于时通过 | Pass if greater
|
||||
Greater,
|
||||
/// 不等于时通过 | Pass if not equal
|
||||
NotEqual,
|
||||
/// 大于等于时通过 | Pass if greater or equal
|
||||
GreaterEqual,
|
||||
/// 总是通过(默认) | Always pass (default)
|
||||
#[default]
|
||||
Always,
|
||||
}
|
||||
|
||||
/// 裁剪矩形
|
||||
///
|
||||
/// Scissor rectangle.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct ScissorRect {
|
||||
/// X 坐标 | X coordinate
|
||||
pub x: i32,
|
||||
/// Y 坐标 | Y coordinate
|
||||
pub y: i32,
|
||||
/// 宽度 | Width
|
||||
pub width: u32,
|
||||
/// 高度 | Height
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
impl ScissorRect {
|
||||
/// 创建新的裁剪矩形
|
||||
///
|
||||
/// Create new scissor rectangle.
|
||||
pub const fn new(x: i32, y: i32, width: u32, height: u32) -> Self {
|
||||
Self { x, y, width, height }
|
||||
}
|
||||
|
||||
/// 从位置和尺寸创建
|
||||
///
|
||||
/// Create from position and size.
|
||||
pub const fn from_pos_size(x: i32, y: i32, width: u32, height: u32) -> Self {
|
||||
Self::new(x, y, width, height)
|
||||
}
|
||||
}
|
||||
|
||||
/// 视口
|
||||
///
|
||||
/// Viewport.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Viewport {
|
||||
/// X 坐标 | X coordinate
|
||||
pub x: f32,
|
||||
/// Y 坐标 | Y coordinate
|
||||
pub y: f32,
|
||||
/// 宽度 | Width
|
||||
pub width: f32,
|
||||
/// 高度 | Height
|
||||
pub height: f32,
|
||||
/// 最小深度 | Min depth
|
||||
pub min_depth: f32,
|
||||
/// 最大深度 | Max depth
|
||||
pub max_depth: f32,
|
||||
}
|
||||
|
||||
impl Default for Viewport {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
width: 1.0,
|
||||
height: 1.0,
|
||||
min_depth: 0.0,
|
||||
max_depth: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Viewport {
|
||||
/// 创建新的视口
|
||||
///
|
||||
/// Create new viewport.
|
||||
pub const fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
|
||||
Self {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
min_depth: 0.0,
|
||||
max_depth: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 渲染状态描述
|
||||
///
|
||||
/// Render state descriptor.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct RenderState {
|
||||
/// 混合模式 | Blend mode
|
||||
pub blend_mode: BlendMode,
|
||||
|
||||
/// 裁剪模式 | Cull mode
|
||||
pub cull_mode: CullMode,
|
||||
|
||||
/// 是否启用深度测试 | Enable depth test
|
||||
pub depth_test: bool,
|
||||
|
||||
/// 是否启用深度写入 | Enable depth write
|
||||
pub depth_write: bool,
|
||||
|
||||
/// 深度比较函数 | Depth comparison function
|
||||
pub depth_func: CompareFunc,
|
||||
|
||||
/// 裁剪矩形(None 表示禁用) | Scissor rect (None to disable)
|
||||
pub scissor: Option<ScissorRect>,
|
||||
}
|
||||
|
||||
impl RenderState {
|
||||
/// 创建默认 2D 渲染状态
|
||||
///
|
||||
/// Create default 2D render state.
|
||||
pub fn default_2d() -> Self {
|
||||
Self {
|
||||
blend_mode: BlendMode::Alpha,
|
||||
cull_mode: CullMode::None,
|
||||
depth_test: false,
|
||||
depth_write: false,
|
||||
depth_func: CompareFunc::Always,
|
||||
scissor: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建不透明 2D 渲染状态
|
||||
///
|
||||
/// Create opaque 2D render state.
|
||||
pub fn opaque_2d() -> Self {
|
||||
Self {
|
||||
blend_mode: BlendMode::None,
|
||||
cull_mode: CullMode::None,
|
||||
depth_test: false,
|
||||
depth_write: false,
|
||||
depth_func: CompareFunc::Always,
|
||||
scissor: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建加法混合状态
|
||||
///
|
||||
/// Create additive blend state.
|
||||
pub fn additive() -> Self {
|
||||
Self {
|
||||
blend_mode: BlendMode::Additive,
|
||||
..Self::default_2d()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 清除标志
|
||||
///
|
||||
/// Clear flags.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ClearFlags {
|
||||
bits: u8,
|
||||
}
|
||||
|
||||
impl ClearFlags {
|
||||
/// 无清除 | No clear
|
||||
pub const NONE: Self = Self { bits: 0 };
|
||||
/// 清除颜色缓冲 | Clear color buffer
|
||||
pub const COLOR: Self = Self { bits: 1 };
|
||||
/// 清除深度缓冲 | Clear depth buffer
|
||||
pub const DEPTH: Self = Self { bits: 2 };
|
||||
/// 清除模板缓冲 | Clear stencil buffer
|
||||
pub const STENCIL: Self = Self { bits: 4 };
|
||||
/// 清除所有缓冲 | Clear all buffers
|
||||
pub const ALL: Self = Self { bits: 7 };
|
||||
|
||||
/// 是否包含颜色清除 | Contains color clear
|
||||
pub const fn has_color(&self) -> bool {
|
||||
self.bits & Self::COLOR.bits != 0
|
||||
}
|
||||
|
||||
/// 是否包含深度清除 | Contains depth clear
|
||||
pub const fn has_depth(&self) -> bool {
|
||||
self.bits & Self::DEPTH.bits != 0
|
||||
}
|
||||
|
||||
/// 是否包含模板清除 | Contains stencil clear
|
||||
pub const fn has_stencil(&self) -> bool {
|
||||
self.bits & Self::STENCIL.bits != 0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitOr for ClearFlags {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(self, rhs: Self) -> Self::Output {
|
||||
Self { bits: self.bits | rhs.bits }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitAnd for ClearFlags {
|
||||
type Output = Self;
|
||||
|
||||
fn bitand(self, rhs: Self) -> Self::Output {
|
||||
Self { bits: self.bits & rhs.bits }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ClearFlags {
|
||||
fn default() -> Self {
|
||||
Self::COLOR
|
||||
}
|
||||
}
|
||||
400
packages/rust/engine-shared/src/types/handle.rs
Normal file
400
packages/rust/engine-shared/src/types/handle.rs
Normal file
@@ -0,0 +1,400 @@
|
||||
//! 类型安全的资源句柄
|
||||
//!
|
||||
//! Type-safe resource handles.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
/// 类型安全的资源句柄
|
||||
///
|
||||
/// 使用 PhantomData 区分不同资源类型,防止句柄混用。
|
||||
/// 包含代数(generation)用于检测过期句柄。
|
||||
///
|
||||
/// Type-safe resource handle using PhantomData to distinguish resource types.
|
||||
/// Includes generation for stale handle detection.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use es_engine_shared::types::handle::*;
|
||||
///
|
||||
/// let texture: TextureHandle = TextureHandle::new(1, 1);
|
||||
/// let buffer: BufferHandle = BufferHandle::new(1, 1);
|
||||
///
|
||||
/// // 编译错误:类型不匹配
|
||||
/// // Compile error: type mismatch
|
||||
/// // let _: TextureHandle = buffer;
|
||||
/// ```
|
||||
#[repr(C)]
|
||||
pub struct Handle<T> {
|
||||
/// 内部 ID | Internal ID
|
||||
id: u32,
|
||||
/// 代数(用于检测过期句柄) | Generation for stale handle detection
|
||||
generation: u32,
|
||||
/// 类型标记 | Type marker
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
// 手动实现 trait,因为 PhantomData 的存在
|
||||
impl<T> Clone for Handle<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for Handle<T> {}
|
||||
|
||||
impl<T> PartialEq for Handle<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id == other.id && self.generation == other.generation
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for Handle<T> {}
|
||||
|
||||
impl<T> Hash for Handle<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.id.hash(state);
|
||||
self.generation.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Handle<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Handle")
|
||||
.field("id", &self.id)
|
||||
.field("generation", &self.generation)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Handle<T> {
|
||||
fn default() -> Self {
|
||||
Self::null()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Handle<T> {
|
||||
/// 创建新句柄
|
||||
///
|
||||
/// Create new handle.
|
||||
#[inline]
|
||||
pub const fn new(id: u32, generation: u32) -> Self {
|
||||
Self {
|
||||
id,
|
||||
generation,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取原始 ID
|
||||
///
|
||||
/// Get raw ID.
|
||||
#[inline]
|
||||
pub const fn id(&self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// 获取代数
|
||||
///
|
||||
/// Get generation.
|
||||
#[inline]
|
||||
pub const fn generation(&self) -> u32 {
|
||||
self.generation
|
||||
}
|
||||
|
||||
/// 创建空句柄
|
||||
///
|
||||
/// Create null handle.
|
||||
#[inline]
|
||||
pub const fn null() -> Self {
|
||||
Self::new(u32::MAX, 0)
|
||||
}
|
||||
|
||||
/// 是否为空句柄
|
||||
///
|
||||
/// Check if null handle.
|
||||
#[inline]
|
||||
pub const fn is_null(&self) -> bool {
|
||||
self.id == u32::MAX
|
||||
}
|
||||
|
||||
/// 是否为有效句柄(非空)
|
||||
///
|
||||
/// Check if valid handle (not null).
|
||||
#[inline]
|
||||
pub const fn is_valid(&self) -> bool {
|
||||
self.id != u32::MAX
|
||||
}
|
||||
|
||||
/// 转换为原始值(用于序列化等)
|
||||
///
|
||||
/// Convert to raw value (for serialization, etc.).
|
||||
#[inline]
|
||||
pub const fn to_raw(&self) -> (u32, u32) {
|
||||
(self.id, self.generation)
|
||||
}
|
||||
|
||||
/// 从原始值创建(用于反序列化等)
|
||||
///
|
||||
/// Create from raw value (for deserialization, etc.).
|
||||
#[inline]
|
||||
pub const fn from_raw(id: u32, generation: u32) -> Self {
|
||||
Self::new(id, generation)
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 资源类型标记 | Resource Type Markers ====================
|
||||
|
||||
/// 缓冲区类型标记 | Buffer type marker
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BufferMarker;
|
||||
|
||||
/// 顶点数组对象类型标记 | Vertex array object type marker
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct VertexArrayMarker;
|
||||
|
||||
/// 着色器程序类型标记 | Shader program type marker
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ShaderMarker;
|
||||
|
||||
/// 纹理类型标记 | Texture type marker
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct TextureMarker;
|
||||
|
||||
/// 材质类型标记 | Material type marker
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct MaterialMarker;
|
||||
|
||||
/// 帧缓冲区类型标记 | Framebuffer type marker
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FramebufferMarker;
|
||||
|
||||
/// 渲染缓冲区类型标记 | Renderbuffer type marker
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RenderbufferMarker;
|
||||
|
||||
/// 采样器类型标记 | Sampler type marker
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct SamplerMarker;
|
||||
|
||||
// ==================== 类型别名 | Type Aliases ====================
|
||||
|
||||
/// 缓冲区句柄 | Buffer handle
|
||||
pub type BufferHandle = Handle<BufferMarker>;
|
||||
|
||||
/// 顶点数组对象句柄 | Vertex array object handle
|
||||
pub type VertexArrayHandle = Handle<VertexArrayMarker>;
|
||||
|
||||
/// 着色器程序句柄 | Shader program handle
|
||||
pub type ShaderHandle = Handle<ShaderMarker>;
|
||||
|
||||
/// 纹理句柄 | Texture handle
|
||||
pub type TextureHandle = Handle<TextureMarker>;
|
||||
|
||||
/// 材质句柄 | Material handle
|
||||
pub type MaterialHandle = Handle<MaterialMarker>;
|
||||
|
||||
/// 帧缓冲区句柄 | Framebuffer handle
|
||||
pub type FramebufferHandle = Handle<FramebufferMarker>;
|
||||
|
||||
/// 渲染缓冲区句柄 | Renderbuffer handle
|
||||
pub type RenderbufferHandle = Handle<RenderbufferMarker>;
|
||||
|
||||
/// 采样器句柄 | Sampler handle
|
||||
pub type SamplerHandle = Handle<SamplerMarker>;
|
||||
|
||||
// ==================== 句柄映射 | Handle Map ====================
|
||||
|
||||
/// 带代数的句柄映射
|
||||
///
|
||||
/// 用于管理资源的分配和释放,支持句柄重用和过期检测。
|
||||
///
|
||||
/// Handle map with generation tracking.
|
||||
/// Used for managing resource allocation and deallocation,
|
||||
/// supports handle reuse and stale detection.
|
||||
pub struct HandleMap<T> {
|
||||
/// 存储项:(资源, 代数) | Items: (resource, generation)
|
||||
items: Vec<Option<(T, u32)>>,
|
||||
/// 空闲列表 | Free list
|
||||
free_list: Vec<u32>,
|
||||
/// 下一个代数 | Next generation
|
||||
next_generation: u32,
|
||||
}
|
||||
|
||||
impl<T> Default for HandleMap<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> HandleMap<T> {
|
||||
/// 创建新的句柄映射
|
||||
///
|
||||
/// Create new handle map.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
items: Vec::new(),
|
||||
free_list: Vec::new(),
|
||||
next_generation: 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建带预分配容量的句柄映射
|
||||
///
|
||||
/// Create handle map with pre-allocated capacity.
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self {
|
||||
items: Vec::with_capacity(capacity),
|
||||
free_list: Vec::new(),
|
||||
next_generation: 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// 插入资源并返回句柄
|
||||
///
|
||||
/// Insert resource and return handle.
|
||||
pub fn insert<M>(&mut self, item: T) -> Handle<M> {
|
||||
let generation = self.next_generation;
|
||||
self.next_generation = self.next_generation.wrapping_add(1);
|
||||
if self.next_generation == 0 {
|
||||
self.next_generation = 1; // 跳过 0,避免与空句柄混淆
|
||||
}
|
||||
|
||||
let id = if let Some(id) = self.free_list.pop() {
|
||||
self.items[id as usize] = Some((item, generation));
|
||||
id
|
||||
} else {
|
||||
let id = self.items.len() as u32;
|
||||
self.items.push(Some((item, generation)));
|
||||
id
|
||||
};
|
||||
|
||||
Handle::new(id, generation)
|
||||
}
|
||||
|
||||
/// 获取资源引用
|
||||
///
|
||||
/// Get resource reference.
|
||||
pub fn get<M>(&self, handle: Handle<M>) -> Option<&T> {
|
||||
self.items
|
||||
.get(handle.id() as usize)?
|
||||
.as_ref()
|
||||
.filter(|(_, gen)| *gen == handle.generation())
|
||||
.map(|(item, _)| item)
|
||||
}
|
||||
|
||||
/// 获取资源可变引用
|
||||
///
|
||||
/// Get mutable resource reference.
|
||||
pub fn get_mut<M>(&mut self, handle: Handle<M>) -> Option<&mut T> {
|
||||
self.items
|
||||
.get_mut(handle.id() as usize)?
|
||||
.as_mut()
|
||||
.filter(|(_, gen)| *gen == handle.generation())
|
||||
.map(|(item, _)| item)
|
||||
}
|
||||
|
||||
/// 移除资源
|
||||
///
|
||||
/// Remove resource.
|
||||
pub fn remove<M>(&mut self, handle: Handle<M>) -> Option<T> {
|
||||
let slot = self.items.get_mut(handle.id() as usize)?;
|
||||
if let Some((_, gen)) = slot {
|
||||
if *gen == handle.generation() {
|
||||
let item = slot.take().map(|(item, _)| item);
|
||||
self.free_list.push(handle.id());
|
||||
return item;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// 检查句柄是否有效
|
||||
///
|
||||
/// Check if handle is valid.
|
||||
pub fn contains<M>(&self, handle: Handle<M>) -> bool {
|
||||
self.get(handle).is_some()
|
||||
}
|
||||
|
||||
/// 获取当前资源数量
|
||||
///
|
||||
/// Get current resource count.
|
||||
pub fn len(&self) -> usize {
|
||||
self.items.len() - self.free_list.len()
|
||||
}
|
||||
|
||||
/// 是否为空
|
||||
///
|
||||
/// Check if empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// 清空所有资源
|
||||
///
|
||||
/// Clear all resources.
|
||||
pub fn clear(&mut self) {
|
||||
self.items.clear();
|
||||
self.free_list.clear();
|
||||
}
|
||||
|
||||
/// 迭代所有有效资源
|
||||
///
|
||||
/// Iterate over all valid resources.
|
||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||
self.items.iter().filter_map(|opt| opt.as_ref().map(|(item, _)| item))
|
||||
}
|
||||
|
||||
/// 迭代所有有效资源(可变)
|
||||
///
|
||||
/// Iterate over all valid resources (mutable).
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
||||
self.items.iter_mut().filter_map(|opt| opt.as_mut().map(|(item, _)| item))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_handle_creation() {
|
||||
let handle: TextureHandle = TextureHandle::new(1, 1);
|
||||
assert_eq!(handle.id(), 1);
|
||||
assert_eq!(handle.generation(), 1);
|
||||
assert!(handle.is_valid());
|
||||
assert!(!handle.is_null());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_handle() {
|
||||
let handle: BufferHandle = BufferHandle::null();
|
||||
assert!(handle.is_null());
|
||||
assert!(!handle.is_valid());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_handle_map() {
|
||||
let mut map: HandleMap<String> = HandleMap::new();
|
||||
|
||||
let h1: TextureHandle = map.insert("texture1".to_string());
|
||||
let h2: TextureHandle = map.insert("texture2".to_string());
|
||||
|
||||
assert_eq!(map.get(h1), Some(&"texture1".to_string()));
|
||||
assert_eq!(map.get(h2), Some(&"texture2".to_string()));
|
||||
assert_eq!(map.len(), 2);
|
||||
|
||||
// 移除后句柄失效
|
||||
let removed = map.remove(h1);
|
||||
assert_eq!(removed, Some("texture1".to_string()));
|
||||
assert_eq!(map.get(h1), None);
|
||||
assert_eq!(map.len(), 1);
|
||||
|
||||
// 重用空闲槽位
|
||||
let h3: TextureHandle = map.insert("texture3".to_string());
|
||||
assert_eq!(h3.id(), h1.id()); // 重用了 h1 的 ID
|
||||
assert_ne!(h3.generation(), h1.generation()); // 但代数不同
|
||||
}
|
||||
}
|
||||
9
packages/rust/engine-shared/src/types/mod.rs
Normal file
9
packages/rust/engine-shared/src/types/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
//! 共享类型定义
|
||||
//!
|
||||
//! Shared type definitions.
|
||||
|
||||
pub mod handle;
|
||||
pub mod vertex;
|
||||
pub mod blend;
|
||||
pub mod uniform;
|
||||
pub mod texture;
|
||||
383
packages/rust/engine-shared/src/types/texture.rs
Normal file
383
packages/rust/engine-shared/src/types/texture.rs
Normal file
@@ -0,0 +1,383 @@
|
||||
//! 纹理相关类型定义
|
||||
//!
|
||||
//! Texture-related type definitions.
|
||||
|
||||
/// 纹理格式
|
||||
///
|
||||
/// Texture format.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum TextureFormat {
|
||||
/// RGBA 8-bit(默认)| RGBA 8-bit (default)
|
||||
#[default]
|
||||
RGBA8,
|
||||
/// RGB 8-bit | RGB 8-bit
|
||||
RGB8,
|
||||
/// 单通道 8-bit | Single channel 8-bit
|
||||
R8,
|
||||
/// 双通道 8-bit | Dual channel 8-bit
|
||||
RG8,
|
||||
/// RGBA 16-bit 浮点 | RGBA 16-bit float
|
||||
RGBA16F,
|
||||
/// RGBA 32-bit 浮点 | RGBA 32-bit float
|
||||
RGBA32F,
|
||||
/// 24-bit 深度 | 24-bit depth
|
||||
Depth24,
|
||||
/// 32-bit 浮点深度 | 32-bit float depth
|
||||
Depth32F,
|
||||
/// 24-bit 深度 + 8-bit 模板 | 24-bit depth + 8-bit stencil
|
||||
Depth24Stencil8,
|
||||
}
|
||||
|
||||
impl TextureFormat {
|
||||
/// 获取每像素字节数
|
||||
///
|
||||
/// Get bytes per pixel.
|
||||
pub const fn bytes_per_pixel(&self) -> usize {
|
||||
match self {
|
||||
Self::R8 => 1,
|
||||
Self::RG8 => 2,
|
||||
Self::RGB8 => 3,
|
||||
Self::RGBA8 => 4,
|
||||
Self::RGBA16F => 8,
|
||||
Self::RGBA32F => 16,
|
||||
Self::Depth24 => 3,
|
||||
Self::Depth32F => 4,
|
||||
Self::Depth24Stencil8 => 4,
|
||||
}
|
||||
}
|
||||
|
||||
/// 是否为深度格式
|
||||
///
|
||||
/// Check if depth format.
|
||||
pub const fn is_depth(&self) -> bool {
|
||||
matches!(self, Self::Depth24 | Self::Depth32F | Self::Depth24Stencil8)
|
||||
}
|
||||
|
||||
/// 是否为浮点格式
|
||||
///
|
||||
/// Check if float format.
|
||||
pub const fn is_float(&self) -> bool {
|
||||
matches!(self, Self::RGBA16F | Self::RGBA32F | Self::Depth32F)
|
||||
}
|
||||
|
||||
/// 获取通道数
|
||||
///
|
||||
/// Get channel count.
|
||||
pub const fn channel_count(&self) -> u32 {
|
||||
match self {
|
||||
Self::R8 | Self::Depth24 | Self::Depth32F => 1,
|
||||
Self::RG8 | Self::Depth24Stencil8 => 2,
|
||||
Self::RGB8 => 3,
|
||||
Self::RGBA8 | Self::RGBA16F | Self::RGBA32F => 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 纹理过滤模式
|
||||
///
|
||||
/// Texture filter mode.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum TextureFilter {
|
||||
/// 最近邻(像素风格,默认)| Nearest neighbor (pixel art, default)
|
||||
#[default]
|
||||
Nearest,
|
||||
/// 线性插值(平滑)| Linear interpolation (smooth)
|
||||
Linear,
|
||||
/// 最近邻 + 最近邻 mipmap | Nearest + nearest mipmap
|
||||
NearestMipmapNearest,
|
||||
/// 线性 + 最近邻 mipmap | Linear + nearest mipmap
|
||||
LinearMipmapNearest,
|
||||
/// 最近邻 + 线性 mipmap | Nearest + linear mipmap
|
||||
NearestMipmapLinear,
|
||||
/// 线性 + 线性 mipmap(三线性过滤)| Linear + linear mipmap (trilinear)
|
||||
LinearMipmapLinear,
|
||||
}
|
||||
|
||||
impl TextureFilter {
|
||||
/// 是否需要 mipmap
|
||||
///
|
||||
/// Check if mipmap required.
|
||||
pub const fn requires_mipmap(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::NearestMipmapNearest
|
||||
| Self::LinearMipmapNearest
|
||||
| Self::NearestMipmapLinear
|
||||
| Self::LinearMipmapLinear
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// 纹理环绕模式
|
||||
///
|
||||
/// Texture wrap mode.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum TextureWrap {
|
||||
/// 钳制到边缘(默认)| Clamp to edge (default)
|
||||
#[default]
|
||||
ClampToEdge,
|
||||
/// 重复 | Repeat
|
||||
Repeat,
|
||||
/// 镜像重复 | Mirrored repeat
|
||||
MirroredRepeat,
|
||||
}
|
||||
|
||||
/// 纹理描述符
|
||||
///
|
||||
/// Texture descriptor.
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct TextureDescriptor {
|
||||
/// 宽度 | Width
|
||||
pub width: u32,
|
||||
/// 高度 | Height
|
||||
pub height: u32,
|
||||
/// 格式 | Format
|
||||
pub format: TextureFormat,
|
||||
/// 缩小过滤 | Minification filter
|
||||
pub filter_min: TextureFilter,
|
||||
/// 放大过滤 | Magnification filter
|
||||
pub filter_mag: TextureFilter,
|
||||
/// S 方向(水平)环绕 | S (horizontal) wrap
|
||||
pub wrap_s: TextureWrap,
|
||||
/// T 方向(垂直)环绕 | T (vertical) wrap
|
||||
pub wrap_t: TextureWrap,
|
||||
/// 是否生成 mipmap | Generate mipmaps
|
||||
pub generate_mipmaps: bool,
|
||||
/// 标签(调试用)| Label (for debugging)
|
||||
pub label: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for TextureDescriptor {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
width: 1,
|
||||
height: 1,
|
||||
format: TextureFormat::RGBA8,
|
||||
filter_min: TextureFilter::Nearest,
|
||||
filter_mag: TextureFilter::Nearest,
|
||||
wrap_s: TextureWrap::ClampToEdge,
|
||||
wrap_t: TextureWrap::ClampToEdge,
|
||||
generate_mipmaps: false,
|
||||
label: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TextureDescriptor {
|
||||
/// 创建新的纹理描述符
|
||||
///
|
||||
/// Create new texture descriptor.
|
||||
pub fn new(width: u32, height: u32) -> Self {
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置格式
|
||||
///
|
||||
/// Set format.
|
||||
pub fn with_format(mut self, format: TextureFormat) -> Self {
|
||||
self.format = format;
|
||||
self
|
||||
}
|
||||
|
||||
/// 设置过滤模式(同时设置缩小和放大)
|
||||
///
|
||||
/// Set filter mode (both min and mag).
|
||||
pub fn with_filter(mut self, filter: TextureFilter) -> Self {
|
||||
self.filter_min = filter;
|
||||
self.filter_mag = filter;
|
||||
self
|
||||
}
|
||||
|
||||
/// 设置环绕模式(同时设置 S 和 T)
|
||||
///
|
||||
/// Set wrap mode (both S and T).
|
||||
pub fn with_wrap(mut self, wrap: TextureWrap) -> Self {
|
||||
self.wrap_s = wrap;
|
||||
self.wrap_t = wrap;
|
||||
self
|
||||
}
|
||||
|
||||
/// 启用 mipmap 生成
|
||||
///
|
||||
/// Enable mipmap generation.
|
||||
pub fn with_mipmaps(mut self) -> Self {
|
||||
self.generate_mipmaps = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// 设置标签
|
||||
///
|
||||
/// Set label.
|
||||
pub fn with_label(mut self, label: impl Into<String>) -> Self {
|
||||
self.label = Some(label.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// 创建像素风格纹理描述符
|
||||
///
|
||||
/// Create pixel art texture descriptor.
|
||||
pub fn pixel_art(width: u32, height: u32) -> Self {
|
||||
Self::new(width, height)
|
||||
.with_filter(TextureFilter::Nearest)
|
||||
.with_wrap(TextureWrap::ClampToEdge)
|
||||
}
|
||||
|
||||
/// 创建平滑纹理描述符
|
||||
///
|
||||
/// Create smooth texture descriptor.
|
||||
pub fn smooth(width: u32, height: u32) -> Self {
|
||||
Self::new(width, height)
|
||||
.with_filter(TextureFilter::Linear)
|
||||
.with_wrap(TextureWrap::ClampToEdge)
|
||||
}
|
||||
|
||||
/// 创建平铺纹理描述符
|
||||
///
|
||||
/// Create tiled texture descriptor.
|
||||
pub fn tiled(width: u32, height: u32) -> Self {
|
||||
Self::new(width, height)
|
||||
.with_filter(TextureFilter::Nearest)
|
||||
.with_wrap(TextureWrap::Repeat)
|
||||
}
|
||||
|
||||
/// 计算纹理数据大小(字节)
|
||||
///
|
||||
/// Calculate texture data size (bytes).
|
||||
pub fn data_size(&self) -> usize {
|
||||
self.width as usize * self.height as usize * self.format.bytes_per_pixel()
|
||||
}
|
||||
}
|
||||
|
||||
/// 纹理状态
|
||||
///
|
||||
/// Texture state.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum TextureState {
|
||||
/// 加载中 | Loading
|
||||
Loading,
|
||||
/// 就绪 | Ready
|
||||
Ready,
|
||||
/// 加载失败 | Failed
|
||||
Failed(String),
|
||||
}
|
||||
|
||||
impl TextureState {
|
||||
/// 是否就绪
|
||||
///
|
||||
/// Check if ready.
|
||||
pub const fn is_ready(&self) -> bool {
|
||||
matches!(self, Self::Ready)
|
||||
}
|
||||
|
||||
/// 是否加载中
|
||||
///
|
||||
/// Check if loading.
|
||||
pub const fn is_loading(&self) -> bool {
|
||||
matches!(self, Self::Loading)
|
||||
}
|
||||
|
||||
/// 是否失败
|
||||
///
|
||||
/// Check if failed.
|
||||
pub const fn is_failed(&self) -> bool {
|
||||
matches!(self, Self::Failed(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// 图片数据(用于纹理上传)
|
||||
///
|
||||
/// Image data (for texture upload).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImageData {
|
||||
/// 宽度 | Width
|
||||
pub width: u32,
|
||||
/// 高度 | Height
|
||||
pub height: u32,
|
||||
/// 像素数据(RGBA8 格式)| Pixel data (RGBA8 format)
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl ImageData {
|
||||
/// 创建新的图片数据
|
||||
///
|
||||
/// Create new image data.
|
||||
pub fn new(width: u32, height: u32, data: Vec<u8>) -> Self {
|
||||
debug_assert_eq!(
|
||||
data.len(),
|
||||
(width * height * 4) as usize,
|
||||
"Data size mismatch"
|
||||
);
|
||||
Self { width, height, data }
|
||||
}
|
||||
|
||||
/// 创建空白图片
|
||||
///
|
||||
/// Create blank image.
|
||||
pub fn blank(width: u32, height: u32) -> Self {
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
data: vec![0; (width * height * 4) as usize],
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建纯色图片
|
||||
///
|
||||
/// Create solid color image.
|
||||
pub fn solid(width: u32, height: u32, r: u8, g: u8, b: u8, a: u8) -> Self {
|
||||
let pixel_count = (width * height) as usize;
|
||||
let mut data = Vec::with_capacity(pixel_count * 4);
|
||||
for _ in 0..pixel_count {
|
||||
data.extend_from_slice(&[r, g, b, a]);
|
||||
}
|
||||
Self { width, height, data }
|
||||
}
|
||||
|
||||
/// 创建白色 1x1 纹理(默认纹理)
|
||||
///
|
||||
/// Create white 1x1 texture (default texture).
|
||||
pub fn white_pixel() -> Self {
|
||||
Self::solid(1, 1, 255, 255, 255, 255)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_texture_descriptor_builder() {
|
||||
let desc = TextureDescriptor::new(256, 256)
|
||||
.with_format(TextureFormat::RGBA8)
|
||||
.with_filter(TextureFilter::Linear)
|
||||
.with_wrap(TextureWrap::Repeat)
|
||||
.with_label("test_texture");
|
||||
|
||||
assert_eq!(desc.width, 256);
|
||||
assert_eq!(desc.height, 256);
|
||||
assert_eq!(desc.filter_min, TextureFilter::Linear);
|
||||
assert_eq!(desc.wrap_s, TextureWrap::Repeat);
|
||||
assert_eq!(desc.label, Some("test_texture".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_image_data_size() {
|
||||
let img = ImageData::blank(64, 64);
|
||||
assert_eq!(img.data.len(), 64 * 64 * 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_texture_format_bytes() {
|
||||
assert_eq!(TextureFormat::R8.bytes_per_pixel(), 1);
|
||||
assert_eq!(TextureFormat::RGBA8.bytes_per_pixel(), 4);
|
||||
assert_eq!(TextureFormat::RGBA16F.bytes_per_pixel(), 8);
|
||||
}
|
||||
}
|
||||
307
packages/rust/engine-shared/src/types/uniform.rs
Normal file
307
packages/rust/engine-shared/src/types/uniform.rs
Normal file
@@ -0,0 +1,307 @@
|
||||
//! Uniform 类型定义
|
||||
//!
|
||||
//! Uniform type definitions.
|
||||
|
||||
use glam::{Vec2, Vec3, Vec4, Mat3, Mat4};
|
||||
|
||||
/// Uniform 值类型
|
||||
///
|
||||
/// Uniform value type.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum UniformValue {
|
||||
/// 单精度浮点 | Single float
|
||||
Float(f32),
|
||||
/// 二维向量 | 2D vector
|
||||
Float2(Vec2),
|
||||
/// 三维向量 | 3D vector
|
||||
Float3(Vec3),
|
||||
/// 四维向量 | 4D vector
|
||||
Float4(Vec4),
|
||||
/// 有符号整数 | Signed integer
|
||||
Int(i32),
|
||||
/// 二维整数向量 | 2D integer vector
|
||||
Int2([i32; 2]),
|
||||
/// 三维整数向量 | 3D integer vector
|
||||
Int3([i32; 3]),
|
||||
/// 四维整数向量 | 4D integer vector
|
||||
Int4([i32; 4]),
|
||||
/// 无符号整数 | Unsigned integer
|
||||
UInt(u32),
|
||||
/// 3x3 矩阵 | 3x3 matrix
|
||||
Mat3(Mat3),
|
||||
/// 4x4 矩阵 | 4x4 matrix
|
||||
Mat4(Mat4),
|
||||
/// 纹理单元索引 | Texture unit index
|
||||
Texture(u32),
|
||||
}
|
||||
|
||||
impl UniformValue {
|
||||
/// 获取类型名称
|
||||
///
|
||||
/// Get type name.
|
||||
pub const fn type_name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Float(_) => "float",
|
||||
Self::Float2(_) => "vec2",
|
||||
Self::Float3(_) => "vec3",
|
||||
Self::Float4(_) => "vec4",
|
||||
Self::Int(_) => "int",
|
||||
Self::Int2(_) => "ivec2",
|
||||
Self::Int3(_) => "ivec3",
|
||||
Self::Int4(_) => "ivec4",
|
||||
Self::UInt(_) => "uint",
|
||||
Self::Mat3(_) => "mat3",
|
||||
Self::Mat4(_) => "mat4",
|
||||
Self::Texture(_) => "sampler2D",
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建颜色 uniform(vec4)
|
||||
///
|
||||
/// Create color uniform (vec4).
|
||||
pub fn color(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||
Self::Float4(Vec4::new(r, g, b, a))
|
||||
}
|
||||
|
||||
/// 创建颜色 uniform(从 u32 RGBA)
|
||||
///
|
||||
/// Create color uniform (from u32 RGBA).
|
||||
pub fn color_from_u32(rgba: u32) -> Self {
|
||||
let r = ((rgba >> 24) & 0xFF) as f32 / 255.0;
|
||||
let g = ((rgba >> 16) & 0xFF) as f32 / 255.0;
|
||||
let b = ((rgba >> 8) & 0xFF) as f32 / 255.0;
|
||||
let a = (rgba & 0xFF) as f32 / 255.0;
|
||||
Self::color(r, g, b, a)
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== From 实现 | From Implementations ====================
|
||||
|
||||
impl From<f32> for UniformValue {
|
||||
fn from(v: f32) -> Self {
|
||||
Self::Float(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec2> for UniformValue {
|
||||
fn from(v: Vec2) -> Self {
|
||||
Self::Float2(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec3> for UniformValue {
|
||||
fn from(v: Vec3) -> Self {
|
||||
Self::Float3(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec4> for UniformValue {
|
||||
fn from(v: Vec4) -> Self {
|
||||
Self::Float4(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for UniformValue {
|
||||
fn from(v: i32) -> Self {
|
||||
Self::Int(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[i32; 2]> for UniformValue {
|
||||
fn from(v: [i32; 2]) -> Self {
|
||||
Self::Int2(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[i32; 3]> for UniformValue {
|
||||
fn from(v: [i32; 3]) -> Self {
|
||||
Self::Int3(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[i32; 4]> for UniformValue {
|
||||
fn from(v: [i32; 4]) -> Self {
|
||||
Self::Int4(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for UniformValue {
|
||||
fn from(v: u32) -> Self {
|
||||
Self::UInt(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mat3> for UniformValue {
|
||||
fn from(v: Mat3) -> Self {
|
||||
Self::Mat3(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mat4> for UniformValue {
|
||||
fn from(v: Mat4) -> Self {
|
||||
Self::Mat4(v)
|
||||
}
|
||||
}
|
||||
|
||||
/// Uniform 绑定描述
|
||||
///
|
||||
/// Uniform binding descriptor.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UniformBinding {
|
||||
/// Uniform 名称 | Uniform name
|
||||
pub name: String,
|
||||
/// Uniform 值 | Uniform value
|
||||
pub value: UniformValue,
|
||||
}
|
||||
|
||||
impl UniformBinding {
|
||||
/// 创建新的 uniform 绑定
|
||||
///
|
||||
/// Create new uniform binding.
|
||||
pub fn new(name: impl Into<String>, value: impl Into<UniformValue>) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
value: value.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Uniform 集合
|
||||
///
|
||||
/// Uniform set.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct UniformSet {
|
||||
/// Uniform 列表 | Uniform list
|
||||
bindings: Vec<UniformBinding>,
|
||||
}
|
||||
|
||||
impl UniformSet {
|
||||
/// 创建新的 uniform 集合
|
||||
///
|
||||
/// Create new uniform set.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// 创建带容量的 uniform 集合
|
||||
///
|
||||
/// Create uniform set with capacity.
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self {
|
||||
bindings: Vec::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置 uniform 值
|
||||
///
|
||||
/// Set uniform value.
|
||||
pub fn set(&mut self, name: impl Into<String>, value: impl Into<UniformValue>) {
|
||||
let name = name.into();
|
||||
let value = value.into();
|
||||
|
||||
// 查找已存在的 uniform
|
||||
for binding in &mut self.bindings {
|
||||
if binding.name == name {
|
||||
binding.value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加新的 uniform
|
||||
self.bindings.push(UniformBinding { name, value });
|
||||
}
|
||||
|
||||
/// 获取 uniform 值
|
||||
///
|
||||
/// Get uniform value.
|
||||
pub fn get(&self, name: &str) -> Option<&UniformValue> {
|
||||
self.bindings
|
||||
.iter()
|
||||
.find(|b| b.name == name)
|
||||
.map(|b| &b.value)
|
||||
}
|
||||
|
||||
/// 移除 uniform
|
||||
///
|
||||
/// Remove uniform.
|
||||
pub fn remove(&mut self, name: &str) -> Option<UniformValue> {
|
||||
if let Some(idx) = self.bindings.iter().position(|b| b.name == name) {
|
||||
Some(self.bindings.remove(idx).value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// 清空所有 uniform
|
||||
///
|
||||
/// Clear all uniforms.
|
||||
pub fn clear(&mut self) {
|
||||
self.bindings.clear();
|
||||
}
|
||||
|
||||
/// 迭代所有 uniform
|
||||
///
|
||||
/// Iterate over all uniforms.
|
||||
pub fn iter(&self) -> impl Iterator<Item = &UniformBinding> {
|
||||
self.bindings.iter()
|
||||
}
|
||||
|
||||
/// 获取 uniform 数量
|
||||
///
|
||||
/// Get uniform count.
|
||||
pub fn len(&self) -> usize {
|
||||
self.bindings.len()
|
||||
}
|
||||
|
||||
/// 是否为空
|
||||
///
|
||||
/// Check if empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.bindings.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_uniform_set() {
|
||||
let mut set = UniformSet::new();
|
||||
|
||||
set.set("u_time", 1.5f32);
|
||||
set.set("u_resolution", Vec2::new(800.0, 600.0));
|
||||
set.set("u_color", Vec4::new(1.0, 0.0, 0.0, 1.0));
|
||||
|
||||
assert_eq!(set.len(), 3);
|
||||
|
||||
if let Some(UniformValue::Float(v)) = set.get("u_time") {
|
||||
assert_eq!(*v, 1.5);
|
||||
} else {
|
||||
panic!("Expected Float");
|
||||
}
|
||||
|
||||
// 更新已存在的 uniform
|
||||
set.set("u_time", 2.0f32);
|
||||
assert_eq!(set.len(), 3); // 数量不变
|
||||
|
||||
if let Some(UniformValue::Float(v)) = set.get("u_time") {
|
||||
assert_eq!(*v, 2.0);
|
||||
} else {
|
||||
panic!("Expected Float");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_color_from_u32() {
|
||||
let color = UniformValue::color_from_u32(0xFF0000FF); // Red
|
||||
if let UniformValue::Float4(v) = color {
|
||||
assert!((v.x - 1.0).abs() < 0.001);
|
||||
assert!((v.y - 0.0).abs() < 0.001);
|
||||
assert!((v.z - 0.0).abs() < 0.001);
|
||||
assert!((v.w - 1.0).abs() < 0.001);
|
||||
} else {
|
||||
panic!("Expected Float4");
|
||||
}
|
||||
}
|
||||
}
|
||||
352
packages/rust/engine-shared/src/types/vertex.rs
Normal file
352
packages/rust/engine-shared/src/types/vertex.rs
Normal file
@@ -0,0 +1,352 @@
|
||||
//! 顶点格式定义
|
||||
//!
|
||||
//! Vertex format definitions.
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
/// 顶点属性类型
|
||||
///
|
||||
/// Vertex attribute type.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum VertexAttributeType {
|
||||
/// 单精度浮点 | Single float
|
||||
Float,
|
||||
/// 二维向量 | 2D vector
|
||||
Float2,
|
||||
/// 三维向量 | 3D vector
|
||||
Float3,
|
||||
/// 四维向量 | 4D vector
|
||||
Float4,
|
||||
/// 有符号整数 | Signed integer
|
||||
Int,
|
||||
/// 二维整数向量 | 2D integer vector
|
||||
Int2,
|
||||
/// 三维整数向量 | 3D integer vector
|
||||
Int3,
|
||||
/// 四维整数向量 | 4D integer vector
|
||||
Int4,
|
||||
/// 无符号整数 | Unsigned integer
|
||||
UInt,
|
||||
/// 二维无符号整数向量 | 2D unsigned integer vector
|
||||
UInt2,
|
||||
/// 三维无符号整数向量 | 3D unsigned integer vector
|
||||
UInt3,
|
||||
/// 四维无符号整数向量 | 4D unsigned integer vector
|
||||
UInt4,
|
||||
/// 归一化的 4 字节颜色(RGBA) | Normalized 4-byte color (RGBA)
|
||||
UByte4Norm,
|
||||
}
|
||||
|
||||
impl VertexAttributeType {
|
||||
/// 获取字节大小
|
||||
///
|
||||
/// Get byte size.
|
||||
#[inline]
|
||||
pub const fn byte_size(&self) -> usize {
|
||||
match self {
|
||||
Self::Float | Self::Int | Self::UInt => 4,
|
||||
Self::Float2 | Self::Int2 | Self::UInt2 => 8,
|
||||
Self::Float3 | Self::Int3 | Self::UInt3 => 12,
|
||||
Self::Float4 | Self::Int4 | Self::UInt4 => 16,
|
||||
Self::UByte4Norm => 4,
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取组件数量
|
||||
///
|
||||
/// Get component count.
|
||||
#[inline]
|
||||
pub const fn component_count(&self) -> u32 {
|
||||
match self {
|
||||
Self::Float | Self::Int | Self::UInt => 1,
|
||||
Self::Float2 | Self::Int2 | Self::UInt2 => 2,
|
||||
Self::Float3 | Self::Int3 | Self::UInt3 => 3,
|
||||
Self::Float4 | Self::Int4 | Self::UInt4 | Self::UByte4Norm => 4,
|
||||
}
|
||||
}
|
||||
|
||||
/// 是否为浮点类型
|
||||
///
|
||||
/// Check if float type.
|
||||
#[inline]
|
||||
pub const fn is_float(&self) -> bool {
|
||||
matches!(self, Self::Float | Self::Float2 | Self::Float3 | Self::Float4)
|
||||
}
|
||||
|
||||
/// 是否为整数类型
|
||||
///
|
||||
/// Check if integer type.
|
||||
#[inline]
|
||||
pub const fn is_integer(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Int | Self::Int2 | Self::Int3 | Self::Int4 |
|
||||
Self::UInt | Self::UInt2 | Self::UInt3 | Self::UInt4
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// 顶点属性描述
|
||||
///
|
||||
/// Vertex attribute descriptor.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VertexAttribute {
|
||||
/// 属性名称(对应 shader 中的 attribute)
|
||||
///
|
||||
/// Attribute name (corresponds to shader attribute).
|
||||
pub name: &'static str,
|
||||
|
||||
/// 属性类型
|
||||
///
|
||||
/// Attribute type.
|
||||
pub attr_type: VertexAttributeType,
|
||||
|
||||
/// 字节偏移
|
||||
///
|
||||
/// Byte offset in vertex.
|
||||
pub offset: usize,
|
||||
|
||||
/// 是否归一化(仅对整数类型有效)
|
||||
///
|
||||
/// Whether to normalize (only valid for integer types).
|
||||
pub normalized: bool,
|
||||
}
|
||||
|
||||
impl VertexAttribute {
|
||||
/// 创建新的顶点属性
|
||||
///
|
||||
/// Create new vertex attribute.
|
||||
pub const fn new(
|
||||
name: &'static str,
|
||||
attr_type: VertexAttributeType,
|
||||
offset: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
attr_type,
|
||||
offset,
|
||||
normalized: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建归一化的顶点属性
|
||||
///
|
||||
/// Create normalized vertex attribute.
|
||||
pub const fn normalized(
|
||||
name: &'static str,
|
||||
attr_type: VertexAttributeType,
|
||||
offset: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
attr_type,
|
||||
offset,
|
||||
normalized: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 顶点布局描述
|
||||
///
|
||||
/// Vertex layout descriptor.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VertexLayout {
|
||||
/// 属性列表
|
||||
///
|
||||
/// Attribute list.
|
||||
pub attributes: Vec<VertexAttribute>,
|
||||
|
||||
/// 步幅(单个顶点字节大小)
|
||||
///
|
||||
/// Stride (bytes per vertex).
|
||||
pub stride: usize,
|
||||
}
|
||||
|
||||
impl VertexLayout {
|
||||
/// 创建新的顶点布局
|
||||
///
|
||||
/// Create new vertex layout.
|
||||
pub fn new(attributes: Vec<VertexAttribute>, stride: usize) -> Self {
|
||||
Self { attributes, stride }
|
||||
}
|
||||
|
||||
/// 从属性列表自动计算步幅
|
||||
///
|
||||
/// Auto-calculate stride from attribute list.
|
||||
pub fn from_attributes(attributes: Vec<VertexAttribute>) -> Self {
|
||||
let stride = attributes
|
||||
.iter()
|
||||
.map(|attr| attr.offset + attr.attr_type.byte_size())
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
Self { attributes, stride }
|
||||
}
|
||||
|
||||
/// 创建 Sprite 顶点布局
|
||||
///
|
||||
/// 布局:position(2) + texcoord(2) + color(4) + aspect(1) = 9 floats = 36 bytes
|
||||
///
|
||||
/// Create sprite vertex layout.
|
||||
/// Layout: position(2) + texcoord(2) + color(4) + aspect(1) = 9 floats = 36 bytes
|
||||
pub fn sprite() -> Self {
|
||||
Self {
|
||||
attributes: vec![
|
||||
VertexAttribute::new("a_position", VertexAttributeType::Float2, 0),
|
||||
VertexAttribute::new("a_texcoord", VertexAttributeType::Float2, 8),
|
||||
VertexAttribute::new("a_color", VertexAttributeType::Float4, 16),
|
||||
VertexAttribute::new("a_aspect", VertexAttributeType::Float, 32),
|
||||
],
|
||||
stride: 36,
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建简单 2D 顶点布局(位置 + 颜色)
|
||||
///
|
||||
/// Create simple 2D vertex layout (position + color).
|
||||
pub fn simple_2d() -> Self {
|
||||
Self {
|
||||
attributes: vec![
|
||||
VertexAttribute::new("a_position", VertexAttributeType::Float2, 0),
|
||||
VertexAttribute::new("a_color", VertexAttributeType::Float4, 8),
|
||||
],
|
||||
stride: 24,
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建带纹理的 2D 顶点布局(位置 + 纹理坐标 + 颜色)
|
||||
///
|
||||
/// Create textured 2D vertex layout (position + texcoord + color).
|
||||
pub fn textured_2d() -> Self {
|
||||
Self {
|
||||
attributes: vec![
|
||||
VertexAttribute::new("a_position", VertexAttributeType::Float2, 0),
|
||||
VertexAttribute::new("a_texcoord", VertexAttributeType::Float2, 8),
|
||||
VertexAttribute::new("a_color", VertexAttributeType::Float4, 16),
|
||||
],
|
||||
stride: 32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 预定义顶点结构 | Predefined Vertex Structures ====================
|
||||
|
||||
/// 精灵顶点
|
||||
///
|
||||
/// Sprite vertex.
|
||||
///
|
||||
/// 与现有 SpriteBatch 顶点格式兼容。
|
||||
/// Compatible with existing SpriteBatch vertex format.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Default, Pod, Zeroable)]
|
||||
pub struct SpriteVertex {
|
||||
/// 位置 | Position
|
||||
pub position: [f32; 2],
|
||||
/// 纹理坐标 | Texture coordinates
|
||||
pub texcoord: [f32; 2],
|
||||
/// 颜色(RGBA) | Color (RGBA)
|
||||
pub color: [f32; 4],
|
||||
/// 宽高比(用于保持纹理比例) | Aspect ratio (for maintaining texture ratio)
|
||||
pub aspect: f32,
|
||||
}
|
||||
|
||||
impl SpriteVertex {
|
||||
/// 顶点布局
|
||||
///
|
||||
/// Vertex layout.
|
||||
pub fn layout() -> VertexLayout {
|
||||
VertexLayout::sprite()
|
||||
}
|
||||
}
|
||||
|
||||
/// 简单 2D 顶点(位置 + 颜色)
|
||||
///
|
||||
/// Simple 2D vertex (position + color).
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Default, Pod, Zeroable)]
|
||||
pub struct Simple2DVertex {
|
||||
/// 位置 | Position
|
||||
pub position: [f32; 2],
|
||||
/// 颜色(RGBA) | Color (RGBA)
|
||||
pub color: [f32; 4],
|
||||
}
|
||||
|
||||
impl Simple2DVertex {
|
||||
/// 创建新顶点
|
||||
///
|
||||
/// Create new vertex.
|
||||
pub const fn new(x: f32, y: f32, r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||
Self {
|
||||
position: [x, y],
|
||||
color: [r, g, b, a],
|
||||
}
|
||||
}
|
||||
|
||||
/// 顶点布局
|
||||
///
|
||||
/// Vertex layout.
|
||||
pub fn layout() -> VertexLayout {
|
||||
VertexLayout::simple_2d()
|
||||
}
|
||||
}
|
||||
|
||||
/// 带纹理的 2D 顶点
|
||||
///
|
||||
/// Textured 2D vertex.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Default, Pod, Zeroable)]
|
||||
pub struct Textured2DVertex {
|
||||
/// 位置 | Position
|
||||
pub position: [f32; 2],
|
||||
/// 纹理坐标 | Texture coordinates
|
||||
pub texcoord: [f32; 2],
|
||||
/// 颜色(RGBA) | Color (RGBA)
|
||||
pub color: [f32; 4],
|
||||
}
|
||||
|
||||
impl Textured2DVertex {
|
||||
/// 创建新顶点
|
||||
///
|
||||
/// Create new vertex.
|
||||
pub const fn new(x: f32, y: f32, u: f32, v: f32, r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||
Self {
|
||||
position: [x, y],
|
||||
texcoord: [u, v],
|
||||
color: [r, g, b, a],
|
||||
}
|
||||
}
|
||||
|
||||
/// 顶点布局
|
||||
///
|
||||
/// Vertex layout.
|
||||
pub fn layout() -> VertexLayout {
|
||||
VertexLayout::textured_2d()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::mem;
|
||||
|
||||
#[test]
|
||||
fn test_sprite_vertex_size() {
|
||||
assert_eq!(mem::size_of::<SpriteVertex>(), 36);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_2d_vertex_size() {
|
||||
assert_eq!(mem::size_of::<Simple2DVertex>(), 24);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_textured_2d_vertex_size() {
|
||||
assert_eq!(mem::size_of::<Textured2DVertex>(), 32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vertex_layout_stride() {
|
||||
let layout = VertexLayout::sprite();
|
||||
assert_eq!(layout.stride, 36);
|
||||
assert_eq!(layout.attributes.len(), 4);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user