Advanced setup with the table variant of love.graphics.setCanvas

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Ekamu
Party member
Posts: 178
Joined: Fri Sep 20, 2013 11:06 am

Advanced setup with the table variant of love.graphics.setCanvas

Post by Ekamu »

Advanced setup with the table variant of love.graphics.setCanvas

Code: Select all

-- Allow love.graphics.stencil calls when drawing to the given Canvas.
love.graphics.setCanvas({canvas, stencil=true})

-- Use multiple simultaneous render targets when drawing to several canvases of the Array Texture type,
-- and use a custom depth buffer as well.
canvas1 = love.graphics.newCanvas(128, 128, 4, {type="array"})
canvas2 = love.graphics.newCanvas(128, 128, 8, {type="array"})
depthcanvas = love.graphics.newCanvas(128, 128, {format="depth24", readable=true})

love.graphics.setCanvas({
    {canvas1, layer = 3},
    {canvas2, layer = 1},
    depthstencil = depthcanvas,
})
I'm having a hard time understanding this but I want to set up a canvas that has multiple layers for a character generator so they all need to be exported as a single PNG. I can already export to the save directory as a PNG but Its my first time using love.canvas and it seems it does not let you draw multiple images on the same canvas. Im sure the above from the wiki is helpful but I just need some explanation on what it all means.
grump
Party member
Posts: 947
Joined: Sat Jul 22, 2017 7:43 pm

Re: Advanced setup with the table variant of love.graphics.setCanvas

Post by grump »

Ekamu wrote: Tue Jun 22, 2021 5:19 pm I want to set up a canvas that has multiple layers for a character generator so they all need to be exported as a single PNG
This is a bit vague, but if you're asking what I think you're asking, then a Canvas with multiple layers does not really help you. It's not like in Photoshop where a bunch of layers combined make up one image. You can't easily save the multi-layer Canvas as a single image either, only one layer at a time, and you can (normally) only draw each layer separately.

Array textures are just a convenient way to combine several textures into one texture object and they are typically used with shaders for more advanced usecases.

It sounds to me like a single-layer Canvas to which you then sequentially draw character parts would be the right thing here, but I might misunderstand your question. Maybe you can post some more details what you're trying to do?
Ekamu
Party member
Posts: 178
Joined: Fri Sep 20, 2013 11:06 am

Re: Advanced setup with the table variant of love.graphics.setCanvas

Post by Ekamu »

I want to set up some kind of layered image that I can export as a single PNG. The purpose is for use in a character generator. So it works by drawing a layered image, each layer draws a variable image that represents a feature of the character such as hair, eyes, clothes e.t.c to be more specific the workflow is as follows.
upload5.PNG
upload5.PNG (89.49 KiB) Viewed 6536 times
1. Load images into a table.
2. Setup a canvas (or image data type) with various images (layered images) on-top of each other.
3. Update the current image if Input changes the index of which image to reference.
4. Draw the images on screen, (animation preview using anim8 over real canvas)
5. If "Tab" is pressed export the current state of the canvas to love save directory (%AppData%)

I've attached a .love of the current state of the character generator. I think your right when you mentioned Its better to use ArrayImages but can I export that as a single PNG or can I convert an ArrayImage to ImageData, encode it and save it to loves save directory as a .png file?
Also you said you can use shaders on ArrayImages, does that mean I can apply a blending mode to a single layer with ability to export as a .png file (convert to ImageData)?
DeGen 0.4.love
(536.06 KiB) Downloaded 134 times
Thanks.

Note: please don't steal the resources in the above .love file, its for use in a commercial product.
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Advanced setup with the table variant of love.graphics.setCanvas

Post by pgimeno »

I may be missing something, but I don't see the need for an image array in this application. Just work with the images in a table, and at saving time, create a canvas with the size of the desired target PNG, draw the images to it, create an ImageData out of it, and save the ImageData as PNG. No need to do anything special with GL image arrays. It's not that the draw calls are going to be a bottleneck in this application, if you draw, say, 6 layers per frame.
Ekamu
Party member
Posts: 178
Joined: Fri Sep 20, 2013 11:06 am

Re: Advanced setup with the table variant of love.graphics.setCanvas

Post by Ekamu »

Ok but I had an issue with setting up a canvas with more than one images drawn at a time? Can you share some example code on how I can set a canvas to draw lets say six images all in one canvas at the same time? Thanks.

I understand the encoding and saving part but for some reason I failed to set a canvas to draw more than one image, it picks the first image, draws it and ignores everything else within the setcanvas structure. Do you set multiple canvases and group them into one canvas or is there something i'm not getting like do you need to clear after every single draw to a canvas?
grump
Party member
Posts: 947
Joined: Sat Jul 22, 2017 7:43 pm

Re: Advanced setup with the table variant of love.graphics.setCanvas

Post by grump »

You call lg.setCanvas with one single-layered Canvas and everything you draw after that will be drawn to that Canvas until you manually unset it. You don't need multiple Canvases.

I'm sure there's examples on the wiki pages for Canvas or setCanvas.
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Advanced setup with the table variant of love.graphics.setCanvas

Post by pgimeno »

