Page 1 of 1

love.graphics.setColor not persistent

Posted: Thu Mar 26, 2015 5:08 am
by kirbunkle
Hi all,

I've modified the tiled map loader library to use the opacity setting that gets saved out with tiled map editor, however, after setting the color, it seems to be reset somehow. I'm wondering if anyone can tell me what's going on.

Here's a snippet:

Code: Select all

      local layer = gMapLayers[z]
      local r, g, b, a = love.graphics.getColor()
      if layer.opacity then
        print(layer.name, layer.opacity * 255)
        love.graphics.setColor(r, 0, b, layer.opacity * 255)
        print(love.graphics.getColor())
      end
      if layer.opacity then
        print(love.graphics.getColor())
      end
 
      for x = minx,maxx do
      for y = miny,maxy do
        local gfx = gTileGfx[TiledMap_GetMapTile(x,y,z)]
        if (gfx) then
            local sx = x*kTileSize - camx + screen_w/2
            local sy = y*kTileSize - camy + screen_h/2
            love.graphics.draw(gfx,sx,sy) -- x, y, r, sx, sy, ox, oy
            if layer.opacity then
              print(love.graphics.getColor())
            end

        end
      end
      if layer.opacity then
        love.graphics.setColor(r, g, b, a)
      end
The output this provides to console is this:

Code: Select all

fore    51
255     0       255     51
255     0       255     51
255     255     255     255
255     255     255     255
255     255     255     255
255     255     255     255
As described in the output, the tiles get drawn with 100% opacity. I would expect it to remain 255, 0, 255, 51 until I set it back, but it seems to be getting reset somehow. Any ideas?

Re: love.graphics.setColor not persistent

Posted: Thu Mar 26, 2015 5:31 am
by arampl
What modifications has been made?
Especially with TiledMap_GetMapTile function.

Re: love.graphics.setColor not persistent

Posted: Thu Mar 26, 2015 9:04 pm
by kirbunkle
arampl wrote:What modifications has been made?
Especially with TiledMap_GetMapTile function.
The only modifications are the ones in the code snippet (except just that when I read in an opacity I set it to the layer).

As far as I understand, nothing should be resetting the color in between me setting it and me drawing in the same function.

Re: love.graphics.setColor not persistent

Posted: Thu Mar 26, 2015 9:54 pm
by s-ol
kirbunkle wrote:
arampl wrote:What modifications has been made?
Especially with TiledMap_GetMapTile function.
The only modifications are the ones in the code snippet (except just that when I read in an opacity I set it to the layer).

As far as I understand, nothing should be resetting the color in between me setting it and me drawing in the same function.
Well, exactly that is the question. If the code is exactly the one you posted, the only thing that can change the color is TiledMap_GetMapTile, which is why we need to see the source of that function.

Re: love.graphics.setColor not persistent

Posted: Fri Mar 27, 2015 12:36 am
by kirbunkle
S0lll0s wrote:
kirbunkle wrote:
arampl wrote:What modifications has been made?
Especially with TiledMap_GetMapTile function.
The only modifications are the ones in the code snippet (except just that when I read in an opacity I set it to the layer).

As far as I understand, nothing should be resetting the color in between me setting it and me drawing in the same function.
Well, exactly that is the question. If the code is exactly the one you posted, the only thing that can change the color is TiledMap_GetMapTile, which is why we need to see the source of that function.
The source is on the wiki under snippets: https://love2d.org/wiki/TiledMapLoader

Did a quick look just to be sure I wasn't being stupid and missing something, and I still don't see anything that would reset the color.

Code: Select all

function TiledMap_GetMapTile (tx,ty,layerid) -- coords in tiles
    local row = gMapLayers[layerid][ty]
    return row and row[tx] or kMapTileTypeEmpty
end

Re: love.graphics.setColor not persistent

Posted: Fri Mar 27, 2015 12:52 am
by kirbunkle
I modified the code to provide more debug output:

Code: Select all

      for x = minx,maxx do
      for y = miny,maxy do
        if layer.opacity then
          print(x, y, love.graphics.getColor())
        end
        local gfx = gTileGfx[TiledMap_GetMapTile(x,y,z)]
        if (gfx) then
            local sx = x*kTileSize - camx + screen_w/2
            local sy = y*kTileSize - camy + screen_h/2
            love.graphics.draw(gfx,sx,sy) -- x, y, r, sx, sy, ox, oy
            if layer.opacity then
              print('c', love.graphics.getColor())
            end

        end
      end
That provided this output:

Code: Select all

0       3       255     0       255     51
0       4       255     0       255     51
0       5       255     0       255     51
0       6       255     0       255     51
0       7       255     0       255     51
0       8       255     0       255     51
0       9       255     0       255     51
0       10      255     0       255     51
0       11      255     0       255     51
0       12      255     0       255     51
0       13      255     0       255     51
1       3       255     255     255     255
1       4       255     255     255     255
1       5       255     255     255     255
1       6       255     255     255     255
...
As soon as it switches to x = 1, it changes.

Re: love.graphics.setColor not persistent

Posted: Fri Mar 27, 2015 12:59 am
by kirbunkle
In case anyone really wants to see my full modified code, here it is.

Code: Select all

-- see https://love2d.org/wiki/TiledMapLoader for latest version
-- loader for "tiled" map editor maps (.tmx,xml-based) http://www.mapeditor.org/
-- supports multiple layers
-- NOTE : function ReplaceMapTileClass (tx,ty,oldTileType,newTileType,fun_callback) end
-- NOTE : function TransmuteMap (from_to_table) end -- from_to_table[old]=new
-- NOTE : function GetMousePosOnMap () return gMouseX+gCamX-gScreenW/2,gMouseY+gCamY-gScreenH/2 end

kTileSize = 32
kMapTileTypeEmpty = 0
local floor = math.floor
local ceil = math.ceil
local max = math.max
local min = math.min
local abs = math.abs
gTileMap_LayerInvisByName = {}

function TiledMap_Load (filepath,tilesize,spritepath_removeold,spritepath_prefix)
    spritepath_removeold = spritepath_removeold or "../"
    spritepath_prefix = spritepath_prefix or ""
    kTileSize = tilesize or kTileSize or 32
    gTileGfx = {}
   
    local tiletype,layers = TiledMap_Parse(filepath)
    gMapLayers = layers
    for first_gid,path in pairs(tiletype) do
        path = spritepath_prefix .. string.gsub(path,"^"..string.gsub(spritepath_removeold,"%.","%%."),"")
        local raw = love.image.newImageData(path)
        local w,h = raw:getWidth(),raw:getHeight()
        local gid = first_gid
        local e = kTileSize
        for y=0,floor(h/kTileSize)-1 do
        for x=0,floor(w/kTileSize)-1 do
            local sprite = love.image.newImageData(kTileSize,kTileSize)
            sprite:paste(raw,0,0,x*e,y*e,e,e)
            gTileGfx[gid] = love.graphics.newImage(sprite)
            gid = gid + 1
        end
        end
    end
end

function TiledMap_GetMapW () return gMapLayers.width end
function TiledMap_GetMapH () return gMapLayers.height end

-- returns the mapwidth actually used by tiles
function TiledMap_GetMapWUsed ()
    local maxx = 0
    local miny = 0
    local maxy = 0
    for layerid,layer in pairs(gMapLayers) do 
        if (type(layer) == "table") then for ty,row in pairs(layer) do
            if (type(row) == "table") then for tx,t in pairs(row) do 
                if (t and t ~= kMapTileTypeEmpty) then 
                    miny = min(miny,ty)
                    maxy = max(maxy,ty)
                    maxx = max(maxx,tx)
                end
            end end
        end end
    end
    return maxx + 1,miny,maxy+1
end

