Feature/physics and tilemap enhancement (#247)
* feat(behavior-tree,tilemap): 修复编辑器连线缩放问题并增强插件系统 * feat(node-editor,blueprint): 新增通用节点编辑器和蓝图可视化脚本系统 * feat(editor,tilemap): 优化编辑器UI样式和Tilemap编辑器功能 * fix: 修复CodeQL安全警告和CI类型检查错误 * fix: 修复CodeQL安全警告和CI类型检查错误 * fix: 修复CodeQL安全警告和CI类型检查错误
This commit is contained in:
@@ -222,8 +222,8 @@ impl Engine {
|
||||
// Render gizmos on top
|
||||
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 开启时始终可见)
|
||||
// Render axis indicator in corner
|
||||
// 在角落渲染坐标轴指示器
|
||||
self.gizmo_renderer.render_axis_indicator(
|
||||
self.context.gl(),
|
||||
self.context.width() as f32,
|
||||
@@ -267,6 +267,52 @@ impl Engine {
|
||||
self.gizmo_renderer.add_rect(x, y, width, height, rotation, origin_x, origin_y, r, g, b, a, show_handles);
|
||||
}
|
||||
|
||||
/// Add a circle gizmo.
|
||||
/// 添加圆形Gizmo。
|
||||
pub fn add_gizmo_circle(
|
||||
&mut self,
|
||||
x: f32,
|
||||
y: f32,
|
||||
radius: f32,
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
a: f32,
|
||||
) {
|
||||
self.gizmo_renderer.add_circle(x, y, radius, r, g, b, a);
|
||||
}
|
||||
|
||||
/// Add a line gizmo.
|
||||
/// 添加线条Gizmo。
|
||||
pub fn add_gizmo_line(
|
||||
&mut self,
|
||||
points: Vec<f32>,
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
a: f32,
|
||||
closed: bool,
|
||||
) {
|
||||
self.gizmo_renderer.add_line(points, r, g, b, a, closed);
|
||||
}
|
||||
|
||||
/// Add a capsule gizmo.
|
||||
/// 添加胶囊Gizmo。
|
||||
pub fn add_gizmo_capsule(
|
||||
&mut self,
|
||||
x: f32,
|
||||
y: f32,
|
||||
radius: f32,
|
||||
half_height: f32,
|
||||
rotation: f32,
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
a: f32,
|
||||
) {
|
||||
self.gizmo_renderer.add_capsule(x, y, radius, half_height, rotation, r, g, b, a);
|
||||
}
|
||||
|
||||
/// Set transform tool mode.
|
||||
/// 设置变换工具模式。
|
||||
pub fn set_transform_mode(&mut self, mode: u8) {
|
||||
|
||||
@@ -322,6 +322,55 @@ impl GameEngine {
|
||||
self.engine.add_gizmo_rect(x, y, width, height, rotation, origin_x, origin_y, r, g, b, a, show_handles);
|
||||
}
|
||||
|
||||
/// Add a circle gizmo outline.
|
||||
/// 添加圆形Gizmo边框。
|
||||
#[wasm_bindgen(js_name = addGizmoCircle)]
|
||||
pub fn add_gizmo_circle(
|
||||
&mut self,
|
||||
x: f32,
|
||||
y: f32,
|
||||
radius: f32,
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
a: f32,
|
||||
) {
|
||||
self.engine.add_gizmo_circle(x, y, radius, r, g, b, a);
|
||||
}
|
||||
|
||||
/// Add a line gizmo.
|
||||
/// 添加线条Gizmo。
|
||||
#[wasm_bindgen(js_name = addGizmoLine)]
|
||||
pub fn add_gizmo_line(
|
||||
&mut self,
|
||||
points: Vec<f32>,
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
a: f32,
|
||||
closed: bool,
|
||||
) {
|
||||
self.engine.add_gizmo_line(points, r, g, b, a, closed);
|
||||
}
|
||||
|
||||
/// Add a capsule gizmo outline.
|
||||
/// 添加胶囊Gizmo边框。
|
||||
#[wasm_bindgen(js_name = addGizmoCapsule)]
|
||||
pub fn add_gizmo_capsule(
|
||||
&mut self,
|
||||
x: f32,
|
||||
y: f32,
|
||||
radius: f32,
|
||||
half_height: f32,
|
||||
rotation: f32,
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
a: f32,
|
||||
) {
|
||||
self.engine.add_gizmo_capsule(x, y, radius, half_height, rotation, r, g, b, a);
|
||||
}
|
||||
|
||||
/// Set transform tool mode.
|
||||
/// 设置变换工具模式。
|
||||
///
|
||||
|
||||
@@ -58,10 +58,30 @@ pub struct GizmoRenderer {
|
||||
/// Pending rectangle data: [x, y, width, height, rotation, origin_x, origin_y, r, g, b, a, show_handles]
|
||||
/// 待渲染的矩形数据
|
||||
rects: Vec<f32>,
|
||||
/// Pending circle data: [x, y, radius, r, g, b, a, segments]
|
||||
/// 待渲染的圆形数据
|
||||
circles: Vec<f32>,
|
||||
/// Pending line data: stored as separate line commands
|
||||
/// 待渲染的线条数据
|
||||
lines: Vec<LineGizmo>,
|
||||
/// Pending capsule data: [x, y, radius, half_height, rotation, r, g, b, a]
|
||||
/// 待渲染的胶囊数据
|
||||
capsules: Vec<f32>,
|
||||
/// Current transform mode
|
||||
transform_mode: TransformMode,
|
||||
}
|
||||
|
||||
/// Line gizmo data
|
||||
/// 线条 gizmo 数据
|
||||
struct LineGizmo {
|
||||
points: Vec<f32>,
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
a: f32,
|
||||
closed: bool,
|
||||
}
|
||||
|
||||
impl GizmoRenderer {
|
||||
/// Create a new gizmo renderer.
|
||||
/// 创建新的Gizmo渲染器。
|
||||
@@ -74,6 +94,9 @@ impl GizmoRenderer {
|
||||
program,
|
||||
vertex_buffer,
|
||||
rects: Vec::new(),
|
||||
circles: Vec::new(),
|
||||
lines: Vec::new(),
|
||||
capsules: Vec::new(),
|
||||
transform_mode: TransformMode::default(),
|
||||
})
|
||||
}
|
||||
@@ -129,6 +152,9 @@ impl GizmoRenderer {
|
||||
/// 清空所有待渲染的Gizmo。
|
||||
pub fn clear(&mut self) {
|
||||
self.rects.clear();
|
||||
self.circles.clear();
|
||||
self.lines.clear();
|
||||
self.capsules.clear();
|
||||
}
|
||||
|
||||
/// Add a rectangle outline gizmo.
|
||||
@@ -164,8 +190,57 @@ impl GizmoRenderer {
|
||||
]);
|
||||
}
|
||||
|
||||
/// Render axis indicator at top-right corner of the viewport.
|
||||
/// 在视口右上角渲染坐标轴指示器。
|
||||
/// Add a circle outline gizmo.
|
||||
/// 添加圆形边框Gizmo。
|
||||
pub fn add_circle(
|
||||
&mut self,
|
||||
x: f32,
|
||||
y: f32,
|
||||
radius: f32,
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
a: f32,
|
||||
) {
|
||||
self.circles.extend_from_slice(&[x, y, radius, r, g, b, a, 32.0]);
|
||||
}
|
||||
|
||||
/// Add a line gizmo.
|
||||
/// 添加线条Gizmo。
|
||||
pub fn add_line(
|
||||
&mut self,
|
||||
points: Vec<f32>,
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
a: f32,
|
||||
closed: bool,
|
||||
) {
|
||||
self.lines.push(LineGizmo { points, r, g, b, a, closed });
|
||||
}
|
||||
|
||||
/// Add a capsule outline gizmo.
|
||||
/// 添加胶囊边框Gizmo。
|
||||
///
|
||||
/// Capsule is defined by center position, radius, half-height (distance from center to cap centers), and rotation.
|
||||
/// 胶囊由中心位置、半径、半高度(从中心到端帽圆心的距离)和旋转定义。
|
||||
pub fn add_capsule(
|
||||
&mut self,
|
||||
x: f32,
|
||||
y: f32,
|
||||
radius: f32,
|
||||
half_height: f32,
|
||||
rotation: f32,
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
a: f32,
|
||||
) {
|
||||
self.capsules.extend_from_slice(&[x, y, radius, half_height, rotation, r, g, b, a]);
|
||||
}
|
||||
|
||||
/// Render axis indicator at bottom-left corner of the viewport.
|
||||
/// 在视口左下角渲染坐标轴指示器。
|
||||
///
|
||||
/// This is drawn in screen space and is not affected by camera pan/zoom.
|
||||
/// 这是在屏幕空间绘制的,不受相机平移/缩放影响。
|
||||
@@ -204,72 +279,23 @@ impl GizmoRenderer {
|
||||
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);
|
||||
// Position in bottom-left corner
|
||||
// 位置在左下角
|
||||
let padding = 35.0;
|
||||
let center_x = -half_w + padding;
|
||||
let center_y = -half_h + padding;
|
||||
let axis_length = 25.0;
|
||||
let arrow_size = 6.0;
|
||||
let label_offset = 8.0;
|
||||
let label_size = 3.5;
|
||||
|
||||
// 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)
|
||||
// X axis line
|
||||
let x_axis = [
|
||||
center_x + origin_radius, center_y,
|
||||
center_x, center_y,
|
||||
x_end_x - arrow_size * 0.3, x_end_y,
|
||||
];
|
||||
unsafe {
|
||||
@@ -280,14 +306,14 @@ impl GizmoRenderer {
|
||||
WebGl2RenderingContext::DYNAMIC_DRAW,
|
||||
);
|
||||
}
|
||||
gl.uniform4f(color_loc.as_ref(), 1.0, 0.4, 0.4, 1.0);
|
||||
gl.uniform4f(color_loc.as_ref(), 0.9, 0.2, 0.2, 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,
|
||||
x_end_x - arrow_size, x_end_y + arrow_size * 0.35,
|
||||
x_end_x - arrow_size, x_end_y - arrow_size * 0.35,
|
||||
];
|
||||
unsafe {
|
||||
let array = js_sys::Float32Array::view(&x_arrow);
|
||||
@@ -324,7 +350,7 @@ impl GizmoRenderer {
|
||||
|
||||
// Y axis line
|
||||
let y_axis = [
|
||||
center_x, center_y + origin_radius,
|
||||
center_x, center_y,
|
||||
y_end_x, y_end_y - arrow_size * 0.3,
|
||||
];
|
||||
unsafe {
|
||||
@@ -335,14 +361,14 @@ impl GizmoRenderer {
|
||||
WebGl2RenderingContext::DYNAMIC_DRAW,
|
||||
);
|
||||
}
|
||||
gl.uniform4f(color_loc.as_ref(), 0.4, 1.0, 0.4, 1.0);
|
||||
gl.uniform4f(color_loc.as_ref(), 0.26, 0.63, 0.28, 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,
|
||||
y_end_x - arrow_size * 0.35, y_end_y - arrow_size,
|
||||
y_end_x + arrow_size * 0.35, y_end_y - arrow_size,
|
||||
];
|
||||
unsafe {
|
||||
let array = js_sys::Float32Array::view(&y_arrow);
|
||||
@@ -382,7 +408,7 @@ impl GizmoRenderer {
|
||||
/// Render all pending gizmos.
|
||||
/// 渲染所有待渲染的Gizmo。
|
||||
pub fn render(&mut self, gl: &WebGl2RenderingContext, camera: &Camera2D) {
|
||||
if self.rects.is_empty() {
|
||||
if self.rects.is_empty() && self.circles.is_empty() && self.lines.is_empty() && self.capsules.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -398,7 +424,23 @@ impl GizmoRenderer {
|
||||
gl.enable_vertex_attrib_array(0);
|
||||
gl.vertex_attrib_pointer_with_i32(0, 2, WebGl2RenderingContext::FLOAT, false, 0, 0);
|
||||
|
||||
// Process each rectangle (12 floats per rect)
|
||||
// Render rectangles
|
||||
self.render_rects(gl, &color_loc, camera);
|
||||
|
||||
// Render circles
|
||||
self.render_circles(gl, &color_loc);
|
||||
|
||||
// Render lines
|
||||
self.render_lines(gl, &color_loc);
|
||||
|
||||
// Render capsules
|
||||
self.render_capsules(gl, &color_loc);
|
||||
|
||||
gl.disable_vertex_attrib_array(0);
|
||||
}
|
||||
|
||||
/// Render all pending rectangles.
|
||||
fn render_rects(&self, gl: &WebGl2RenderingContext, color_loc: &Option<web_sys::WebGlUniformLocation>, camera: &Camera2D) {
|
||||
let rect_stride = 12;
|
||||
let rect_count = self.rects.len() / rect_stride;
|
||||
|
||||
@@ -417,7 +459,6 @@ impl GizmoRenderer {
|
||||
let a = self.rects[offset + 10];
|
||||
let show_handles = self.rects[offset + 11] > 0.5;
|
||||
|
||||
// Calculate transformed corners
|
||||
let vertices = self.calculate_rect_vertices(x, y, width, height, rotation, origin_x, origin_y);
|
||||
|
||||
unsafe {
|
||||
@@ -432,31 +473,157 @@ impl GizmoRenderer {
|
||||
gl.uniform4f(color_loc.as_ref(), r, g, b, a);
|
||||
gl.draw_arrays(WebGl2RenderingContext::LINE_LOOP, 0, 4);
|
||||
|
||||
// Only draw transform handles if explicitly requested
|
||||
// 只有明确请求时才绘制变换手柄
|
||||
if show_handles {
|
||||
// Draw transform handles based on mode
|
||||
match self.transform_mode {
|
||||
TransformMode::Select => {
|
||||
// Just the selection box (already drawn)
|
||||
}
|
||||
TransformMode::Select => {}
|
||||
TransformMode::Move => {
|
||||
// Draw move arrows at center
|
||||
self.draw_move_handles(gl, &color_loc, x, y, rotation, camera);
|
||||
self.draw_move_handles(gl, color_loc, x, y, rotation, camera);
|
||||
}
|
||||
TransformMode::Rotate => {
|
||||
// Draw rotation circle
|
||||
self.draw_rotate_handles(gl, &color_loc, x, y, width.max(height) * 0.6, camera);
|
||||
self.draw_rotate_handles(gl, color_loc, x, y, width.max(height) * 0.6, camera);
|
||||
}
|
||||
TransformMode::Scale => {
|
||||
// Draw scale handles at corners
|
||||
self.draw_scale_handles(gl, &color_loc, x, y, width, height, rotation, origin_x, origin_y, camera);
|
||||
self.draw_scale_handles(gl, color_loc, x, y, width, height, rotation, origin_x, origin_y, camera);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gl.disable_vertex_attrib_array(0);
|
||||
/// Render all pending circles.
|
||||
fn render_circles(&self, gl: &WebGl2RenderingContext, color_loc: &Option<web_sys::WebGlUniformLocation>) {
|
||||
let circle_stride = 8;
|
||||
let circle_count = self.circles.len() / circle_stride;
|
||||
|
||||
for i in 0..circle_count {
|
||||
let offset = i * circle_stride;
|
||||
let x = self.circles[offset];
|
||||
let y = self.circles[offset + 1];
|
||||
let radius = self.circles[offset + 2];
|
||||
let r = self.circles[offset + 3];
|
||||
let g = self.circles[offset + 4];
|
||||
let b = self.circles[offset + 5];
|
||||
let a = self.circles[offset + 6];
|
||||
let segments = self.circles[offset + 7] as usize;
|
||||
|
||||
let mut vertices = Vec::with_capacity(segments * 2);
|
||||
for j in 0..segments {
|
||||
let angle = (j as f32 / segments as f32) * std::f32::consts::PI * 2.0;
|
||||
vertices.push(x + radius * angle.cos());
|
||||
vertices.push(y + radius * angle.sin());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let array = js_sys::Float32Array::view(&vertices);
|
||||
gl.buffer_data_with_array_buffer_view(
|
||||
WebGl2RenderingContext::ARRAY_BUFFER,
|
||||
&array,
|
||||
WebGl2RenderingContext::DYNAMIC_DRAW,
|
||||
);
|
||||
}
|
||||
|
||||
gl.uniform4f(color_loc.as_ref(), r, g, b, a);
|
||||
gl.draw_arrays(WebGl2RenderingContext::LINE_LOOP, 0, segments as i32);
|
||||
}
|
||||
}
|
||||
|
||||
/// Render all pending lines.
|
||||
fn render_lines(&self, gl: &WebGl2RenderingContext, color_loc: &Option<web_sys::WebGlUniformLocation>) {
|
||||
for line in &self.lines {
|
||||
if line.points.len() < 4 {
|
||||
continue;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let array = js_sys::Float32Array::view(&line.points);
|
||||
gl.buffer_data_with_array_buffer_view(
|
||||
WebGl2RenderingContext::ARRAY_BUFFER,
|
||||
&array,
|
||||
WebGl2RenderingContext::DYNAMIC_DRAW,
|
||||
);
|
||||
}
|
||||
|
||||
gl.uniform4f(color_loc.as_ref(), line.r, line.g, line.b, line.a);
|
||||
let point_count = (line.points.len() / 2) as i32;
|
||||
if line.closed {
|
||||
gl.draw_arrays(WebGl2RenderingContext::LINE_LOOP, 0, point_count);
|
||||
} else {
|
||||
gl.draw_arrays(WebGl2RenderingContext::LINE_STRIP, 0, point_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Render all pending capsules.
|
||||
fn render_capsules(&self, gl: &WebGl2RenderingContext, color_loc: &Option<web_sys::WebGlUniformLocation>) {
|
||||
let capsule_stride = 9;
|
||||
let capsule_count = self.capsules.len() / capsule_stride;
|
||||
let segments = 16;
|
||||
|
||||
for i in 0..capsule_count {
|
||||
let offset = i * capsule_stride;
|
||||
let cx = self.capsules[offset];
|
||||
let cy = self.capsules[offset + 1];
|
||||
let radius = self.capsules[offset + 2];
|
||||
let half_height = self.capsules[offset + 3];
|
||||
let rotation = self.capsules[offset + 4];
|
||||
let r = self.capsules[offset + 5];
|
||||
let g = self.capsules[offset + 6];
|
||||
let b = self.capsules[offset + 7];
|
||||
let a = self.capsules[offset + 8];
|
||||
|
||||
let cos_r = rotation.cos();
|
||||
let sin_r = rotation.sin();
|
||||
|
||||
let mut vertices = Vec::with_capacity((segments * 2 + 4) * 2);
|
||||
|
||||
// Draw capsule in local space then rotate:
|
||||
// - Top semicircle at y = +half_height, arc from angle 0 to PI
|
||||
// - Right line from top-right to bottom-right
|
||||
// - Bottom semicircle at y = -half_height, arc from angle PI to 2*PI
|
||||
// - Left line from bottom-left to top-left (closed by LINE_LOOP)
|
||||
|
||||
// Top semicircle (arc curving upward)
|
||||
for j in 0..=segments {
|
||||
let angle = (j as f32 / segments as f32) * std::f32::consts::PI;
|
||||
// Local coordinates: semicircle centered at (0, half_height)
|
||||
// angle 0 -> (radius, half_height), angle PI -> (-radius, half_height)
|
||||
// We want it to curve UP, so use cos for x, sin for y offset
|
||||
let lx = radius * angle.cos();
|
||||
let ly = half_height + radius * angle.sin();
|
||||
// Rotate and translate to world
|
||||
let wx = cx + lx * cos_r - ly * sin_r;
|
||||
let wy = cy + lx * sin_r + ly * cos_r;
|
||||
vertices.push(wx);
|
||||
vertices.push(wy);
|
||||
}
|
||||
|
||||
// Bottom semicircle (arc curving downward)
|
||||
for j in 0..=segments {
|
||||
let angle = (j as f32 / segments as f32) * std::f32::consts::PI;
|
||||
// Local coordinates: semicircle centered at (0, -half_height)
|
||||
// angle 0 -> (-radius, -half_height), angle PI -> (radius, -half_height)
|
||||
// We want it to curve DOWN, so negate both cos and sin offset
|
||||
let lx = -radius * angle.cos();
|
||||
let ly = -half_height - radius * angle.sin();
|
||||
// Rotate and translate to world
|
||||
let wx = cx + lx * cos_r - ly * sin_r;
|
||||
let wy = cy + lx * sin_r + ly * cos_r;
|
||||
vertices.push(wx);
|
||||
vertices.push(wy);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let array = js_sys::Float32Array::view(&vertices);
|
||||
gl.buffer_data_with_array_buffer_view(
|
||||
WebGl2RenderingContext::ARRAY_BUFFER,
|
||||
&array,
|
||||
WebGl2RenderingContext::DYNAMIC_DRAW,
|
||||
);
|
||||
}
|
||||
|
||||
gl.uniform4f(color_loc.as_ref(), r, g, b, a);
|
||||
gl.draw_arrays(WebGl2RenderingContext::LINE_LOOP, 0, ((segments + 1) * 2) as i32);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set transform mode.
|
||||
|
||||
Reference in New Issue
Block a user