Dijkstra fill style tile map lighting.

I was reading about how someone was doing lighting inside a minecraft clone and this is where this example comes from. The main goal was to create a lighting system for my current 2d space mining game. I have already put it into it.

The code can be pasted inside codea and should show a lighted random map with a ai random runner with a light. Maybe you need to run it a couple of times for the right map to show up.

I have to figure out still how to do multiple lights. Some kind of temp array per light and blending maybe?

-- Map Torches


-- tilemap with lighting(no multiple torches-untested)
-- basically a 4way dijkstra fill with lower transparancy at every step
-- addtional less transparancy when block 1(solid)
-- ai is a move to target(random destination)

-- Use this function to perform your initial setup
function setup()
    print("Hello World! - restart if weird/stuck")
    mw = 50
    mh = 50
    tw = WIDTH/mw
    th = HEIGHT/mh
    map = {}
    lightmap = {}
    for x=0,mw do
        map[x]={}
        lightmap[x]={}
        for y=0,mh do
            map[x][y]=0
            lightmap[x][y]=0
        end
    end
    for i=1,3 do
        wallrect(math.random(5,30),math.random(5,30),20,3)
    end
    x = math.random(1,mw-1)
    y = math.random(1,mh-1)
    while map[x][y]==1 do
        x = math.random(1,mw-1)
        y = math.random(1,mh-1)
    end
    placetorch(x,y)
    lmpos = vec2(x,y)
    lmdes = vec2(10,10)
    cnt=0
end 

-- This function gets called once every frame
function draw() 
    -- This sets a dark background color 
    background(0,0,0)

    -- move the torch around
    cnt=cnt+1
    if cnt>8 then
        cnt=0
        
        oldx = lmpos.x
        oldy = lmpos.y
        if lmpos.x < lmdes.x then lmpos.x=lmpos.x+1 end
        if lmpos.y < lmdes.y then lmpos.y=lmpos.y+1 end
        if lmpos.x > lmdes.x then lmpos.x=lmpos.x-1 end
        if lmpos.y > lmdes.y then lmpos.y=lmpos.y-1 end
        if map[lmpos.x][lmpos.y]==1 then
            lmpos.x = oldx
            lmpos.y = oldy
        end
        if math.random()<.1 then
            lmdes.x = math.random(0,mw)
            lmdes.y = math.random(0,mh)
            while lightmap[lmdes.x][lmdes.y]<20 and lmdes:dist(vec2(oldx,oldy))<5 do
                lmdes.x = math.random(0,mw)
                lmdes.y = math.random(0,mh)
            end
        end
        clearlightmap()
        placetorch(lmpos.x,lmpos.y)
    end
    -- Do your drawing here
    drawmap()
    fill(255,255,0)
    stroke(255)
    rect(lmpos.x*tw,lmpos.y*th,tw,th)
end

function clearlightmap()
    for x=0,mw do
        lightmap[x]={}
        for y=0,mh do
            lightmap[x][y]=0
        end
    end
end

function placetorch(x,y)
    orig = vec2(x,y)
    openlist={}
    table.insert(openlist,vec2(x,y))
    lightmap[x][y]=255
    -- left top right bottom of our position
    --switch = {vec2(-1,0),vec2(0,-1),vec2(1,0),vec2(0,1)}
    switch = {vec2(-1,0),vec2(-1,-1),vec2(0,-1),vec2(1,-1),vec2(1,0),vec2(1,1),vec2(0,1),vec2(-1,1)}
    while #openlist>0 do
        x = openlist[1].x
        y = openlist[1].y
        table.remove(openlist,1)
        if lightmap[x][y]>10 and vec2(x,y):dist(orig)<12 then
            for i = 1,#switch do
                pos = vec2(x+switch[i].x,y+switch[i].y)
                if pos.x>=0 and pos.y>=0 and pos.x<mw and pos.y<mh then
                    if lightmap[pos.x][pos.y]==0 then
                        if map[x][y]==0 then
                            lightmap[pos.x][pos.y]=lightmap[x][y]/100*90
                        else
                            lightmap[pos.x][pos.y]=lightmap[x][y]/100*60
                        end
                        if map[pos.x][pos.y]==1 then
                            lightmap[pos.x][pos.y]=lightmap[pos.x][pos.y]/2
                        end
                        if lightmap[pos.x][pos.y]<0 then lightmap[pos.x][pos.y]=0 end
                        if lightmap[pos.x][pos.y]>0 then 
                            table.insert(openlist,vec2(pos.x,pos.y))
                        end
                    end
                 end
            end
        end
    
    end
    blendlight2(orig.x,orig.y)
