scroll list and minor fixes
This commit is contained in:
parent
cbb2a4c405
commit
3ed3736631
10 changed files with 445 additions and 97 deletions
3
Luxe/assets/wip/Knob3.image.lx
Normal file
3
Luxe/assets/wip/Knob3.image.lx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
image = {
|
||||
source = "assets/wip/Knob3.png"
|
||||
}
|
||||
BIN
Luxe/assets/wip/Knob3.png
(Stored with Git LFS)
Normal file
BIN
Luxe/assets/wip/Knob3.png
(Stored with Git LFS)
Normal file
Binary file not shown.
|
|
@ -16,8 +16,9 @@ import "blocks/ui/simple_text" for UISimpleText
|
|||
import "blocks/ui/ui" for Ui
|
||||
import "blocks/ui/slider" for UiSlider
|
||||
import "blocks/ui/box" for UiBox
|
||||
import "blocks/ui/scroll_box" for UiScrollBox
|
||||
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/util" for Util
|
||||
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.set_clip(list, true)
|
||||
UILayout.set_contain(_ent, list, UILayoutContain.column | UILayoutContain.start) //|
|
||||
UILayout.set_behave(_ent, list, UILayoutBehave.fill)
|
||||
UILayout.set_margin(_ent, list, 40, 0, 0, 0)
|
||||
|
||||
|
|
@ -218,14 +217,16 @@ class UiAdventure{
|
|||
if(!adventure) return
|
||||
|
||||
//rebuild
|
||||
var i = -1
|
||||
adventure.adventurers.each{ |adventurer|
|
||||
i = i + 1
|
||||
var item = UiBox.create(_ent)
|
||||
Control.child_add(list, item)
|
||||
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_id(item, "Person %(i)")
|
||||
Control.set_events(item) {|event|
|
||||
if(UI.event_cancelled(_ent, event.id)) return
|
||||
if(event.type == UIEvent.press){
|
||||
|
|
@ -234,6 +235,7 @@ class UiAdventure{
|
|||
_game.focus.value = adventurer
|
||||
}
|
||||
}
|
||||
UiScrollBox.add(list, item)
|
||||
|
||||
var head = UIImage.create(_ent)
|
||||
Control.child_add(item, head)
|
||||
|
|
@ -284,6 +286,82 @@ class UiAdventure{
|
|||
_page.on_change(true){|val|
|
||||
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(){
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class UiCompass{
|
|||
if(event.control != control) return
|
||||
|
||||
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
|
||||
if(inside) {
|
||||
state["pressed"] = true
|
||||
|
|
@ -46,10 +46,11 @@ class UiCompass{
|
|||
} else if(event.type == UIEvent.release){
|
||||
state["pressed"] = false
|
||||
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 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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
151
Luxe/blocks/ui/scroll_box.wren
Normal file
151
Luxe/blocks/ui/scroll_box.wren
Normal 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -11,25 +11,44 @@ import "math/math" for M
|
|||
import "blocks/debug" for DrawDebug
|
||||
|
||||
class UiSlider{
|
||||
static horizontal{"horiz"}
|
||||
static vertical{"vert"}
|
||||
|
||||
static create(ent){
|
||||
var compass = Control.create(ent)
|
||||
var slider = Control.create(ent)
|
||||
var style = PathStyle.new()
|
||||
style.color = [1,1,1,1]
|
||||
style.thickness = 1
|
||||
Control.set_allow_input(compass, true)
|
||||
Control.set_state_data(compass, {"value": 0.5, "style": style, "pressed": false}) //angle is in radians between 0 and tau
|
||||
Control.set_render(compass) {|control, state, x, y, w, h|
|
||||
Control.set_allow_input(slider, true)
|
||||
var state = {"value": 0.2,
|
||||
"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)
|
||||
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"])
|
||||
var image = Assets.image("assets/wip/SliderHandle")
|
||||
if(state["centerLine"]) {
|
||||
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)]
|
||||
UI.draw_image(control, (M.lerp(x+h/2, x+w-h/2, state["value"]) - img_size.x/2).round,
|
||||
y+h/2-img_size.y/2, depth, img_size.x, img_size.y,
|
||||
var slider_x
|
||||
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)
|
||||
}
|
||||
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.type == UIEvent.press){
|
||||
state["pressed"] = true
|
||||
|
|
@ -37,14 +56,36 @@ class UiSlider{
|
|||
} else if(event.type == UIEvent.release){
|
||||
state["pressed"] = false
|
||||
UI.uncapture(control)
|
||||
} else if(event.type == UIEvent.move && state["pressed"]){
|
||||
var abs_x = Control.get_pos_x_abs(control)
|
||||
var rel_pos = M.inv_lerp(abs_x+h/2, abs_x+w-h/2, event.x)
|
||||
}
|
||||
if((event.type == UIEvent.move || event.type == UIEvent.press) && state["pressed"]){
|
||||
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)
|
||||
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){
|
||||
|
|
@ -52,4 +93,18 @@ class UiSlider{
|
|||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -42,6 +42,8 @@ class Game is Ready {
|
|||
} //ready
|
||||
|
||||
tick(delta) {
|
||||
Globals["Delta"] = delta
|
||||
|
||||
var mouse_pos = Vector.new(Input.mouse_x(), Input.mouse_y())
|
||||
var game_mouse = Globals["Renderer"].game_mouse(mouse_pos)
|
||||
game_mouse = Camera.screen_point_to_world(app.camera, game_mouse.x, game_mouse.y)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ class M{
|
|||
return result
|
||||
}
|
||||
|
||||
static pow2(value){
|
||||
return value * value
|
||||
}
|
||||
|
||||
static lerp(from, to, value){
|
||||
//if the range are numbers, we assume the interpolation value is too
|
||||
if(from is Num && to is Num) return from + (to - from) * value
|
||||
|
|
|
|||
|
|
@ -1,51 +1,44 @@
|
|||
|
||||
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
|
||||
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 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 {
|
||||
float2 canvas_size,
|
||||
#0 image2D tex_mask
|
||||
float2 canvas_size,
|
||||
#0 image2D tex_mask
|
||||
}
|
||||
|
||||
stage vertex vert(
|
||||
input { View view },
|
||||
stage vertex vert(
|
||||
input { View view, UI ui },
|
||||
vertex in {
|
||||
#0 float4 pos,
|
||||
#1 float4 color,
|
||||
#2 float2 uv,
|
||||
#3 float4 data
|
||||
#3 float4 data,
|
||||
#4 float2 bounds
|
||||
},
|
||||
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,
|
||||
float2 uv,
|
||||
float2 mask_uv,
|
||||
|
|
@ -53,52 +46,99 @@ stage fragment frag(
|
|||
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;
|
||||
}
|
||||
out.uv = in.uv;
|
||||
out.color = in.color;
|
||||
out.clip = in.data.x;
|
||||
out.page = in.pos.w;
|
||||
|
||||
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 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
|
||||
float bounds_u = 0.0;
|
||||
float bounds_v = 0.0;
|
||||
if(in.bounds.x != 0.0) { bounds_u = (in.pos.x / in.bounds.x); }
|
||||
if(in.bounds.y != 0.0) { bounds_v = 1.0 - (in.pos.y / in.bounds.y); }
|
||||
|
||||
//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;
|
||||
}
|
||||
//bounds_u/v is the 0...1 value for where this vert is in the text item itself
|
||||
//Note we flip the bounds_v because the text is in world space, bottom left origin, y+ up,
|
||||
//canvas space is top left, y+ down so it has to be reversed to be bounds local in canvas space
|
||||
|
||||
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 to_canvas_u = in.bounds.x / input.ui.canvas_size.x;
|
||||
float to_canvas_v = in.bounds.y / input.ui.canvas_size.y;
|
||||
|
||||
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);
|
||||
//to_canvas_u is the text item width and height in 0...1 relative to the canvas
|
||||
//e.g if the text item width is 100 and the canvas is 200, the value is 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);
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
material_basis = {
|
||||
vertex_format = "luxe.textured"
|
||||
vertex_format = "luxe.ui_font"
|
||||
shaders = {
|
||||
vertex = { library="shaders/pixel_text_ui" function="vert" }
|
||||
fragment = { library="shaders/pixel_text_ui" function="frag" }
|
||||
}
|
||||
depth_test = true
|
||||
depth_write = false
|
||||
depth_write = true
|
||||
depth_compare = "less_equal"
|
||||
stencil_test = false
|
||||
write_mask = { red=true green=true blue=true alpha=true }
|
||||
|
|
@ -21,6 +21,13 @@ material_basis = {
|
|||
winding = "counter_clockwise"
|
||||
layers = ["default"]
|
||||
inputs = {
|
||||
ui.tex_mask = {
|
||||
type = "image"
|
||||
value = {
|
||||
type = "image2D"
|
||||
sampler_state = "nearest_clamp"
|
||||
}
|
||||
}
|
||||
font.pages = {
|
||||
type = "image"
|
||||
count = 8
|
||||
|
|
@ -59,5 +66,9 @@ material_basis = {
|
|||
}
|
||||
]
|
||||
}
|
||||
ui.canvas_size = {
|
||||
type = "float2"
|
||||
value = [1 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue