Skip to content

make all circle drawing high-precision and faster too #943

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions src/quad_gl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub(crate) use crate::models::Vertex;
pub enum DrawMode {
Triangles,
Lines,
Circle,
}

#[derive(Debug, Clone, Copy, PartialEq)]
Expand Down Expand Up @@ -381,6 +382,7 @@ impl PipelinesStorage {
const LINES_PIPELINE: GlPipeline = GlPipeline(1);
const TRIANGLES_DEPTH_PIPELINE: GlPipeline = GlPipeline(2);
const LINES_DEPTH_PIPELINE: GlPipeline = GlPipeline(3);
const CIRCLE_PIPELINE: GlPipeline = GlPipeline(4);

fn new(ctx: &mut dyn RenderingBackend) -> PipelinesStorage {
let shader = ctx
Expand All @@ -398,6 +400,21 @@ impl PipelinesStorage {
)
.unwrap_or_else(|e| panic!("Failed to load shader: {e}"));

let circle_shader = ctx
.new_shader(
match ctx.info().backend {
Backend::OpenGl => ShaderSource::Glsl {
vertex: shader::CIRCLE_VERTEX,
fragment: shader::CIRCLE_FRAGMENT,
},
Backend::Metal => ShaderSource::Msl {
program: shader::CIRCLE_METAL,
},
},
shader::meta(),
)
.unwrap_or_else(|e| panic!("Failed to load shader: {}", e));

let params = PipelineParams {
color_blend: Some(BlendState::new(
Equation::Add,
Expand Down Expand Up @@ -468,6 +485,19 @@ impl PipelinesStorage {
);
assert_eq!(lines_depth_pipeline, Self::LINES_DEPTH_PIPELINE);

let circle_pipeline = storage.make_pipeline(
ctx,
circle_shader,
PipelineParams {
primitive_type: PrimitiveType::Triangles,
..params
},
false,
vec![],
vec![],
);
assert_eq!(circle_pipeline, Self::CIRCLE_PIPELINE);

storage
}

Expand Down Expand Up @@ -540,6 +570,8 @@ impl PipelinesStorage {
(DrawMode::Triangles, true) => Self::TRIANGLES_DEPTH_PIPELINE,
(DrawMode::Lines, false) => Self::LINES_PIPELINE,
(DrawMode::Lines, true) => Self::LINES_DEPTH_PIPELINE,
(DrawMode::Circle, false) => Self::CIRCLE_PIPELINE,
(DrawMode::Circle, true) => Self::CIRCLE_PIPELINE,
}
}

Expand Down Expand Up @@ -1107,6 +1139,78 @@ mod shader {
return in.color * tex.sample(texSmplr, in.uv);
}
"#;

pub const CIRCLE_VERTEX: &str = r#"#version 100
attribute vec3 position;
attribute vec2 texcoord;
attribute vec4 color0;
varying lowp vec2 fragCoord;
varying lowp vec4 color;
uniform mat4 Model;
uniform mat4 Projection;
void main() {
gl_Position = Projection * Model * vec4(position, 1);
color = color0 / 255.0;
fragCoord = texcoord;
}"#;

pub const CIRCLE_FRAGMENT: &str = r#"#version 100
precision lowp float;
varying lowp vec4 color;
varying lowp vec2 fragCoord;
uniform sampler2D Texture;
void main() {
vec2 uv = fragCoord * 2.0 - 1.0;
float distance = length(uv);
if (distance < 1.0) {
gl_FragColor = color;
} else {
gl_FragColor = vec4(0.0);
}
}"#;

pub const CIRCLE_METAL: &str = r#"
#include <metal_stdlib>
using namespace metal;

struct VertexIn {
float3 position [[attribute(0)]];
float2 texcoord [[attribute(1)]];
float4 color0 [[attribute(2)]];
};

struct VertexOut {
float4 position [[position]];
float2 fragCoord;
float4 color;
};

struct Uniforms {
float4x4 model;
float4x4 projection;
};

vertex VertexOut vertexShader(VertexIn in [[stage_in]], constant Uniforms& uniforms [[buffer(0)]]) {
VertexOut out;

out.position = uniforms.projection * uniforms.model * float4(in.position, 1.0);
out.color = in.color0 / 255.0;
out.fragCoord = in.texcoord;

return out;
}

fragment float4 fragmentShader(VertexOut in [[stage_in]], texture2d<float> tex [[texture(0)]], sampler texSampler [[sampler(0)]]) {
float2 uv = in.fragCoord * 2.0 - 1.0;
float distance = length(uv);

if (distance < 1.0) {
return in.color;
} else {
return float4(0.0, 0.0, 0.0, 0.0);
}
}"#;

pub fn uniforms() -> Vec<(&'static str, UniformType)> {
vec![
("Projection", UniformType::Mat4),
Expand Down
17 changes: 13 additions & 4 deletions src/shapes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,20 @@ pub fn draw_poly_lines(
}

/// Draws a solid circle centered at `[x, y]` with a given radius `r` and `color`.
///
/// This is not a perfect circle, but only a polygon approximation.
/// If this is an issue for you, consider using `draw_poly(x, y, 255, r, 0., color)` instead.
pub fn draw_circle(x: f32, y: f32, r: f32, color: Color) {
draw_poly(x, y, 20, r, 0., color);
let context = get_context();

let vertices = [
Vertex::new(x - r, y - r, 0., 0.0, 0.0, color),
Vertex::new(x + r, y - r, 0., 1.0, 0.0, color),
Vertex::new(x + r, y + r, 0., 1.0, 1.0, color),
Vertex::new(x - r, y + r, 0., 0.0, 1.0, color),
];
let indices: [u16; 6] = [0, 1, 2, 0, 2, 3];

context.gl.texture(None);
context.gl.draw_mode(DrawMode::Circle);
context.gl.geometry(&vertices, &indices);
}

/// Draws a circle outline centered at `[x, y]` with a given radius, line `thickness` and `color`.
Expand Down
Loading