end
function blendlight2(x,y)
    for x1 = x-10,x+10 do
    for y1 = y-10,y+10 do
        if x1>=0 and y1>=0 and x1<mw and y1<mh then
            z=0
            d=0
            for y2=y1-1,y1+1 do
                for x2=x1-1,x1+1 do
                    if x2>=0 and y2>=0 and x2<mw and y2<mh then
                        z=z+lightmap[x2][y2]
                        d=d+1
                        
                    end
                end
            end
            lightmap[x1][y1]=z/d      
        end
        end
        end
end
-- this is a flickering style of lighting
function blendlight(x,y)
    for i = 1,(6*6)*100 do
        x1 = x+math.random(-6,6)
        y1 = y+math.random(-6,6)
        if x1>=0 and y1>=0 and x1<mw and y1<mh then
            z=0
            d=0
            for y2=y1-1,y1+1 do
                for x2=x1-1,x1+1 do
                    if x2>=0 and y2>=0 and x2<mw and y2<mh then
                        z=z+lightmap[x2][y2]
                        d=d+1
                    
                    end
                end
            end
            lightmap[x1][y1]=z/d      
        end
    end
end
function wallrect(x,y,w,h)
    for y1=y,y+h do
        for x1=x,x+w do
            if x1>=0 and y1>=0 and x1<mw and y1<mh then
                map[x1][y1]=1
            end
        end
    end
end
function drawmap() 
    for y = 0,mh,1 do
        for x = 0,mw,1 do
            if lightmap[x][y]>0 then
            if map[x][y]==0 then
                fill(40,40,0,lightmap[x][y])
                stroke(40,40,0,lightmap[x][y])
                rect(x*tw,y*th,tw,th)
            end
            if map[x][y]==1 then
                fill(255,155,55,lightmap[x][y])
                stroke(255,155,55,lightmap[x][y])
                rect(x*tw,y*th,tw,th)
                end
            end
        end
    end
end

@Pakz Here’s something I thru together with 2 lights. Not sure if it’s what you’re after. I just put in a bunch of cubes instead of trying to do paths.

function setup()
    parameter.number("lx1",-50,50,-5)
    parameter.number("lz1",-50,50,-5)
    parameter.number("lx2",-50,50,5)
    parameter.number("lz2",-50,50,0)
    
    scene = craft.scene()
    scene.sun.active = false
    scene.sky.active = false
    scene.ambientColor =  color(26,26,26,255)   
     
    v=scene.camera:add(OrbitViewer, vec3(0,0,0), 50, 0, 3000)
    v.rx,v.ry=30,30
    
    for x=-50,50 do
        for z=-50,50 do
            local floor = scene:entity()
            floor.position=vec3(x,-1,z)
            local r = floor:add(craft.renderer, craft.model.cube(vec3(.5,.5,.5)))
            r.material = craft.material(asset.builtin.Materials.Specular)
        end
    end
    
    light1 = scene:entity():add(craft.light, SPOT)
    light1.angle = 50
    light1.color = color(255)
    light1.entity.y = 5
    
    light2 = scene:entity():add(craft.light, SPOT)
    light2.angle = 50
    light2.color = color(255)
    light2.entity.y = 5
    
    s1=scene:entity()
    s1.position=vec3(0,-1,0)
    s1.model = craft.model.icosphere(.5,2)
    s1.material = craft.material(asset.builtin.Materials.Basic)
    
    s2=scene:entity()
    s2.position=vec3(10,-1,0)
    s2.model = craft.model.icosphere(.5,2)
    s2.material = craft.material(asset.builtin.Materials.Basic)
end

function draw()
    update(DeltaTime)    
    scene:draw()
end

