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:
YHH
2025-12-26 14:50:35 +08:00
committed by GitHub
parent a84ff902e4
commit 155411e743
1936 changed files with 4147 additions and 11578 deletions

View File

@@ -0,0 +1,481 @@
//! Main 3D renderer implementation.
//! 主3D渲染器实现。
//!
//! Provides perspective and orthographic 3D rendering with depth testing.
//! 提供带深度测试的透视和正交3D渲染。
use es_engine_shared::{
traits::backend::GraphicsBackend,
types::{
handle::ShaderHandle,
blend::{RenderState, CompareFunc, CullMode, BlendMode as SharedBlendMode},
},
Mat4,
};
use std::collections::HashMap;
use crate::backend::WebGL2Backend;
use super::camera3d::Camera3D;
use super::batch::{SimpleVertex3D, FLOATS_PER_SIMPLE_VERTEX_3D};
use super::texture::TextureManager;
use super::material::{Material, BlendMode, UniformValue};
use super::shader::{SIMPLE3D_VERTEX_SHADER, SIMPLE3D_FRAGMENT_SHADER};
/// Convert local BlendMode to shared BlendMode.
/// 将本地 BlendMode 转换为共享 BlendMode。
fn to_shared_blend_mode(mode: BlendMode) -> SharedBlendMode {
match mode {
BlendMode::None => SharedBlendMode::None,
BlendMode::Alpha => SharedBlendMode::Alpha,
BlendMode::Additive => SharedBlendMode::Additive,
BlendMode::Multiply => SharedBlendMode::Multiply,
BlendMode::Screen => SharedBlendMode::Screen,
BlendMode::PremultipliedAlpha => SharedBlendMode::PremultipliedAlpha,
}
}
/// Mesh submission data for batched 3D rendering.
/// 用于批处理3D渲染的网格提交数据。
#[derive(Debug, Clone)]
pub struct MeshSubmission {
/// Vertex data (position, uv, color).
/// 顶点数据位置、UV、颜色
pub vertices: Vec<SimpleVertex3D>,
/// Index data.
/// 索引数据。
pub indices: Vec<u32>,
/// Model transformation matrix.
/// 模型变换矩阵。
pub transform: Mat4,
/// Material ID.
/// 材质 ID。
pub material_id: u32,
/// Texture ID.
/// 纹理 ID。
pub texture_id: u32,
}
/// 3D Renderer with perspective/orthographic camera support.
/// 支持透视/正交相机的3D渲染器。
pub struct Renderer3D {
/// 3D camera.
/// 3D相机。
camera: Camera3D,
/// Default 3D shader.
/// 默认3D着色器。
default_shader: ShaderHandle,
/// Custom shaders by ID.
/// 按ID存储的自定义着色器。
custom_shaders: HashMap<u32, ShaderHandle>,
/// Next shader ID for auto-assignment.
/// 自动分配的下一个着色器ID。
next_shader_id: u32,
/// Materials by ID.
/// 按ID存储的材质。
materials: HashMap<u32, Material>,
/// Pending mesh submissions for this frame.
/// 本帧待渲染的网格提交。
mesh_queue: Vec<MeshSubmission>,
/// Clear color.
/// 清除颜色。
clear_color: [f32; 4],
/// Whether depth test is enabled.
/// 是否启用深度测试。
depth_test_enabled: bool,
/// Whether depth write is enabled.
/// 是否启用深度写入。
depth_write_enabled: bool,
}
impl Renderer3D {
/// Create a new 3D renderer.
/// 创建新的3D渲染器。
pub fn new(backend: &mut WebGL2Backend) -> Result<Self, String> {
// Compile default 3D shader
// 编译默认3D着色器
let default_shader = backend
.compile_shader(SIMPLE3D_VERTEX_SHADER, SIMPLE3D_FRAGMENT_SHADER)
.map_err(|e| format!("Failed to compile 3D shader: {:?}", e))?;
let (width, height) = (backend.width() as f32, backend.height() as f32);
let camera = Camera3D::new(width, height, std::f32::consts::FRAC_PI_4);
let mut materials = HashMap::new();
materials.insert(0, Material::default());
Ok(Self {
camera,
default_shader,
custom_shaders: HashMap::new(),
next_shader_id: 100,
materials,
mesh_queue: Vec::new(),
clear_color: [0.1, 0.1, 0.12, 1.0],
depth_test_enabled: true,
depth_write_enabled: true,
})
}
/// Submit a mesh for rendering.
/// 提交网格进行渲染。
pub fn submit_mesh(&mut self, submission: MeshSubmission) {
self.mesh_queue.push(submission);
}
/// Submit a simple textured quad at position.
/// 在指定位置提交一个简单的纹理四边形。
pub fn submit_quad(
&mut self,
position: [f32; 3],
size: [f32; 2],
texture_id: u32,
color: [f32; 4],
material_id: u32,
) {
let half_w = size[0] / 2.0;
let half_h = size[1] / 2.0;
let vertices = vec![
SimpleVertex3D::new([-half_w, -half_h, 0.0], [0.0, 1.0], color),
SimpleVertex3D::new([half_w, -half_h, 0.0], [1.0, 1.0], color),
SimpleVertex3D::new([half_w, half_h, 0.0], [1.0, 0.0], color),
SimpleVertex3D::new([-half_w, half_h, 0.0], [0.0, 0.0], color),
];
let indices = vec![0, 1, 2, 2, 3, 0];
let transform = Mat4::from_translation(glam::Vec3::new(
position[0],
position[1],
position[2],
));
self.mesh_queue.push(MeshSubmission {
vertices,
indices,
transform,
material_id,
texture_id,
});
}
/// Render all submitted meshes.
/// 渲染所有已提交的网格。
pub fn render(
&mut self,
backend: &mut WebGL2Backend,
texture_manager: &TextureManager,
) -> Result<(), String> {
if self.mesh_queue.is_empty() {
return Ok(());
}
// Apply 3D render state (depth test enabled)
// 应用3D渲染状态启用深度测试
let render_state = RenderState {
blend_mode: SharedBlendMode::Alpha,
cull_mode: CullMode::Back,
depth_test: self.depth_test_enabled,
depth_write: self.depth_write_enabled,
depth_func: CompareFunc::LessEqual,
scissor: None,
};
backend.apply_render_state(&render_state);
// Get view-projection matrix
// 获取视图-投影矩阵
let view_projection = self.camera.view_projection_matrix();
let mut current_material_id = u32::MAX;
let mut current_texture_id = u32::MAX;
for submission in &self.mesh_queue {
// Bind material/shader if changed
// 如果材质/着色器变化则绑定
if submission.material_id != current_material_id {
current_material_id = submission.material_id;
let material = self
.materials
.get(&submission.material_id)
.cloned()
.unwrap_or_default();
let shader = if material.shader_id == 0 {
self.default_shader
} else {
self.custom_shaders
.get(&material.shader_id)
.copied()
.unwrap_or(self.default_shader)
};
backend.bind_shader(shader).ok();
backend.set_blend_mode(to_shared_blend_mode(material.blend_mode));
// Set view-projection matrix
// 设置视图-投影矩阵
backend
.set_uniform_mat4("u_viewProjection", &view_projection)
.ok();
backend.set_uniform_i32("u_texture", 0).ok();
// Apply custom uniforms
// 应用自定义 uniforms
for name in material.uniforms.names() {
if let Some(value) = material.uniforms.get(name) {
match value {
UniformValue::Float(v) => {
backend.set_uniform_f32(name, *v).ok();
}
UniformValue::Vec2(v) => {
backend
.set_uniform_vec2(
name,
es_engine_shared::Vec2::new(v[0], v[1]),
)
.ok();
}
UniformValue::Vec3(v) => {
backend
.set_uniform_vec3(
name,
es_engine_shared::Vec3::new(v[0], v[1], v[2]),
)
.ok();
}
UniformValue::Vec4(v) => {
backend
.set_uniform_vec4(
name,
es_engine_shared::Vec4::new(v[0], v[1], v[2], v[3]),
)
.ok();
}
UniformValue::Int(v) => {
backend.set_uniform_i32(name, *v).ok();
}
UniformValue::Mat3(v) => {
backend
.set_uniform_mat3(name, &es_engine_shared::Mat3::from_cols_array(v))
.ok();
}
UniformValue::Mat4(v) => {
backend
.set_uniform_mat4(name, &es_engine_shared::Mat4::from_cols_array(v))
.ok();
}
UniformValue::Sampler(v) => {
backend.set_uniform_i32(name, *v).ok();
}
}
}
}
}
// Bind texture if changed
// 如果纹理变化则绑定
if submission.texture_id != current_texture_id {
current_texture_id = submission.texture_id;
texture_manager.bind_texture_via_backend(backend, submission.texture_id, 0);
}
// Set model matrix for this mesh
// 设置此网格的模型矩阵
backend
.set_uniform_mat4("u_model", &submission.transform)
.ok();
// TODO: For now, we'll render each mesh individually
// In the future, implement proper mesh batching
// 目前我们逐个渲染网格,未来实现正确的网格批处理
// Create temporary vertex buffer and VAO for this mesh
// 为此网格创建临时顶点缓冲区和VAO
self.render_mesh_immediate(backend, &submission.vertices, &submission.indices)?;
}
// Reset to 2D render state
// 重置为2D渲染状态
let default_state = RenderState::default();
backend.apply_render_state(&default_state);
self.mesh_queue.clear();
Ok(())
}
/// Render a mesh immediately (no batching).
/// 立即渲染网格(无批处理)。
fn render_mesh_immediate(
&self,
backend: &mut WebGL2Backend,
vertices: &[SimpleVertex3D],
indices: &[u32],
) -> Result<(), String> {
use es_engine_shared::types::vertex::{VertexLayout, VertexAttribute, VertexAttributeType};
use es_engine_shared::BufferUsage;
// Create vertex layout for SimpleVertex3D
// 为SimpleVertex3D创建顶点布局
let layout = VertexLayout {
attributes: vec![
VertexAttribute {
name: "a_position",
attr_type: VertexAttributeType::Float3,
offset: 0,
normalized: false,
},
VertexAttribute {
name: "a_texCoord",
attr_type: VertexAttributeType::Float2,
offset: 12, // 3 * 4 bytes
normalized: false,
},
VertexAttribute {
name: "a_color",
attr_type: VertexAttributeType::Float4,
offset: 20, // 3 * 4 + 2 * 4 bytes
normalized: false,
},
],
stride: FLOATS_PER_SIMPLE_VERTEX_3D * 4, // 9 * 4 = 36 bytes
};
// Convert vertices to bytes
// 将顶点转换为字节
let vertex_data: &[u8] = bytemuck::cast_slice(vertices);
// Create buffers
// 创建缓冲区
let vertex_buffer = backend
.create_vertex_buffer(vertex_data, BufferUsage::Dynamic)
.map_err(|e| format!("Failed to create vertex buffer: {:?}", e))?;
let index_buffer = backend
.create_index_buffer_u32(indices, BufferUsage::Dynamic)
.map_err(|e| format!("Failed to create index buffer: {:?}", e))?;
// Create VAO
// 创建VAO
let vao = backend
.create_vertex_array(vertex_buffer, Some(index_buffer), &layout)
.map_err(|e| format!("Failed to create VAO: {:?}", e))?;
// Draw
// 绘制
backend
.draw_indexed_u32(vao, indices.len() as u32, 0)
.map_err(|e| format!("Failed to draw: {:?}", e))?;
// Cleanup
// 清理
backend.destroy_vertex_array(vao);
backend.destroy_buffer(vertex_buffer);
backend.destroy_buffer(index_buffer);
Ok(())
}
/// Get mutable reference to camera.
/// 获取相机的可变引用。
#[inline]
pub fn camera_mut(&mut self) -> &mut Camera3D {
&mut self.camera
}
/// Get reference to camera.
/// 获取相机的引用。
#[inline]
pub fn camera(&self) -> &Camera3D {
&self.camera
}
/// Set clear color.
/// 设置清除颜色。
pub fn set_clear_color(&mut self, r: f32, g: f32, b: f32, a: f32) {
self.clear_color = [r, g, b, a];
}
/// Get clear color.
/// 获取清除颜色。
pub fn get_clear_color(&self) -> [f32; 4] {
self.clear_color
}
/// Resize viewport.
/// 调整视口大小。
pub fn resize(&mut self, width: f32, height: f32) {
self.camera.set_viewport(width, height);
}
/// Enable or disable depth testing.
/// 启用或禁用深度测试。
pub fn set_depth_test(&mut self, enabled: bool) {
self.depth_test_enabled = enabled;
}
/// Enable or disable depth writing.
/// 启用或禁用深度写入。
pub fn set_depth_write(&mut self, enabled: bool) {
self.depth_write_enabled = enabled;
}
/// Compile a custom shader.
/// 编译自定义着色器。
pub fn compile_shader(
&mut self,
backend: &mut WebGL2Backend,
vertex: &str,
fragment: &str,
) -> Result<u32, String> {
let handle = backend
.compile_shader(vertex, fragment)
.map_err(|e| format!("{:?}", e))?;
let id = self.next_shader_id;
self.next_shader_id += 1;
self.custom_shaders.insert(id, handle);
Ok(id)
}
/// Register a material.
/// 注册材质。
pub fn register_material(&mut self, material: Material) -> u32 {
let id = self.materials.keys().max().unwrap_or(&0) + 1;
self.materials.insert(id, material);
id
}
/// Register material with specific ID.
/// 使用特定ID注册材质。
pub fn register_material_with_id(&mut self, id: u32, material: Material) {
self.materials.insert(id, material);
}
/// Get material by ID.
/// 按ID获取材质。
pub fn get_material(&self, id: u32) -> Option<&Material> {
self.materials.get(&id)
}
/// Get mutable material by ID.
/// 按ID获取可变材质。
pub fn get_material_mut(&mut self, id: u32) -> Option<&mut Material> {
self.materials.get_mut(&id)
}
/// Clean up resources.
/// 清理资源。
pub fn destroy(self, backend: &mut WebGL2Backend) {
backend.destroy_shader(self.default_shader);
for (_, handle) in self.custom_shaders {
backend.destroy_shader(handle);
}
}
}