
local blankCanvas   = newCanvas(10,10)
local w = 1
local cube = {
  {--right
    { w, -1,  1,  0,  1, 1},
    { w, -1, -1,  0,  0, 1},
    { w,  1, -1,  0,  0, 0},
    { w,  1, -1,  0,  0, 0}, -- dup
    { w,  1,  1,  0,  1, 0},
    { w, -1,  1,  0,  1, 1},  -- dup
  },
  {--left
    {-w, -1, -1,  0,  1, 1},
    {-w, -1,  1,  0,  0, 1},
    {-w,  1,  1,  0,  0, 0},
    {-w,  1,  1,  0,  0, 0}, -- dup
    {-w,  1, -1,  0,  1, 0},
    {-w, -1, -1,  0,  1, 1}, -- dup
  },
  {--top
    { 1,  w, -1,  0,  1, 1},
    {-1,  w, -1,  0,  0, 1},
    {-1,  w,  1,  0,  0, 0},
    {-1,  w,  1,  0,  0, 0}, -- dup
    { 1,  w,  1,  0,  1, 0},
    { 1,  w, -1,  0,  1, 1}, -- dup
  },
  {--bottom
    {-1, -w, -1,  0,  1, 1},
    { 1, -w, -1,  0,  0, 1},
    { 1, -w,  1,  0,  0, 0},
    { 1, -w,  1,  0,  0, 0}, -- dup
    {-1, -w,  1,  0,  1, 0},
    {-1, -w, -1,  0,  1, 1}, -- dup
  },
  {--front
    {-1, -1,  w,  0,  1, 1},
    { 1, -1,  w,  0,  0, 1},
    { 1,  1,  w,  0,  0, 0},
    { 1,  1,  w,  0,  0, 0}, -- dup
    {-1,  1,  w,  0,  1, 0},
    {-1, -1,  w,  0,  1, 1}, -- dup
  },
  {--back
    { 1, -1, -w,  0,  1, 1},
    {-1, -1, -w,  0,  0, 1},
    {-1,  1, -w,  0,  0, 0},
    {-1,  1, -w,  0,  0, 0}, -- dup
    { 1,  1, -w,  0,  1, 0},
    { 1, -1, -w,  0,  1, 1}, -- dup
  },
}
local cubeDirs = {
  {vec3(1,0,0),   vec3(0,1,0)},
  {vec3(-1,0,0),  vec3(0,1,0)},
  {vec3(0,-1,0),  vec3(0,0,-1)},
  {vec3(0,1,0),   vec3(0,0,-1)},
  {vec3(0,0,1),   vec3(0,1,0)},
  {vec3(0,0,-1),  vec3(0,1,0)},
}

local norms = {
	      	{-1,0,0},{0,-1,0},{0,0,1},
	      	{1,0,0},{0,1,0},{0,0,-1}
	      }

local shadowTypeNums = {point=1,spot=2,direction=3}

function updateRoomBounds(room)
  for m,model in ipairs(room.terrain.models) do
    if not model.bbox then  
      model.bbox = calcBBOXFromMesh(model.mesh)
    end
    local bbox = model.bbox
    room.bounds = m==1 and {min=bbox.min:clone(),max=bbox.max:clone()} or room.bounds
    room.bounds.min.x = math.min(room.bounds.min.x, bbox.min.x)
    room.bounds.min.y = math.min(room.bounds.min.y, bbox.min.y)
    room.bounds.min.z = math.min(room.bounds.min.z, bbox.min.z)
    room.bounds.max.x = math.max(room.bounds.max.x, bbox.max.x)
    room.bounds.max.y = math.max(room.bounds.max.y, bbox.max.y)
    room.bounds.max.z = math.max(room.bounds.max.z, bbox.max.z)
  end
  room.bounds.min = room.bounds.min - vec3(50)
	room.bounds.max = room.bounds.max + vec3(50)
	room.bounds.center = (room.bounds.min+room.bounds.max)/2
end


function setStaticProbes(room)
  local abs,floor,ceil = math.abs,math.floor,math.ceil
  local size = room.lighting.probes.gridsize
  local size2 = size*size
  local spacing = room.lighting.probes.spacing
  local bmin,bmax = room.bounds.min,room.bounds.max
  local probes = room.lightProbes
  local pos,upper,lower = vec3(),vec3(),vec3()
  local index
  local triangles = {}

  for m,model in ipairs(room.terrain.models) do 
    for i=1,model.mesh:getVertexCount(),1 do
      if not model.isEdge then 
        pos=-vec3(model.mesh:getVertex( i ))
        pos = (vec3(-bmax.x,-bmax.y,-bmin.z)-pos)/spacing
        local n = size-1
            
        upper:set(n-abs(floor(pos.x)),n-abs(floor(pos.y)),abs(floor(pos.z)))
        lower:set(n-abs(ceil (pos.x)),n-abs(ceil (pos.y)),abs(ceil (pos.z)))

        probes[lower.x*size2+lower.y*size+lower.z+1].active = true 
        probes[upper.x*size2+lower.y*size+lower.z+1].active = true 
        probes[lower.x*size2+upper.y*size+lower.z+1].active = true 
        probes[lower.x*size2+lower.y*size+upper.z+1].active = true 
        probes[upper.x*size2+lower.y*size+upper.z+1].active = true 
        probes[lower.x*size2+upper.y*size+upper.z+1].active = true 
        probes[upper.x*size2+upper.y*size+lower.z+1].active = true 
        probes[upper.x*size2+upper.y*size+upper.z+1].active = true 

        if (i-1)%3 == 0 then 
         table.insert(triangles,{
           vec3(model.mesh:getVertex( i )),
           vec3(model.mesh:getVertex( i+1 )),
           vec3(model.mesh:getVertex( i+2 )),
           m,i
         })
        end
      end
    end
  end
  
  room.terrain.triangles = triangles
  room.terrain.bvh = cpml.bvh(triangles)

  local active = 0
  for p,probe in ipairs(probes) do  
    if probe.active then 
      active=active+1
      for i=1,36,1 do 
        room.lighting.probes.mesh:setVertexAttribute((p-1)*36+i, 3, 1,1,1,1 )
      end
    end
  end
  print("active probes",active)
end

function bvhRoom(room)
  local triangles = {}
  for m,model in ipairs(room.terrain.models) do 
    for i=1,model.mesh:getVertexCount(),1 do
      if not model.isEdge then 
        if (i-1)%3 == 0 then 
         table.insert(triangles,{
           vec3(model.mesh:getVertex( i )),
           vec3(model.mesh:getVertex( i+1 )),
           vec3(model.mesh:getVertex( i+2 )),
           m,i
         })
        end
      end
    end
  end
  
  room.terrain.triangles = triangles
  room.terrain.bvh = cpml.bvh(triangles)
end

local function setValues(t,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17)
  t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8],t[9],t[10],t[11],t[12],t[13],t[14],t[15],t[16],t[17]=
  v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17
  return t
end

function random_in_unit_sphere()
  local point = vec3()
  while (true) do
    point:set( math.random()*2-1,math.random()*2-1,math.random()*2-1)
    if (point:len2() >= 1)then 
      return point:normalize2()
    end
  end
end

function random_in_hemisphere( normal) 
    local in_unit_sphere = random_in_unit_sphere();
    -- print(in_unit_sphere,normal)
    if (vec3.dot(in_unit_sphere, normal) > 0) then -- In the same hemisphere as the normal
        return in_unit_sphere
    else
        return -in_unit_sphere
      end
end

local tv1,tv2,tv3 = {0,0,0,0, 0,0, 0,0,0,0, 0,0,0},{0,0,0,0, 0,0, 0,0,0,0, 0,0,0},{0,0,0,0, 0,0, 0,0,0,0, 0,0,0}
local tempRay = {}
local tempNormal  = vec3()
local tempAlbedo  = vec3()
local directLight = vec3()
function ray_color( origin, direction,  room,  depth, diffuse ) 

  -- If we've exceeded the ray bounce limit, no more light is gathered.
  if (depth <= 0) then
    directLight:set(0.5,0.5,1)
    local size = 50
    local lightPosition = vec3(room.lightPosition[1])+vec3(math.random()*size-size/2,math.random()*size-size/2,math.random()*size-size/2)
    local lightDirection = lightPosition-origin
    local lightDistance = (lightDirection):len()
    local lightDistance2 = (lightDirection):len2()
    tempRay.origin = origin+direction
    tempRay.direction = lightDirection
    if vec3.dot(lightDirection, direction) < 0 then 
      directLight:set(0,0,0)
    else
      local lightArea = size
      local lightCosine = math.abs(lightDirection.y)
      local pdf = lightDistance2 / (lightCosine * lightArea)
      for t=1,#room.terrain.triangles,1 do 
        local point, distance = cpml.intersect.ray_triangle(tempRay, room.terrain.triangles[t])
        if point and distance<1 then
          directLight:set(0,0,0)
          break 
        end
      end
      return directLight/pdf
    end
    return directLight
  end

  if diffuse then 
    return ray_color( origin+direction, random_in_hemisphere(direction),  room,  depth-1) 
  else

    local lastDistance = math.huge
    local intP,intD,intT,tri
    tempRay.origin = origin
    tempRay.direction = direction
    for t=1,#room.terrain.triangles,1 do 
      local point, distance = cpml.intersect.ray_triangle(tempRay, room.terrain.triangles[t])
      if point and distance<lastDistance  then
        lastDistance=distance
        intP = point 
        intD = distance 
        intT = t
      end
    end
    if intP and intD and intT then 
      local tri=room.terrain.triangles[intT]
      local model = room.terrain.models[tri[4]]
      setValues(tv1,model.mesh:getVertex(tri[5]))
      tempNormal:set(tv1[11],tv1[12],tv1[13])
      tempAlbedo:set(tv1[7],tv1[8],tv1[9])
      return tempAlbedo * ray_color(intP+tempNormal,random_in_hemisphere(tempNormal), room, depth-1);
    end

    -- local hit=room.terrain.bvh:intersectRay(ray.origin,ray.direction)
    -- if hit and hit.triangleIndex then 
    --   local tri=room.terrain.triangles[hit.triangleIndex]
    --   local model = room.terrain.models[tri[4]]
    --   setValues(tv1,model.mesh:getVertex(tri[5]))
    --   -- vec3(tv1[7],tv1[8],tv1[9]) * 0.5
    --   local normal = vec3(tv1[11],tv1[12],tv1[13])
    --   local target = hit.intersectionPoint  + random_in_hemisphere(normal);
    --   return ray_color( {origin=hit.intersectionPoint, direction=target - hit.intersectionPoint},  room,  depth-1)*0.5 
    -- end
  end

  local col = vec3(room.sky.color.r,room.sky.color.g,room.sky.color.b)
  direction:normalize2();
  t = 0.5*(direction.y + 1);
  -- col:scale3(1.0-t)
  return col