function update(dt)
    scene:update(dt)

    light1.entity.z = lz1
    light1.entity.x = lx1   
    
    light2.entity.z = lz2
    light2.entity.x = lx2
    
    s1.z = lz1
    s1.x = lx1   
         
    s2.z = lz2
    s2.x = lx2   
end

@Pakz very cool, tried it out and it looks good. For multiple lights, could you compute them all into a buffer (additively) then use that to render the output?

@dave1707 I think your way might be considered cheating if the goal is to compute 2D light transport using an algorithm

I had modified the code and made it so there can be more torches. I did not understand it at first when I should of been adding up the light. I was taking the
Avarage, but it works now.

-- Multiple Map Torches


-- tilemap with lighting(multiple torches)
-- basically a 8way dijkstra fill with lower transparancy at every step
-- walls lower light spread more.
-- addtional less transparancy when block 1(solid)
-- ai is a roamer, move to target(random destination inside lightmap)

-- Use this function to perform your initial setup
function setup()
    print("Hello World! - restart if weird/stuck")
    mw = 50
    mh = 50
    tw = WIDTH/mw
    th = HEIGHT/mh
    map = {}
    lightmap = {}
    lightmapbase = {}-- contains all stationary lights
    for x=0,mw do
        map[x]={}
        lightmapbase[x]={}
        lightmap[x]={}
        for y=0,mh do
            map[x][y]=0
            lightmapbase[x][y]=0
            lightmap[x][y]=0
        end
    end
    for i=1,3 do
        wallrect(math.random(5,30),math.random(5,30),20,3)
    end
    
    
    for i =0,3 do
        x = math.random(1,mw-1)
        y = math.random(1,mh-1)
        while map[x][y]==1 or (map[x][y+1]==0 and map[x][y-1]==0) do
            x = math.random(1,mw-1)
            y = math.random(1,mh-1)
        end
        clearlightmap()
        placetorch(x,y)
        mixlightmaptobase()
    end
    mixlightmaps()
    
    x = math.random(1,mw-1)
    y = math.random(1,mh-1)
    while map[x][y]==1 do
        x = math.random(1,mw-1)
        y = math.random(1,mh-1)
    end

    lmpos = vec2(x,y)
    lmdes = vec2(10,10)
    cnt=0
end 

-- This function gets called once every frame
function draw() 
    -- This sets a dark background color 
    background(0,0,0)
    
    -- move the torch around
    cnt=cnt+1
    if cnt>8 then
        cnt=0
        
        oldx = lmpos.x
        oldy = lmpos.y
        if lmpos.x < lmdes.x then lmpos.x=lmpos.x+1 end
        if lmpos.y < lmdes.y then lmpos.y=lmpos.y+1 end
        if lmpos.x > lmdes.x then lmpos.x=lmpos.x-1 end
        if lmpos.y > lmdes.y then lmpos.y=lmpos.y-1 end
        if map[lmpos.x][lmpos.y]==1 then
            lmpos.x = oldx
            lmpos.y = oldy
        end
        if math.random()<.1 then
            lmdes.x = math.random(0,mw)
            lmdes.y = math.random(0,mh)
            while lightmap[lmdes.x][lmdes.y]<20 and lmdes:dist(vec2(oldx,oldy))<5 do
                lmdes.x = math.random(0,mw)
                lmdes.y = math.random(0,mh)
            end
        end
        clearlightmap()
        placetorch(lmpos.x,lmpos.y)
        --placetorch(lmpos.x,lmpos.y)
        --placetorch(lmpos.x,lmpos.y)
        mixlightmaps()
    end
    -- Do your drawing here
    drawmap()
    fill(255,255,0)
    stroke(255)
    rect(lmpos.x*tw,lmpos.y*th,tw,th)
end
-- add current lightmap to lightmap base. count up light particles.
function mixlightmaptobase()
    for y=0,mh do
        for x=0,mw do
            lightmapbase[x][y]=lightmapbase[x][y]+lightmap[x][y]
            if lightmapbase[x][y]>255 then lightmapbase[x][y]=255 end

        end
    end