Ekamu wrote: Wed Jun 23, 2021 3:06 pm Ok but I had an issue with setting up a canvas with more than one images drawn at a time? Can you share some example code on how I can set a canvas to draw lets say six images all in one canvas at the same time? Thanks.
Not at a time, just one by one, but sure.

Note that the PNG file format is not multi-layered, so the animations need to be laid out as a texture atlas.

Code: Select all

  -- baseWidth and baseHeight are the width and height of a rectangle where
  -- any frame of the animation fits. If you use a constant size for all frames,
  -- use that size as baseWidth and baseHeight.
  --
  -- nHorizTiles and nVertTiles are supposed to be dimensioned appropriately
  -- so that nHorizTiles * nVertTiles >= nFrames. You also need to be careful
  -- that neither of the resulting dimensions (baseWidth * nHorizTiles, and
  -- baseHeight * nVertTiles) is > 2048, so it works in any OpenGL no matter
  -- how old.
  --
  -- nFrames is the number of frames in the animation.
  local canvas = love.graphics.newCanvas(baseWidth * nHorizTiles, baseHeight * nVertTiles)
  local horizTile = 0
  local vertTile = 0
  love.graphics.setCanvas(canvas)
  for i = 1, nFrames do
    -- Draw every frame in the animation to the canvas.
    love.graphics.draw(table_with_frames[i], horizTile * baseWidth, vertTile * baseHeight)
    horizTile = horizTile + 1
    if horizTile >= nHorizTiles then
      horizTile = 0
      vertTile = vertTile + 1
      assert(vertTile <= nVertTiles, "Incorrect nHorizTiles and nVertTiles for this nFrames")
    end
  end
  love.graphics.setCanvas()
  local imgdata = canvas:newImageData()
  imgdata:encode("png", saveName)
For 6 images, a possible choice is nHorizTiles = 3 and nVertTiles = 2.
Ekamu
Party member
Posts: 178
Joined: Fri Sep 20, 2013 11:06 am

Re: Advanced setup with the table variant of love.graphics.setCanvas

Post by Ekamu »

I am using a function to setup a canvas it works thanks for your help, but I have a specific layer named "skin" that I want to render using blend mode "multiplied" and alpha mode as "premultiplied". It seems when I try set this up in the loop, nothing takes effect and its all drawn as normal.

Code: Select all

function setCanvas(canvas,imageTable,imageLayers)
  love.graphics.setCanvas(canvas)
      love.graphics.clear()
      for i = 1, #imageLayers do
          if imageLayers[i] == "skin" then
            love.graphics.setBlendMode("multiply","premultiplied") --default is 1,1
            love.graphics.setColor(1, 1, 1, 1)
            love.graphics.draw(imageTable["skin"], 0, 0)
          end
            love.graphics.setBlendMode("alpha","alphamultiply") --default is 1,1
            love.graphics.setColor(1, 1, 1, 1)
            love.graphics.draw(imageTable[imageLayers[i]], 0, 0)
      end
  love.graphics.setCanvas()
end
The sprite layer is setup like this, its passed as an argument to the function setCanvas as imageLayers.

Code: Select all

sprite.layers = {
  "body",
  "skin",
  "hair",
  "brows",
  "eyes",
  "nose",
  "mouth",
  "outfit",
}
imageTable is the actual table of lg.newImages setup. canvas is a lg.newCanvas
skinnotmultipliedPNG.PNG
skinnotmultipliedPNG.PNG (77.64 KiB) Viewed 6443 times
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Advanced setup with the table variant of love.graphics.setCanvas

Post by pgimeno »

Ekamu wrote: Thu Jun 24, 2021 8:43 am

Code: Select all

          if imageLayers[i] == "skin" then
            love.graphics.setBlendMode("multiply","premultiplied") --default is 1,1
            love.graphics.setColor(1, 1, 1, 1)
            love.graphics.draw(imageTable["skin"], 0, 0)
          end <------ ????
            love.graphics.setBlendMode("alpha","alphamultiply") --default is 1,1
            love.graphics.setColor(1, 1, 1, 1)
            love.graphics.draw(imageTable[imageLayers[i]], 0, 0)
Shouldn't that be an "else"? It seems to me that you're overwriting the multiplied layer with a normal layer, because after drawing the skin in multiply mode, you're drawing it again in normal alpha mode. You don't want to overwrite what's drawn so far, hence the "else".
Ekamu
Party member
Posts: 178
Joined: Fri Sep 20, 2013 11:06 am

Re: Advanced setup with the table variant of love.graphics.setCanvas

Post by Ekamu »

Yes, I was messing with the code but originally it was like this. Still, It doesn't make a difference.

Code: Select all

function setCanvas(canvas,imageTable,imageLayers)
  love.graphics.setCanvas(canvas)
      love.graphics.clear()
      for i = 1, #imageLayers do
        if imageLayers[i] == "skin" then
          love.graphics.setBlendMode("multiply","premultiplied") --default is 1,1
          love.graphics.setColor(1, 1, 1, 1)
        else
          love.graphics.setBlendMode("alpha") --default is 1,1
          love.graphics.setColor(1, 1, 1, 1)
        end
        love.graphics.draw(imageTable[imageLayers[i]], 0, 0)
      end
  love.graphics.setCanvas()
end
You can see for yourself. (love 11.3)
deGen.love
(512.41 KiB) Downloaded 132 times
Post Reply

Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 61 guests