require( "globals" ) local controls = require( "controls" ) local skin = require( "skin" ) local settings = require( "settings" ) local util = require( "util" ) local mouse = require( "mouse" ) local render = {} local scroll_title_amount = 0 function render.stack( pass ) pass:draw( skin.mdl_amp, 0, 0, 0 ) if settings.session.show_eq then pass:draw( skin.mdl_eq, 0, 0, 0 ) end if settings.session.show_pl then pass:draw( skin.mdl_pl, 0, 0, 0 ) end pass:draw( skin.mdl_case, 0, 0, 0 ) if settings.session.show_eq or settings.session.show_pl then pass:draw( skin.mdl_case, 0, -metrics.case_height - metrics.feet_height, 0 ) end if settings.session.show_eq and settings.session.show_pl then pass:draw( skin.mdl_case, 0, 2 * (0 - metrics.case_height) - (2 * metrics.feet_height), 0 ) end -- color LEDs if settings.session.show_eq then render.led( pass, "amp_led_eq" ) end if settings.session.show_pl then render.led( pass, "amp_led_pl" ) end if winamp_info.shuffle == 1 then render.led( pass, "amp_led_shuffle" ) end if winamp_info.repeatval == 1 then render.led( pass, "amp_led_repeat" ) end if winamp_info.eq_on == 1 and settings.session.show_eq then render.led( pass, "eq_led_on" ) end if winamp_info.eq_auto == 1 and settings.session.show_eq then render.led( pass, "eq_led_auto" ) end -- color sliders render.tint_slider( pass, "amp_slider_vol", "amp_slider_vol_bg" ) render.tint_slider( pass, "amp_slider_pan", "amp_slider_pan_bg" ) if settings.session.show_eq then render.tint_slider( pass, "eq_slider_preamp", "eq_slider_preamp_bg" ) for i = 1, 10 do local eq_slider = "eq_slider_" .. i local eq_slider_bg = "eq_slider_" .. i .. "_bg" render.tint_slider( pass, eq_slider, eq_slider_bg ) end end -- small lcds render.small_lcd( pass, "amp_lcd_khz_bg", winamp_info.track_khz ) render.small_lcd( pass, "amp_lcd_kbps_bg", winamp_info.track_kbps ) -- track lcd render.current_track_lcd( pass ) -- mono/stereo labels font:setPixelDensity( metrics.pixel_density_monostereo ) local col1, col2 = { 0.8, 0.8, 0.8 }, { 0, 1, 0 } if winamp_info.mono_stereo == 1 then col1, col2 = col2, col1 end render.label( pass, "amp_label_mono", col1, "mono" ) render.label( pass, "amp_label_stereo", col2, "stereo" ) -- amp main lcd render.amp_main_lcd( pass ) -- playlist main lcd if settings.session.show_pl then render.playlist_main_lcd( pass ) end -- playlist total time if settings.session.show_pl then render.playlist_lcd_total_time( pass ) end -- eq graph if settings.session.show_eq then render.eq_graph( pass ) end -- playlist presets lcd if settings.session.show_eq then render.presets_list_lcd( pass ) end end function render.led( pass, led_id ) local parent = controls[ led_id ].parent local x, y, z = controls[ led_id ].pos:unpack() local px, py, pz = controls[ parent ].model:getNodePosition( parent ) local dx, dy, dz = controls[ led_id ].dim:unpack() pass:setColor( 0, 1, 0 ) pass:plane( x, y, pz + 0.0026, dx, dy ) end function render.tint_slider( pass, slider_id, bg_id ) local min = controls[ slider_id ].min local max = controls[ slider_id ].max local val, y, z = controls[ slider_id ].model:getNodePosition( slider_id ) if controls[ slider_id ].type == "slider_v" then x, val, z = controls[ slider_id ].model:getNodePosition( slider_id ) end local t = (val - min) / (max - min) if slider_id == "amp_slider_pan" then t = math.abs( 2 * t - 1 ); end local R = t local G = 1 - t local B = 0 local x, y, z = controls[ bg_id ].pos:unpack() local dx, dy, dz = controls[ bg_id ].dim:unpack() pass:setColor( R, G, B, 1 ) pass:plane( x, y, z + 0.001, dx, dy ) pass:setColor( 1, 1, 1, 1 ) end function render.set_environment( pass ) pass:setFaceCull( "back" ) pass:setShader( shader ) pass:send( "cubemap", environmentMap ) pass:send( "sphericalHarmonics", sphericalHarmonics ) end function render.set_environment_with_background( pass ) pass:setShader( "fill" ) pass:setDepthWrite( false ) pass:fill( backgrounds[ backgrounds.active_idx ] ) pass:setDepthWrite( true ) pass:setFaceCull( "back" ) pass:setShader( shader ) pass:send( "cubemap", environmentMap ) pass:send( "sphericalHarmonics", sphericalHarmonics ) end function render.small_lcd( pass, id, info ) font:setPixelDensity( metrics.pixel_density_small_lcd ) local x, y, z = controls[ id ].pos:unpack() local dx, dy, dz = controls[ id ].dim:unpack() local tex = controls[ id ].tex local tex_pass = controls[ id ].pass tex_pass:reset() tex_pass:setProjection( 1, mat4():orthographic( tex_pass:getDimensions() ) ) tex_pass:setColor( 0, 1, 0, 1 ) local tdx, tdy = tex:getDimensions() tex_pass:text( tostring( info ), tdx / 2, tdy / 2, 0, 1 ) pass:setMaterial( tex ) pass:plane( x, y, z + 0.001, dx, dy ) pass:setMaterial() passes[ #passes + 1 ] = tex_pass end function render.playlist_lcd_total_time( pass ) font:setPixelDensity( metrics.pixel_density_track_list ) local x, y, z = controls[ "pl_lcd_total_time" ].pos:unpack() local dx, dy, dz = controls[ "pl_lcd_total_time" ].dim:unpack() local tex = controls[ "pl_lcd_total_time" ].tex local tex_pass = controls[ "pl_lcd_total_time" ].pass tex_pass:reset() tex_pass:setProjection( 1, mat4():orthographic( tex_pass:getDimensions() ) ) tex_pass:setColor( 0, 1, 0, 1 ) local tdx, tdy = tex:getDimensions() local track_secs = winamp_info.cur_track_length_secs if track_secs < 10 then track_secs = "0" .. track_secs end local pl_hours = playlist.total_time_hours local pl_mins = playlist.total_time_mins if pl_mins < 10 then pl_mins = "0" .. pl_mins end local pl_secs = playlist.total_time_secs if pl_secs < 10 then pl_secs = "0" .. pl_secs end local str = winamp_info.cur_track_length_mins .. ":" .. track_secs .. "/" .. pl_hours .. ":" .. pl_mins .. ":" .. pl_secs local str_len = font:getWidth( str ) tex_pass:text( str, str_len / 2, tdy / 2, 0, 1 ) pass:setMaterial( tex ) pass:plane( x, y, z + 0.001, dx, dy ) pass:setMaterial() passes[ #passes + 1 ] = tex_pass end function render.playlist_lcd_track_time( pass, str ) font:setPixelDensity( metrics.pixel_density_track_list ) local x, y, z = controls[ "pl_lcd_track_time" ].pos:unpack() local dx, dy, dz = controls[ "pl_lcd_track_time" ].dim:unpack() local tex = controls[ "pl_lcd_track_time" ].tex local tex_pass = controls[ "pl_lcd_track_time" ].pass tex_pass:reset() tex_pass:setProjection( 1, mat4():orthographic( tex_pass:getDimensions() ) ) tex_pass:setColor( 0, 1, 0, 1 ) local tdx, tdy = tex:getDimensions() local str_len = font:getWidth( str ) tex_pass:text( str, tdx - (str_len / 2), tdy / 2, 0, 1 ) pass:setMaterial( tex ) pass:plane( x, y, z + 0.001, dx, dy ) pass:setMaterial() passes[ #passes + 1 ] = tex_pass end function render.current_track_lcd( pass ) local x, y, z = controls[ "amp_lcd_info_bg" ].pos:unpack() local dx, dy, dz = controls[ "amp_lcd_info_bg" ].dim:unpack() local tex = controls[ "amp_lcd_info_bg" ].tex local tex_pass = controls[ "amp_lcd_info_bg" ].pass tex_pass:reset() tex_pass:setProjection( 1, mat4():orthographic( tex_pass:getDimensions() ) ) tex_pass:setColor( 0, 1, 0, 1 ) local tdx, tdy = tex:getDimensions() font:setPixelDensity( metrics.pixel_density_title ) local w = font:getWidth( winamp_info.cur_track_title .. " *** " ) if w > tdx then if timer_scroll_title:get_elapsed() > 0.2 then timer_scroll_title:reset() if scroll_title_amount > w then scroll_title_amount = 0 end scroll_title_amount = scroll_title_amount + 30 end tex_pass:text( winamp_info.cur_track_title .. " *** ", (w / 2) - scroll_title_amount, tdy / 2, 0, 1 ) tex_pass:text( winamp_info.cur_track_title .. " *** ", (w + (w / 2)) - scroll_title_amount, tdy / 2, 0, 1 ) else scroll_title_amount = 0 tex_pass:text( winamp_info.cur_track_title, (w / 2) - scroll_title_amount, tdy / 2, 0, 1 ) end pass:setMaterial( tex ) pass:plane( x, y, z + 0.001, dx, dy ) pass:setMaterial() passes[ #passes + 1 ] = tex_pass end function render.amp_main_lcd( pass ) local x, y, z = controls[ "amp_lcd_main_bg" ].pos:unpack() local dx, dy, dz = controls[ "amp_lcd_main_bg" ].dim:unpack() local tex = controls[ "amp_lcd_main_bg" ].tex local tex_pass = controls[ "amp_lcd_main_bg" ].pass tex_pass:reset() tex_pass:setSampler( "nearest" ) tex_pass:setProjection( 1, mat4():orthographic( tex_pass:getDimensions() ) ) tex_pass:setColor( 0, 1, 0, 1 ) tex_pass:plane( 250, 30, 0, 12, 4 ) tex_pass:plane( 250, 46, 0, 12, 4 ) -- playing = 1, paused = 3, stopped = 0 if winamp_info.playing_state == 0 then tex_pass:plane( 78, 38, 0, 20, 20 ) pass:draw( tex, x, y, z + 0.001, dx ) elseif winamp_info.playing_state == 3 then tex_pass:plane( 66, 38, 0, 12, 20 ) tex_pass:plane( 90, 38, 0, 12, 20 ) pass:draw( tex, x, y, z + 0.001, dx ) elseif winamp_info.playing_state == 1 then tex_pass:plane( 58, 26, 0, 12, 12 ) tex_pass:polygon( 78, 21, 0, 95, 38, 0, 78, 55, 0 ) tex_pass:setColor( 0.31, 0.06, 0, 1 ) tex_pass:plane( 58, 50, 0, 12, 12 ) pass:draw( tex, x, y, z + 0.001, dx ) end if winamp_info.playing_state == 1 or winamp_info.playing_state == 3 then -- render bars and peaks local xoff = 58 local yoff = 0 local peaks_y = 0 for i = 1, 19 do local val = skin.sa_bars.data[ i ] if val > 0 then tex_pass:setColor( 1, 1, 1, 1 ) tex_pass:setMaterial( skin.sa_bars[ val ] ) tex_pass:plane( xoff, 116, 0, 12, 56 ) end peaks_y = skin.sa_bars.peaks[ i ].y if peaks_y > 0 then yoff = util.map_range( 1, 14, 144, 88, peaks_y ) tex_pass:setMaterial() tex_pass:setColor( 0.59, 0.59, 0.59, 1 ) tex_pass:plane( xoff, yoff, 0, 12, 4 ) end xoff = xoff + 16 end -- track time local do_draw = true if winamp_info.playing_state == 3 and timer_flash_time:get_elapsed() > 1 then do_draw = false end if timer_flash_time:get_elapsed() > 2 then timer_flash_time:reset() do_draw = true end if do_draw then tex_pass:setColor( 1, 1, 1, 1 ) local mins = winamp_info.time_display_mode == 0 and winamp_info.elapsed_mins or winamp_info.remaining_mins local secs = winamp_info.time_display_mode == 0 and winamp_info.elapsed_secs or winamp_info.remaining_secs local dg1, dg2, dg3, dg4 = 0, 0, 0, 0 if mins > 9 then dg1 = math.floor( mins / 10 ) dg2 = mins % 10 else dg2 = mins end if secs > 9 then dg3 = math.floor( secs / 10 ) dg4 = secs % 10 else dg4 = secs end tex_pass:setMaterial( skin.digits[ dg1 + 1 ] ) tex_pass:plane( 166, 38, 0, 36, 52 ) tex_pass:setMaterial( skin.digits[ dg2 + 1 ] ) tex_pass:plane( 214, 38, 0, 36, 52 ) tex_pass:setMaterial( skin.digits[ dg3 + 1 ] ) tex_pass:plane( 286, 38, 0, 36, 52 ) tex_pass:setMaterial( skin.digits[ dg4 + 1 ] ) tex_pass:plane( 334, 38, 0, 36, 52 ) local str = dg1 .. dg2 .. ":" .. dg3 .. dg4 if winamp_info.time_display_mode == 1 then tex_pass:setMaterial( skin.digits[ 11 ] ) tex_pass:plane( 118, 38, 0, 36, 52 ) str = "-" .. str end -- avoid recalculating digits and render pl track time too (lame but still) if settings.session.show_pl then render.playlist_lcd_track_time( pass, str ) end tex_pass:setMaterial() end end passes[ #passes + 1 ] = tex_pass end function render.label( pass, id, col, str ) local x, y, z = controls[ id ].pos:unpack() local dx, dy, dz = controls[ id ].dim:unpack() local tex = controls[ id ].tex local tex_pass = controls[ id ].pass tex_pass:reset() tex_pass:setProjection( 1, mat4():orthographic( tex_pass:getDimensions() ) ) tex_pass:setColor( col ) local tdx, tdy = tex:getDimensions() tex_pass:text( str, tdx / 2, tdy / 2, 0, 1 ) pass:setShader() pass:draw( tex, x, y, z + 0.001, dx ) passes[ #passes + 1 ] = tex_pass end function render.eq_graph( pass ) local x, y, z = controls[ "eq_graph" ].pos:unpack() local dx, dy, dz = controls[ "eq_graph" ].dim:unpack() local tex = controls[ "eq_graph" ].tex local tex_pass = controls[ "eq_graph" ].pass tex_pass:reset() tex_pass:setSampler( "nearest" ) tex_pass:setProjection( 1, mat4():orthographic( tex_pass:getDimensions() ) ) tex_pass:setColor( 0.73, 0.8, 0.87, 1 ) -- preamp line local pval = util.map_range( 63, 0, 2, 74, winamp_info.eq_preamp ) tex_pass:line( 0, pval - 1, 0, 452, pval - 1, 0 ) tex_pass:line( 0, pval + 0, 0, 452, pval + 0, 0 ) tex_pass:line( 0, pval + 1, 0, 452, pval + 1, 0 ) -- eq graph local c = curve:render( 64, 0, 1 ) for i = 1, (#c / 3) - 1 do local val = c[ (i - 1) * 3 + 2 ] local t = 1 - ((val - 2) / (73 - 0)) local R = t local G = 1 - t local B = 0 local p1 = (i - 1) * 3 + 1 local p2 = i * 3 + 1 local x1, y1 = c[ p1 ], c[ p1 + 1 ] local x2, y2 = c[ p2 ], c[ p2 + 1 ] tex_pass:setColor( R, G, B ) tex_pass:line( x1, y1 - 2, 0, x2, y2 - 2, 0 ) tex_pass:line( x1, y1 - 1, 0, x2, y2 - 1, 0 ) tex_pass:line( x1, y1 + 0, 0, x2, y2 + 0, 0 ) tex_pass:line( x1, y1 + 1, 0, x2, y2 + 1, 0 ) tex_pass:line( x1, y1 + 2, 0, x2, y2 + 2, 0 ) end pass:setShader() pass:draw( tex, x, y, z + 0.001, dx ) passes[ #passes + 1 ] = tex_pass end function render.playlist_main_lcd( pass ) local x, y, z = controls[ "pl_lcd_list" ].pos:unpack() local dx, dy, dz = controls[ "pl_lcd_list" ].dim:unpack() local tex = controls[ "pl_lcd_list" ].tex local tex_pass = controls[ "pl_lcd_list" ].pass font:setPixelDensity( metrics.pixel_density_track_list ) tex_pass:reset() tex_pass:setSampler( "linear" ) tex_pass:setProjection( 1, mat4():orthographic( tex_pass:getDimensions() ) ) local tdx, tdy = tex:getDimensions() if #playlist.tracks > metrics.num_visible_tracks then local max_scroll = ((#playlist.tracks - metrics.num_visible_tracks) * metrics.track_height_pixels) local item_y = -(playlist.scroll % metrics.track_height_pixels) + metrics.half_track_height_pixels local start_idx = math.floor( playlist.scroll / metrics.track_height_pixels ) + 1 start_idx = util.clamp( 1, #playlist.tracks - metrics.num_visible_tracks, start_idx ) local end_idx = start_idx + metrics.num_visible_tracks for i = start_idx, end_idx do if playlist.selected_idx == i then tex_pass:setColor( 0.2, 0.2, 0.6, 1 ) tex_pass:plane( tdx / 2, item_y, 0, tdx, metrics.track_height_pixels ) end local item = playlist.tracks[ i ] local w = font:getWidth( item ) tex_pass:setColor( 0, 1, 0, 1 ) tex_pass:text( item, w / 2, item_y, 0, 1 ) item_y = item_y + metrics.track_height_pixels end pass:draw( tex, x, y, z + 0.001, dx ) passes[ #passes + 1 ] = tex_pass elseif #playlist.tracks > 0 and #playlist.tracks <= metrics.num_visible_tracks then local item_y = metrics.half_track_height_pixels for i = 1, #playlist.tracks do if playlist.selected_idx == i then tex_pass:setColor( 0.2, 0.2, 0.6, 1 ) tex_pass:plane( tdx / 2, item_y, 0, tdx, metrics.track_height_pixels ) end local item = playlist.tracks[ i ] local w = font:getWidth( item ) tex_pass:setColor( 0, 1, 0, 1 ) tex_pass:text( item, w / 2, item_y, 0, 1 ) item_y = item_y + metrics.track_height_pixels end pass:draw( tex, x, y, z + 0.001, dx ) passes[ #passes + 1 ] = tex_pass end end function render.presets_list_lcd( pass ) local x, y, z = controls[ "eq_lcd_presets_list" ].pos:unpack() local dx, dy, dz = controls[ "eq_lcd_presets_list" ].dim:unpack() local tex = controls[ "eq_lcd_presets_list" ].tex local tex_pass = controls[ "eq_lcd_presets_list" ].pass font:setPixelDensity( metrics.pixel_density_track_list ) tex_pass:reset() tex_pass:setSampler( "linear" ) tex_pass:setProjection( 1, mat4():orthographic( tex_pass:getDimensions() ) ) local tdx, tdy = tex:getDimensions() if #presets_list.items > metrics.num_visible_presets then local max_scroll = ((#presets_list.items - metrics.num_visible_presets) * metrics.track_height_pixels) local item_y = -(presets_list.scroll % metrics.track_height_pixels) + metrics.half_track_height_pixels local start_idx = math.floor( presets_list.scroll / metrics.track_height_pixels ) + 1 start_idx = util.clamp( 1, #presets_list.items - metrics.num_visible_presets, start_idx ) local end_idx = start_idx + metrics.num_visible_presets for i = start_idx, end_idx do if presets_list.selected_idx == i then tex_pass:setColor( 0.2, 0.2, 0.6, 1 ) tex_pass:plane( tdx / 2, item_y, 0, tdx, metrics.track_height_pixels ) end local item = presets_list.items[ i ] local w = font:getWidth( item ) tex_pass:setColor( 0, 1, 0, 1 ) tex_pass:text( item, w / 2, item_y, 0, 1 ) item_y = item_y + metrics.track_height_pixels end elseif #presets_list.items > 0 and #presets_list.items <= metrics.num_visible_presets then local item_y = metrics.half_track_height_pixels for i = 1, #presets_list.items do if presets_list.selected_idx == i then tex_pass:setColor( 0.2, 0.2, 0.6, 1 ) tex_pass:plane( tdx / 2, item_y, 0, tdx, metrics.track_height_pixels ) end local item = presets_list.items[ i ] local w = font:getWidth( item ) tex_pass:setColor( 0, 1, 0, 1 ) tex_pass:text( item, w / 2, item_y, 0, 1 ) item_y = item_y + metrics.track_height_pixels end end pass:draw( tex, x, y, z + 0.001, dx ) passes[ #passes + 1 ] = tex_pass end function render.about_app( pass ) about_app.wavy_pass:reset() about_app.wavy_pass:setClear( 0, 0, 0 ) about_app.wavy_pass:setProjection( 1, mat4():orthographic( about_app.wavy_pass:getDimensions() ) ) about_app.wavy_pass:setSampler( "linear" ) font:setPixelDensity( metrics.pixel_density_track_list ) local char_h = font:getHeight() about_app.wavy_pass:setColor( 0, 1, 0 ) local scale = settings.session.win_h * 0.001 local offset_y = (#about_app.text * scale) + settings.session.win_h - (200 * scale) for i = 1, #about_app.text do local line = about_app.text[ i ] about_app.wavy_pass:text( line, settings.session.win_w / 2, (((char_h * 3) + (char_h / 2) * scale)) + offset_y - about_app.scroll, 0, scale, 0, 0, 0, 0, 0 ) offset_y = offset_y + (char_h * scale) end if not about_app.scroll_pause then about_app.scroll = about_app.scroll + 70 * lovr.timer.getDelta() end local max_scroll = (#about_app.text * char_h * scale) + settings.session.win_h if about_app.scroll > max_scroll then about_app.scroll = 0 end pass:setDepthWrite( false ) pass:setShader( about_app.wavy_shader ) pass:setProjection( 1, mat4():orthographic( pass:getDimensions() ) ) pass:draw( about_app.wavy_tex, settings.session.win_w / 2, settings.session.win_h / 2, 0 ) passes[ #passes + 1 ] = about_app.wavy_pass pass:setShader() pass:setColor( 0.05, 0.05, 0.05 ) pass:plane( settings.session.win_w / 2, char_h * 1.5, 0, settings.session.win_w - 40, (char_h * 3) - 40 ) pass:setColor( 0.5, 0.5, 0.5 ) pass:text( "ESC to exit - Left click to toggle scrolling", settings.session.win_w / 2, char_h * 1.5, 0, scale ) end function render.app( pass ) if about_app.show then render.about_app( pass ) else if settings.session.background_mode == e_background_mode.shader then pass:setDepthWrite( false ) pass:setShader( vis_shaders[ vis_shaders.active_idx ].shader ) pass:send( 'bars', skin.sa_bars.data ) local tex_count = #vis_shaders[ vis_shaders.active_idx ].textures if tex_count > 0 then for i = 1, tex_count do local name = vis_shaders[ vis_shaders.active_idx ].textures[ i ].name local texture = vis_shaders[ vis_shaders.active_idx ].textures[ i ].texture pass:send( name, texture ) end end pass:fill() pass:setDepthWrite( true ) render.set_environment( pass ) elseif settings.session.background_mode == e_background_mode.solid then lovr.graphics.setBackgroundColor( unpack( settings.session.background_color ) ) render.set_environment( pass ) else render.set_environment_with_background( pass ) end end local pose = mat4( vector( camera.pan.x, camera.pan.y, camera.zoom ), vector( 1, 1, 1 ), quat( camera.angle, 0, 1, 0 ) ) pass:setViewPose( 1, pose, true ) if not about_app.show then render.stack( pass ) end -- phywire.draw( pass, world ) end return render