├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── assets ├── level.png ├── light.glsl ├── light.png ├── occlusion_gradient.png ├── occlusion_rims.png ├── occlusion_solid.png └── parcel.json ├── custom_index.html ├── occlusion_masks.psd ├── project.flow ├── screenshot.png └── src ├── DigitalCircleParcelProgress.hx └── Main.hx /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Haxe binaries 2 | bin/ 3 | 4 | # Windows image file caches 5 | Thumbs.db 6 | ehthumbs.db 7 | 8 | # Folder config file 9 | Desktop.ini 10 | 11 | # Recycle Bin used on file shares 12 | $RECYCLE.BIN/ 13 | 14 | # Windows Installer files 15 | *.cab 16 | *.msi 17 | *.msm 18 | *.msp 19 | 20 | # Windows shortcuts 21 | *.lnk 22 | 23 | # ========================= 24 | # Operating System Files 25 | # ========================= 26 | 27 | # OSX 28 | # ========================= 29 | 30 | .DS_Store 31 | .AppleDouble 32 | .LSOverride 33 | 34 | # Thumbnails 35 | ._* 36 | 37 | # Files that might appear on external disk 38 | .Spotlight-V100 39 | .Trashes 40 | 41 | # Directories potentially created on remote AFP share 42 | .AppleDB 43 | .AppleDesktop 44 | Network Trash Folder 45 | Temporary Items 46 | .apdisk 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kenton Hamaluik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LuxeLights 2 | 3 | A simple demo showing how to use render textures and shaders to create a _very basic_ 2D lighting effect (no shadows, no light "clipping"). 4 | 5 | >PLEASE NOTE! This demo was created using an ALPHA version of [Luxe](http://luxeengine.com/). As such, it may very well be obsolete by the time you look at it. It is current and working as of `alpha-2.0`, however the API can and likely will change without any hesitation! You've been warned! 6 | 7 | [Click here to check out a live demo.](http://hamaluik.github.io/LuxeLights/) 8 | 9 | A screenshot of it in action: 10 | 11 | ![Screenshot of the custom preloader in action](https://raw.github.com/hamaluik/LuxeLights/master/screenshot.png) 12 | -------------------------------------------------------------------------------- /assets/level.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamaluik/LuxeLights/9359584a2549ebe65d403f7c1f497dd17591a495/assets/level.png -------------------------------------------------------------------------------- /assets/light.glsl: -------------------------------------------------------------------------------- 1 | // shader taken from: http://www.alcove-games.com/opengl-es-2-tutorials/lightmap-shader-fire-effect-glsl/ 2 | 3 | uniform sampler2D tex0; 4 | uniform sampler2D lightMap; 5 | 6 | varying vec2 tcoord; 7 | varying vec4 color; 8 | uniform vec4 ambientColor; 9 | uniform vec2 resolution; 10 | 11 | void main() { 12 | vec4 diffuseColour = texture2D(tex0, tcoord); 13 | vec2 lightCoord = (gl_FragCoord.xy / resolution.xy); 14 | vec4 light = texture2D(lightMap, lightCoord); 15 | 16 | vec3 ambient = ambientColor.rgb * ambientColor.a; 17 | vec3 intensity = ambient + light.rgb; 18 | vec3 finalColor = diffuseColour.rgb * intensity; 19 | 20 | gl_FragColor = color * vec4(finalColor, diffuseColour.a); 21 | } 22 | -------------------------------------------------------------------------------- /assets/light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamaluik/LuxeLights/9359584a2549ebe65d403f7c1f497dd17591a495/assets/light.png -------------------------------------------------------------------------------- /assets/occlusion_gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamaluik/LuxeLights/9359584a2549ebe65d403f7c1f497dd17591a495/assets/occlusion_gradient.png -------------------------------------------------------------------------------- /assets/occlusion_rims.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamaluik/LuxeLights/9359584a2549ebe65d403f7c1f497dd17591a495/assets/occlusion_rims.png -------------------------------------------------------------------------------- /assets/occlusion_solid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamaluik/LuxeLights/9359584a2549ebe65d403f7c1f497dd17591a495/assets/occlusion_solid.png -------------------------------------------------------------------------------- /assets/parcel.json: -------------------------------------------------------------------------------- 1 | { 2 | "textures" : [ 3 | { "id" : "assets/level.png" }, 4 | { "id" : "assets/light.png" }, 5 | { "id" : "assets/occlusion_solid.png" }, 6 | { "id" : "assets/occlusion_rims.png" }, 7 | { "id" : "assets/occlusion_gradient.png" } 8 | ], 9 | "shaders" : [ 10 | { "ps_id":"assets/light.glsl" } 11 | ] 12 | } -------------------------------------------------------------------------------- /custom_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | {{#each project.app.web.libs~}} 16 | 17 | {{/each}} 18 | 19 | 20 | 21 | 22 | 23 |