end



local function sendVolumes(shader,name,volumes,zero)
	for i=zero and 0 or 1,6 ,1 do 
	  shader:send(string.format("%s%i",name,i),type(volumes)=="table" and volumes[i] or volumes)
	end
end

local function setVolumeFilter(vct,filter)
  for b,buffer in ipairs({"diffuse","normal","light"}) do 
    for i=0,6,1 do 
      vct[buffer][i]:setFilter(filter,filter)
      if i>0 then 
	      vct[buffer][i]:setMipmapFilter(filter,0)
	    end
    end
  end
  vct.occlusion:setFilter(filter,filter)
  vct.occlusion:setMipmapFilter(filter,0)
end

local function mipmapVoxels(vct,buffer,normal,alpha)
	local mainShader = alpha and shaders.voxelNormalmip or shaders.voxelMipmap
	local shader = normal and shaders.voxelLightmip or mainShader
  gfx.setShader(shader)
  gfx.setBlendMode("alpha")
	sendVolumes(shader,"volume", buffer[0])
	if normal then 
		sendVolumes(shader,"normal", normal)
	end
  for mip=1,6,1 do 
  	if mip==2 then 
  		if alpha then 
	  		shader=mainShader
	  		gfx.setShader(shader)
	  	end
  		sendVolumes(shader,"volume", buffer)
  	end
    shader:send("mip",mip)
    local slices = vct.resolution/(2^mip)
  	for slice=1,slices,1 do 
      shader:send("z",slice-1)
    	gfx.setCanvas({
    		{buffer[1], layer=slice, mipmap=mip}, 
    		{buffer[2], layer=slice, mipmap=mip}, 
    		{buffer[3], layer=slice, mipmap=mip}, 
    		{buffer[4], layer=slice, mipmap=mip}, 
    		{buffer[5], layer=slice, mipmap=mip}, 
    		{buffer[6], layer=slice, mipmap=mip}
    	})
    	gfx.clear(0,0,0,0)
    	gfx.draw(vct.dummy,1/mip)
    end
  end
end