end
-- create our main lightmap for drawing the map
-- adding the stationary lights(base) with current lightmap.
-- add up all light particles.
function mixlightmaps()
    for y=0,mh do
        for x=0,mw do
            lightmap[x][y]=(lightmapbase[x][y]+lightmap[x][y])
            if lightmap[x][y]>255 then lightmap[x][y]=255 end

        end
    end
end

function clearlightmapbase()
    for x=0,mw do
        lightmapbase[x]={}
        for y=0,mh do
            lightmapbase[x][y]=0
        end
    end       
end

function clearlightmap()
    for x=0,mw do
        lightmap[x]={}
        for y=0,mh do
            lightmap[x][y]=0
        end
    end
end

function placetorch(x,y)
    orig = vec2(x,y)
    openlist={}
    table.insert(openlist,vec2(x,y))
    lightmap[x][y]=255
    -- left top right bottom of our position
    --switch = {vec2(-1,0),vec2(0,-1),vec2(1,0),vec2(0,1)}
    switch = {vec2(-1,0),vec2(-1,-1),vec2(0,-1),vec2(1,-1),vec2(1,0),vec2(1,1),vec2(0,1),vec2(-1,1)}
    while #openlist>0 do
        x = openlist[1].x
        y = openlist[1].y
        table.remove(openlist,1)
        if lightmap[x][y]>10 and vec2(x,y):dist(orig)<12 then
            for i = 1,#switch do
                pos = vec2(x+switch[i].x,y+switch[i].y)
                if pos.x>=0 and pos.y>=0 and pos.x<mw and pos.y<mh then
                    if lightmap[pos.x][pos.y]==0 then
                        if map[x][y]==0 then
                            lightmap[pos.x][pos.y]=(lightmap[x][y]/100)*80
                        else
                            lightmap[pos.x][pos.y]=(lightmap[x][y]/100)*30
                        end
                        if map[pos.x][pos.y]==1 then
                            lightmap[pos.x][pos.y]=lightmap[pos.x][pos.y]/2
                        end
                        if lightmap[pos.x][pos.y]<0 then lightmap[pos.x][pos.y]=0 end
                        if lightmap[pos.x][pos.y]>0 then 
                            table.insert(openlist,vec2(pos.x,pos.y))
                        end
                    end
                 end
            end
        end
    
    end
    blendlight(orig.x,orig.y)
end
function blendlight2(x,y)
    for x1 = x-10,x+10 do
    for y1 = y-10,y+10 do
        if x1>=0 and y1>=0 and x1<mw and y1<mh then
            z=0
            d=0
            for y2=y1-1,y1+1 do
                for x2=x1-1,x1+1 do
                    if x2>=0 and y2>=0 and x2<mw and y2<mh then
                        z=z+lightmap[x2][y2]
                        d=d+1
                        
                    end
                end
            end
            lightmap[x1][y1]=z/d      
        end
        end
        end
end
-- this is a flickering style of lighting
function blendlight(x,y)
    for i = 1,(6*6)*100 do
        x1 = x+math.random(-6,6)
        y1 = y+math.random(-6,6)
        if x1>=0 and y1>=0 and x1<mw and y1<mh then
            z=0
            d=0
            for y2=y1-1,y1+1 do
                for x2=x1-1,x1+1 do
                    if x2>=0 and y2>=0 and x2<mw and y2<mh then
                        z=z+lightmap[x2][y2]
                        d=d+1
                    
                    end
                end
            end
            lightmap[x1][y1]=z/d      
        end
    end
end
function wallrect(x,y,w,h)
    for y1=y,y+h do
        for x1=x,x+w do
            if x1>=0 and y1>=0 and x1<mw and y1<mh then
                map[x1][y1]=1
            end
        end
    end
end
function drawmap() 
    for y = 0,mh,1 do
        for x = 0,mw,1 do
            if lightmap[x][y]>0 then
            if map[x][y]==0 then
                fill(40,40,0,lightmap[x][y])
                stroke(40,40,0,lightmap[x][y])
                rect(x*tw,y*th,tw,th)
            end
            if map[x][y]==1 then
                fill(255,155,55,lightmap[x][y])
                stroke(255,155,55,lightmap[x][y])
                rect(x*tw,y*th,tw,th)
                end
            end
        end
    end
end