Use the and keys to adjust the ambient intensity. Use the mouse to move a local light around the scene. Use 1, 2, and 3 to select solid, rim, and gradient occlusion textures respectively.

24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /occlusion_masks.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamaluik/LuxeLights/9359584a2549ebe65d403f7c1f497dd17591a495/occlusion_masks.psd -------------------------------------------------------------------------------- /project.flow: -------------------------------------------------------------------------------- 1 | { 2 | 3 | luxe:{ 4 | window: { 5 | width:960, 6 | height:640, 7 | title:'Lights', 8 | fullscreen:false, 9 | resizable:true, 10 | borderless:false 11 | } 12 | }, 13 | 14 | project : { 15 | name : 'Luxe Lights', 16 | version : '0.0.1', 17 | author : 'FuzzyWuzzie', 18 | 19 | app : { 20 | name : 'LuxeLights', 21 | package : 'com.blazingmammothgames.luxe' 22 | }, 23 | 24 | build : { 25 | dependencies : { 26 | luxe : '*', 27 | } 28 | }, 29 | 30 | files : { 31 | assets : 'assets/', 32 | index : { path:'custom_index.html => index.html', template:'project', not_listed:true } 33 | } 34 | 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamaluik/LuxeLights/9359584a2549ebe65d403f7c1f497dd17591a495/screenshot.png -------------------------------------------------------------------------------- /src/DigitalCircleParcelProgress.hx: -------------------------------------------------------------------------------- 1 | package ; 2 | 3 | import luxe.resource.Resource; 4 | import luxe.Parcel; 5 | import luxe.Visual; 6 | import luxe.Color; 7 | import luxe.options.ParcelProgressOptions; 8 | import luxe.Text; 9 | import luxe.Vector; 10 | 11 | class DigitalCircleParcelProgress { 12 | var parcel:Parcel; 13 | 14 | var ticks:Array = new Array(); 15 | var progressText:Text; 16 | 17 | var options:ParcelProgressOptions; 18 | 19 | public function new(_options:ParcelProgressOptions) { 20 | options = _options; 21 | 22 | if(options.bar == null) { 23 | options.bar = new Color( ).rgb(0xC7F464); 24 | } 25 | 26 | if(options.background == null) { 27 | options.background = new Color( ).rgb(0x556270); 28 | } 29 | 30 | // create the ticks 31 | for(i in 0...20) { 32 | var angle:Float = (i * (Math.PI / 10)) - (Math.PI / 2); 33 | var start:Vector = new Vector(30 * Math.cos(angle) + Luxe.screen.mid.x, 30 * Math.sin(angle) + Luxe.screen.mid.y); 34 | var end:Vector = new Vector(40 * Math.cos(angle) + Luxe.screen.mid.x, 40 * Math.sin(angle) + Luxe.screen.mid.y); 35 | 36 | ticks.push(new Visual({ 37 | color: options.background, 38 | no_scene: true, 39 | geometry : Luxe.draw.line({ 40 | p0: start, 41 | p1: end 42 | }), 43 | depth: 998 44 | })); 45 | } 46 | 47 | // create the percent text 48 | progressText = new luxe.Text({ 49 | text: '0%', 50 | align: center, 51 | align_vertical: center, 52 | point_size: 12, 53 | pos: Luxe.screen.mid, 54 | color: new Color().rgb(0xFF6B6B) 55 | }); 56 | 57 | // intercept the oncomplete and onprogress callbacks 58 | options.parcel.options.oncomplete = oncomplete; 59 | options.parcel.options.onprogress = onprogress; 60 | } 61 | 62 | public function onprogress(r:Resource) { 63 | var amount = options.parcel.current_count / options.parcel.total_items; 64 | for(i in 0...20) { 65 | if(amount >= (i / 20)) { 66 | ticks[i].color = options.bar; 67 | } 68 | } 69 | 70 | progressText.text = Std.int(amount * 100) + "%"; 71 | } 72 | 73 | public function oncomplete(p:Parcel) { 74 | for(tick in ticks) { 75 | tick.destroy(); 76 | } 77 | progressText.destroy(); 78 | 79 | if(options.oncomplete != null) { 80 | options.oncomplete(options.parcel); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /src/Main.hx: -------------------------------------------------------------------------------- 1 | import luxe.Color; 2 | import luxe.Input; 3 | import luxe.Log; 4 | import luxe.Rectangle; 5 | import luxe.Sprite; 6 | import luxe.Vector; 7 | import phoenix.Batcher; 8 | import phoenix.RenderTexture; 9 | import phoenix.Texture; 10 | import luxe.Parcel; 11 | import phoenix.Shader; 12 | 13 | class Main extends luxe.Game { 14 | // use a render texture for the "lightmap" 15 | var lightRenderTexture:RenderTexture; 16 | // it also needs it's own geometry batcher 17 | var lightBatcher:Batcher; 18 | 19 | // set the ambient colour to a deep blue for a nice nighttime effect 20 | var ambientColor:Vector = new Vector(0.3, 0.3, 0.7, 0.7); 21 | 22 | // sprite that will get rendered into the lightmap rendertexture 23 | var lightSprite:Sprite; 24 | var campfireLight:Sprite; 25 | var occluderSprite:Sprite; 26 | 27 | // a shader that all lit entities must use 28 | var lightShader:Shader; 29 | 30 | // the main background level 31 | var levelSprite:Sprite; 32 | 33 | // three types of occlusion textures to play with 34 | var occlusionTexture_solid:Texture; 35 | var occlusionTexture_rims:Texture; 36 | var occlusionTexture_gradient:Texture; 37 | 38 | // a timer to make the fire light flicker nicely 39 | var fireUpdateNextTime:Float = 0; 40 | 41 | override function ready() { 42 | // load the parcel 43 | Luxe.loadJSON("assets/parcel.json", function(jsonParcel) { 44 | var parcel = new Parcel(); 45 | parcel.from_json(jsonParcel.json); 46 | 47 | // show a loading bar 48 | // use a fancy custom loading bar (https://github.com/FuzzyWuzzie/CustomLuxePreloader) 49 | new DigitalCircleParcelProgress({ 50 | parcel: parcel, 51 | oncomplete: assetsLoaded 52 | }); 53 | 54 | // start loading! 55 | parcel.load(); 56 | }); 57 | } //ready 58 | 59 | function assetsLoaded(_) { 60 | // create a level sprite to display in the background 61 | levelSprite = new Sprite({ 62 | texture: Luxe.resources.find_texture('assets/level.png'), 63 | pos: Luxe.screen.mid, 64 | size: new Vector(960, 640), 65 | depth: 0 66 | }); 67 | levelSprite.texture.filter = phoenix.Texture.FilterType.nearest; 68 | 69 | // --- NOW LIGHTS --- 70 | 71 | // create a render texture for the lights 72 | lightRenderTexture = new RenderTexture(Luxe.resources, new Vector(1024, 1024)); 73 | 74 | // and a batcher for all the lights 75 | lightBatcher = Luxe.renderer.create_batcher({ 76 | name: 'lights_batcher', 77 | no_add: true 78 | }); 79 | // set the batcher's viewport to be the same as the main camera 80 | lightBatcher.view.viewport = Luxe.camera.viewport; 81 | 82 | // create the light sprite which will render to the lightRenderTexture 83 | lightSprite = new Sprite({ 84 | texture: Luxe.resources.find_texture('assets/light.png'), 85 | pos: Luxe.screen.mid, 86 | size: new Vector(128, 128), 87 | batcher: lightBatcher, 88 | color: new Color().rgb(0xffe786) 89 | }); 90 | lightSprite.texture.filter = phoenix.Texture.FilterType.nearest; 91 | 92 | // create the campfire light in the same way as the mouse light 93 | campfireLight = new Sprite({ 94 | texture: Luxe.resources.find_texture('assets/light.png'), 95 | pos:Luxe.screen.mid, 96 | size: new Vector(256, 256), 97 | batcher: lightBatcher, 98 | color: new Color().rgb(0xff4d07) 99 | }); 100 | 101 | // load the various occlusion textures 102 | occlusionTexture_solid = Luxe.resources.find_texture('assets/occlusion_solid.png'); 103 | occlusionTexture_rims = Luxe.resources.find_texture('assets/occlusion_rims.png'); 104 | occlusionTexture_gradient = Luxe.resources.find_texture('assets/occlusion_gradient.png'); 105 | 106 | // make em nice and pixely 107 | occlusionTexture_solid.filter = phoenix.Texture.FilterType.nearest; 108 | occlusionTexture_rims.filter = phoenix.Texture.FilterType.nearest; 109 | occlusionTexture_gradient.filter = phoenix.Texture.FilterType.nearest; 110 | 111 | // create the occluder 112 | occluderSprite = new Sprite({ 113 | texture: occlusionTexture_solid, 114 | pos: Luxe.screen.mid, 115 | size: new Vector(960, 640), 116 | depth: 997, 117 | batcher: lightBatcher 118 | }); 119 | 120 | // setup the light shader 121 | lightShader = Luxe.resources.find_shader('assets/light.glsl|default'); 122 | levelSprite.shader = lightShader; 123 | 124 | // set the ambient colour 125 | lightShader.set_vector4('ambientColor', ambientColor); 126 | 127 | // and the resolution (for lightmap texture lookup) 128 | lightShader.set_vector2('resolution', new Vector(1024, 1024)); 129 | 130 | // move to the second slot 131 | lightRenderTexture.slot = 1; 132 | lightShader.set_texture('lightMap', lightRenderTexture); 133 | } 134 | 135 | override function onkeyup( e:KeyEvent ) { 136 | 137 | if(e.keycode == Key.escape) { 138 | Luxe.shutdown(); 139 | } 140 | // use the up and down keys to control the ambient light strength 141 | else if(e.keycode == Key.up) { 142 | ambientColor.w = Math.min(ambientColor.w + 0.1, 1); 143 | if(lightShader != null) { 144 | lightShader.set_vector4('ambientColor', ambientColor); 145 | } 146 | } 147 | else if(e.keycode == Key.down) { 148 | ambientColor.w = Math.max(ambientColor.w - 0.1, 0); 149 | if(lightShader != null) { 150 | lightShader.set_vector4('ambientColor', ambientColor); 151 | } 152 | } 153 | // set the different occlusion textures 154 | else if(e.keycode == Key.key_1) { 155 | occluderSprite.texture = occlusionTexture_solid; 156 | } 157 | else if(e.keycode == Key.key_2) { 158 | occluderSprite.texture = occlusionTexture_rims; 159 | } 160 | else if(e.keycode == Key.key_3) { 161 | occluderSprite.texture = occlusionTexture_gradient; 162 | } 163 | 164 | } //onkeyup 165 | 166 | override public function onmousemove(event:MouseEvent) { 167 | if(lightSprite != null) { 168 | // make the lightSprite follow the cursor 169 | lightSprite.pos.x = event.pos.x; 170 | lightSprite.pos.y = event.pos.y; 171 | } 172 | } 173 | // use the pre-render function to render to render textures 174 | override function onprerender() { 175 | // wait for the batcher to be up and running 176 | if(lightBatcher == null) { 177 | return; 178 | } 179 | 180 | // switch the render texture to the lights texture 181 | Luxe.renderer.target = lightRenderTexture; 182 | 183 | // clear it 184 | Luxe.renderer.clear(new Color(0, 0, 0, 0)); 185 | 186 | // draw all the lights using the batcher 187 | lightBatcher.draw(); 188 | 189 | // unset the render target 190 | // so that we go back to rendering to the screen 191 | Luxe.renderer.target = null; 192 | } 193 | 194 | override function update(dt:Float) { 195 | // flicker the campfire 196 | fireUpdateNextTime -= dt; 197 | if(campfireLight != null && fireUpdateNextTime <= 0) { 198 | campfireLight.scale = new Vector((Math.random() - 0.5) * 0.1 + 1, (Math.random() - 0.5) * 0.1 + 1); 199 | fireUpdateNextTime = Math.random() * 0.1; 200 | } 201 | } //update 202 | 203 | 204 | } //Main 205 | --------------------------------------------------------------------------------