working aim from hold slice correctly

This commit is contained in:
jacob 2024-08-05 13:01:15 -05:00
parent 630afc411b
commit 3049e02b57
9 changed files with 167 additions and 56 deletions

View File

@ -273,10 +273,11 @@ void __asan_unpoison_memory_region(void const volatile *add, size_t);
#define RGB_32_F(r, g, b) RGBA_32_F(r, g, b, 1.f) #define RGB_32_F(r, g, b) RGBA_32_F(r, g, b, 1.f)
#define COLOR_WHITE RGB_32(0xFF, 0xFF, 0xFF) #define COLOR_WHITE RGB_32(0xFF, 0xFF, 0xFF)
#define COLOR_BLACK RGB_32(0, 0, 0 ) #define COLOR_BLACK RGB_32(0 , 0 , 0 )
#define COLOR_RED RGB_32(0xFF, 0, 0 ) #define COLOR_RED RGB_32(0xFF, 0 , 0 )
#define COLOR_GREEN RGB_32(0, 0xFF, 0 ) #define COLOR_GREEN RGB_32(0 , 0xFF, 0 )
#define COLOR_BLUE RGB_32(0, 0, 0xFF) #define COLOR_BLUE RGB_32(0 , 0 , 0xFF)
#define COLOR_YELLOW RGB_32(0xFF, 0xFF, 0 )
/* Barrier */ /* Barrier */
#if COMPILER_MSVC #if COMPILER_MSVC
@ -347,6 +348,9 @@ GLOBAL const f64 *_f64_infinity = (f64 *)&_f64_infinity_u64;
#define F32_INFINITY (*_f32_infinity) #define F32_INFINITY (*_f32_infinity)
#define F64_INFINITY (*_f64_infinity) #define F64_INFINITY (*_f64_infinity)
#define F32_IS_NAN(x) (x != x)
#define F64_IS_NAN(x) (x != x)
#define PI ((f32)3.14159265358979323846) #define PI ((f32)3.14159265358979323846)
#define TAU ((f32)6.28318530717958647693) #define TAU ((f32)6.28318530717958647693)

View File