-- x,y= position for nearest-distance(square,not round), z= layer, maxrad= optional limit for searching
-- returns x,y
-- if x,y can be far outside map, set a sensible maxrad, otherwise it'll get very slow since searching outside map isn't optimized
function TiledMap_GetNearestTileByTypeOnLayer (x,y,z,iTileType,maxrad)
    local w = TiledMap_GetMapW()
    local h = TiledMap_GetMapW()
    local maxrad2 = max(x,w-x,y,h-y) if (maxrad) then maxrad2 = min(maxrad2,maxrad) end
    if (TiledMap_GetMapTile(x,y,z) == iTileType) then return x,y end
    for r = 1,maxrad2 do 
        for i=-r,r do 
            local xa,ya = x+i,y-r if (TiledMap_GetMapTile(xa,ya,z) == iTileType) then return xa,ya end -- top
            local xa,ya = x+i,y+r if (TiledMap_GetMapTile(xa,ya,z) == iTileType) then return xa,ya end -- bot
            local xa,ya = x-r,y+i if (TiledMap_GetMapTile(xa,ya,z) == iTileType) then return xa,ya end -- left
            local xa,ya = x+r,y+i if (TiledMap_GetMapTile(xa,ya,z) == iTileType) then return xa,ya end -- right
        end
    end
end

function TiledMap_GetMapTile (tx,ty,layerid) -- coords in tiles
    local row = gMapLayers[layerid][ty]
    return row and row[tx] or kMapTileTypeEmpty
end

function TiledMap_SetMapTile (tx,ty,layerid,v) -- coords in tiles
    local row = gMapLayers[layerid][ty]
    if (not row) then row = {} gMapLayers[layerid][ty] = row end
    row[tx] = v
end

-- todo : maybe optimize during parse xml for types registered as to-be-listed before parsing ?
function TiledMap_ListAllOfTypeOnLayer (layerid,iTileType)
    local res = {}
    local w = TiledMap_GetMapW()
    local h = TiledMap_GetMapH()
    for x=0,w-1 do 
    for y=0,h-1 do 
        if (TiledMap_GetMapTile(x,y,layerid) == iTileType) then table.insert(res,{x=x,y=y}) end
    end
    end
    return res
end

function TiledMap_GetLayerZByName (layername) for z,layer in ipairs(gMapLayers) do if (layer.name == layername) then return z end end end
function TiledMap_SetLayerInvisByName (layername) gTileMap_LayerInvisByName[layername] = true end

function TiledMap_IsLayerVisible (z)
    local layer = gMapLayers[z]
    return layer and (not gTileMap_LayerInvisByName[layer.name or "?"])
end

function TiledMap_GetTilePosUnderMouse (mx,my,camx,camy)
    return  floor((mx+camx-love.graphics.getWidth()/2)/kTileSize),
            floor((my+camy-love.graphics.getHeight()/2)/kTileSize)
end

function TiledMap_DrawNearCam (camx,camy,fun_layercallback)
    camx,camy = floor(camx),floor(camy)
    local screen_w = love.graphics.getWidth()
    local screen_h = love.graphics.getHeight()
    local minx,maxx = floor((camx-screen_w/2)/kTileSize),ceil((camx+screen_w/2)/kTileSize)
    local miny,maxy = floor((camy-screen_h/2)/kTileSize),ceil((camy+screen_h/2)/kTileSize)
    for z = 1,#gMapLayers do 
    if (fun_layercallback) then fun_layercallback(z,gMapLayers[z]) end
    if (TiledMap_IsLayerVisible(z)) then
      local layer = gMapLayers[z]
      local r, g, b, a = love.graphics.getColor()
      if layer.opacity then
        print(layer.name, layer.opacity * 255)
        love.graphics.setColor(r, 0, b, layer.opacity * 255)
        print(love.graphics.getColor())
      end
      if layer.opacity then
        print('a', love.graphics.getColor())
      end
 
      for x = minx,maxx do
      for y = miny,maxy do
        if layer.opacity then
          print(x, y, love.graphics.getColor())
        end
        local gfx = gTileGfx[TiledMap_GetMapTile(x,y,z)]
        if (gfx) then
            local sx = x*kTileSize - camx + screen_w/2
            local sy = y*kTileSize - camy + screen_h/2
            love.graphics.draw(gfx,sx,sy) -- x, y, r, sx, sy, ox, oy
            if layer.opacity then
              print('c', love.graphics.getColor())
            end

        end
      end
      if layer.opacity then
        love.graphics.setColor(r, g, b, a)
      end
    end
    end
    end
