Modding Discussion An experiment in pixel rendering via animationParts

Discussion in 'Starbound Modding' started by Supergeek, Jan 23, 2014.

  1. Supergeek

    Supergeek Scruffy Nerf-Herder

    TL;DR: You can't display hundreds of animationParts without suffering a performance penalty.

    I've been working on a mod that allows independent control of pixels on a tiny, modular screen. I have made an "LCD" object that I can place on the background.

    Here is a gif to explain what I mean:

    [​IMG]

    These characters aren't sprites; this display is made up of a matrix of 8x8 independent pixel sprites as animationParts inside an object. I found a bitmap font online and wrote code to convert it to pixels. Right now, it just cycles through the 128 characters of the font, but I could make it display anything I wanted as each pixel is individually addressable.

    Here is a bigger example:

    [​IMG]

    This gif shows 10 LCDs (5 across, 2 high) cycling through the font. With a GTX 560 on an i7 2600k, I get around 17 FPS here. With 8 LCDs, I get 50 FPS. I have tried different scriptDeltas and optimizations to setAnimationState, but the framerate doesn't change, so I can assume that the rendering speed is purely a result of the game rendering an extra 512 or 640 animationParts (depending on # of LCD objects) every frame. I imagine this framerate varies based on the beefiness of the machine it is running on.

    As you can see, it is working. However, I have put the project on hold and ceased my efforts toward getting it to work on a larger scale because its performance is so poor.

    In case you were curious about the details, here is some of my methodology.

    This is what the animationParts segment of my lcd.object file looks like:
    Code:
        "animationParts" : {
        "screen" : "lcd.png",
        "pixel_0_0" : "pixels.png",
        "pixel_0_1" : "pixels.png",
        "pixel_0_2" : "pixels.png",
        "pixel_0_3" : "pixels.png",
        "pixel_0_4" : "pixels.png",
        "pixel_0_5" : "pixels.png",
        "pixel_0_6" : "pixels.png",
        "pixel_0_7" : "pixels.png",
    ...and so on, (64 total)
    
    lcd.animation has a bunch of these in "animatedParts":
    Code:
      "pixel_0_0" : { "default" : "off", "states" : { "on" : { "frames" : 1 }, "off" : { "frames" : 1 } } },
      "pixel_0_1" : { "default" : "off", "states" : { "on" : { "frames" : 1 }, "off" : { "frames" : 1 } } },
      "pixel_0_2" : { "default" : "off", "states" : { "on" : { "frames" : 1 }, "off" : { "frames" : 1 } } },
    ...and so on (64 total again)
    
    and these in "parts":
    
      "pixel_0_0" : { "properties" : { "offset" : [0.000, 0.875], "centered" : false, "zLevel" : 1 }, "partStates" : { "pixel_0_0" :
      { "on" : { "properties" : { "image" : "pixels.png:green" } }, "off" : { "properties" : { "image" : "pixels.png:black" } } } } },
      "pixel_0_1" : { "properties" : { "offset" : [0.000, 0.750], "centered" : false, "zLevel" : 1 }, "partStates" : { "pixel_0_1" :
      { "on" : { "properties" : { "image" : "pixels.png:green" } }, "off" : { "properties" : { "image" : "pixels.png:black" } } } } },
      "pixel_0_2" : { "properties" : { "offset" : [0.000, 0.625], "centered" : false, "zLevel" : 1 }, "partStates" : { "pixel_0_2" :
      { "on" : { "properties" : { "image" : "pixels.png:green" } }, "off" : { "properties" : { "image" : "pixels.png:black" } } } } },
    ...and so on. Guess how many?
    
    pixels.png is just a small graphic with a bunch of single pixels of various colors. If the mod were more performant, I might expand the mod to allow different colors of pixels. Right now, there's just green and black.

    Some excerpts from my lcd.lua. I use a lot of temp variables where I could compact the code, but I code long-form so I can test stuff along the way since debugging is hard in Starbound.

    Code:
    in init:
    
         myfont = {}
         fontdata = {}
         loadFontData() -- loads hex strings into a table
    
         for i,j in ipairs(fontdata) do -- this loads the hex strings into a table of binary strings, ("000101101" etc)
           local mycharset = {}
           for k, l in pairs(j) do
             local decnumber = tonumber(l, 16) -- converts font hex data to decimal
             local binary = basen(decnumber, 2) -- and then to a zero-padded binary string (took me a while to get this right!)
             table.insert(mycharset, binary)
           end
           local tempchar = table.concat(mycharset,"")
           table.insert(myfont, tempchar)
         end
    
    
    main runs cyclefont() once every scriptDelta:
    
    function cyclefont()
       if fontcount == 0 or fontcount == nil or fontcount == 128 then -- belt and suspenders!
         fontcount = 64 -- start at the common glyphs
       else
         fontcount = fontcount + 1
       end
       display(myfont[fontcount])
    end
    
    
    function display(character) -- character is a 64-character string of 0s and 1s
       for i=0,7 do
         for j=0,7 do
           local position = i*8 + j + 1
           setpixel(j,i,binToOnOff(string.sub(character, position, position)))
         end
       end
    end
    
    
    function setpixel(xpixel, ypixel, pixelstate)
       if entity.animationState("pixel_".. xpixel .. "_" .. ypixel) ~= pixelstate then
         entity.setAnimationState("pixel_".. xpixel .. "_" .. ypixel, pixelstate)
       end
    end
    
    
    function loadFontData()
    
       fontdata = {
       -- characters of a bitmap font in hex format
       -- slightly more compact than 64-character binary strings; could be better, but it's what I found.
         { "00", "00", "00", "00", "00", "00", "00", "00" },
         { "00", "3E", "41", "55", "41", "55", "49", "3E" },
         { "00", "3E", "7F", "6B", "7F", "6B", "77", "3E" },
    ...and so on.
    
    Right now, my opinion is that a pixel-based display using animationParts is just not practically feasible with the current state of Starbound's rendering engine. I will not release or work further on this mod until a way is found to improve performance.

    In my mind, only 8 usable characters (a 32x16 pixel display, essentially) at a good framerate even on a beefy machine just isn't performant enough to be practical, since most people will probably want more than just a few characters. It's only useful if it's practical at scale for the majority of hardware that can run Starbound comfortably, especially with multiplayer in mind.

    I figured this might happen, so I ain't even mad. It was a fun experiment and I enjoyed trying to push the envelope and see what the engine is capable of. Perhaps further down the road, the game will optimize animationParts and objects, and improve rendering on items that do not change between frames, or provide an alternate method of rendering arbitrary pixels.

    [​IMG]

    I would welcome any comments or suggestions for improving performance in this thread. Or, alternate rendering methods.

    Edit: Minor rewrite for clarity, and to add some comments in the code.
     
    Last edited: Jan 23, 2014
    Crystan, severedskullz and metadept like this.
  2. VERY Interesting.
     
  3. Supergeek

    Supergeek Scruffy Nerf-Herder

    I decided to start working on a replacement that uses a single sprite per character instead of pixels. It's going to be much simpler than the other mod, and may provide some graphing/graphical capability.
     
  4. Supergeek

    Supergeek Scruffy Nerf-Herder

    Progress...

    [​IMG]

    32 8x8 pixel sprite objects, each with 128 states (One for each character in the font.) No FPS slowdown.

    This is the Commodore 64 font, btw, hence the weird symbols. And it's backwards because we can't control the orientation of animated wired objects.
     
    Last edited: Jan 24, 2014
    charity236 likes this.

Share This Page