@ -196,6 +196,28 @@ void draw_solid_poly_line(struct renderer_canvas *canvas, struct v2_array array,
} }
} }
void draw_solid_circle_line(struct renderer_canvas *canvas, struct v2 pos, f32 radius, f32 thickness, u32 color, u32 detail)
{
struct temp_arena scratch = scratch_begin_no_conflict();
struct v2 *points = arena_push_array(scratch.arena, struct v2, detail);
for (u32 i = 0; i < detail; ++i) {
struct v2 p = V2(
radius * math_cos(i * (PI * 2.f) / detail),
radius * math_sin(i * (PI * 2.f) / detail)
);
points[i] = v2_add(pos, p);
}
struct v2_array a = {
.points = points,
.count = detail
};
draw_solid_poly_line(canvas, a, true, thickness, color);
scratch_end(scratch);
}
void draw_solid_quad_line(struct renderer_canvas *canvas, struct quad quad, f32 thickness, u32 color) void draw_solid_quad_line(struct renderer_canvas *canvas, struct quad quad, f32 thickness, u32 color)
{ {
struct v2 points[] = { quad.p1, quad.p2, quad.p3, quad.p4 }; struct v2 points[] = { quad.p1, quad.p2, quad.p3, quad.p4 };

View File

@ -33,6 +33,7 @@ void draw_solid_rect(struct renderer_canvas *canvas, struct rect rect, u32 color
void draw_solid_line(struct renderer_canvas *canvas, struct v2 start, struct v2 end, f32 thickness, u32 color); void draw_solid_line(struct renderer_canvas *canvas, struct v2 start, struct v2 end, f32 thickness, u32 color);
void draw_solid_ray(struct renderer_canvas *canvas, struct v2 start, struct v2 ray, f32 thickness, u32 color); void draw_solid_ray(struct renderer_canvas *canvas, struct v2 start, struct v2 ray, f32 thickness, u32 color);
void draw_solid_poly_line(struct renderer_canvas *canvas, struct v2_array array, b32 loop, f32 thickness, u32 color); void draw_solid_poly_line(struct renderer_canvas *canvas, struct v2_array array, b32 loop, f32 thickness, u32 color);
void draw_solid_circle_line(struct renderer_canvas *canvas, struct v2 pos, f32 radius, f32 thickness, u32 color, u32 detail);
void draw_solid_quad_line(struct renderer_canvas *canvas, struct quad quad, f32 thickness, u32 color); void draw_solid_quad_line(struct renderer_canvas *canvas, struct quad quad, f32 thickness, u32 color);
void draw_solid_rect_line(struct renderer_canvas *canvas, struct rect rect, f32 thickness, u32 color); void draw_solid_rect_line(struct renderer_canvas *canvas, struct rect rect, f32 thickness, u32 color);
void draw_solid_arrow_line(struct renderer_canvas *canvas, struct v2 start, struct v2 end, f32 thickness, f32 arrowhead_height, u32 color); void draw_solid_arrow_line(struct renderer_canvas *canvas, struct v2 start, struct v2 end, f32 thickness, f32 arrowhead_height, u32 color);

View File

@ -179,6 +179,7 @@ INTERNAL void game_update(void)
e->sprite = sprite_tag_from_path(STR("res/graphics/tim.ase")); e->sprite = sprite_tag_from_path(STR("res/graphics/tim.ase"));
//e->sprite_span_name = STR("idle.unarmed"); //e->sprite_span_name = STR("idle.unarmed");
//e->sprite_span_name = STR("idle.one_handed");
e->sprite_span_name = STR("idle.two_handed"); e->sprite_span_name = STR("idle.two_handed");
entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED); entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED);
@ -309,7 +310,7 @@ INTERNAL void game_update(void)
struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite);
struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("pivot"), ent->animation_frame); struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("pivot"), ent->animation_frame);
struct v2 sprite_size = v2_div(sheet->frame_size, (f32)PIXELS_PER_UNIT); struct v2 sprite_size = v2_div(sheet->frame_size, (f32)PIXELS_PER_UNIT);
struct v2 pivot_pos = v2_mul_v2(slice.center_norm, sprite_size); struct v2 pivot_pos = v2_mul_v2(slice.center, sprite_size);
ent->sprite_xform = xform_with_scale(XFORM_POS(v2_neg(pivot_pos)), sprite_size); ent->sprite_xform = xform_with_scale(XFORM_POS(v2_neg(pivot_pos)), sprite_size);
} }
@ -349,21 +350,64 @@ INTERNAL void game_update(void)
/* Update focus */ /* Update focus */
ent->focus = G.world.player_aim; ent->focus = G.world.player_aim;
#if 0
struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite);
struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("hold"), ent->animation_frame); struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("hold"), ent->animation_frame);
struct v2 hold_pos = xform_mul_v2(ent->xform_world, xform_mul_v2(ent->sprite_xform, slice.center_norm)); struct v2 hold_pos = xform_mul_v2(ent->xform_world, xform_mul_v2(ent->sprite_xform, slice.center));
struct v2 hold_dir = xform_basis_mul_v2(ent->xform_world, xform_basis_mul_v2(ent->sprite_xform, slice.dir_norm)); struct v2 hold_dir = xform_basis_mul_v2(ent->xform_world, xform_basis_mul_v2(ent->sprite_xform, slice.dir));
struct v2 focus_pos = v2_add(ent->xform_world.og, ent->focus); struct v2 focus_pos = v2_add(ent->xform_world.og, ent->focus);
struct v2 hold_focus_dir = v2_sub(focus_pos, hold_pos); struct v2 hold_focus_dir = v2_sub(focus_pos, hold_pos);
/* Only rotate if rotation to focus around origin is possible */ /* Only rotate if rotation to focus around origin is possible */
f32 ent_hold_len = v2_len(v2_sub(hold_pos, ent->xform_world.og)); f32 min_hold_radius = 0;
{
struct v2 center = xform_mul_v2(ent->sprite_xform_world, slice.center);
struct v2 center_dir = v2_norm(xform_basis_mul_v2(ent->sprite_xform_world, slice.dir));
struct v2 minpoint = v2_closest_point_ray(center, center_dir, ent->xform_world.og);
min_hold_radius = v2_len(v2_sub(minpoint, ent->xform_world.og));
}
f32 ent_focus_len = v2_len(v2_sub(focus_pos, ent->xform_world.og)); f32 ent_focus_len = v2_len(v2_sub(focus_pos, ent->xform_world.og));
if (ent_focus_len > ent_hold_len) { if (ent_focus_len > min_hold_radius) {
ent->xform = xform_rotate(ent->xform, v2_angle_from_dirs(hold_dir, hold_focus_dir)); ent->xform = xform_rotate(ent->xform, v2_angle_from_dirs(hold_dir, hold_focus_dir));
} }
#else
struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite);
struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("hold"), ent->animation_frame);
struct xform xf = ent->xform;
struct v2 ent_pos = ent->xform_world.og;
struct v2 focus_pos = v2_add(ent_pos, ent->focus);
struct v2 hold_pos = xform_mul_v2(ent->xform_world, xform_mul_v2(ent->sprite_xform, slice.center));
struct v2 hold_dir = xform_basis_mul_v2(ent->xform_world, xform_basis_mul_v2(ent->sprite_xform, slice.dir));
struct v2 hold_ent_dir = v2_sub(ent_pos, hold_pos);
struct v2 focus_ent_dir = v2_sub(ent_pos, focus_pos);
f32 hold_ent_len = v2_len(hold_ent_dir);
f32 focus_ent_len = v2_len(focus_ent_dir);
f32 final_hold_angle_btw_ent_and_focus = v2_angle_from_dirs(v2_norm(hold_ent_dir), v2_norm(hold_dir));
f32 final_focus_angle_btw_ent_and_hold = math_asin((math_sin(final_hold_angle_btw_ent_and_focus) * hold_ent_len) / focus_ent_len);
f32 final_ent_angle_btw_focus_and_hold = PI - (final_focus_angle_btw_ent_and_hold + final_hold_angle_btw_ent_and_focus);
f32 final_hold_ent_angle_offset;
{
struct xform ent_world_xform_unrotated = xform_with_rotation(ent->xform_world, 0);
struct v2 hold_pos_unrotated = xform_mul_v2(ent_world_xform_unrotated, xform_mul_v2(ent->sprite_xform, slice.center));
final_hold_ent_angle_offset = v2_angle_from_dirs(V2(0, -1), v2_norm(v2_sub(hold_pos_unrotated, ent_world_xform_unrotated.og)));
}
f32 final_ent_angle = v2_angle_from_dirs(V2(0, -1), v2_sub(focus_pos, ent_pos)) + final_ent_angle_btw_focus_and_hold - final_hold_ent_angle_offset;
if (!F32_IS_NAN(final_ent_angle)) {
xf = xform_with_rotation(xf, final_ent_angle);
}
ent->xform = xf;
#endif
} }
/* ========================== * /* ========================== *

View File

@ -641,6 +641,14 @@ INLINE f32 v2_angle_from_points(struct v2 pt1, struct v2 pt2)
return v2_angle(v2_sub(pt2, pt1)); return v2_angle(v2_sub(pt2, pt1));
} }
INLINE struct v2 v2_closest_point_ray(struct v2 ray_pos, struct v2 ray_dir_norm, struct v2 p)
{
struct v2 ray_p_dir = v2_sub(p, ray_pos);
f32 dot = v2_dot(ray_dir_norm, ray_p_dir);
struct v2 ray_dir_closest = v2_mul(ray_dir_norm, dot);
return v2_add(ray_pos, ray_dir_closest);
}
/* ========================== * /* ========================== *
* Mat4x4 * Mat4x4
* ========================== */ * ========================== */