end


-- ***** ***** ***** ***** ***** xml parser


-- LoadXML from http://lua-users.org/wiki/LuaXml
function LoadXML(s)
  local function LoadXML_parseargs(s)
    local arg = {}
    string.gsub(s, "(%w+)=([\"'])(.-)%2", function (w, _, a)
    arg[w] = a
    end)
    return arg
  end
  local stack = {}
  local top = {}
  table.insert(stack, top)
  local ni,c,label,xarg, empty
  local i, j = 1, 1
  while true do
    ni,j,c,label,xarg, empty = string.find(s, "<(%/?)([%w:]+)(.-)(%/?)>", i)
    if not ni then break end
    local text = string.sub(s, i, ni-1)
    if not string.find(text, "^%s*$") then
      table.insert(top, text)
    end
    if empty == "/" then  -- empty element tag
      table.insert(top, {label=label, xarg=LoadXML_parseargs(xarg), empty=1})
    elseif c == "" then   -- start tag
      top = {label=label, xarg=LoadXML_parseargs(xarg)}
      table.insert(stack, top)   -- new level
    else  -- end tag
      local toclose = table.remove(stack)  -- remove top
      top = stack[#stack]
      if #stack < 1 then
        error("nothing to close with "..label)
      end
      if toclose.label ~= label then
        error("trying to close "..toclose.label.." with "..label)
      end
      table.insert(top, toclose)
    end
    i = j+1
  end
  local text = string.sub(s, i)
  if not string.find(text, "^%s*$") then
    table.insert(stack[#stack], text)
  end
  if #stack > 1 then
    error("unclosed "..stack[stack.n].label)
  end
  return stack[1]
end


-- ***** ***** ***** ***** ***** parsing the tilemap xml file

local function getTilesets(node)
    local tiles = {}
    for k, sub in ipairs(node) do
        if (sub.label == "tileset") then
            tiles[tonumber(sub.xarg.firstgid)] = sub[1].xarg.source
        end
    end
    return tiles
end

local function getLayers(node)
    local layers = {}
    layers.width = 0
    layers.height = 0
    for k, sub in ipairs(node) do
        if (sub.label == "layer") then --  and sub.xarg.name == layer_name
            layers.width  = max(layers.width ,tonumber(sub.xarg.width ) or 0)
            layers.height = max(layers.height,tonumber(sub.xarg.height) or 0)
            local layer = {}
            table.insert(layers,layer)
            layer.name = sub.xarg.name
            print("layername",layer.name)
            layer.opacity = sub.xarg.opacity
            print("opacity", layer.opacity)
            width = tonumber(sub.xarg.width)
            i = 0
            j = 0
            for l, child in ipairs(sub[1]) do
                if (j == 0) then
                    layer[i] = {}
                end
                layer[i][j] = tonumber(child.xarg.gid)
                j = j + 1
                if j >= width then
                    j = 0
                    i = i + 1
                end
            end
        end
    end
    return layers
end

function TiledMap_Parse(filename)
    local xml = LoadXML(love.filesystem.read(filename))
    local tiles = getTilesets(xml[2])
    local layers = getLayers(xml[2])
    return tiles, layers
end

Re: love.graphics.setColor not persistent

Posted: Fri Mar 27, 2015 1:23 am
by arampl
I've looked into your snippet more carefully and realized that this lines:

Code: Select all

if layer.opacity then
        love.graphics.setColor(r, g, b, a)
end
are inside the first loop "for x = minx,maxx do".

So, presumably, this is the place where goes color setting.

Re: love.graphics.setColor not persistent

Posted: Fri Mar 27, 2015 4:33 am
by kirbunkle
arampl wrote:I've looked into your snippet more carefully and realized that this lines:

Code: Select all

if layer.opacity then
        love.graphics.setColor(r, g, b, a)
end
are inside the first loop "for x = minx,maxx do".

So, presumably, this is the place where goes color setting.
Wow. Chalk this one up to bad code formatting, I didn't even notice that I was still inside the outer loop.

I'm really sorry for wasting your guys' time. Thanks for your help anyway.