scroll list and minor fixes

This commit is contained in:
Ronja 2020-10-11 17:22:57 +02:00
parent cbb2a4c405
commit 3ed3736631
10 changed files with 445 additions and 97 deletions

View file

@ -0,0 +1,3 @@
image = {
source = "assets/wip/Knob3.png"
}

BIN
Luxe/assets/wip/Knob3.png (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -16,8 +16,9 @@ import "blocks/ui/simple_text" for UISimpleText
import "blocks/ui/ui" for Ui import "blocks/ui/ui" for Ui
import "blocks/ui/slider" for UiSlider import "blocks/ui/slider" for UiSlider
import "blocks/ui/box" for UiBox import "blocks/ui/box" for UiBox
import "blocks/ui/scroll_box" for UiScrollBox
import "blocks/ui/compass" for UiCompass import "blocks/ui/compass" for UiCompass
import "blocks/ui/info" for UiInfo //this is a cyclic dependency waiting to happen import "blocks/ui/info" for UiInfo //this is a cyclic dependency waiting to happen...
import "math/math" for M import "math/math" for M
import "math/util" for Util import "math/util" for Util
import "blocks/human/human" for Human import "blocks/human/human" for Human
@ -197,10 +198,8 @@ class UiAdventure{
} }
} }
var list = UiBox.create(_ent) var list = UiScrollBox.create(_ent)
Control.child_add(page, list) Control.child_add(page, list)
Control.set_clip(list, true)
UILayout.set_contain(_ent, list, UILayoutContain.column | UILayoutContain.start) //|
UILayout.set_behave(_ent, list, UILayoutBehave.fill) UILayout.set_behave(_ent, list, UILayoutBehave.fill)
UILayout.set_margin(_ent, list, 40, 0, 0, 0) UILayout.set_margin(_ent, list, 40, 0, 0, 0)
@ -218,14 +217,16 @@ class UiAdventure{
if(!adventure) return if(!adventure) return
//rebuild //rebuild
var i = -1
adventure.adventurers.each{ |adventurer| adventure.adventurers.each{ |adventurer|
i = i + 1
var item = UiBox.create(_ent) var item = UiBox.create(_ent)
Control.child_add(list, item)
UILayout.set_behave(_ent, item, UILayoutBehave.left | UILayoutBehave.right)//| UILayout.set_behave(_ent, item, UILayoutBehave.left | UILayoutBehave.right)//|
UILayout.set_margin(_ent, item, 0, 0, 0, 0) UILayout.set_margin(_ent, item, 0, 0, 0, 0)
UILayout.set_contain(_ent, item, UILayoutContain.row | UILayoutContain.start)//| UILayout.set_contain(_ent, item, UILayoutContain.row | UILayoutContain.start)//|
Control.set_size(item, -1, 12) Control.set_size(item, -1, 12)
Control.set_allow_input(item, true) Control.set_allow_input(item, true)
Control.set_id(item, "Person %(i)")
Control.set_events(item) {|event| Control.set_events(item) {|event|
if(UI.event_cancelled(_ent, event.id)) return if(UI.event_cancelled(_ent, event.id)) return
if(event.type == UIEvent.press){ if(event.type == UIEvent.press){
@ -234,6 +235,7 @@ class UiAdventure{
_game.focus.value = adventurer _game.focus.value = adventurer
} }
} }
UiScrollBox.add(list, item)
var head = UIImage.create(_ent) var head = UIImage.create(_ent)
Control.child_add(item, head) Control.child_add(item, head)
@ -284,6 +286,82 @@ class UiAdventure{
_page.on_change(true){|val| _page.on_change(true){|val|
Control.set_visible(page, val == UiAdventure.resources) Control.set_visible(page, val == UiAdventure.resources)
} }
var list = UiScrollBox.create(_ent)
Control.child_add(page, list)
UILayout.set_behave(_ent, list, UILayoutBehave.fill)
UILayout.set_margin(_ent, list, 40, 0, 0, 0)
var tiny_head = Assets.image("assets/wip/8Head")
var x_image = Assets.image("assets/wip/8Cross")
_itemListItems = []
_game.adventures.planning.on_change(true) {|adventure|
//cleanup
_itemListItems.each{|item|
Control.destroy(item)
}
_itemListItems.clear()
if(!adventure) return
//rebuild
adventure.adventurers.each{ |adventurer|
var item = UiBox.create(_ent)
UILayout.set_behave(_ent, item, UILayoutBehave.left | UILayoutBehave.right)//|
UILayout.set_margin(_ent, item, 0, 0, 0, 0)
UILayout.set_contain(_ent, item, UILayoutContain.row | UILayoutContain.start)//|
Control.set_size(item, -1, 12)
Control.set_allow_input(item, true)
Control.set_events(item) {|event|
if(UI.event_cancelled(_ent, event.id)) return
if(event.type == UIEvent.press){
_ui.ui_mode = Ui.Info
_ui.info.page.value = UiInfo.human
_game.focus.value = adventurer
}
}
UiScrollBox.add(list, item)
var head = UIImage.create(_ent)
Control.child_add(item, head)
Control.set_size(head, 8, 8)
UIImage.set_image(head, tiny_head)
UIImage.set_color(head, Human.get_color(adventurer))
UILayout.set_margin(_ent, head, 2, 0, 0, 0)
var name = UILabel.create(_ent)
Control.child_add(item, name)
UILabel.set_align_vertical(name, TextAlign.bottom)
UILabel.set_font(name, _ui.font)
UILabel.set_text_size(name, 8)
UILabel.set_text(name, Human.get_name(adventurer))
UILayout.set_behave(_ent, name, UILayoutBehave.hfill | UILayoutBehave.left)//|
UILayout.set_margin(_ent, name, 2, 1, 0, 1)
var remove = ImageButton.create(_ent)
Control.child_add(item, remove)
Control.set_size(remove, 8, 8)
UIImage.set_image(remove, x_image)
UIImage.set_color(remove, Color.hex(0xec172a))
UILayout.set_margin(_ent, remove, 0, 0, 2, 0)
ImageButton.set_tooltip(remove, "remove")
ImageButton.set_state_change(remove) { |data, button|
if(data["press"]){
Frame.end{
Globals["Tooltip"].clear(button)
Util.remove(adventure.adventurers, adventurer)
_game.adventures.planning.emit()
}
}
}
_itemListItems.add(item)
}
UILayout.commit(_ent)
UI.commit(_ent)
}
} }
depart(){ depart(){

View file

@ -37,7 +37,7 @@ class UiCompass{
if(event.control != control) return if(event.control != control) return
if(event.type == UIEvent.press){ if(event.type == UIEvent.press){
var relative_pos = AABB.grow(AABB.new(x, y, w, h),[2, 2]).relative_pos([event.x, event.y]) var relative_pos = AABB.new(x, y, w, h).relative_pos([event.x, event.y])
var inside = M.length(relative_pos.map{|comp| comp - 0.5}) < 0.5 var inside = M.length(relative_pos.map{|comp| comp - 0.5}) < 0.5
if(inside) { if(inside) {
state["pressed"] = true state["pressed"] = true
@ -46,10 +46,11 @@ class UiCompass{
} else if(event.type == UIEvent.release){ } else if(event.type == UIEvent.release){
state["pressed"] = false state["pressed"] = false
UI.uncapture(control) UI.uncapture(control)
} else if(event.type == UIEvent.move && state["pressed"]){ }
if((event.type == UIEvent.move || event.type == event.type == UIEvent.press) && state["pressed"]){
var center = [x + w/2, y + h/2] var center = [x + w/2, y + h/2]
var diff = [event.x - center.x, event.y - center.y] var diff = [event.x - center.x, event.y - center.y]
var angle = Math.atan2(-diff.y, diff.x) - Num.tau / 4 var angle = Math.atan2(-diff.y, diff.x) - Num.tau / 4 //flip y because ui coordinates are different; shift by 1/4 circle constant to make up 0°
UI.events_emit(control, UIEvent.change, Math.degrees(angle)) UI.events_emit(control, UIEvent.change, Math.degrees(angle))
} }
} }

View file

@ -0,0 +1,151 @@
import "luxe: ui/control" for Control
import "luxe: world" for UI, World, UIEvent, UILayout, UILayoutContain, UILayoutBehave
import "luxe: draw" for PathStyle
import "luxe: assets" for Assets
import "luxe: render" for Image
import "luxe: math" for Math
import "luxe: game" for Frame
import "globals" for Globals
import "math/rect" for AABB
import "math/math" for M
import "blocks/debug" for DrawDebug
import "blocks/ui/slider" for UiSlider
class UiScrollBox{
static create(ent){
var box = Control.create(ent)
var style = PathStyle.new()
style.color = [1,1,1,1]
style.thickness = 1
Control.set_allow_input(box, true)
var state = {"style": style, "pressed": false, "position": 0, "scrollSpeed": 4, "ent": ent, "spring": 32, "contentHeight": 0}
Control.set_state_data(box, state)
var slider = UiSlider.create(ent)
Control.child_add(box, slider)
UiSlider.set_line(slider, false)
UiSlider.set_direction(slider, UiSlider.vertical)
UiSlider.set_handle(slider, Assets.image("assets/wip/Knob3"))
UiSlider.set_value(slider, 0)
Control.set_size(slider, 7, -1)
UILayout.set_behave(ent, slider, UILayoutBehave.right | UILayoutBehave.vfill) //|
UILayout.set_margin(ent, slider, 1, 0, 1, 0)
Control.set_events(slider){|event|
if(event.type == UIEvent.change){
var position = 0
var max_height = (Control.get_height(state["childContainer"]) + 1) - state["contentHeight"]
if(max_height < 0){
position = M.lerp(0, max_height, event.change)
}
set_position(box, position)
}
}
state["slider"] = slider
var childContainer = Control.create(ent)
Control.child_add(box, childContainer)
UILayout.set_behave(ent, childContainer, UILayoutBehave.fill)
UILayout.set_margin(ent, childContainer, 1, 1, 7, 2) //don't include border, fix bottom bug, freedom to side slider
Control.set_clip(childContainer, true)
UILayout.set_contain(ent, childContainer, UILayoutContain.column | UILayoutContain.start) //|
state["childContainer"] = childContainer
var alignmentChild = Control.create(ent)
Control.child_add(childContainer, alignmentChild)
Control.set_size(alignmentChild, -1, 0)
UILayout.set_behave(ent, alignmentChild, UILayoutBehave.hfill | UILayoutBehave.top) //| //make it wide
UILayout.set_margin(ent, alignmentChild, 0, state["position"], 0, 0) //pos will always be 0 here, but writing it explicitly makes intent clearer
state["alignmentChild"] = alignmentChild
Control.set_render(box) {|control, state, x, y, w, h|
var scrollPos = state["position"]
if(scrollPos >= 0.5){
scrollPos = M.lerp(scrollPos, 0, M.pow2(-state["spring"] * Globals["Delta"]))
set_position(control, scrollPos, true)
}
var container_height = Control.get_height(childContainer) + 1 //+1 is here to fix clip bug
var max_pos = Math.min(container_height - state["contentHeight"], 0)
if(scrollPos <= max_pos - 0.5){
scrollPos = M.lerp(scrollPos, max_pos, M.pow2(-state["spring"] * Globals["Delta"]))
set_position(control, scrollPos, true)
}
var depth = UI.draw_depth_of(control, 0)
UI.draw_rect(control, x+0.5, y+0.5, depth, w-1, h-1, 0, state["style"])
}
Control.set_process(box){|control, state, event, x, y, w, h|
if(event.control != control) return
if(event.type == UIEvent.scroll){
var scrollPos = state["position"]
scrollPos = scrollPos + event.y * state["scrollSpeed"] * -1
set_position(control, scrollPos)
}
}
return box
}
static add(box, child){
var state = Control.get_state_data(box)
Control.child_add(state["childContainer"], child)
var ent = state["ent"]
UILayout.commit(ent)
UI.commit(ent)
update_content(box)
}
static remove(box, child){
var state = Control.get_state_data(box)
Control.child_remove(state["childContainer"], child)
var ent = state["ent"]
UILayout.commit(ent)
UI.commit(ent)
update_content(box)
}
static update_content(box){
var state = Control.get_state_data(box)
var container = state["childContainer"]
var child_count = Control.child_count(container)
//if only the alignmentChild exists
if(child_count <= 1){
state["contentHeight"] = 0
return
}
var first_child = Control.child_get(container, 1)
var upper_bound = Control.get_pos_y_abs(first_child)
var last_child = Control.child_get(container, child_count - 1)
var lower_bound = Control.get_pos_y_abs(last_child) + Control.get_height(last_child)
state["contentHeight"] = lower_bound - upper_bound //its lower - upper because of the y-negative coordinates
}
static set_position(box, position) {
set_position(box, position, false)
}
static set_position(box, position, delay_commit){
var state = Control.get_state_data(box)
state["position"] = position
var ent = state["ent"]
UILayout.set_margin(ent, state["alignmentChild"], 0, position, 0, 0)
if(!delay_commit){
UILayout.commit(ent)
} else {
Frame.end{
UILayout.commit(ent)
}
}
var rel_pos = 0
var max_height = (Control.get_height(state["childContainer"]) + 1) - state["contentHeight"]
if(max_height < 0){
rel_pos = M.clamp(M.inv_lerp(0, max_height, position), 0, 1)
}
UiSlider.set_value(state["slider"], rel_pos)
}
}

View file

@ -11,25 +11,44 @@ import "math/math" for M
import "blocks/debug" for DrawDebug import "blocks/debug" for DrawDebug
class UiSlider{ class UiSlider{
static horizontal{"horiz"}
static vertical{"vert"}
static create(ent){ static create(ent){
var compass = Control.create(ent) var slider = Control.create(ent)
var style = PathStyle.new() var style = PathStyle.new()
style.color = [1,1,1,1] style.color = [1,1,1,1]
style.thickness = 1 style.thickness = 1
Control.set_allow_input(compass, true) Control.set_allow_input(slider, true)
Control.set_state_data(compass, {"value": 0.5, "style": style, "pressed": false}) //angle is in radians between 0 and tau var state = {"value": 0.2,
Control.set_render(compass) {|control, state, x, y, w, h| "style": style,
"pressed": false,
"centerLine": true,
"handle": Assets.image("assets/wip/SliderHandle"),
"direction": UiSlider.horizontal
}
Control.set_state_data(slider, state)
Control.set_render(slider) {|control, state, x, y, w, h|
var depth = UI.draw_depth_of(control, 0) var depth = UI.draw_depth_of(control, 0)
UI.draw_rect(control, x+0.5, y+0.5, depth, w-1, h-1, 0, state["style"]) UI.draw_rect(control, x+0.5, y+0.5, depth, w-1, h-1, 0, state["style"])
UI.draw_line(control, x + h/2, y + h/2, x + w - h/2, y + h/2, depth, state["style"]) if(state["centerLine"]) {
var image = Assets.image("assets/wip/SliderHandle") UI.draw_line(control, x + h/2, y + h/2, x + w - h/2, y + h/2, depth, state["style"])
}
var image = state["handle"]
var img_size = [Image.get_width(image), Image.get_height(image)] var img_size = [Image.get_width(image), Image.get_height(image)]
UI.draw_image(control, (M.lerp(x+h/2, x+w-h/2, state["value"]) - img_size.x/2).round, var slider_x
y+h/2-img_size.y/2, depth, img_size.x, img_size.y, var slider_y
if(state["direction"] == UiSlider.horizontal){
slider_x = (M.lerp(x+h/2, x+w-h/2, state["value"]) - img_size.x/2).round
slider_y = y+h/2-img_size.y/2
} else if(state["direction"] == UiSlider.vertical){
slider_x = x+w/2-img_size.x/2
slider_y = (M.lerp(y+w/2, y+h-w/2, state["value"]) - img_size.y/2).round
}
UI.draw_image(control, slider_x, slider_y, depth, img_size.x, img_size.y,
0, [1,1,1,1], [0, 0, 1, 1], image, true) 0, [1,1,1,1], [0, 0, 1, 1], image, true)
} }
Control.set_process(compass){|control, state, event, x,y,w,h| Control.set_process(slider){|control, state, event, x,y,w,h|
if(event.control != control) return if(event.control != control) return
if(event.type == UIEvent.press){ if(event.type == UIEvent.press){
state["pressed"] = true state["pressed"] = true
@ -37,14 +56,36 @@ class UiSlider{
} else if(event.type == UIEvent.release){ } else if(event.type == UIEvent.release){
state["pressed"] = false state["pressed"] = false
UI.uncapture(control) UI.uncapture(control)
} else if(event.type == UIEvent.move && state["pressed"]){ }
var abs_x = Control.get_pos_x_abs(control) if((event.type == UIEvent.move || event.type == UIEvent.press) && state["pressed"]){
var rel_pos = M.inv_lerp(abs_x+h/2, abs_x+w-h/2, event.x) var min
var max
var value
if(state["direction"] == UiSlider.horizontal){
var abs_x = Control.get_pos_x_abs(control)
min = abs_x+h/2
max = abs_x+w-h/2
value = event.x
} else if(state["direction"] == UiSlider.vertical){
var abs_y = Control.get_pos_y_abs(control)
min = abs_y+w/2
max = abs_y+h-w/2
value = event.y
}
var rel_pos = M.inv_lerp(min, max, value)
rel_pos = M.clamp(rel_pos, 0, 1) rel_pos = M.clamp(rel_pos, 0, 1)
UI.events_emit(control, UIEvent.change, rel_pos) UI.events_emit(control, UIEvent.change, rel_pos)
} }
} }
return compass
//default events impl, overridable!
Control.set_events(slider){|event|
if(event.type == UIEvent.change){
set_value(slider, event.change)
}
}
return slider
} }
static set_value(slider, value){ static set_value(slider, value){
@ -52,4 +93,18 @@ class UiSlider{
data["value"] = value data["value"] = value
} }
static set_line(slider, line){
var data = Control.get_state_data(slider)
data["centerLine"] = line
}
static set_direction(slider, direction){
var data = Control.get_state_data(slider)
data["direction"] = direction
}
static set_handle(slider, handle){
var data = Control.get_state_data(slider)
data["handle"] = handle
}
} }

View file

@ -42,6 +42,8 @@ class Game is Ready {
} //ready } //ready
tick(delta) { tick(delta) {
Globals["Delta"] = delta
var mouse_pos = Vector.new(Input.mouse_x(), Input.mouse_y()) var mouse_pos = Vector.new(Input.mouse_x(), Input.mouse_y())
var game_mouse = Globals["Renderer"].game_mouse(mouse_pos) var game_mouse = Globals["Renderer"].game_mouse(mouse_pos)
game_mouse = Camera.screen_point_to_world(app.camera, game_mouse.x, game_mouse.y) game_mouse = Camera.screen_point_to_world(app.camera, game_mouse.x, game_mouse.y)

View file

@ -26,6 +26,10 @@ class M{
return result return result
} }
static pow2(value){
return value * value
}
static lerp(from, to, value){ static lerp(from, to, value){
//if the range are numbers, we assume the interpolation value is too //if the range are numbers, we assume the interpolation value is too
if(from is Num && to is Num) return from + (to - from) * value if(from is Num && to is Num) return from + (to - from) * value

View file

@ -1,51 +1,44 @@
input View { input View {
mat4 mvp, mat4 mvp,
mat4 proj, mat4 proj,
mat4 proj_inverse, mat4 proj_inverse,
mat4 view, mat4 view,
mat4 world, //geometry.world atm mat4 world, //geometry.world atm
float2 fov, //fov.x, fov.y float2 fov, //fov.x, fov.y
float2 resolution, float2 resolution,
float4 target_region, float4 target_region,
float4 target_region_size float4 target_region_size
} }
input Font { input Font {
image2D pages[8] image2D pages[8]
}
input UI {
float2 canvas_size,
float textured,
float pixelated,
#0 image2D tex_mask,
#1 image2D tex,
#1 image2D tex_pixelated
} }
input FUI { input FUI {
float2 canvas_size, float2 canvas_size,
#0 image2D tex_mask #0 image2D tex_mask
} }
stage vertex vert( stage vertex vert(
input { View view }, input { View view, UI ui },
vertex in { vertex in {
#0 float4 pos, #0 float4 pos,
#1 float4 color, #1 float4 color,
#2 float2 uv, #2 float2 uv,
#3 float4 data #3 float4 data,
#4 float2 bounds
}, },
fragment out { fragment out {
float4 color,
float2 uv,
float2 mask_uv,
float clip
})
{
out.uv = in.uv;
out.color = in.color;
out.clip = in.data.x;
out.mask_uv = float2(in.data.y, 1.0 - in.data.z);
stage.pos = input.view.mvp * float4(in.pos.xyz, 1.0);
}
stage fragment frag(
input { FUI ui, Font font },
fragment in {
float4 color, float4 color,
float2 uv, float2 uv,
float2 mask_uv, float2 mask_uv,
@ -53,52 +46,99 @@ stage fragment frag(
float page float page
}) })
{ {
//outside mask uvs? out.uv = in.uv;
bool outside_mask = in.mask_uv.x < 0.0 || out.color = in.color;
in.mask_uv.x > 1.0 || out.clip = in.data.x;
in.mask_uv.y < 0.0 || out.page = in.pos.w;
in.mask_uv.y > 1.0;
if(outside_mask) {
discard;
// stage.color[0] = float4(1,0.2,0.3,1);
return;
}
float4 mask = texture(input.ui.tex_mask, in.mask_uv); //for fonts the text pos is text local and needs to be converted to canvas space.
//we pass the bounds of the text item in via the vertices (I know) so we can calculate bounds-local space.
//Then we convert that to canvas space, by * (bounds_w/canvas_w)
//Then finally we add to the canvas local u/v from the data.z/w in
//16 bit mask float bounds_u = 0.0;
// float mask_clip = round(mask.r * 65535.0); float bounds_v = 0.0;
//8 bit mask if(in.bounds.x != 0.0) { bounds_u = (in.pos.x / in.bounds.x); }
int lsb = int(round(mask.r * 255.0)); if(in.bounds.y != 0.0) { bounds_v = 1.0 - (in.pos.y / in.bounds.y); }
int msb = int(round(mask.g * 255.0)) * 256; //<< 8;
int mask_clip = lsb | msb;
// int mask_clip = lsb + msb; //webgl1
//outside our clipping bounds? //bounds_u/v is the 0...1 value for where this vert is in the text item itself
int my_clip = int(in.clip); //Note we flip the bounds_v because the text is in world space, bottom left origin, y+ up,
if(my_clip != 0 && mask_clip < my_clip) { //canvas space is top left, y+ down so it has to be reversed to be bounds local in canvas space
discard;
// stage.color[0] = float4(0,1,1,0.2);
return;
}
int page = int(in.page); float to_canvas_u = in.bounds.x / input.ui.canvas_size.x;
float4 msdf_sample = texture(input.font.pages[0], in.uv.xy); float to_canvas_v = in.bounds.y / input.ui.canvas_size.y;
if(page == 1) { msdf_sample = texture(input.font.pages[1], in.uv.xy); }
if(page == 2) { msdf_sample = texture(input.font.pages[2], in.uv.xy); }
if(page == 3) { msdf_sample = texture(input.font.pages[3], in.uv.xy); }
if(page == 4) { msdf_sample = texture(input.font.pages[4], in.uv.xy); }
if(page == 5) { msdf_sample = texture(input.font.pages[5], in.uv.xy); }
if(page == 6) { msdf_sample = texture(input.font.pages[6], in.uv.xy); }
if(page == 7) { msdf_sample = texture(input.font.pages[7], in.uv.xy); }
float r = msdf_sample.r; //to_canvas_u is the text item width and height in 0...1 relative to the canvas
float g = msdf_sample.g; //e.g if the text item width is 100 and the canvas is 200, the value is 0.5
float b = msdf_sample.b;
float median = max(min(r, g), min(max(r, g), b));
float opacity = float(median > 0.5);
stage.color[0] = float4(in.color.rgb, in.color.a * opacity * mask.a); //then what we do is add in.data.y/z which is the text x/y position,
//but relative to the canvas space. we add this to shift the x/y into place.
} bounds_u = (bounds_u * to_canvas_u) + in.data.y;
bounds_v = (bounds_v * to_canvas_v) + in.data.z;
out.mask_uv = float2(bounds_u, bounds_v);
stage.pos = input.view.mvp * float4(in.pos.xyz, 1.0);
}
stage fragment frag(
input { FUI ui, Font font },
fragment in {
float4 color,
float2 uv,
float2 mask_uv,
float clip,
float page
})
{
//outside mask uvs?
bool outside_mask = in.mask_uv.x < 0.0 ||
in.mask_uv.x > 1.0 ||
in.mask_uv.y < 0.0 ||
in.mask_uv.y > 1.0;
if(outside_mask) {
discard;
// stage.color[0] = float4(1,0.2,0.3,1);
return;
}
float4 mask = texture(input.ui.tex_mask, in.mask_uv);
//16 bit mask
// float mask_clip = round(mask.r * 65535.0);
//8 bit mask
int lsb = int(round(mask.r * 255.0));
int msb = int(round(mask.g * 255.0)) * 256; //<< 8;
int mask_clip = lsb | msb;
// int mask_clip = lsb + msb; //webgl1
//outside our clipping bounds?
int my_clip = int(in.clip);
if(my_clip != 0 && mask_clip < my_clip) {
discard;
// stage.color[0] = float4(0,1,1,0.2);
return;
}
int page = int(in.page);
float4 msdf_sample = texture(input.font.pages[0], in.uv.xy);
if(page == 1) { msdf_sample = texture(input.font.pages[1], in.uv.xy); }
if(page == 2) { msdf_sample = texture(input.font.pages[2], in.uv.xy); }
if(page == 3) { msdf_sample = texture(input.font.pages[3], in.uv.xy); }
if(page == 4) { msdf_sample = texture(input.font.pages[4], in.uv.xy); }
if(page == 5) { msdf_sample = texture(input.font.pages[5], in.uv.xy); }
if(page == 6) { msdf_sample = texture(input.font.pages[6], in.uv.xy); }
if(page == 7) { msdf_sample = texture(input.font.pages[7], in.uv.xy); }
float r = msdf_sample.r;
float g = msdf_sample.g;
float b = msdf_sample.b;
float median = max(min(r, g), min(max(r, g), b));
float opacity = float(median > 0.5);
stage.color[0] = float4(in.color.rgb, in.color.a * opacity * mask.a);
}

View file

@ -1,11 +1,11 @@
material_basis = { material_basis = {
vertex_format = "luxe.textured" vertex_format = "luxe.ui_font"
shaders = { shaders = {
vertex = { library="shaders/pixel_text_ui" function="vert" } vertex = { library="shaders/pixel_text_ui" function="vert" }
fragment = { library="shaders/pixel_text_ui" function="frag" } fragment = { library="shaders/pixel_text_ui" function="frag" }
} }
depth_test = true depth_test = true
depth_write = false depth_write = true
depth_compare = "less_equal" depth_compare = "less_equal"
stencil_test = false stencil_test = false
write_mask = { red=true green=true blue=true alpha=true } write_mask = { red=true green=true blue=true alpha=true }
@ -21,6 +21,13 @@ material_basis = {
winding = "counter_clockwise" winding = "counter_clockwise"
layers = ["default"] layers = ["default"]
inputs = { inputs = {
ui.tex_mask = {
type = "image"
value = {
type = "image2D"
sampler_state = "nearest_clamp"
}
}
font.pages = { font.pages = {
type = "image" type = "image"
count = 8 count = 8
@ -59,5 +66,9 @@ material_basis = {
} }
] ]
} }
ui.canvas_size = {
type = "float2"
value = [1 1]
}
} }
} }