View File

@ -518,39 +518,37 @@ INTERNAL struct sprite_sheet init_sheet_from_ase_result(struct arena *arena, str
struct sprite_sheet_slice *slice = &slice_group->frame_slices[(start * slice_group->per_frame_count) + index_in_frame]; struct sprite_sheet_slice *slice = &slice_group->frame_slices[(start * slice_group->per_frame_count) + index_in_frame];
slice->original = true; slice->original = true;
f32 x1 = ase_slice->x1; f32 x1_px = ase_slice->x1;
f32 y1 = ase_slice->y1; f32 y1_px = ase_slice->y1;
f32 x2 = ase_slice->x2; f32 x2_px = ase_slice->x2;
f32 y2 = ase_slice->y2; f32 y2_px = ase_slice->y2;
f32 width_px = x2_px - x1_px;
f32 height_px = y2_px - y1_px;
f32 x1 = (x1_px - frame_center.x) / frame_size.x;
f32 y1 = (y1_px - frame_center.y) / frame_size.y;
f32 x2 = (x2_px - frame_center.x) / frame_size.x;
f32 y2 = (y2_px - frame_center.y) / frame_size.y;
f32 width = x2 - x1; f32 width = x2 - x1;
f32 height = y2 - y1; f32 height = y2 - y1;
f32 x1_norm = (x1 - frame_center.x) / frame_size.x;
f32 y1_norm = (y1 - frame_center.y) / frame_size.y;
f32 x2_norm = (x2 - frame_center.x) / frame_size.x;
f32 y2_norm = (y2 - frame_center.y) / frame_size.y;
f32 width_norm = x2_norm - x1_norm;
f32 height_norm = y2_norm - y1_norm;
/* Rect */ /* Rect */
struct rect rect_px = RECT(x1_px, y1_px, width_px, height_px);
struct rect rect = RECT(x1, y1, width, height); struct rect rect = RECT(x1, y1, width, height);
/* Rect norm */
struct rect rect_norm = RECT(x1_norm, y1_norm, width_norm, height_norm);
/* Center */ /* Center */
struct v2 center_px = V2(x1_px + (width_px * 0.5f), y1_px + (height_px * 0.5f));
struct v2 center = V2(x1 + (width * 0.5f), y1 + (height * 0.5f)); struct v2 center = V2(x1 + (width * 0.5f), y1 + (height * 0.5f));
/* Center norm */
struct v2 center_norm = V2(x1_norm + (width_norm * 0.5f), y1_norm + (height_norm * 0.5f));
/* Dir */ /* Dir */
struct v2 dir = V2(center.x, 0); struct v2 dir_px = V2(center_px.x, 0);
/* Dir norm */ struct v2 dir = V2(0, -0.5);
struct v2 dir_norm = V2(0, -0.5);
slice->rect_px = rect_px;
slice->center_px = center_px;
slice->dir_px = dir_px;
slice->rect = rect; slice->rect = rect;
slice->rect_norm = rect_norm;
slice->center = center; slice->center = center;
slice->center_norm = center_norm;
slice->dir = dir; slice->dir = dir;
slice->dir_norm = dir_norm;
node->index_in_frame = index_in_frame; node->index_in_frame = index_in_frame;
if (start < node->earliest_frame) { if (start < node->earliest_frame) {
@ -618,14 +616,15 @@ INTERNAL struct sprite_sheet init_sheet_from_ase_result(struct arena *arena, str
for (u32 i = 0; i < ase.num_frames; ++i) { for (u32 i = 0; i < ase.num_frames; ++i) {
/* Use ray slice in ray group */ /* Use ray slice in ray group */
struct sprite_sheet_slice *ray_slice = &ray_slice_group->frame_slices[i * point_slices_per_frame]; struct sprite_sheet_slice *ray_slice = &ray_slice_group->frame_slices[i * point_slices_per_frame];
struct v2 ray_end = ray_slice->center; struct v2 ray_end = ray_slice->center_px;
struct v2 ray_end_norm = ray_slice->center_norm; struct v2 ray_end_norm = ray_slice->center;
/* Apply to each point slice in point group */ /* Apply to each point slice in point group */
for (u32 j = 0; j < point_slices_per_frame; ++j) { for (u32 j = 0; j < point_slices_per_frame; ++j) {
struct sprite_sheet_slice *point_slice = &point_slice_group->frame_slices[(i * point_slices_per_frame) + j]; struct sprite_sheet_slice *point_slice = &point_slice_group->frame_slices[(i * point_slices_per_frame) + j];
point_slice->dir = v2_sub(ray_end, point_slice->center); point_slice->dir_px = v2_sub(ray_end, point_slice->center_px);
point_slice->dir_norm = v2_sub(ray_end_norm, point_slice->center_norm); point_slice->dir = v2_sub(ray_end_norm, point_slice->center);
point_slice->has_ray = true;
} }
} }
@ -971,10 +970,17 @@ struct sprite_sheet_slice sprite_sheet_get_slice(struct sprite_sheet *sheet, str
return group->frame_slices[frame_index * group->per_frame_count]; return group->frame_slices[frame_index * group->per_frame_count];
} }
} }
/* Return 'pivot' by default */
struct sprite_sheet_slice res = { 0 }; struct sprite_sheet_slice res = { 0 };
res.center = v2_mul(sheet->frame_size, 0.5f); if (string_eq(name, STR("pivot"))) {
res.dir = V2(res.center.x, 0); res.center_px = v2_mul(sheet->frame_size, 0.5f);
res.dir_norm = V2(0, -0.5); res.dir_px = V2(res.center_px.x, 0);
res.dir = V2(0, -0.5);
} else {
res = sprite_sheet_get_slice(sheet, STR("pivot"), frame_index);
}
return res; return res;
} }

