diff --git a/colorpicker.wren b/colorpicker.wren index c055535..80fa7ec 100644 --- a/colorpicker.wren +++ b/colorpicker.wren @@ -1,7 +1,9 @@ -import "luxe: world" for Entity, UIClear -import "luxe: ui/control" for Control -import "luxe: ui/list" for UIList -import "luxe: ui/button" for UIButton +import "luxe: world" for Entity, UIClear, UILayoutMode, UIBehave, UIContain, Assets, Material, UI, UIEvent +import "luxe: ui" for Control, UIList, UIWindow, UIButton, UIPanel, UIImage +import "luxe: id" for ID +import "luxe: draw" for PathStyle +import "luxe: math" for Math +import "luxe: color" for Color var Color_spaces = [ { @@ -38,15 +40,117 @@ var Modes = [ }, { "name": "Circle", - "icon": "round" //● + "icon": "round", //● } ] class ColorPicker{ static create(ui: Entity) : Control{ //setup root - var root = Control.create(ui) - Control.set_size(root, 350, 400) + var panel = UIWindow.create(ui) + Control.set_size(panel, 350, 400) + Control.set_id(panel, "panel.%(ID.unique())") + Control.set_state_data(panel, [1, 0, 0, 1]) + + var color_view = Control.create(ui) + Control.set_behave(color_view, UIBehave.fill) + Control.set_margin(color_view, 0, 40, 0, 0) + Control.child_add(panel, color_view) + Control.set_id(panel, "color_view.%(ID.unique())") + + var color_wheel = Control.create(ui) + Control.set_size(color_wheel, 200, 200) + Control.child_add(color_view, color_wheel) + Control.set_state_data(color_wheel, {"ring":null, "triangle":null, "hue": 0}) + Control.set_process(color_wheel){|control, state, event, x,y,w,h| + if(event.control != control) return + if(event.type == UIEvent.move){ + x = Control.get_pos_x_abs(control) + y = Control.get_pos_y_abs(control) + var center = [x + w/2, y + h/2] + if(state["ring"] == "captured"){ + var diff = [event.x - center.x, event.y - center.y] + var angle = Math.atan2(-diff.x, diff.y) + Num.pi + var hue = angle / Num.tau + + var color = Control.get_state_data(panel) + var hsv = Color.rgb2hsv(color) + hsv[0] = hue + state["hue"] = hue + color = Color.hsv2rgb(hsv) + Control.set_state_data(panel, color) + UI.events_emit(control, UIEvent.change, state) + UI.events_emit(panel, UIEvent.change, color) + } else { + var distance = Math.dist2D(center, event) + if(distance > 70 && distance < 100){ + state["ring"] = "hover" + } else { + state["ring"] = null + } + } + } else if(event.type == UIEvent.press && event.button == 1) { + if(!state["ring"]) return + state["ring"] = "captured" + UI.capture(control) + } else if(event.type == UIEvent.release && event.button == 1) { + state["ring"] = null + UI.uncapture(control) + } + } + Control.set_allow_input(color_wheel, true) + + var color_ring = UIImage.create(ui) + Control.set_size(color_ring, 200, 200) + Control.set_contain(color_ring, UIContain.justify) + var color_ring_mat = Material.create("materials/color_ring") + UIImage.set_material(color_ring, color_ring_mat) + Control.child_add(color_wheel, color_ring) + + var color_triangle = UIImage.create(ui) + Control.set_margin(color_triangle, 30, 30, 0, 0) + Control.set_size(color_triangle, 140, 140) + Control.set_contain(color_triangle, UIContain.justify) + var color_tri_mat = Material.create("materials/color_triangle") + UIImage.set_material(color_triangle, color_tri_mat) + Control.child_add(color_wheel, color_triangle) + Control.set_events(color_wheel) {|event| + if(event.type == UIEvent.change){ + var hue = event.change["hue"] + UIImage.set_angle(color_triangle, hue * -360) + Material.set_input(color_tri_mat, "triangle.hue", hue) + } + } + + var color_wheel_overlay = Control.create(ui) + Control.set_behave(color_wheel_overlay, UIBehave.fill) + Control.set_margin(color_wheel_overlay, 0, 0, 0, 0) + Control.set_render(color_wheel_overlay){|control, state, x, y, w, h| + var depth = UI.draw_depth_of(control, 0) + var center = [x + w/2, y + h/2] + var style: PathStyle = PathStyle.new() + var wheel_state = Control.get_state_data(color_wheel) + var hover = wheel_state["ring"] + + var hue = wheel_state["hue"] + var angle = hue * Num.tau + var direction = [angle.sin, -angle.cos] + style.thickness = 4 + style.color = hover ? [0.5,0.5,0.5,1] : [0.22,0.22,0.22,1] + var from = [center.x + direction.x * 70, center.y + direction.y * 70] + var to = [center.x + direction.x * 100, center.y + direction.y * 100] + UI.draw_line(control, from.x, from.y, to.x, to.y, depth, style) + + style.color = [0.22,0.22,0.22,1] + style.thickness = 2 + UI.draw_ring(control, center.x, center.y, depth, w/2, h/2, 0, 360, 8, style) + UI.draw_ring(control, center.x, center.y, depth, w/2*0.7, h/2*0.7, 0, 360, 8, style) + } + Control.child_add(color_wheel, color_wheel_overlay) + + return panel + + /* //setup colorspace buttons //todo: this should be a dropdown??? @@ -117,6 +221,8 @@ class ColorPicker{ //todo return root + + */ } static build_triangle_editor(ui: Entity, root: Control){ diff --git a/game.wren b/game.wren index e234758..1248d43 100644 --- a/game.wren +++ b/game.wren @@ -1,7 +1,7 @@ import "luxe: game" for Ready import "luxe: assets" for Assets import "luxe: input" for Input, Key -import "luxe: world" for World, Entity, Transform, Sprite, Values, Tags, Camera, UI +import "luxe: world" for World, Entity, Transform, Sprite, Values, Tags, Camera, UI, UILayoutMode import "luxe: math" for Math import "luxe: draw" for Draw import "luxe: io" for IO @@ -28,6 +28,7 @@ class Game is Ready { var ui = Entity.create(app.ui, "ui") UI.create(ui, 0, 0, app.width, app.height, 0, app.ui_camera) + UI.set_layout_mode(ui, UILayoutMode.flex) var picker = ColorPicker.create(ui) Control.set_pos(picker, 100, 100) } //ready diff --git a/materials/color_ring.material_basis.lx b/materials/color_ring.material_basis.lx new file mode 100644 index 0000000..90acc04 --- /dev/null +++ b/materials/color_ring.material_basis.lx @@ -0,0 +1,33 @@ +material_basis = { + vertex_format = "luxe.textured" + shaders = { + vertex = { library="materials/shaders" function="vert" } + fragment = { library="materials/shaders" function="frag_color_ring" } + } + depth_test = true + depth_write = true + depth_compare = "less_equal" + stencil_test = false + write_mask = { red=true green=true blue=true alpha=true } + blending = true + alpha_blend = "add" + rgb_blend = "add" + src_alpha = "source_alpha" + src_rgb = "source_alpha" + dest_alpha = "one_minus_source_alpha" + dest_rgb = "one_minus_source_alpha" + blend_color = [0 0 0 0] + cull = "none" + winding = "counter_clockwise" + layers = ["default"] + inputs = { + wheel.outer_distance = { + type = "float", + value = 0.5, + }, + wheel.inner_distance = { + type = "float", + value = 0.35, + } + } +} \ No newline at end of file diff --git a/materials/color_triangle.material_basis.lx b/materials/color_triangle.material_basis.lx new file mode 100644 index 0000000..b477259 --- /dev/null +++ b/materials/color_triangle.material_basis.lx @@ -0,0 +1,33 @@ +material_basis = { + vertex_format = "luxe.textured" + shaders = { + vertex = { library="materials/shaders" function="vert" } + fragment = { library="materials/shaders" function="frag_color_triangle" } + } + depth_test = true + depth_write = true + depth_compare = "less_equal" + stencil_test = false + write_mask = { red=true green=true blue=true alpha=true } + blending = true + alpha_blend = "add" + rgb_blend = "add" + src_alpha = "source_alpha" + src_rgb = "source_alpha" + dest_alpha = "one_minus_source_alpha" + dest_rgb = "one_minus_source_alpha" + blend_color = [0 0 0 0] + cull = "none" + winding = "counter_clockwise" + layers = ["default"] + inputs = { + triangle.hue = { + type = "float", + value = 0.5, + }, + triangle.size = { + type = "float", + value = 0.25, + } + } +} \ No newline at end of file diff --git a/materials/shaders.emsl b/materials/shaders.emsl new file mode 100644 index 0000000..b36a5a0 --- /dev/null +++ b/materials/shaders.emsl @@ -0,0 +1,102 @@ +input View { + mat4 mvp, + mat4 proj, + mat4 proj_inverse, + mat4 view, + mat4 world, //geometry.world atm + float2 fov, //fov.x, fov.y + float2 resolution, + float4 target_region, + float4 target_region_size +} + +input ColorWheel { + float outer_distance, + float inner_distance +} + +input ColorTriangle { + float hue, + float size +} + +stage vertex vert( + input { View view }, + vertex in { #0 float4 pos, #1 float4 color, #2 float2 uv }, + fragment out { float4 color, float2 uv}) +{ + out.color = in.color; + out.uv = in.uv; + + stage.pos = input.view.mvp * float4(in.pos.xyz, 1.0); +} + +stage fragment frag_color_ring( + input { ColorWheel wheel }, + fragment in { float4 color, float2 uv }) +{ + float TAU = 6.2831853071795864769252867665590057683943387987502116419498891846; + + float2 centered_coords = in.uv - 0.5; + float center_dist = length(centered_coords); + + float change = dFdx(in.uv.x); //this assumes the object is unrotated and uniformly scaled + float alpha = clamp(min(center_dist - input.wheel.inner_distance, -center_dist + input.wheel.outer_distance - change) / change, 0.0, 1.0); + + float angle = atan(centered_coords.y, centered_coords.x); + float normalized_angle = mod(angle / TAU + 0.25, 1.0); + float3 hue = hue2rgb(normalized_angle); + + float4 color = float4(float3(hue), alpha); + + if(color.a <= 0.0) { + discard; + return; + } + + stage.color[0] = color; +} + +stage fragment frag_color_triangle( + input { ColorTriangle triangle }, + fragment in { float4 color, float2 uv }) +{ + float TAU = 6.2831853071795864769252867665590057683943387987502116419498891846; + float cos30 = 0.866025403784438; //cos(30°) + + float2 centered_coords = in.uv - 0.5; + + float2 down = float2(0.0, -1.0); + float2 top_right = float2(cos30, 0.5); + float2 top_left = float2(-cos30, 0.5); + + float down_dist = dot(down, centered_coords); + float top_right_dist = dot(top_right, centered_coords); + float top_left_dist = dot(top_left, centered_coords); + float dist = min(min(top_right_dist, top_left_dist), down_dist); + dist += input.triangle.size; + + float change = length(float2(dFdx(in.uv.x), dFdy(in.uv.y))); //this assumes the object is uniformly scaled + float alpha = clamp(dist/change, 0.0, 1.0); + + float3 hue = hue2rgb(input.triangle.hue); + + float4 color = float4(float3(hue), alpha); + + if(color.a <= 0.0) { + discard; + return; + } + + stage.color[0] = color; +} + +float3 hue2rgb(float hue) { + hue = fract(hue); //only use fractional part + float r = abs(hue * 6.0 - 3.0) - 1.0; //red + float g = 2.0 - abs(hue * 6.0 - 2.0); //green + float b = 2.0 - abs(hue * 6.0 - 4.0); //blue + float3 rgb = float3(r,g,b); //combine components + rgb = clamp(rgb, 0.0, 1.0); //clamp between 0 and 1 + return rgb; +} \ No newline at end of file diff --git a/outline/inputs.input.lx b/outline/inputs.input.lx deleted file mode 100644 index dc45459..0000000 --- a/outline/inputs.input.lx +++ /dev/null @@ -1,23 +0,0 @@ -input = { - nodes = [ - { name = "ui" where = "front" channels = ["c01"] } - { name = "game" where = "after: ui" channels = ["c02"] } - ] - - map = { - left = { keys = ["key_a", "left"] } - right = { keys = ["key_d", "right"] } - up = { keys = ["key_w", "up"] } - down = { keys = ["key_s", "down"] } - jump = { - keys = ["key_x", "up", "key_w", "space"] - mouse = ["left"] - gamepad = [0] - } - - next = { - keys = ["key_x", "up", "key_w", "space", "enter", "escape"] - mouse = ["left", "right"] - } - } -} diff --git a/outline/settings.settings.lx b/outline/settings.settings.lx index 6a2169e..d9d74e3 100644 --- a/outline/settings.settings.lx +++ b/outline/settings.settings.lx @@ -15,5 +15,5 @@ engine = { depth = 24 } - ui.debug_vis = true + //ui.debug_vis = true } \ No newline at end of file diff --git a/project.modules.lx b/project.modules.lx index b793eaf..2ce9e6b 100644 --- a/project.modules.lx +++ b/project.modules.lx @@ -1,3 +1,3 @@ modules = { - luxe = "2021.0.2" + luxe = "dev" } //modules