function updateLights(view)
  local room = view.room
  if not room.sky.skybox.mesh then 
    local cubeVerts = {}
    local scale = 5000
    for f,face in ipairs(cube) do 
      for v,vert in ipairs(face) do 
        table.insert(cubeVerts,{vert[1]*scale, vert[2]*scale, vert[3]*scale, 1, 0,0, 1,1,1,1})
      end
    end
    room.sky.skybox.mesh =  gfx.newMesh(zformat,cubeVerts,"triangles")
    room.sky.skybox.mesh:setTexture(getImage())
  end
  

  if  (not room.lights and not editor.debug.no_lights) or (not room.lighting.enabled) or LOVR then 
    return 
  end
  trace("Update Lights")

  
  local render = room.lighting.render

  room.lightShader = 
 -- (room.lighting.vct.enabled and not room.lighting.vct.useShadowmap) and shaders.voxelLight or 
  shaders.deferredLight

	updateRoomBounds(room)

  if room.lighting.probes.enabled then 
    room.lighting.probes.gridsize   = 8
    room.lighting.probes.resolution = 16
    room.lighting.probes.amount     = room.lighting.probes.amount or 0
    room.lighting.probes.samples    = 1
    room.lighting.probes.downsample = 2
    room.lighting.probes.dimensions =  1024
    room.lighting.probes.atlasSize  = room.lighting.probes.atlasSize or 0.2
    if not room.lightProbes then 
      room.lighting.probes.lastprobe = 1
      room.lighting.probes.lastray = 1
      
      room.lightProbes = {nocopy=true}
      local probes = room.lightProbes
      local downsample = room.lighting.probes.downsample
      local dimensions = room.lighting.probes.dimensions
      local resolution = room.lighting.probes.resolution
      local samples =room.lighting.probes.samples
      local size = room.lighting.probes.gridsize
      local num = size^3
      local max = room.bounds.max
      local min = room.bounds.min
      local spacing = (max-min)/(size-1)
      local umax,vmax = mf(dimensions/(resolution*6)),mf(dimensions/resolution)
      local ustep,vstep = (resolution*6)/dimensions,resolution/dimensions
      local verts1 = {}
      local n = 0
      local u,v = 0,0
      local scale = 8
      local inset = resolution/4
      local a = 0

      room.bounds.size = (max-min)
      room.lighting.probes.spacing = (max-min)/(size-1)
      print("spacing",inspect(room.lighting.probes.spacing))
      spacing = vec3(spacing.x,spacing.y,spacing.z)

      room.lighting.probes.buffer     = newCanvas(view.bounds.w,view.bounds.h)
      room.lighting.probes.canvas     = newCanvas(dimensions,dimensions)
      room.lighting.probes.projected  = newCanvas(dimensions,dimensions)
      room.lighting.probes.convolved  = newCanvas(dimensions/downsample,dimensions/downsample)
      room.lighting.probes.depth      = newCanvas(dimensions,dimensions,{format="depth16",readable=true})

      room.lighting.probes.buffer:renderTo(function()gfx.clear(0,0,0,0)end)
      -- room.lighting.probes.canvas:setFilter("nearest","nearest")
      -- room.lighting.probes.projected:setFilter("nearest","nearest")
      -- room.lighting.probes.projected:setFilter("nearest","nearest")

      gfx.push("all")
      gfx.setCanvas(room.lighting.probes.canvas)
      gfx.clear(0,0,0,1)
      room.lighting.probes.num = num

      colors = {{1,0,0,1}, {0,1,0,1}, {0,0,1,1},  {0,0,0,1}, {1,1,1,1}, {0,1,1,1}}

      for x=0,size-1,1 do 
        for y=0,size-1,1 do 
          for z=0,size-1,1 do 
            n = n+1 
            local xx,yy,zz = x*spacing.x+max.x-room.bounds.size.x,y*spacing.y+max.y-room.bounds.size.y,z*spacing.z+min.z
            for f=0,5,1 do 
              local u0,u1 = u + f*vstep, u + (f+1)*vstep
              -- local u0,u1 = u , u + vstep
              local v0,v1 = v , v + vstep
              local face = cube[f+1]
              table.insert( verts1, {xx+scale*face[1][1],yy+scale*face[1][2],zz+scale*face[1][3],  1,  u1, v1 ,1,1,1,a}) 
              table.insert( verts1, {xx+scale*face[2][1],yy+scale*face[2][2],zz+scale*face[2][3],  1,  u0, v1 ,1,1,1,a}) 
              table.insert( verts1, {xx+scale*face[3][1],yy+scale*face[3][2],zz+scale*face[3][3],  1,  u0, v0 ,1,1,1,a}) 
              table.insert( verts1, {xx+scale*face[4][1],yy+scale*face[4][2],zz+scale*face[4][3],  1,  u0, v0 ,1,1,1,a}) 
              table.insert( verts1, {xx+scale*face[5][1],yy+scale*face[5][2],zz+scale*face[5][3],  1,  u1, v0 ,1,1,1,a}) 
              table.insert( verts1, {xx+scale*face[6][1],yy+scale*face[6][2],zz+scale*face[6][3],  1,  u1, v1 ,1,1,1,a}) 
              -- gfx.setColor(math.random(),math.random(),math.random(),1)
              -- gfx.setColor(unpackColor(colors[f+1]))
              -- gfx.setColor(x/size,y/size,z/size,1)
              -- gfx.rectangle("fill",u0*dimensions,v0*dimensions,resolution,resolution)
            end
            probes[n] = probes[n] or {}
              probes[n].position  = vec3(xx,yy,zz)
              probes[n].uv        = vec2(u*dimensions,v*dimensions)
              probes[n].active    = false
              probes[n].id        = n
              probes[n].face      = mf(math.random()*5)+1
            u = u + ustep
            if u+ustep > 1 then 
              u=0
              v = v+vstep 
            end
          end
        end
      end

      gfx.pop()

      room.lighting.probes.concube = gfx.newMesh(zformat, lume.slice(verts1,1,36),  "triangles")
      for i=1,36, 1 do 
        room.lighting.probes.concube:setVertexAttribute(i,1,
          verts1[i][1]-probes[1].position.x,
          verts1[i][2]-probes[1].position.y,
          verts1[i][3]-probes[1].position.z,
          1
        )
        room.lighting.probes.concube:setVertexAttribute(i,3, 1,1,1,1)
      end
      room.lighting.probes.mesh       = gfx.newMesh(zformat, verts1,  "triangles")

      -- room.lighting.probes.mesh:setTexture(room.lighting.probes.canvas)
      room.lighting.probes.concube:setTexture(room.lighting.probes.canvas)

      setStaticProbes(room)

      room.lighting.probes.pixel = gfx.newMesh(zformat,4,"fan")
      
      local rays = {}
      local faces = {}
      local sampleres = resolution*samples
      for f=0,5,1 do 
        local face = cube[f+1]
        local mat = mat4.from_perspective(90, 1, 0.1, 10000)
        local mat2 = mat4.from_perspective(90, 1, 0.1, 100)
        table.insert(faces,{
          dir = cubeDirs[f+1][1],
          mat = mat4():mul(
            mat,
            mat4.look_at(mat4(), 
              mat4.identity(), 
              vec3(0), 
              cubeDirs[f+1][1], 
              cubeDirs[f+1][2]
            )
          ),
          mat2 = mat4():mul(
            mat4():mul(
              mat2,
              mat4.look_at(mat4(), 
                mat4(), 
                vec3(0), 
                cubeDirs[f+1][1], 
                cubeDirs[f+1][2]
              )
            ),
            mat4():translate(mat4(),(cubeDirs[f+1][1]*scale*0.95))
          )
        })

        for x=1,sampleres,1 do 
          for y=1,sampleres,1 do 
            table.insert(rays,{
              uv = vec2(f*resolution+x,y),
              direction = vec3.lerp(
                vec3.lerp(vec3(face[3]),vec3(face[5]),(x-0.5)/sampleres), 
                vec3.lerp(vec3(face[2]),vec3(face[1]),(x-0.5)/sampleres), 
                (y-0.5)/sampleres
              )
            })
          end
        end
      end

      room.lighting.probes.rays  = lume.shuffle(rays) 
      room.lighting.probes.rays.nocopy=true
      room.lighting.probes.faces  = faces
      room.lighting.probes.faces.nocopy=true

      room.activeProbes = {nocopy=true}
      for p,probe in ipairs(room.lightProbes) do 
        if probe.active then  
          table.insert(room.activeProbes , probe)
        end
      end

      room.shuffledProbes = lume.shuffle(room.activeProbes)
      room.shuffledProbes.nocopy=true

      
    end

    room.lighting.probes.mesh:setTexture(room.lighting.probes.convolve and room.lighting.probes.canvas or room.lighting.probes.depth)

    gfx.push("all")
    gfx.setShader()
    gfx.setCanvas({room.lighting.probes.canvas,depth=true,depthstencil=room.lighting.probes.depth})
    gfx.setDepthMode("lequal", true)

    local probes = room.shuffledProbes
    local p = room.lighting.probes
    local dimensions = p.dimensions
    local downsample = p.downsample
    local res =p.resolution
    local pixel = p.pixel
    local rays = p.rays
    local faces = p.faces

    local numtris,maxtris,numprobes,numrays = 0,0,0,0
    local v1,v2,v3 = {0,0,0,0, 0,0, 0,0,0,0, 0,0,0},{0,0,0,0, 0,0, 0,0,0,0, 0,0,0},{0,0,0,0, 0,0, 0,0,0,0, 0,0,0}
    local count = 8
    local limit = p.lastprobe+count

    local rasterize = true
    local r,g,b = view.room.sky.color.r,view.room.sky.color.g,view.room.sky.color.b

    if gfx.camera and gfx.camera.vp then --keyDown("r") and keyDown("t") then 
      collectgarbage("stop")
      if rasterize then -- fill probes by rasterization
        room.lighting.probes.rendering = true
        local probesUpdated = {}
        local temp = mat4()
        local n=0
        
        while (p.lastprobe<limit) do
          n=n+1
          numrays=numrays+1
          p.lastprobe=p.lastprobe+1
          if p.lastprobe>#probes then 
            p.lastprobe=1
            limit = limit-#probes
          end
          p.lastray   = (p.lastray==5) and 1 or p.lastray+1
          local probe = probes[p.lastprobe]
          probesUpdated[n] = probe
          probe.face = probe.face==6 and 1 or probe.face+1
          local f     = probe.face
          local face  = faces[f]
          temp:mul(face.mat,temp:translate(mat4(),-probe.position))
          gfx.setDepthMode("always", true)
          gl.Viewport(probe.uv.x+res*(f-1),probe.uv.y,res,res)
          gfx.setColor(1,1,1,1)
          gfx.setShader(shaders.forwardRender) 
          shaders.forwardRender:send("MVP", "column",temp )
          gfx.setMeshCullMode("back")
          gfx.draw3D(room.sky.skybox.mesh)
          gfx.setDepthMode("lequal", true)
          scene.drawPaths(view,room, temp)
        end

        if 1>2 and room.lighting.probes.convolve then 
          gfx.setCanvas(room.lighting.probes.projected)
          gfx.setShader(shaders.forwardRender) 
          gfx.setDepthMode()
          gfx.setColor(1,1,1,1)
          gfx.setMeshCullMode("none")
          for p,probe in ipairs(probesUpdated) do 
            for i=1,36, 1 do 
              local j = (i-1)%6+1
              local x = (j>1 and j<5) and 0 or 1
              local y = (j>2 and j<6) and 0 or 1
              room.lighting.probes.concube:setVertexAttribute(i ,2, (probe.uv.x+(x+mf((i-1)/6))*res)/dimensions, (probe.uv.y+y*res)/dimensions)
            end

            -- room.lighting.probes.concube:setDrawRange(1,36)

            for f=1,6,1 do 
              gl.Viewport(probe.uv.x+res*(f-1),probe.uv.y,res,res)
              shaders.forwardRender:send("MVP", "column",faces[f].mat2)
              gfx.draw3D(room.lighting.probes.concube)
            end
          end

          gfx.setCanvas(room.lighting.probes.convolved)
          gfx.setShader(shaders.blur13) 
          shaders.blur13:send("Size",res/downsample)
          shaders.blur13:send("Resolution",{dimensions,dimensions})
          gfx.setDepthMode()
          local zoom = 2
          for p,probe in ipairs(probesUpdated) do 
            for f=1,6,1 do 
              -- gl.Viewport(probe.uv.x+res*(f-1),probe.uv.y,res,res)
              local x,y,scale = (probe.uv.x+res*(f-1)),(probe.uv.y),res/downsample
              local xx,yy = x+res/2,y+res/2,
              gfx.setScissor(x/downsample,y/downsample,scale,scale)
              gfx.push()
              gfx.scale(1/downsample,1/downsample)
              -- gfx.scale(zoom,zoom)
              -- gfx.translate(x*zoom,y*zoom)
              gfx.draw(room.lighting.probes.projected, xx,yy,0, zoom,zoom, xx,yy )
              gfx.pop()
            end
          end
        end
        room.lighting.probes.rendering = false
      else  -- fill probes by casting rays
        while (p.lastprobe<limit) do
          numrays=numrays+1
          p.lastprobe=p.lastprobe+1
          if p.lastprobe>#probes then 
            p.lastprobe=1
            limit = limit-#probes
          end
          p.lastray = (p.lastray==#rays) and 1 or p.lastray+1
          local probe = probes[p.lastprobe]
          local r = math.max((p.lastray+p.lastprobe)%(#rays),1)
          local ray   = rays[r]
          local hit=room.terrain.bvh:intersectRay(probe.position,ray.direction)
          if hit and hit.triangleIndex then 
            local tri=room.terrain.triangles[hit.triangleIndex]
            local model = room.terrain.models[tri[4]]
            setValues(v1,model.mesh:getVertex(tri[5]))
            gfx.setColor(v1[7],v1[8],v1[9],1/room.lighting.probes.samples)
            gfx.points(probe.uv.x+ray.uv.x,probe.uv.y+ray.uv.y)
          end
        end
      end
    end



    trace(string.format("active probes: %i/%i",#room.activeProbes,room.lighting.probes.num))
    gfx.pop()

    if room.lighting.probes.test and editor and editor.lastIntersect then 
      room.lighting.probes.testing = 0 
      local dist = 99999999
      for p,probe in ipairs(room.activeProbes) do 
        local d = vec3.dist(probe.position, -editor.lastIntersect)
        if d<dist then 
          dist = d 
          room.lighting.probes.testing = probe.id
        end
      end
      if room.lighting.probes.testing>0 then 
        -- print( room.lighting.probes.testing)
        room.lighting.probes.mesh:setDrawRange((room.lighting.probes.testing-1)*36+1,36)
      end
    end
    if room.lighting.probes.testing == 0  or not room.lighting.probes.test then 
      room.lighting.probes.mesh:setDrawRange(1,room.lighting.probes.mesh:getVertexCount())
    end

    gfx.setShader()
  end

  _pathTraceOnly = render.enabled and render.run and (render.lock)
  if (not roomsView) and room.lighting.static.enabled and room.lighting.render.enabled and room.lighting.render.run and view.gbuffer and view.camera.pos and editingLevel then 
    collectgarbage("stop")

    local refresh = false
    render.gpu = render.gpu or true
    render.atlasSize = 256

    if  not room.terrain.triangles then 
      bvhRoom(room)
    end

    if render.gpu then 
    -- print(#room.terrain.triangles)
      if not render.triTex then 
        render.trisData = limage.newImageData(render.atlasSize,render.atlasSize,"rgba16f")
        -- render.gridData = limage.newImageData(render.atlasSize,render.atlasSize,"rgba16f")
        local tris = room.terrain.triangles
        local abs,floor,ceil = math.abs,math.floor,math.ceil
        local bmin,bmax = room.bounds.min,room.bounds.max
        local aabb
        for t=1,#room.terrain.triangles,1 do 
          local tt = t-1
          local tri=tris[t]
          local pixelX,pixelY = tt%render.atlasSize, math.floor(tt/render.atlasSize)*9
          local model = room.terrain.models[tri[4]]
          local x1,y1,z1,w1, u1,v1, r1,g1,b1,a1, nx1,ny1,nz1 = model.mesh:getVertex(tri[5])
          local x2,y2,z2,w2, u2,v2, r2,g2,b2,a2, nx2,ny2,nz2 = model.mesh:getVertex(tri[5]+1)
          local x3,y3,z3,w3, u3,v3, r3,g3,b3,a3, nx3,ny3,nz3 = model.mesh:getVertex(tri[5]+2)
          
          render.trisData:setPixel(pixelX,pixelY,  x1,y1,z1)
          render.trisData:setPixel(pixelX,pixelY+1,x2,y2,z2)
          render.trisData:setPixel(pixelX,pixelY+2,x3,y3,z3)

          render.trisData:setPixel(pixelX,pixelY+3,r1,g1,b1)
          render.trisData:setPixel(pixelX,pixelY+4,r2,g2,b2)
          render.trisData:setPixel(pixelX,pixelY+5,r3,g3,b3)

          render.trisData:setPixel(pixelX,pixelY+6,nx1,ny1,nz1)
          render.trisData:setPixel(pixelX,pixelY+7,nx2,ny2,nz2)
          render.trisData:setPixel(pixelX,pixelY+8,nx3,ny3,nz3)
        end

        -- for t=1,#room.terrain.triangles,1 do 
        --   local tt = t-1
        --   local tri=tris[t]
        --   for x=0,8,1 do 
        --     for y=0,8,1 do 
        --       for z=0,8,1 do 
        --         triangle_aabb(tri,aabb)
        --       end
        --     end
        --   end
        -- end

        
        render.trisTex  = gfx.newImage(render.trisData)
        -- render.gridTex  = gfx.newImage(render.gridData)
        -- shaders.pathTrace:send('numTris',#room.terrain.triangles)
        -- shaders.pathTrace:send('triangles',render.trisTex)
      end
    end


    local v1,v2,v3 = {0,0,0,0, 0,0, 0,0,0,0, 0,0,0},{0,0,0,0, 0,0, 0,0,0,0, 0,0,0},{0,0,0,0, 0,0, 0,0,0,0, 0,0,0}

    render.block = render.block or vec2()
    render.numrays = render.numrays or 32
    render.depth = render.depth or 4
    render.scale = render.scale or 1
    render.blurSize = render.blurSize or 2
    render.samples = 1
    render.exposure = render.exposure or 1
    local resx,resy = view.bounds.w/render.scale,view.bounds.h/render.scale
    render.tile = resx
    if (not render.angle) or (not render.pos) or (not render.fov) then 
      render.pos = view.camera.pos:clone()
      render.angle = vec3(view.camera.angle)
      render.fov = view.camera.fov
    end
    if not render.res or resx~=render.res.x or resy~=render.res.y then 
      render.res=vec2(resx,resy)
      render.rays = nil
      refresh = true
      render.dummy = newCanvas(render.tile,render.tile,{mipmaps="none"})
      render.dummy2 = newCanvas(render.tile,render.tile,{mipmaps="none"})
      render.canvas = newCanvas(resx,resy,{format = "rgba16f"})
      render.canvas2 = newCanvas(resx,resy)
      render.canvas:setFilter("nearest","nearest")
      render.canvas2:setFilter("nearest","nearest")
    end
    if not render.gpu and not render.rays then 
      
      render.rrays = {nocopy=true}
      for x=1,resx,1 do 
        for y=1,resy,1 do 
          local ray = {uv = vec2(resx-x,y) ,direction = vec3(x/resx-0.5,(y/resy-0.5)/game.aspectRatio,1)}
          table.insert(render.rrays,ray)
        end
      end
      render.rays = lume.shuffle(render.rrays) 
      render.rays.nocopy=true
      
      

      render.data = render.canvas:newImageData()
      render.image = gfx.newImage(render.data)

      render.lastray = 1
    end

    local ambc,amba = room.lighting.ambient.color,room.lighting.ambient.amount

    local diff = (not render.lock) and (
         (view.camera.pos-render.pos):len()>0 
      or (vec3(view.camera.angle)-render.angle):len()>0 
      or math.abs(render.fov- view.camera.fov)>0
      or render.amb~= (ambc.r%0.03+ambc.g^2+ambc.b)*amba+math.sin(render.exposure*77)
    )
    
    if (not render.lock) then 
      render.pos = view.camera.pos:clone()
      render.angle = vec3(view.camera.angle)
      render.fov = view.camera.fov
      render.amb = (ambc.r%0.03+ambc.g^2+ambc.b)*amba+math.sin(render.exposure*77)
    end


    if refresh or (view.camera.stopped and diff) then 
      print("refresh pathtracing buffers")
      render.lastray = 1
      render.block.x,render.block.y = 0,0 
      render.passes = 0
      gfx.push("all")
      gfx.setCanvas(render.canvas)
      gfx.clear(0,0,0,0)
      gfx.setCanvas(render.canvas2)
      gfx.clear(0,0,0,0)

      if not render.gpu then 
        render.position_buffer = view.gbuffer.position:newImageData()
        render.normal_buffer = view.gbuffer.normal:newImageData()
        render.diffuse_buffer = view.gbuffer.diffuse:newImageData()
        render.data = render.canvas:newImageData()
      end

      gfx.pop()
    end
    local rays = render.shuffle and render.rays or render.rrays 
    -- print("stopped",view.camera.stopped,diff)
    -- local start = ffi.new("float[8]")

    
    render.rendering =  render.lock or  (view.camera.stopped and not diff) 
    if render.lock or  (view.camera.stopped and not diff) then 
      ----------------------------- HARDWARE
      if render.gpu then 
        render.block.y = (render.block.x+render.tile>resx) and (render.block.y+render.tile>resy and 0 or render.block.y+render.tile) or render.block.y
        render.block.x = render.block.x+render.tile>resx and 0 or render.block.x +render.tile
        render.newPass = (render.block.y == 0 and render.block.x == 0)
        render.passes = render.newPass and render.passes+1 or render.passes
        -- print(render.block.x,render.block.y ,render.passes)

        shaders.pathTrace:send('numTris',#room.terrain.triangles)
        shaders.pathTrace:send('time',main_dt)
        shaders.pathTrace:send('gbuffer_diffuse',view.gbuffer.diffuse)
        shaders.pathTrace:send('gbuffer_position',view.gbuffer.position)
        shaders.pathTrace:send('gbuffer_normal',view.gbuffer.normal)
        shaders.pathTrace:send('triangles',render.trisTex)
        shaders.pathTrace:send("ambientColor",{ambc.r*amba,ambc.g*amba,ambc.b*amba})
        -- shaders.pathTrace:send("skyColor",colorTable(room.sky.color))
        shaders.ptmap:send('image_size',{resx,resy})
        shaders.ptmap:send('samples',render.passes)
        shaders.ptmap:send('exposure',render.exposure)
        shaders.ptmap:send('blur',render.blurEnabled and render.blurSize or 0)
        shaders.ptmap:send('gbuffer_position',view.gbuffer.position)
        shaders.ptmap:send('gbuffer_normal',view.gbuffer.normal)
        shaders.ptmap:send('eye_position',vec3Table2(-zcam.pos))

        gfx.push("all")
        gfx.setBlendMode("add")
        gfx.setShader(shaders.pathTrace)
        gfx.setCanvas(render.canvas)     
        gfx.draw(render.dummy,render.block.x,render.block.y)


        if render.newPass then 
          gfx.setBlendMode("alpha")
          gfx.setShader(shaders.ptmap)
          gfx.setCanvas(render.canvas2)
          gfx.draw(render.canvas)
        end
        
        gfx.pop()
      ----------------------------- SOFTWARE
      else 
        local r2,g2,b2,a2

        local i = 1 
        while true do

          render.lastray = (render.lastray==#rays) and 1 or render.lastray+1
          local pray = rays[render.lastray]
          local x,y,z = render.position_buffer:getPixel(
            math.clamp(0,renderWidth-1,mf(pray.uv.x*render.scale)),
            math.clamp(0,renderHeight-1,mf(pray.uv.y*render.scale))
          )
          local u,v,w = render.normal_buffer:getPixel(
            math.clamp(0,renderWidth-1,mf(pray.uv.x*render.scale)),
            math.clamp(0,renderHeight-1,mf(pray.uv.y*render.scale))
          )
          local r,g,b = render.diffuse_buffer:getPixel(
            math.clamp(0,renderWidth-1,mf(pray.uv.x*render.scale)),
            math.clamp(0,renderHeight-1,mf(pray.uv.y*render.scale))
          )
          local col = vec3(r,g,b)
          local dr,dg,db,da = render.data:getPixel(pray.uv.x,pray.uv.y-1)
          if x==0 then 
            col = vec3(room.sky.color.r,room.sky.color.g,room.sky.color.b)
            if da==0 then 
              local direction =   vec3.rotate(vec3.rotate(pray.direction+vec3(0,0,-lerp(0,0.5,(render.fov-35)/35 )), render.angle.y  , vec3.unit_x), pi-render.angle.x  , vec3.unit_y)
              direction:normalize2();
              t = 0.5*(direction.y + 1);
              -- col:scale3(1.0-t)
              render.data:setPixel(pray.uv.x,pray.uv.y-1, dr+col.x,dg+col.y,db+col.z, da+1)
            end
            -- i=i+1
          else
            i=i+1
            col = ray_color(vec3(x,y,z),vec3(u*2-1,v*2-1,w*2-1),room,render.depth,col)
            render.data:setPixel(pray.uv.x,pray.uv.y-1, dr+col.x,dg+col.y,db+col.z, da+1)
          end


          if i==render.numrays then 
            break
          end
          -- gfx.points(ray.uv.x+0.0001,ray.uv.y+0.0001)
        end
        
        -- shaders.interp:send("image_size",{renderWidth/2,renderHeight/2})
        -- gfx.setShader(shaders.interp)
        render.image:replacePixels(render.data)
        gfx.push("all")
        gfx.setShader(shaders.rtmap)
        gfx.setCanvas(render.canvas2)
        gfx.draw(render.image)
        gfx.pop()
      end
    end
    -- collectgarbage("restart")
  end

  if room.lighting.vct.enabled then 
  	local vct = room.lighting.vct
    local anisotropicBase = false
    vct.resolution = 128--vct.resolution or 64
    vct.bounces = vct.bounces or 1
		room.lighting.vct.show = room.lighting.vct.show or "diffuse"
    local buffers = {"diffuse","normal","light"}
    local formats = {"srgba8","rgba8","rgba16f"}
    local res = vct.resolution
    local rand = math.random

    if (not vct.buffer) then 
	    vct.ortho =  mat4()
	    vct.inverse =  mat4()
	    vct.sliceBox = {min=vec3(),max=vec3()}
    	vct.staticRendered=false
    	vct.drawCalls = 0
      vct.slice = 0
      vct.dir = 1
      vct.screen = {w=view.bounds.w,h=view.bounds.h}
      
      local cubeVerts = {}
      local scale = 1
      for f,face in ipairs(cube) do 
        for v,vert in ipairs(face) do 
          table.insert(cubeVerts,{vert[1]*scale, vert[2]*scale, vert[3]*scale, 1, 0,0, 1,1,1,1})
        end
      end
      
      vct.mesh =  gfx.newMesh(zformat,cubeVerts,"triangles")
      

      for b,buffer in ipairs(buffers) do 
        vct[buffer] = {}
        for i=1,6,1 do 
          table.insert(vct[buffer],gfx.newCanvas( res/2, res/2, res/2, {format=formats[buffer],type="volume",mipmaps="manual"}))
        end
        vct[buffer][0] = gfx.newCanvas( res, res, res, {type="volume",mipmaps="none"})
      end

      vct.occlusion = gfx.newCanvas( res, res, res, {type="volume",format="r8",mipmaps="auto"} )

      vct.diffuse3D = gfx.newCanvas( res, res, res, {type="volume",mipmaps="none"} )
      vct.diffuse3D:setFilter("nearest","nearest")

      vct.normal3D = gfx.newCanvas( res, res, res, {format="rgba8",type="volume",mipmaps="none"} )
      vct.normal3D:setFilter("nearest","nearest")

      vct.buffer= newCanvas( view.bounds.w,view.bounds.h )
      vct.buffer:setFilter("nearest","nearest")
      vct.dummy = newCanvas( res,res )
      vct.dummy:renderTo(function()
        gfx.clear(1,1,1,1)
      end)

    end

    if vct.screen.w ~= view.bounds.w or vct.screen.h ~= view.bounds.h then 
    	vct.buffer= newCanvas( view.bounds.w,view.bounds.h )
      vct.buffer:setFilter("nearest","nearest")
    end

    vct.scale = {
      (room.bounds.max.x-room.bounds.min.x)/2,
      (room.bounds.max.y-room.bounds.min.y)/2,
      (room.bounds.max.z-room.bounds.min.z)/2
    }
    vct.position = {
      -(room.bounds.max.x-room.bounds.min.x)/2+room.bounds.max.x,
      -(room.bounds.max.y-room.bounds.min.y)/2+room.bounds.max.y,
      (room.bounds.max.z-room.bounds.min.z)/2+room.bounds.min.z
    }
    
    local xStep,yStep,zStep = 
      (room.bounds.max.x-room.bounds.min.x)/res,
      (room.bounds.max.y-room.bounds.min.y)/res,
      (room.bounds.max.z-room.bounds.min.z)/res

    -- trace(room.bounds.min.x,room.bounds.max.x)
    -- trace(room.bounds.min.y,room.bounds.max.y)
    -- trace(room.bounds.min.z,room.bounds.max.z)

    setVolumeFilter(vct,"nearest")
    gfx.push("all")
    
    gfx.setColor(1,1,1,1)
    gfx.setShader(shaders.forwardRender) 
    
    gfx.setDepthMode("less", true)

   
    
    local dirmax = 6
    for i =1, vct.resolution==64 and 16 or 8,1 do
      vct.dir = vct.slice ==res and (vct.dir==dirmax and 1 or vct.dir + 1) or vct.dir
      vct.slice = vct.slice==res and 1 or vct.slice + 1

      -- trace("vdir",vct.dir)
      -- trace("vslice",vct.slice)


 			----------------------------------
 			-- possibly draw from each axis once with both sides as outputs?
 			----------------------------------
      if vct.dir ==1 or vct.dir ==4 then 
        vct.ortho:mul(
          mat4.from_ortho(
            -room.bounds.min.z,-room.bounds.max.z,
            room.bounds.max.y, room.bounds.min.y, 
            -room.bounds.max.x+xStep*(vct.slice-1),-room.bounds.max.x+xStep*vct.slice
          ) ,
          mat4():rotate(mat4(),-pi/2,vec3(0,1,0))
        )
      elseif vct.dir ==2 or vct.dir ==5 then 
        vct.ortho:mul(
          mat4.from_ortho(
             room.bounds.min.x, room.bounds.max.x, 
            room.bounds.max.z, room.bounds.min.z, 
            room.bounds.min.y+yStep*(vct.slice-1),room.bounds.min.y+yStep*vct.slice
          ) ,
          mat4():rotate(mat4(),-pi/2,vec3(1,0,0))
        ) 
      elseif vct.dir ==3 or vct.dir ==6 then 
        vct.ortho = mat4.from_ortho(
          room.bounds.min.x, room.bounds.max.x, 
          room.bounds.max.y, room.bounds.min.y, 
          -room.bounds.min.z-zStep*(vct.slice-1),-room.bounds.min.z-zStep*vct.slice
        )   
      end
      
      vct.inverse:invert(vct.ortho)
      vct.sliceBox.min,vct.sliceBox.max = 
      	vct.inverse*vec3(-1),vct.inverse*vec3(2)

      -- draw geometry to temp gbuffer slice
      -- gfx.setMeshCullMode(vct.dir>3 and "back" or "front")
      gfx.setMeshCullMode("none")
      gl.Disable(gle.CULL_FACE)
      vct.back = vct.dir>3
      shaders.forwardRender:send("MVP", "column", vct.ortho )

      if not vct.staticRendered then 
      	vct.rendering = true
        trace("voxelizing statics")
        gfx.setCanvas({
        	{vct.diffuse3D	, layer=vct.slice},
        	{vct.normal3D		, layer=vct.slice}
        })
        gfx.clear(0,0,0,0)
        scene.drawPaths(view,room, vct.ortho)
        gfx.setShader()
        vct.rendering = false
      end

      if vct.slice == res and not vct.staticRendered  then 
      -- flip directional volumes and gen mips
        gfx.setShader(shaders.voxelFlip)
        shaders.voxelFlip:send( "dir",vct.dir)
        for b,buffer in ipairs({"diffuse","normal"}) do 
	      	gfx.push()
	      	gfx.setBlendMode("alpha","premultiplied")
          shaders.voxelFlip:send( "volume",vct[buffer.."3D"])
          for slice = 1 , res , 1 do 
            shaders.voxelFlip:send( "z", (slice-1)/res)
            gfx.setCanvas(vct[buffer][0],	slice)
            gfx.draw(vct.dummy)
          end
          gfx.pop()
        end


        if  vct.dir==dirmax then 
        	-- use diffuse alpha for occlusion base
	        for slice = 1 , res , 1 do 
	        	gfx.setShader(shaders.voxelAlpha)
	        	gfx.setCanvas(vct.occlusion,slice)
	        	shaders.voxelAlpha:send("z",(slice-1)/res)
	        	shaders.voxelAlpha:send("volume",vct.diffuse[0])
	        	gfx.draw(vct.dummy)
	        end

        	mipmapVoxels(vct,vct.diffuse)
        	mipmapVoxels(vct,vct.normal,nil,true)
          vct.staticRendered = true
          print("vct.drawCalls",vct.drawCalls)
          vct.drawCalls = 0
        end
      end


      vct.back = false

      -- lights/emissives     
      if vct.staticRendered  then 
	      shaders.voxelInject:send("volumeScale",    vct.scale)
	      shaders.voxelInject:send("volumePosition", vct.position)
	      shaders.voxelInject:send("voxelVolumeSize", vct.resolution)
	      shaders.voxelInject:send("voxelVolumeInverseSize", 1/vct.resolution)
	      shaders.voxelInject:send("lightPosition",  unpack(room.lightPosition))
	      shaders.voxelInject:send("lightColor",     unpack(room.lightColor))
	      shaders.voxelInject:send("lightPower",     unpack(room.lightPower))
	      shaders.voxelInject:send("lightRange",     unpack(room.lightRange))
	      gfx.push("all")
	      gfx.setShader(shaders.voxelInject)
	      
	      shaders.voxelInject:send( "occlusion",vct.occlusion)
	      shaders.voxelInject:send( "z",(vct.slice-1)/res)
	      shaders.voxelInject:send( "normal",vct.normal[0])
	      shaders.voxelInject:send( "diffuse",vct.diffuse[0])
	      gfx.setCanvas(vct.light[0],vct.slice)
	      gfx.clear(0,0,0,0)
	      gfx.draw(vct.dummy)
	      gfx.pop()
	      if vct.slice==res then 
	      	mipmapVoxels(vct,vct.light,vct.normal)
	      end
	    end
    end

    -- bounce light
    if vct.staticRendered then 

    end

    -- fade

    gfx.pop()
  end

  if #room.lightPosition>0 then 
    local n = 1 
    local factor = 0.2
    for l=1,#room.lightPosition,1 do 
      n= n +3
      local d = l*40
      local t = editor.debug.move_testlights and time*0.1 or 1
      local r,g,b = hsvToRgb(math.sin(t+n*l^2)/2,0.6,1)
        -- room.lightColor[l]   = {r,g,b}
      if not (room.lights[l] and room.lights[l].entity) then 
        room.lightPosition[l]   = {
          -250+factor*math.cos(t+1+d+d^2)*d,
          math.cos(math.sin(l^2+t+frandom(d)))*800-900,
          factor*math.sin(t-d)*d+400
        }
        room.lightRange[l]=600
        room.lightColor[l]={r,g,b}
      end
    end
  end

  local dynamicShadow,shadowStatic = blankCanvas,blankCanvas
  
  for l,light in lume.ripairs(room.lights) do
    local entity = scene.getEntityByID(light.entity) 
    if not (entity and light.isLight)  then 
      table.remove(room.lights,l)
    end
  end
  trace("num lights",#room.lights)


  for l,light in ipairs(room.lights) do 
    local entity = scene.getEntityByID(light.entity) 
    light.position = entity.selected and entity.nextPosition or entity.position
    light.cache   = entity.light.cache
    light.shadow  = entity.light.shadow
    light.type = light.type or "point"
    light.selected = entity.selected
    if not (light.mats and light.mats[1]) then 
	    -- print("lighttarget 1",inspect(light.target))
    end
    light.target = light.target or vec3Table(light.position+vec3(0,light.range,0))
    light.direction = light.direction or vec3(vec3.sub2(light.position,light.target))
    light.aperture = light.aperture or 45
    light.falloff = light.falloff or 0
    local typeChanged = light.ptype ~= light.type

    if light.targetEntity then 
    	local targetEntity = scene.getEntityByID(light.targetEntity) 
    	trace("TARGET 1")
	    if targetEntity then 
	    	trace("TARGET 22")
	    	local either = entity.selected or  targetEntity.selected
	    	local both   = entity.selected and targetEntity.selected
		    if light.type=="point" or (not either) or (#selectedEntities>1) or both then -- and not both) or (#selectedEntities>2) then
		    	scene.deleteEntity(targetEntity)
			  	light.targetEntity = nil 
		    else
		    	trace("TARGET 333")
		    	if light.lock then 
			    	vec3.set(light.target,targetEntity.selected and targetEntity.nextPosition or targetEntity.position)
			    else
			    	if  targetEntity.selected then 
			    		vec3.set(light.target,targetEntity.selected and targetEntity.nextPosition or targetEntity.position)
				    	light.direction:set(vec3.sub2(light.position,light.target))
				    else
				    	targetEntity:setPosition(light.position-light.direction*light.range)
				    end
			    end
		    end
		  else
		  	light.targetEntity=nil
		  end
		else
    	-- print("LIGHT TARG 1")
			if light.type=="spot" and entity.selected and #selectedEntities==1 then
				print("LIGHT TARG 2")
	    	local targetEntity = editor.newEntity(nil,{
	    		position=light.lock and vec3(light.target) or (light.position-light.direction*light.range),
	    		simple=true,
	    	})
	    	light.targetEntity = targetEntity.uid
	    end
	  end

	  if light.lock then 
	  	light.direction:set(vec3.sub2(light.position,light.target))
	  	-- light.direction.y=-light.direction.y
	  end
	  light.direction = vec3.normalize(light.direction)
	  -- light.direction = vec3(math.sin(time),math.cos(time),math.sin(time*0.8)+math.cos(time+2.1))
    

    if light.range~=light.prange or light.paperture~=light.aperture or typeChanged or not light.mats then 
    	-- cone :)
    	if light.type=="spot" then 
				local verts = {}
				local w = math.tan(math.rad(light.aperture/2))*light.range*1.1
				-- print("aperture",math.rad(light.aperture))
				local y1,y2,y3 = 0,light.range,light.range--+math.sin(math.rad(light.aperture))*light.range*0.5
				local step = pi/16
				for a=0,2*pi-step,step do 
					local x1,z1 = math.sin(a)*w,math.cos(a)*w
					local x2,z2 = math.sin(a+step)*w,math.cos(a+step)*w
					table.insert(verts,{0,  0,y1,1, 1,1,1,1})
					table.insert(verts,{x2,z2,y2,1, 1,1,1,1})
					table.insert(verts,{x1,z1,y2,1, 1,1,1,1})

					table.insert(verts,{x1,z1,y3,1, 1,1,1,1})
					table.insert(verts,{x1,z1,y2,1, 1,1,1,1})
					table.insert(verts,{x2,z2,y2,1, 1,1,1,1})

					table.insert(verts,{x1,z1,y3,1, 1,1,1,1})
					table.insert(verts,{x2,z2,y2,1, 1,1,1,1})
					table.insert(verts,{x2,z2,y3,1, 1,1,1,1})

					table.insert(verts,{0,  0,y3,1, 1,1,1,1})
					table.insert(verts,{x1,z1,y3,1, 1,1,1,1})
					table.insert(verts,{x2,z2,y3,1, 1,1,1,1})
				end
				light.mesh  =  gfx.newMesh(zformat3,verts,"triangles")
			end
			if light.type~="point" then 
				light.line  =  gfx.newMesh(zformat3,{
					{0,0,0,1, 1,1,1,1},
					{0,0,light.range,1, 1,1,1,1},
					{0,0,0,1, 1,1,1,1},
				},"triangles")
			end
    	light.remake = true
    end


    if light.shadow then 

      if (light.remake and not contextMenu.open) or typeChanged or not light.mats then 
      	light.mats = {nocopy=true}
      	light.remake = false
		    light.resolution = light.type=="direction" and 1024 or mf(math.sqrt(light.range)*40)

      	if light.canvas then 
      		light.canvas:release()
      	end

        light.cam    = nil
        print("light.resolution",light.resolution)
        light.canvas = newCanvas(light.resolution * (light.type=="point" and 6 or 1),light.resolution,{format = "depth16",readable=true})
        

		    if light.type=="point" then 
					local mat = mat4.from_perspective(90, 1, 0.1, light.range)
					for f=1,6,1 do 
					  table.insert(light.mats, mat4():mul(mat, mat4.look_at(mat4(), mat4.identity(), vec3(0), cubeDirs[f][1], cubeDirs[f][2] )))
					end
				end

				local num = 500
				if light.type=="spot" then
					light.mats[1] = mat4.from_perspective(light.aperture, 1, 1, light.range)
				end
				if light.type=="direction" then 
					light.mats[1] = mat4.from_ortho(num, -num, num, -num, 0.1, light.range)
				end
			end

      if light.canvas then 
        if light.soft then 
  	    	light.canvas:setDepthSampleMode()
  	    	light.canvas:setFilter("linear","linear")
  	    else
  	    	light.canvas:setDepthSampleMode( "less" ,false)
  	    end

        dynamicShadow = light.canvas
        if editingLevel then 
          shadowStatic = light.canvas
        end

        drawShadow(view,room,light)
      end

      if light.cache and not editingLevel  then 
        if not light.canvasCache then 
          light.canvasCache = newCanvas(light.resolution,light.resolution,{format = "depth16", readable=true})
          if LOVE then light.canvasCache:setDepthSampleMode( "less" ,false) end
          if LOVE then light.canvasCache:setFilter("linear","linear") end
          drawShadow(view,room,light,true)
        end
        shadowStatic = light.canvasCache
      else
        if light.canvasCache then 
          light.canvasCache:release()
          light.canvasCache=nil
        end
      end

      light.ptype = light.type
      light.prange = light.range
      light.paperture = light.aperture
    end

    light.color = light.color or  {r=1,g=1,b=1}
    room.lightPosition[l]   = vec3Table2(light.position)
    room.lightColor[l]      = {light.color.r,light.color.g,light.color.b}
    room.lightPower[l]      = entity.light.strength
    room.lightRange[l]      = light.range
    room.lightShadowed[l]   = light.shadow 
    room.lightType[l]       = shadowTypeNums[light.type] or 1
    room.lightQuad[l] 			= light.quad or {0,0,64,64}
    room.lightAperture[l] 	= light.aperture
    room.lightFalloff[l]    = light.falloff
    room.lightDirection[l]  = vec3Table2(light.direction)
  end

  if view.bounds and view.bounds.w then 
    shaders.forwardRender:send( "canvasSize", {view.bounds.w,view.bounds.h})
  end

  if forwardShading then 
    room.lightShader:send( "lightPosition",  unpack(room.lightPosition))
    room.lightShader:send( "lightColor",     unpack(room.lightColor))
    room.lightShader:send( "lightPower",     unpack(room.lightPower))
    room.lightShader:send( "lightRange",     unpack(room.lightRange))
    room.lightShader:send( "lightShadowed",  unpack(room.lightShadowed))
  end
end

function isLit(room)
  return room.lighting.enabled and room.lighting.dynamic.enabled
end

local shadowMat = mat4()
function drawShadow(view,room,light,cache)
	if not light.position then return end
  scene.drawingShadow   = true 
  gfx.setCanvas({depth=true,depthstencil=light.canvas})
  gfx.setDepthMode("lequal", true)
  gfx.clear(1,1,1,1,0,1)
  shaders.shadowStatic:send("lightPosition",vec3Table2(light.position))
  shaders.shadowStatic:send("lightRange",light.range)
  for i=1,light.type=="point" and 6 or 1, 1 do

  	light.matrix = mat4(light.mats[i])
		if light.type~="point" then 
			light.matrix:mul(light.matrix,shadowMat:look_at(nil, vec3(0), -light.direction, vec3(0,1,0)))
			-- pointMatrix(light.matrix,light.direction)
		end
		light.matrix:mul(light.matrix,shadowMat:translate(mat4(),-light.position))
    gfx.setDepthMode("always", true)
    gl.Viewport(light.resolution*(i-1),0,light.resolution,light.resolution)
    gfx.setColor(1,1,1,1)
    
    gfx.setDepthMode("lequal", true)

  	-- draw statics
    if (not light.cache) or (light.cache and cache) or (editingLevel) then 
      scene.drawPaths(view,room, light.matrix)
    end

    -- draw dynamics
    -- if (not light.cache) or (light.cache and not cache) then 
    --  shaders.forwardRender:send("scene.drawingShadow",true)
    --    gfx.setMeshCullMode( "front" )
    --    drawRoomStuff   (view,room)
    --  shaders.forwardRender:send("scene.drawingShadow",false)
    -- end
  end
  scene.drawingShadow   = false
end


function pointMatrix(m,v)
  local ang1 = vec2.angle_between(vec2(0,1),vec2(v.x,v.z))
  local x = vec2(v.x,v.z):len()
  local ang2 = vec2.angle_between(vec2(1,0),vec2(x,v.y))
	rotateMatrix(m,vec3(pi+(v.x>0 and ang1 or -ang1),(v.y>0 and ang2 or -ang2),0))
	return m
end
local lightMat = mat4()
function drawLight(view,room,l)
	local light = room.lights[l]
  local dist = vec3.dist(-view.camera.pos,vec3(room.lightPosition[l]))
  
  gfx.setColor(room.lightColor[l])
  lightMat = lightMat:identity()
  local proxy
  
  if room.lightType[l]<3 then 
	  if room.lightType[l]==1 then 
	  	gfx.setMeshCullMode(dist<room.lightRange[l] and "back" or "front")
		  lightMat:scale(lightMat,vec3(room.lightRange[l]))
		  if light then 
		  	light.mesh = game.lightSphere
	  	end
	  	proxy =  game.lightSphere
	  else
	  	gfx.setMeshCullMode("back")
	  	proxy =  light and light.mesh or game.lightSphere
	  	pointMatrix(lightMat,light.direction)
	  end
	  lightMat:translate(lightMat,vec3(room.lightPosition[l]))
	  lightMat:transpose(lightMat)
  else
  	proxy =  patterns.atlas
  end
  local shadowed = (room.lightShadowed[l]==true and light and light.canvas~=nil)
  room.lightShader:send("modelMatrix",    lightMat)
  room.lightShader:send("lightPosition",  room.lightPosition[l])
  room.lightShader:send("lightColor",    room.lightColor[l])
  room.lightShader:send("lightPower",    room.lightPower[l])
  room.lightShader:send("lightRange",    room.lightRange[l])
  shaders.deferredLight:send("lightAperture",    room.lightAperture[l])
  shaders.deferredLight:send("lightFalloff",    room.lightFalloff[l])
  shaders.deferredLight:send("lightType",    room.lightType[l])
  shaders.deferredLight:send("lightDirection",  room.lightDirection[l])
  shaders.deferredLight:send("lightShadowed", shadowed)
  shaders.deferredLight:send("lightSoftness",   light and light.soft and (light.softness or 0)+0.0001 or 0 )
  shaders.deferredLight:send("lightSamples",    light and light.soft and (light.softness or 0)*65+1 or 0)
  if shadowed then 
	 	shaders.deferredLight:send("lightMatrix", 	"column",		light.matrix )
	 	if light.soft then 
			shaders.deferredLight:send("lightShadow2",   		light.canvas )
		else
			shaders.deferredLight:send("lightShadow",   		light.canvas )
		end
	end
  gfx.draw(proxy)

  shaders.deferredLight:send("lightType",    1)
end

function renderLights(view)
  local room = view.room
  local vct = room.lighting.vct
  local probes = room.lighting.probes
  local render = room.lighting.render

  if (render.enabled and render.run) or not view.gbuffer then return end


  bench.push("lights")

  if editor.debug.no_lights or not room.lighting.enabled or not room.lighting.dynamic.enabled then 
    gfx.setCanvas(view.canvas)
    gfx.clear(room.sky.color.r,room.sky.color.g,room.sky.color.b,1)
    gfx.draw(view.gbuffer.diffuse)
  else

    editor.debug.num_test_lights = editor.debug.num_test_lights or 20
    local lights = editor.debug.test_lights and math.min(#room.lightPosition,editor.debug.num_test_lights) or lume.count(room.lights)

    if not game.lightSphere then 
	    game.lightSphere =   gfx.newMesh(zformat2,obj.load("models/sphere.obj"),"triangles")
		end

    -- setViewCanvas(view)
  
    gfx.setCanvas(view.gbuffer.light)
    gfx.clear     (0,0,0,1,false,false)
    gfx.setDepthMode("less", false)
    gfx.setColor(1,1,1,1)
    gfx.setShader(room.lightShader)

    shaders.deferredLight:send("gbuffer_diffuse", view.gbuffer.diffuse)
    room.lightShader:send("gbuffer_position", view.gbuffer.position)
    room.lightShader:send("gbuffer_normal",   view.gbuffer.normal)
    room.lightShader:send("projectionMatrix",zcam.perspective)
    room.lightShader:send("viewMatrix",zcam.viewMatrix)
    room.lightShader:send("eyePosition", vec3Table2(-zcam.pos))

    -- shaders.deferredLight:send("shadowAtlasSize",4096)
    if altDown and not (vct.enabled  and vct.showVoxels)then 
    	local names = {"diffuse","normal","light","proxy geometry","traced"}
    	for i=1,vct.enabled and 5 or 4 ,1 do 
    		if keyDown(tostring(i)) then 
          trace(names[i])
		    	editor.tempTip(names[i])
		    end
	    end
    end

    if (altDown and keyDown("4")) then 
      gfx.setDepthMode("less", false)
      gfx.setCanvas(view.gbuffer.diffuse)
      room.lightShader:send("debug",   true)
      gfx.setWireframe(true)
    else
      room.lightShader:send("debug",   false)
      gfx.setBlendMode("add")
    end

  --   if vct.enabled and editor.lastIntersect and not contextMenu.open then 
  --   	mouse.pushClick(function()
		--     room.testLightPosition={-editor.lastIntersect.x,-editor.lastIntersect.y-60,-editor.lastIntersect.z}
		--   end)
	 --  end
	 --  if room.lights[1] and room.testLightPosition then 
		--   room.lights[1]:setPosition(room.testLightPosition)
		-- end

		if vct.enabled then 
			setVolumeFilter(vct,"linear")
    	for s,shader in ipairs({shaders.voxelTrace,shaders.voxelOcclusion,shaders.voxelLight}) do
        shader:send("volumeScale",vct.scale)
        shader:send("volumePosition",vct.position)
        shader:send("voxelVolumeSize", vct.resolution)
        shader:send("voxelVolumeInverseSize", 1/vct.resolution)
        shader:send("gbuffer_position", view.gbuffer.position)
        shader:send("gbuffer_normal",  view.gbuffer.normal)
        if shader ~=  shaders.voxelTrace then 
	        shader:send("vbuffer_occlusion",vct.occlusion)
	      else 
          shader:send("eyePosition",  {-zcam.pos.x,-zcam.pos.y,-zcam.pos.z})
        end
      end
		end

    for l=1,lights,1 do 
      drawLight(view,room,l)
    end

    gfx.reset()
    gfx.setColor(1,1,1,1)
    -- gfx.setDepthMode("always",false)
    gfx.setCanvas(view.canvas)
    local ambc,amba = room.lighting.ambient.color,room.lighting.ambient.amount
    	local r,g,b = ambc.r*amba,ambc.g*amba,ambc.b*amba
			-- gfx.clear 		(r,g,b,1)
     -- gfx.clear(room.sky.color.r*room.lighting.ambient.amount,room.sky.color.g*room.lighting.ambient.amount,room.sky.color.b*room.lighting.ambient.amount,1)


    if room.sky.skybox.mesh then 
      gfx.push("all")
      gfx.setDepthMode("less", true)
      room.lightShader:send("debug",   true)
      gfx.setShader(room.lightShader)
      local lightMat = lightMat:identity()
      lightMat:scale(lightMat,vec3(1/5))
      lightMat:translate(lightMat,vec3(zcam.pos))
      lightMat:transpose(lightMat)
      room.lightShader:send("modelMatrix",   lightMat)
      gfx.setCanvas({view.canvas,depthstencil=view.gbuffer.depth} )
    	camera.adjust 	(view.camera,view)
			gfx.setCamera ( view.camera )
			-- gfx.setShader(shaders.forwardRender) 
			-- shaders.forwardRender:send("MVP", "column",gfx.camera.vp)
      local a =room.lighting.ambient.amount
			gfx.setColor(r,g,b,1)
			gfx.setMeshCullMode("none")
			gfx.draw3D(room.sky.skybox.mesh) 
      gfx.pop()
		end

    local combine = vct.enabled and shaders.voxelCombine or shaders.deferredCombine
    combine:send("ambientLight",room.lighting.ambient.amount)
    combine:send("ambientColor",colorTable(room.lighting.ambient.color))
    combine:send("gbuffer_position", view.gbuffer.position)
    combine:send("gbuffer_diffuse",  view.gbuffer.diffuse)
    combine:send("gbuffer_depth",  view.gbuffer.depth)
    combine:send("gbuffer_normal",  view.gbuffer.normal)

    --temp
    

    if vct.enabled  and vct.showVoxels   then 
      game.setResolution(0.5)
      gfx.setCanvas(  view.canvas )
      gfx.setDepthMode("less", true)
      gfx.setShader(shaders.voxelRender)
      setVolumeFilter(vct,"nearest")
      shaders.voxelRender:send("volumeScale",vct.scale)
      shaders.voxelRender:send("volumePosition",vct.position)
      shaders.voxelRender:send("projectionMatrix",zcam.perspective)
      shaders.voxelRender:send("viewMatrix",zcam.viewMatrix)
      shaders.voxelRender:send("lod",lume.round(vct.lod or 0,1))
      shaders.voxelRender:send( "occlusion",vct.occlusion)
      shaders.voxelRender:send( "average",vct.showAverage==false)
      shaders.voxelRender:send("renderOcclusion",vct.show=="occlusion")
      sendVolumes(shaders.voxelRender,"volume",vct[vct.show== "occlusion" and "diffuse" or vct.show],true)
      shaders.voxelRender:send("eyePosition",  {-zcam.pos.x,-zcam.pos.y,-zcam.pos.z})
      gfx.setMeshCullMode("none")
      gfx.draw(vct.mesh)
    else
      if altDown and (editor.pickingColor or keyDown("1")) then 
        gfx.draw(view.gbuffer.diffuse)
      elseif altDown and keyDown("2") then 
        gfx.draw(view.gbuffer.normal)
      elseif altDown and keyDown("3") then 
        gfx.draw(view.gbuffer.light)
      elseif  altDown and keyDown("5") and probes.enabled then 
         gfx.draw(probes.buffer)
      else

        -- sample probes
        if probes.enabled then 
          shaders.deferredCombine:send("gbuffer_probed",  room.lighting.probes.buffer)
          shaders.probeLights:send("gbuffer_normal",    view.gbuffer.normal)
          shaders.probeLights:send("probeMap",        room.lighting.probes.convolved)
          -- shaders.probeLights:send("probeDepth",       room.lighting.probes.depth)
          shaders.probeLights:send("probeGridSize",     room.lighting.probes.gridsize)
          shaders.probeLights:send("probeResolution",   room.lighting.probes.resolution/room.lighting.probes.downsample)
          shaders.probeLights:send("probeDimensions",   room.lighting.probes.dimensions/room.lighting.probes.downsample)
          shaders.probeLights:send("probeAmount",       room.lighting.probes.amount)
          shaders.probeLights:send("probeTesting",      room.lighting.probes.testing )
          shaders.probeLights:send("probeSpacing",      {room.lighting.probes.spacing:unpack()})
          shaders.probeLights:send("probeMin",        {room.bounds.min:unpack()})
          gfx.push("all")
            gfx.setShader(shaders.probeLights)
            gfx.setCanvas(room.lighting.probes.buffer)
            gfx.draw(view.gbuffer.diffuse) -- dummy
          gfx.pop()
        end

        -- trace voxels
        if vct.enabled then 
          trace("tracing voxels")
          sendVolumes(shaders.voxelTrace,"vbuffer_light",vct.diffuse,true) -- temp
          gfx.push("all")
            gfx.setShader(vct.showOcclusion and shaders.voxelOcclusion or shaders.voxelTrace)
            gfx.setCanvas(vct.buffer)
            gfx.clear(0,0,0,0)
            gfx.draw(view.gbuffer.diffuse) -- used as dummy
          gfx.pop()
          if  (altDown and keyDown("5") and vct.enabled) or vct.showTraced then 
            gfx.draw(vct.buffer)
          else
            shaders.voxelCombine:send("eyePosition",  {-zcam.pos.x,-zcam.pos.y,-zcam.pos.z})
          	shaders.voxelCombine:send("volumeScale",vct.scale)
  		      shaders.voxelCombine:send("volumePosition",vct.position)
            shaders.voxelCombine:send("gbuffer_traced",  vct.buffer)
            shaders.voxelCombine:send("gbuffer_normal",  view.gbuffer.normal)
            gfx.setShader(shaders.voxelCombine) 

            --pass samples to the shader
            local quality = 32
            local f = { }
            local range = 32
            for i = 1, quality do
              local r = i / quality * math.pi * 2
              local d = (0.5 + i % 4) / 4
              f[#f+1] = {math.cos(r)*d*range / love.graphics.getWidth(), math.sin(r)*d*range / love.graphics.getHeight(), (1-d)^2 / quality}
            end
            shaders.voxelCombine:send("samples", unpack(f))

  	        gfx.draw(view.gbuffer.light)
          end
        else 
          combine:send("toon",  room.lighting.toon.enabled and 1 or 0 )
          combine:send("toonWidth",  room.lighting.toon.amount or 2 )
	        gfx.setShader(shaders.deferredCombine) 
	        gfx.draw(view.gbuffer.light)
	      end
      end



      gfx.push("all")
      gfx.setShader(room.lightShader)
      gfx.setCanvas({view.canvas,depthstencil=view.gbuffer.depth} )

      for l,light in ipairs(room.lights) do   
	      local entity = scene.getEntityByID(light.entity)
	    	if entity then 

		      local target = scene.getEntityByID(entity.light.targetEntity)
	    		gfx.push("all")
		    		gfx.setDepthMode("less", false)
	    			gfx.setShader(shaders.forwardRender)
		    		drawSimple(entity,true)
		    		if target then 
		    			drawSimple(target,true,true)
		    		end
	    		gfx.pop()


			  	gfx.push("all")
				  	gfx.setMeshCullMode("back")
				  	gfx.setDepthMode("less", false)
				    shaders.deferredLight:send("debug",   true)
				    gfx.setWireframe(true)
				    gfx.setColor(1,1,1,0.01)
				    lightMat = lightMat:identity()
				    pointMatrix(lightMat,light.direction)
			  	 	
			  	 	local selected = (entity.selected or ( target and target.selected))
			  	 	if  selected and light.type~="direction" then 
			  	 		if light.type=="point" then 
			  	 			local dist = vec3.dist(-view.camera.pos,light.position)
			  	 			gfx.setMeshCullMode(dist<light.range and "back" or "front")
							  lightMat:scale(lightMat,vec3(light.range))
						  end
					  	lightMat:translate(lightMat,light.position)
						  lightMat:transpose(lightMat)
						  shaders.deferredLight:send("modelMatrix",    lightMat)
					  	gfx.draw3D(light.mesh)
					  end
				  	if light.type~="point" then
				  		light.line:setVertexAttribute(2,1,  0,0,vec3.dist(light.position,light.target),1)
					  	gfx.setMeshCullMode("none")
					  	gfx.setColor(1,1,1,selected and 0.1 or 0.01)
					  	gfx.draw3D(light.line)
					  end
			    gfx.pop()
				end
			end
      gfx.setDepthMode("less", true)
      room.lightShader:send("debug",   true)

      -- if editor.debug.test_lights then 
      	gfx.setColor(1,1,1,1)
        for l=1,lights,1 do   
          lightMat = lightMat:identity()
          lightMat:scale(lightMat,vec3(room.lightRange[l]/(room.lights[l] and 200 or 100)))
          lightMat:translate(lightMat,vec3(room.lightPosition[l]))
          lightMat:transpose(lightMat)
          room.lightShader:send("modelMatrix",   lightMat)
          gfx.setColor(room.lightColor[l])
          gfx.draw(game.lightSphere)
        end
      -- end

      lightMat = lightMat:identity()
      lightMat:transpose(lightMat)
      if room.lighting.probes.enabled and room.lighting.probes.showProbes then 
        room.lightShader:send("modelMatrix",  lightMat)
        room.lightShader:send("drawDepth",  not room.lighting.probes.convolve)
        gfx.draw(room.lighting.probes.mesh)
        room.lightShader:send("drawDepth",  false)
      end

      gfx.pop()
    end

    pushPost()
    gfx.push("all")
		gfx.setShader(shaders.shadowPreview)
		trace("editor.debug.draw_shadow_maps",editor.debug.draw_shadow_maps)
		if  (altDown and keyDown("l")) or editor.debug.draw_shadow_maps then 
			for l,light in ipairs(room.lights) do
				if light.selected and light.canvas then 
					local s = 0.3
		    	light.canvas:setDepthSampleMode()
					shaders.shadowPreview:send("shadow",light.canvas)
					gfx.draw(shadowMapTester,0,0,0,(light.type=="point" and 6 or 1 )*s,s)
					gfx.setShader()
					gfx.print("resolution:"..light.resolution,0,512*s)
				end
			end
		end
		gfx.pop()
    popPost()
  end


  bench.pop("lights")
end

function renderLightDebug()

  local l = editor and currentRoom and currentRoom.room.lighting
  if l
    and l.enabled 
    and l.probes.enabled  
    and l.probes.showProbes 
    and l.probes.showAtlas
    and l.probes.canvas then 
    scale=l.probes.atlasSize --/ (l.probes.convolve and l.probes.downsample or 1)
    pushPost()
    if not l.probes.convolve then 
      gfx.setShader(shaders.depth)
    end
    gfx.draw(
      l.probes.convolve and l.probes.canvas or l.probes.depth,
      screenWidth-l.probes.dimensions*l.probes.atlasSize,0,0,scale,scale
    )
    popPost()
    -- gfx.setWireframe(true)
    -- gfx.draw(room.lighting.probes.convolver)
    -- gfx.setWireframe(false)
  end
  local l = editor and currentRoom and currentRoom.room.lighting
  if l
    and editingLevel
    and not roomsView
    and l.enabled 
    and l.render.enabled  
    and l.render.run
    and l.render.rendering
    -- and l.render.showAtlas
    and l.render.canvas then 
    -- scale=l.probes.atlasSize --/ (l.probes.convolve and l.probes.downsample or 1)
    -- if not l.probes.convolve then 
    --  gfx.setShader(shaders.depth)
    -- end
    -- gfx.setWireframe(true)
    pushPost()
    -- shaders.simple_blur2:send("image_size",{renderWidth/l.render.scale,renderHeight/l.render.scale})
    -- shaders.simple_blur2:send("intensity",0.5)
    -- shaders.simple_blur2:send("alpha",1)
      -- gfx.setShader(shaders.simple_blur2)
      gfx.setShader()
      gfx.setBlendMode("replace")
      -- gfx.setBlendMode("alpha","alphamultiply")
      local lockedScale = l.render.lock and 0.5 or 1
      gfx.draw(l.render.canvas2,0,0,0,(l.render.scale/game.resolution)*lockedScale,(l.render.scale/game.resolution)*lockedScale)
      l.render.trisTex:setFilter("nearest","nearest")
      -- gfx.draw(l.render.triTex,0,0,0,1,1)
      gfx.setShader()
    popPost()

    gfx.setShader()
    -- gfx.draw(room.lighting.probes.convolver)
    -- gfx.setWireframe(false)
  end 

end