View File

@ -93,14 +93,21 @@ struct sprite_sheet_span {
}; };
struct sprite_sheet_slice { struct sprite_sheet_slice {
/* Values suffixed with '_norm' are in the range -0.5 (top / left edge) -> +0.5 (bottom / right edge) */ /* If true, this slice was not copied over from another frame in the sprite sheet */
b32 original; b32 original;
/* If true, the slice has a corresponding '.ray' slice affecting the 'dir' fields */
b32 has_ray;
/* Values are in the range -0.5 (top / left edge) -> +0.5 (bottom / right edge) */
struct rect rect; struct rect rect;
struct rect rect_norm;
struct v2 center; struct v2 center;
struct v2 center_norm;
struct v2 dir; struct v2 dir;
struct v2 dir_norm;
/* '_px' values retain the original sprite pixel dimensions */
struct rect rect_px;
struct v2 center_px;
struct v2 dir_px;
}; };
struct sprite_sheet_slice_array { struct sprite_sheet_slice_array {

View File

@ -100,7 +100,7 @@ struct string string_from_float(struct arena *arena, f64 f, u32 precision)
u8 *final_text = arena_dry_push(arena, u8); u8 *final_text = arena_dry_push(arena, u8);
u64 final_len = 0; u64 final_len = 0;
if (f != f) { if (F32_IS_NAN(f)) {
final_len += string_copy(arena, STR("NaN")).len; final_len += string_copy(arena, STR("NaN")).len;
} else if (f == F64_INFINITY) { } else if (f == F64_INFINITY) {
final_len += string_copy(arena, STR("inf")).len; final_len += string_copy(arena, STR("inf")).len;

View File

@ -433,8 +433,9 @@ INTERNAL void user_update(void)
world_copy_replace(&G.world, t1); world_copy_replace(&G.world, t1);
/* Blend time */ /* Blend world globals */
G.world.time = math_lerp64(t0->time, t1->time, (f64)tick_blend); G.world.time = math_lerp64(t0->time, t1->time, (f64)tick_blend);
G.world.player_aim = v2_lerp(t0->player_aim, t1->player_aim, tick_blend);
/* Blend entities */ /* Blend entities */
struct entity_array t0_entities = entity_store_as_array(&t0->entity_store); struct entity_array t0_entities = entity_store_as_array(&t0->entity_store);
@ -823,6 +824,17 @@ INTERNAL void user_update(void)
debug_draw_xform(ent->xform_world); debug_draw_xform(ent->xform_world);
} }
/* Draw aim arrow */
if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) {
struct sprite_sheet *sheet = sprite_sheet_from_tag_async(sprite_frame_scope, ent->sprite);
struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("hold"), ent->animation_frame);
struct v2 start = xform_mul_v2(ent->sprite_xform_world, slice.center);
start = xform_mul_v2(G.world_view, start);
struct v2 end = v2_add(ent->xform.og, ent->focus);
end = xform_mul_v2(G.world_view, end);
draw_solid_arrow_line(G.viewport_canvas, start, end, 3, 10, RGBA_32_F(1, 1, 1, 0.5));
}
/* Draw slices */ /* Draw slices */
if (!sprite_tag_is_nil(ent->sprite)) { if (!sprite_tag_is_nil(ent->sprite)) {
struct sprite_tag sprite = ent->sprite; struct sprite_tag sprite = ent->sprite;
@ -830,12 +842,29 @@ INTERNAL void user_update(void)
for (u64 i = 0; i < sheet->slice_groups_count; ++i) { for (u64 i = 0; i < sheet->slice_groups_count; ++i) {
struct sprite_sheet_slice_group *group = &sheet->slice_groups[i]; struct sprite_sheet_slice_group *group = &sheet->slice_groups[i];
if (string_ends_with(group->name, STR(".ray"))) continue;
for (u32 j = 0; j < group->per_frame_count; ++j) { for (u32 j = 0; j < group->per_frame_count; ++j) {
struct sprite_sheet_slice slice = group->frame_slices[(ent->animation_frame * group->per_frame_count) + j]; struct sprite_sheet_slice slice = group->frame_slices[(ent->animation_frame * group->per_frame_count) + j];
struct quad quad = quad_from_rect(slice.rect_norm);
struct v2 center = xform_mul_v2(ent->sprite_xform_world, slice.center);
center = xform_mul_v2(G.world_view, center);
if (!slice.has_ray) {
struct quad quad = quad_from_rect(slice.rect);
quad = quad_mul_xform(quad, ent->sprite_xform_world); quad = quad_mul_xform(quad, ent->sprite_xform_world);
draw_solid_quad_line(G.viewport_canvas, quad_mul_xform(quad, G.world_view), 3, COLOR_RED); quad = quad_mul_xform(quad, G.world_view);
draw_solid_quad_line(G.viewport_canvas, quad, 2, RGBA_32_F(1, 0, 0.5, 1));
}
draw_solid_circle(G.viewport_canvas, center, 3, RGBA_32_F(1, 0, 0, 1), 20);
if (slice.has_ray) {
struct v2 ray = xform_basis_mul_v2(ent->sprite_xform_world, slice.dir);
ray = xform_basis_mul_v2(G.world_view, ray);
ray = v2_mul(v2_norm(ray), 50);
draw_solid_arrow_ray(G.viewport_canvas, center, ray, 2, 10, RGBA_32_F(1, 0, 0.5, 1));
}
} }
} }
} }
@ -852,16 +881,6 @@ INTERNAL void user_update(void)
draw_solid_arrow_line(G.viewport_canvas, start, end, thickness, arrow_height, color); draw_solid_arrow_line(G.viewport_canvas, start, end, thickness, arrow_height, color);
} }
/* Draw aim */
if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) {
u32 color = RGBA_32_F(0.75, 0, 0.75, 0.5);
f32 thickness = 3;
f32 arrow_height = 10;
struct v2 pos = xform_mul_v2(G.world_view, ent->xform_world.og);
struct v2 aim_ray = xform_basis_mul_v2(G.world_view, ent->focus);
draw_solid_arrow_ray(G.viewport_canvas, pos, aim_ray, thickness, arrow_height, color);
}
/* Draw camera rect */ /* Draw camera rect */
if (entity_has_prop(ent, ENTITY_PROP_CAMERA)) { if (entity_has_prop(ent, ENTITY_PROP_CAMERA)) {
u32 color = ent == active_camera ? RGBA_32_F(1, 1, 1, 0.5) : RGBA_32_F(0, 0.75, 0, 0.5); u32 color = ent == active_camera ? RGBA_32_F(1, 1, 1, 0.5) : RGBA_32_F(0, 0.75, 0, 0.5);