///|
fn with_blend_mode(
blend_mode : @render.BlendMode2D,
action : () -> Unit,
) -> Unit {
match blend_mode {
Alpha => action()
Additive => {
@raylib.begin_blend_mode(@raylib.BlendAdditive)
action()
@raylib.end_blend_mode()
}
Multiply => {
@raylib.begin_blend_mode(@raylib.BlendMultiplied)
action()
@raylib.end_blend_mode()
}
}
}
///|
pub fn render2d_begin_pass(
viewport : @smath.Rect,
load_op : @render2d_types.RenderPassLoadOp2D,
clear_color : @render.Color,
target : @render2d_types.RenderPassTarget2D,
) -> Unit {
backend.active_2d_offscreen_target = None
let mut pass_viewport = viewport
match target {
Screen => ()
Offscreen(name) => {
let width = @cmp.maximum(1, viewport.size[X].ceil().to_int())
let height = @cmp.maximum(1, viewport.size[Y].ceil().to_int())
match ensure_offscreen_target_2d(name, width, height) {
Some(offscreen_target) => {
offscreen_target.render_texture.begin_mode()
backend.active_2d_offscreen_target = Some(name)
pass_viewport = {
position: @smath.Vec2::zero(),
size: Vec2(width.to_double(), height.to_double()),
}
}
None =>
pass_viewport = {
position: @smath.Vec2::zero(),
size: @smath.Vec2::zero(),
}
}
}
}
reset_clip_stack()
push_clip_rect(pass_viewport)
if load_op is Clear {
mark_frame_drawn()
@raylib.draw_rectangle_rec(
to_ray_rect(
pass_viewport.position[X],
pass_viewport.position[Y],
pass_viewport.size[X],
pass_viewport.size[Y],
),
to_ray_color(clear_color),
)
}
}
///|
pub fn render2d_end_pass() -> Unit {
reset_clip_stack()
if backend.active_2d_offscreen_target is Some(_) {
@raylib.end_texture_mode()
}
backend.active_2d_offscreen_target = None
}
///|
pub fn push_clip_rect(rect : @smath.Rect) -> Unit {
let next = match clip_stack.last() {
Some(current) =>
clip_rect_intersection(current, rect).unwrap_or({
position: @smath.Vec2::zero(),
size: @smath.Vec2::zero(),
})
None => rect
}
clip_stack.push(next)
refresh_clip_stack()
}
///|
pub fn pop_clip_rect() -> Unit {
if clip_stack.is_empty() {
return
}
ignore(clip_stack.pop())
refresh_clip_stack()
}
///|
pub fn reset_clip_stack() -> Unit {
while !clip_stack.is_empty() {
ignore(clip_stack.pop())
}
@raylib.end_scissor_mode()
}
///|
pub fn register_key_events(pressed_keys : Set[@inputs.Code]) -> Unit {
backend.key_events = Some(pressed_keys)
}
///|
pub fn register_mouse_events(
mouse : @inputs.Mouse,
mouse_relative_delta : Ref[@smath.Vec2?],
mouse_wheel : @inputs.MouseWheel,
) -> Unit {
backend.mouse_state = Some(mouse)
backend.mouse_relative_delta = Some(mouse_relative_delta)
backend.mouse_wheel = Some(mouse_wheel)
}
///|
pub fn register_touch_events(
touches : Map[@inputs.TouchId, @inputs.TouchPoint],
changes : Array[@inputs.TouchEvent],
) -> Unit {
backend.touch_state = Some(touches)
backend.touch_changes_state = Some(changes)
}
///|
pub fn register_gamepad_events(
gamepads : Set[@inputs.Gamepad],
buttons : Set[@inputs.GamepadButtonInput],
axes : Map[@inputs.GamepadAxisInput, Double],
) -> Unit {
backend.gamepads_state = Some(gamepads)
backend.gamepad_buttons_state = Some(buttons)
backend.gamepad_axes_state = Some(axes)
sync_gamepad_events()
}
///|
pub fn lock_mouse(lock_state_ref : Ref[Bool]) -> Unit {
backend.lock_state = Some(lock_state_ref)
backend.lock_requested = lock_state_ref.val
sync_mouse_lock()
backend.cursor_locked = @raylib.is_cursor_hidden()
lock_state_ref.val = backend.cursor_locked
}
///|
fn transform_is_identity_2d(transform : @smath.Transform) -> Bool {
(transform.a - 1.0).abs() < 0.000001 &&
transform.b.abs() < 0.000001 &&
transform.c.abs() < 0.000001 &&
(transform.d - 1.0).abs() < 0.000001 &&
transform.tx.abs() < 0.000001 &&
transform.ty.abs() < 0.000001
}
///|
fn bind_solid_color_texture() -> Unit {
@rl.set_texture(@raylib.get_texture_id(get_white_texture()))
}
///|
fn tuple_to_ray_vector2(point : (Float, Float)) -> @raylib.Vector2 {
@raylib.Vector2::new(point.0, point.1)
}
///|
fn draw_solid_quad(
p0 : (Float, Float),
p1 : (Float, Float),
p2 : (Float, Float),
p3 : (Float, Float),
color : @render.Color,
) -> Unit {
let ray_color = to_ray_color(color)
@raylib.draw_triangle(
tuple_to_ray_vector2(p0),
tuple_to_ray_vector2(p3),
tuple_to_ray_vector2(p2),
ray_color,
)
@raylib.draw_triangle(
tuple_to_ray_vector2(p0),
tuple_to_ray_vector2(p2),
tuple_to_ray_vector2(p1),
ray_color,
)
}
///|
fn draw_line_loop(
points : Array[(Float, Float)],
color : @render.Color,
) -> Unit {
if points.length() < 2 {
return
}
let ray_color = to_ray_color(color)
for index in 0.. Bool {
guard command.text != "" || !command.sections.is_empty() else { return true }
guard cosmic_texture_for_text(command) is Some(cached_texture) else {
return false
}
let measured = measure_text_with_cosmic(command).unwrap_or(
Vec2(
cached_texture.width.to_double() / cached_texture.raster_scale,
cached_texture.height.to_double() / cached_texture.raster_scale,
),
)
let mut x = command.position[X]
let mut y = command.position[Y]
match command.style.align {
Left => ()
Center => x -= measured[X] / 2.0
Right => x -= measured[X]
}
match command.style.baseline {
Top => ()
Center => y -= measured[Y] / 2.0
Bottom => y -= measured[Y]
}
draw_textured_quad(
cached_texture.texture,
cached_texture.width.to_double(),
cached_texture.height.to_double(),
0.0,
0.0,
cached_texture.width.to_double(),
cached_texture.height.to_double(),
0.0,
0.0,
cached_texture.width.to_double() / cached_texture.raster_scale,
cached_texture.height.to_double() / cached_texture.raster_scale,
x,
y,
command.transform,
@smath.Transform::identity(),
white_render_color(),
)
true
}
///|
pub fn draw_text(command : @render2d_types.TextDrawCommand2D) -> Unit {
mark_frame_drawn()
with_blend_mode(command.blend_mode, fn() {
if !transform_is_identity_2d(command.transform) {
if draw_text_with_cosmic_transform(command) {
()
} else if draw_text_with_cosmic(command) {
()
} else {
let font = get_font_by_family(command.style.family)
let measured = measure_text(command.style, command.text)
let text_width = measured[X]
let text_height = measured[Y]
let mut x = command.position[X]
let mut y = command.position[Y]
match command.style.align {
Left => ()
Center => x -= text_width / 2.0
Right => x -= text_width
}
match command.style.baseline {
Top => ()
Center => y -= text_height / 2.0
Bottom => y -= text_height
}
@raylib.draw_text_ex(
font,
command.text,
to_ray_vector2(x, y),
to_float(command.style.size),
RAYLIB_DEFAULT_TEXT_SPACING,
to_ray_color(command.style.color),
)
}
} else if draw_text_with_cosmic(command) {
()
} else {
let font = get_font_by_family(command.style.family)
let measured = measure_text(command.style, command.text)
let text_width = measured[X]
let text_height = measured[Y]
let mut x = command.position[X]
let mut y = command.position[Y]
match command.style.align {
Left => ()
Center => x -= text_width / 2.0
Right => x -= text_width
}
match command.style.baseline {
Top => ()
Center => y -= text_height / 2.0
Bottom => y -= text_height
}
@raylib.draw_text_ex(
font,
command.text,
to_ray_vector2(x, y),
to_float(command.style.size),
RAYLIB_DEFAULT_TEXT_SPACING,
to_ray_color(command.style.color),
)
}
})
}
///|
pub fn measure_text(
style : @render2d_types.TextStyle2D,
text : String,
) -> @smath.Vec2 {
if text == "" {
return Vec2(0.0, cosmic_font_size(style.size))
}
let command : @render2d_types.TextDrawCommand2D = {
text,
sections: [],
position: @smath.Vec2::zero(),
transform: @smath.Transform::identity(),
blend_mode: Alpha,
style,
max_width: None,
max_height: None,
wrap: false,
ellipsis: false,
}
if measure_text_with_cosmic(command) is Some(measured) {
return measured
}
let font = get_font_by_family(style.family)
let measured = @raylib.measure_text_ex(
font,
text,
to_float(style.size),
RAYLIB_DEFAULT_TEXT_SPACING,
)
Vec2(vector2_x(measured), vector2_y(measured))
}
///|
pub fn measure_text_block(
command : @render2d_types.TextDrawCommand2D,
) -> @smath.Vec2 {
measure_text_with_cosmic(command).unwrap_or(
measure_text(command.style, command.text),
)
}
///|
pub fn draw_rect(command : @render2d_types.RectDrawCommand2D) -> Unit {
mark_frame_drawn()
with_blend_mode(command.blend_mode, fn() {
if !transform_is_identity_2d(command.transform) {
let p0 = transform_image_point(
command.rect.position[X],
command.rect.position[Y],
command.transform,
0.0,
0.0,
)
let p1 = transform_image_point(
command.rect.position[X],
command.rect.position[Y],
command.transform,
command.rect.size[X],
0.0,
)
let p2 = transform_image_point(
command.rect.position[X],
command.rect.position[Y],
command.transform,
command.rect.size[X],
command.rect.size[Y],
)
let p3 = transform_image_point(
command.rect.position[X],
command.rect.position[Y],
command.transform,
0.0,
command.rect.size[Y],
)
draw_solid_quad(p0, p1, p2, p3, command.fill_color)
if command.stroke_color is Some(stroke_color) {
draw_line_loop([p0, p1, p2, p3], stroke_color)
}
} else {
let rect = to_ray_rect(
command.rect.position[X],
command.rect.position[Y],
command.rect.size[X],
command.rect.size[Y],
)
@raylib.draw_rectangle_rec(rect, to_ray_color(command.fill_color))
if command.stroke_color is Some(stroke_color) {
@raylib.draw_rectangle_lines_ex(rect, 1.0, to_ray_color(stroke_color))
}
}
})
}
///|
pub fn draw_circle(command : @render2d_types.CircleDrawCommand2D) -> Unit {
mark_frame_drawn()
with_blend_mode(command.blend_mode, fn() {
if !transform_is_identity_2d(command.transform) {
let center = transform_image_point(
command.center[X],
command.center[Y],
command.transform,
0.0,
0.0,
)
let fill = command.fill_color
let outline : Array[(Float, Float)] = []
let fan : Array[@raylib.Vector2] = [tuple_to_ray_vector2(center)]
let segments = 40
for index in 0.. Unit {
mark_frame_drawn()
with_blend_mode(command.blend_mode, fn() {
let points : Array[(Float, Float)] = command.vertices.map(fn(vertex) {
transform_image_point(
command.origin[X],
command.origin[Y],
command.transform,
vertex[X],
vertex[Y],
)
})
let color = to_ray_color(command.color)
match command.topology {
TriangleList => {
let triangle_count = points.length() / 3
for index in 0.. 0.0 {
@raylib.draw_triangle(
tuple_to_ray_vector2(a),
tuple_to_ray_vector2(c),
tuple_to_ray_vector2(b),
color,
)
} else {
@raylib.draw_triangle(
tuple_to_ray_vector2(a),
tuple_to_ray_vector2(b),
tuple_to_ray_vector2(c),
color,
)
}
}
}
LineList => {
let line_count = points.length() / 2
for index in 0.. Unit {
let triangles = @render2d_types.tessellate_gradient_rect(command)
if triangles.is_empty() {
return
}
mark_frame_drawn()
with_blend_mode(command.blend_mode, fn() {
ignore(@rl.check_render_batch_limit(triangles.length() * 3))
bind_solid_color_texture()
@rl.begin(@rl.Triangles)
@rl.normal3f(0.0, 0.0, 1.0)
for triangle in triangles {
for point in [triangle.a, triangle.b, triangle.c] {
let (r, g, b, a) = to_ray_color_bytes(point.color)
@rl.color4ub(r, g, b, a)
@rl.tex_coord2f(0.0, 0.0)
@rl.vertex2f(
Float::from_double(point.position[X]),
Float::from_double(point.position[Y]),
)
}
}
@rl.end_()
})
}
///|
pub fn draw_rounded_chrome(
command : @render2d_types.RoundedChromeDrawCommand2D,
) -> Unit {
let triangles = @render2d_types.tessellate_rounded_chrome(command)
if triangles.is_empty() {
return
}
mark_frame_drawn()
with_blend_mode(command.blend_mode, fn() {
ignore(@rl.check_render_batch_limit(triangles.length() * 3))
bind_solid_color_texture()
@rl.begin(@rl.Triangles)
@rl.normal3f(0.0, 0.0, 1.0)
for triangle in triangles {
for point in [triangle.a, triangle.b, triangle.c] {
let (r, g, b, a) = to_ray_color_bytes(point.color)
@rl.color4ub(r, g, b, a)
@rl.tex_coord2f(0.0, 0.0)
@rl.vertex2f(
Float::from_double(point.position[X]),
Float::from_double(point.position[Y]),
)
}
}
@rl.end_()
})
}
///|
pub fn draw_colored_geometry(
command : @render2d_types.ColoredGeometryDrawCommand2D,
) -> Unit {
if command.triangles.is_empty() {
return
}
mark_frame_drawn()
with_blend_mode(command.blend_mode, fn() {
ignore(@rl.check_render_batch_limit(command.triangles.length() * 3))
bind_solid_color_texture()
@rl.begin(@rl.Triangles)
@rl.normal3f(0.0, 0.0, 1.0)
for triangle in command.triangles {
for point in [triangle.a, triangle.b, triangle.c] {
let (r, g, b, a) = to_ray_color_bytes(point.color)
@rl.color4ub(r, g, b, a)
@rl.tex_coord2f(0.0, 0.0)
@rl.vertex2f(
Float::from_double(point.position[X]),
Float::from_double(point.position[Y]),
)
}
}
@rl.end_()
})
}
///|
fn begin_3d(camera : @render3d_types.FrameCamera3D) -> Unit {
@raylib.begin_mode_3d(to_ray_camera3d(camera))
}
///|
fn end_3d() -> Unit {
@raylib.end_mode_3d()
}
///|
pub fn sync_mesh_asset(
handle : @render3d_types.MeshHandle,
mesh : @render3d_types.MeshAsset,
) -> Unit {
if backend.meshes3d.get(handle) is Some(existing) && existing != mesh {
clear_triangle_mesh_cache_for_handle(handle)
}
backend.meshes3d.set(handle, mesh)
}
///|
pub fn sync_material_asset(
handle : @render3d_types.MaterialHandle,
material : @render3d_types.StandardMaterial3D,
) -> Unit {
backend.materials3d.set(handle, material)
ensure_material_mipmaps(material)
}