Feature/runtime cdn and plugin loader (#240)
* feat(ui): 完善 UI 布局系统和编辑器可视化工具 * refactor: 移除 ModuleRegistry,统一使用 PluginManager 插件系统 * fix: 修复 CodeQL 警告并提升测试覆盖率 * refactor: 分离运行时入口点,解决 runtime bundle 包含 React 的问题 * fix(ci): 添加 editor-core 和 editor-runtime 到 CI 依赖构建步骤 * docs: 完善 ServiceContainer 文档,新增 Symbol.for 模式和 @InjectProperty 说明 * fix(ci): 修复 type-check 失败问题 * fix(ci): 修复类型检查失败问题 * fix(ci): 修复类型检查失败问题 * fix(ci): behavior-tree 构建添加 @tauri-apps 外部依赖 * fix(ci): behavior-tree 添加 @tauri-apps/plugin-fs 类型依赖 * fix(ci): platform-web 添加缺失的 behavior-tree 依赖 * fix(lint): 移除正则表达式中不必要的转义字符
This commit is contained in:
@@ -220,12 +220,33 @@ impl Engine {
|
||||
self.renderer.render(self.context.gl(), &self.texture_manager)?;
|
||||
|
||||
// Render gizmos on top
|
||||
self.gizmo_renderer.render(self.context.gl(), self.renderer.camera());
|
||||
if self.show_gizmos {
|
||||
self.gizmo_renderer.render(self.context.gl(), self.renderer.camera());
|
||||
// Render axis indicator in corner (always visible when gizmos are on)
|
||||
// 在角落渲染坐标轴指示器(当 gizmos 开启时始终可见)
|
||||
self.gizmo_renderer.render_axis_indicator(
|
||||
self.context.gl(),
|
||||
self.context.width() as f32,
|
||||
self.context.height() as f32,
|
||||
);
|
||||
}
|
||||
self.gizmo_renderer.clear();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Render sprites only without clearing the screen.
|
||||
/// 仅渲染精灵,不清除屏幕。
|
||||
///
|
||||
/// This is used for overlay rendering (e.g., UI layer on top of world).
|
||||
/// 用于叠加渲染(例如,UI 层叠加在世界上)。
|
||||
pub fn render_overlay(&mut self) -> Result<()> {
|
||||
// Render sprites without clearing
|
||||
// 渲染精灵但不清屏
|
||||
self.renderer.render(self.context.gl(), &self.texture_manager)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a rectangle gizmo.
|
||||
/// 添加矩形Gizmo。
|
||||
pub fn add_gizmo_rect(
|
||||
@@ -447,6 +468,14 @@ impl Engine {
|
||||
// Render gizmos if enabled
|
||||
if show_gizmos {
|
||||
self.gizmo_renderer.render(viewport.gl(), &camera);
|
||||
// Render axis indicator in corner
|
||||
// 在角落渲染坐标轴指示器
|
||||
let (vp_width, vp_height) = viewport.dimensions();
|
||||
self.gizmo_renderer.render_axis_indicator(
|
||||
viewport.gl(),
|
||||
vp_width as f32,
|
||||
vp_height as f32,
|
||||
);
|
||||
}
|
||||
self.gizmo_renderer.clear();
|
||||
|
||||
|
||||
@@ -163,6 +163,18 @@ impl GameEngine {
|
||||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||||
}
|
||||
|
||||
/// Render sprites as overlay (without clearing screen).
|
||||
/// 渲染精灵作为叠加层(不清除屏幕)。
|
||||
///
|
||||
/// This is used for UI rendering on top of the world content.
|
||||
/// 用于在世界内容上渲染 UI。
|
||||
#[wasm_bindgen(js_name = renderOverlay)]
|
||||
pub fn render_overlay(&mut self) -> std::result::Result<(), JsValue> {
|
||||
self.engine
|
||||
.render_overlay()
|
||||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||||
}
|
||||
|
||||
/// Load a texture from URL.
|
||||
/// 从URL加载纹理。
|
||||
///
|
||||
|
||||
@@ -164,6 +164,221 @@ impl GizmoRenderer {
|
||||
]);
|
||||
}
|
||||
|
||||
/// Render axis indicator at top-right corner of the viewport.
|
||||
/// 在视口右上角渲染坐标轴指示器。
|
||||
///
|
||||
/// This is drawn in screen space and is not affected by camera pan/zoom.
|
||||
/// 这是在屏幕空间绘制的,不受相机平移/缩放影响。
|
||||
pub fn render_axis_indicator(
|
||||
&self,
|
||||
gl: &WebGl2RenderingContext,
|
||||
viewport_width: f32,
|
||||
viewport_height: f32,
|
||||
) {
|
||||
// Skip if viewport is too small
|
||||
if viewport_width < 100.0 || viewport_height < 100.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
gl.use_program(Some(&self.program));
|
||||
|
||||
// Disable depth test for screen-space UI
|
||||
gl.disable(WebGl2RenderingContext::DEPTH_TEST);
|
||||
|
||||
// Create orthographic projection for screen space (NDC: -1 to 1)
|
||||
let half_w = viewport_width / 2.0;
|
||||
let half_h = viewport_height / 2.0;
|
||||
|
||||
let projection = [
|
||||
1.0 / half_w, 0.0, 0.0,
|
||||
0.0, 1.0 / half_h, 0.0,
|
||||
0.0, 0.0, 1.0,
|
||||
];
|
||||
|
||||
let proj_loc = gl.get_uniform_location(&self.program, "u_projection");
|
||||
gl.uniform_matrix3fv_with_f32_array(proj_loc.as_ref(), false, &projection);
|
||||
|
||||
let color_loc = gl.get_uniform_location(&self.program, "u_color");
|
||||
|
||||
gl.bind_buffer(WebGl2RenderingContext::ARRAY_BUFFER, Some(&self.vertex_buffer));
|
||||
gl.enable_vertex_attrib_array(0);
|
||||
gl.vertex_attrib_pointer_with_i32(0, 2, WebGl2RenderingContext::FLOAT, false, 0, 0);
|
||||
|
||||
// Position in top-right corner (increased padding to prevent X label clipping)
|
||||
// 位置在右上角(增加边距防止 X 标签被裁剪)
|
||||
let padding_x = 70.0; // More padding on X for the label
|
||||
let padding_y = 55.0;
|
||||
let center_x = half_w - padding_x;
|
||||
let center_y = half_h - padding_y;
|
||||
let axis_length = 30.0; // Longer axes for better visibility
|
||||
let arrow_size = 8.0;
|
||||
let label_offset = 10.0;
|
||||
let label_size = 4.0;
|
||||
|
||||
// Draw semi-transparent background circle for better visibility
|
||||
// 绘制半透明背景圆以提高可见性
|
||||
let bg_segments = 32;
|
||||
let bg_radius = 45.0;
|
||||
let mut bg_vertices = Vec::with_capacity((bg_segments + 1) * 2);
|
||||
bg_vertices.push(center_x);
|
||||
bg_vertices.push(center_y);
|
||||
for i in 0..=bg_segments {
|
||||
let angle = (i as f32 / bg_segments as f32) * std::f32::consts::PI * 2.0;
|
||||
bg_vertices.push(center_x + bg_radius * angle.cos());
|
||||
bg_vertices.push(center_y + bg_radius * angle.sin());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let array = js_sys::Float32Array::view(&bg_vertices);
|
||||
gl.buffer_data_with_array_buffer_view(
|
||||
WebGl2RenderingContext::ARRAY_BUFFER,
|
||||
&array,
|
||||
WebGl2RenderingContext::DYNAMIC_DRAW,
|
||||
);
|
||||
}
|
||||
gl.uniform4f(color_loc.as_ref(), 0.1, 0.1, 0.1, 0.7);
|
||||
gl.draw_arrays(WebGl2RenderingContext::TRIANGLE_FAN, 0, (bg_segments + 2) as i32);
|
||||
|
||||
// Draw origin point (filled circle)
|
||||
// 绘制原点(实心圆)
|
||||
let origin_segments = 12;
|
||||
let origin_radius = 3.0;
|
||||
let mut origin_vertices = Vec::with_capacity((origin_segments + 1) * 2);
|
||||
origin_vertices.push(center_x);
|
||||
origin_vertices.push(center_y);
|
||||
for i in 0..=origin_segments {
|
||||
let angle = (i as f32 / origin_segments as f32) * std::f32::consts::PI * 2.0;
|
||||
origin_vertices.push(center_x + origin_radius * angle.cos());
|
||||
origin_vertices.push(center_y + origin_radius * angle.sin());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let array = js_sys::Float32Array::view(&origin_vertices);
|
||||
gl.buffer_data_with_array_buffer_view(
|
||||
WebGl2RenderingContext::ARRAY_BUFFER,
|
||||
&array,
|
||||
WebGl2RenderingContext::DYNAMIC_DRAW,
|
||||
);
|
||||
}
|
||||
gl.uniform4f(color_loc.as_ref(), 0.8, 0.8, 0.8, 1.0);
|
||||
gl.draw_arrays(WebGl2RenderingContext::TRIANGLE_FAN, 0, (origin_segments + 2) as i32);
|
||||
|
||||
// X axis (red, pointing right)
|
||||
let x_end_x = center_x + axis_length;
|
||||
let x_end_y = center_y;
|
||||
|
||||
// X axis line (thicker effect with multiple lines)
|
||||
let x_axis = [
|
||||
center_x + origin_radius, center_y,
|
||||
x_end_x - arrow_size * 0.3, x_end_y,
|
||||
];
|
||||
unsafe {
|
||||
let array = js_sys::Float32Array::view(&x_axis);
|
||||
gl.buffer_data_with_array_buffer_view(
|
||||
WebGl2RenderingContext::ARRAY_BUFFER,
|
||||
&array,
|
||||
WebGl2RenderingContext::DYNAMIC_DRAW,
|
||||
);
|
||||
}
|
||||
gl.uniform4f(color_loc.as_ref(), 1.0, 0.4, 0.4, 1.0);
|
||||
gl.draw_arrays(WebGl2RenderingContext::LINES, 0, 2);
|
||||
|
||||
// X arrow head (filled triangle)
|
||||
let x_arrow = [
|
||||
x_end_x, x_end_y,
|
||||
x_end_x - arrow_size, x_end_y + arrow_size * 0.4,
|
||||
x_end_x - arrow_size, x_end_y - arrow_size * 0.4,
|
||||
];
|
||||
unsafe {
|
||||
let array = js_sys::Float32Array::view(&x_arrow);
|
||||
gl.buffer_data_with_array_buffer_view(
|
||||
WebGl2RenderingContext::ARRAY_BUFFER,
|
||||
&array,
|
||||
WebGl2RenderingContext::DYNAMIC_DRAW,
|
||||
);
|
||||
}
|
||||
gl.draw_arrays(WebGl2RenderingContext::TRIANGLES, 0, 3);
|
||||
|
||||
// X label
|
||||
let lx = x_end_x + label_offset;
|
||||
let ly = x_end_y;
|
||||
let x_label = [
|
||||
lx - label_size, ly + label_size,
|
||||
lx + label_size, ly - label_size,
|
||||
lx - label_size, ly - label_size,
|
||||
lx + label_size, ly + label_size,
|
||||
];
|
||||
unsafe {
|
||||
let array = js_sys::Float32Array::view(&x_label);
|
||||
gl.buffer_data_with_array_buffer_view(
|
||||
WebGl2RenderingContext::ARRAY_BUFFER,
|
||||
&array,
|
||||
WebGl2RenderingContext::DYNAMIC_DRAW,
|
||||
);
|
||||
}
|
||||
gl.draw_arrays(WebGl2RenderingContext::LINES, 0, 4);
|
||||
|
||||
// Y axis (green, pointing up)
|
||||
let y_end_x = center_x;
|
||||
let y_end_y = center_y + axis_length;
|
||||
|
||||
// Y axis line
|
||||
let y_axis = [
|
||||
center_x, center_y + origin_radius,
|
||||
y_end_x, y_end_y - arrow_size * 0.3,
|
||||
];
|
||||
unsafe {
|
||||
let array = js_sys::Float32Array::view(&y_axis);
|
||||
gl.buffer_data_with_array_buffer_view(
|
||||
WebGl2RenderingContext::ARRAY_BUFFER,
|
||||
&array,
|
||||
WebGl2RenderingContext::DYNAMIC_DRAW,
|
||||
);
|
||||
}
|
||||
gl.uniform4f(color_loc.as_ref(), 0.4, 1.0, 0.4, 1.0);
|
||||
gl.draw_arrays(WebGl2RenderingContext::LINES, 0, 2);
|
||||
|
||||
// Y arrow head (filled triangle)
|
||||
let y_arrow = [
|
||||
y_end_x, y_end_y,
|
||||
y_end_x - arrow_size * 0.4, y_end_y - arrow_size,
|
||||
y_end_x + arrow_size * 0.4, y_end_y - arrow_size,
|
||||
];
|
||||
unsafe {
|
||||
let array = js_sys::Float32Array::view(&y_arrow);
|
||||
gl.buffer_data_with_array_buffer_view(
|
||||
WebGl2RenderingContext::ARRAY_BUFFER,
|
||||
&array,
|
||||
WebGl2RenderingContext::DYNAMIC_DRAW,
|
||||
);
|
||||
}
|
||||
gl.draw_arrays(WebGl2RenderingContext::TRIANGLES, 0, 3);
|
||||
|
||||
// Y label
|
||||
let lx = y_end_x;
|
||||
let ly = y_end_y + label_offset;
|
||||
let y_label = [
|
||||
lx - label_size, ly + label_size,
|
||||
lx, ly,
|
||||
lx + label_size, ly + label_size,
|
||||
lx, ly,
|
||||
lx, ly,
|
||||
lx, ly - label_size * 0.8,
|
||||
];
|
||||
unsafe {
|
||||
let array = js_sys::Float32Array::view(&y_label);
|
||||
gl.buffer_data_with_array_buffer_view(
|
||||
WebGl2RenderingContext::ARRAY_BUFFER,
|
||||
&array,
|
||||
WebGl2RenderingContext::DYNAMIC_DRAW,
|
||||
);
|
||||
}
|
||||
gl.draw_arrays(WebGl2RenderingContext::LINES, 0, 6);
|
||||
|
||||
// Cleanup
|
||||
gl.disable_vertex_attrib_array(0);
|
||||
}
|
||||
|
||||
/// Render all pending gizmos.
|
||||
/// 渲染所有待渲染的Gizmo。
|
||||
pub fn render(&mut self, gl: &WebGl2RenderingContext, camera: &Camera2D) {
|
||||
|
||||
Reference in New Issue
Block a user