[Lua] Palette Based Graphics Programming

Started by Scavenger, Wed 27/11/2013 15:35:39

Previous topic - Next topic

Calin Leafshade

It's not mentioned in the code so it looks like Denzil planned to implement it but never did.

you could try Lua.Evaluate.

Code: ags

int i = 0;
while(i < 255)
    Lua.Evaluate(String.Format("palette_r[%d] = %d", i, value));
    i++;
end


Scavenger

Got this:

QuoteScript link failed: Runtime Error: Unresolved import 'Lua::Evaluate^2'

I am really sorry I am having so much trouble with this. x3

Calin Leafshade

Not a problem dude.

Seems that function is also missing.

try:

Code: ags

Lua.SetVar(String.Format("palette_r[%d]",i), value)

Scavenger

Okay, I managed to make ProcessPalette start working! It's pretty smooth up until Next error is this particularily nebulous one:

QuoteAttempt to compare nil with number

Code: Lua
function ProcessPalette (...)
  local palindex = {...}
  local paltemp = {...}
  local i = 0
  local i2 = 1
  local upperboundary = 0
  C8Palgorithm = {}
  C8PaletteMap = {}
  while (i < table.getn(palindex))
  do
    --paltemp [i] = ags.palette[i].r + ags.palette[i].r + ags.palette[i].r + ags.palette[i].b +  ags.palette[i].g + ags.palette[i].g + ags.palette[i].g + ags.palette[i].g
    paltemp [i] = palette_r [i] + palette_r [i] + palette_r [i] + palette_b [i] +  palette_g [i] + palette_g [i] + palette_g [i] + palette_g [i]
    paltemp [i] = bit.rshift(paltemp [i],3) 
    i = i + 1
  end
  i = 0
  C8palgorithm = {}
  while (i < table.getn(palindex))
  do
      if (i > table.getn(palindex)-2) then upperboundary = 255
      else upperboundary = (paltemp [i] + paltemp [i + 1])/2  --THIS IS THE LINE IT CHOKES AT
      end
      
      while (i2 < upperboundary)
      do
        C8palgorithm [i2] = palindex[i]
        i2 = i2 + 1
      end
    i = i + 1
  end
    
   i= 0
   paltemp = {}
   while (i < 255)
      do
       --paltemp [i] = ags.palette[i].r + ags.palette[i].r + ags.palette[i].r + ags.palette[i].b +  ags.palette[i].g + ags.palette[i].g + ags.palette[i].g + ags.palette[i].g
       paltemp [i] = palette_r [i] + palette_r [i] + palette_r [i] + palette_b [i] +  palette_g [i] + palette_g [i] + palette_g [i] + palette_g [i]
       paltemp [i] = bit.rshift(paltemp [i],3)
       i = i+ 1
      end
    i = 0
    while (i < 255)
      do
       C8PaletteMap [i] = C8palgorithm [paltemp[i]]
       i = i + 1
      end
  
end


Splitting the line into two:

Code: Lua
        upperboundary = paltemp [i] + paltemp [i + 1]
        upperboundary = upperboundary /2 --It chokes here now.


I'm sure I'll be able to figure out errors like these when I get more used to Lua, but... choking on a simple division?

Calin Leafshade

dont use table.getn use the # operator instead. #palindex returns the number of values in the array (numberically indexed) part of a table.

Quote
Attempt to compare nil with number

That error is for a comparison (i.e < > <= >= == ~=). If the value were nil you'd get an arithmatic error.
Are you sure thats the offending line?

Scavenger

#45
Okay, copied the lua file into Notepad++ to count the lines there, solved that issue.

But there are so many other issues cropping up I am beginning to lose hope that I'll ever get this done. I don't think I'm quite qualified to even do this.

1) The colourisation function crashes AGS with the same
Quote
Error: [Lua] [string "local select = select;local error = error;l..."]:1: bad argument #2 to 'setter' (number expected, got nil)
Error. I have no idea what is going wrong there. I added a UsingLua bool you can switch on and off to see the intended effect there.

2) Only Object 0 is affected by translucency. Even though in repeatedly_execute_always it iterates through all of the objects on screen, only Object 0 will be translucent.

3) Other objects are invisible behind a translucent object. I think this is related to 2, as somewhere along the line the function is just choking. Characters render fine. This is odd, as I knew I tested this in the AGSScript version. It just... stopped working.

I'll put the project here:
https://dl.dropboxusercontent.com/u/50882197/Game/paltest321_errors.zip
If anyone wants to take a look. It uses the FakeScreen functions (DrawObject, DrawCharacter) to build the background and the characters on it so that the translucency drawing functions know what is behind the object at the time. The workflow goes:

repeatedly execute always: Is there translucency to be rendered? If so, create a surface and start building on it until a translucent object needs to be rendered. Then, render the translucent object (DrawObject) and set that to be the object's graphic.

Denzil Quixode

Hi, hi, really sorry you've been having all these issues with the Lua plugin! Please bear with me a little. This is a really cool project and I always wanted people to use the plugin for ambitious interesting things so I feel like I've really dropped the ball on this one. I have posted some stuff in the plugin thread about where the problem seems to be in issue 1.) there. I will address missing API functions and the lousy documentation as soon as I can. Sorry again!

Denzil Quixode

#47
Two problems I noticed in the Lua Colourise() function:

  • cpixel is sometimes -1, and C8PaletteMap[-1] doesn't exist, so surface.DrawingColor is being set to nil, which is invalid
  • there's an infinite loop problem in there:
Code: lua
while (i < w) do

  -- ( . . . ) --

  i = j + 1
end
    (...should be i = i + 1)


Also, this bit in ProcessPalette:
Code: Lua
while (i < palsiz)
do
  if (i > palsiz) then upperboundary = 255
  -- ... --


...looks like it should probably have "-2" added, to be equivalent to the AGSScript version in pal2.asc:

Code: AGS
while (i < palsiz)
{
  if (i > palsiz -2) upperboundary = 255;

Scavenger

It's cool, Denzil, I'm really liking this plugin and it can do some amazing things compared to plain AGSScript. It's mostly my newness to Lua that is holding me back.

I corrected those errors, but I'm still getting the same problem:

Code: Lua
function Colourise (ds)
  local surface = ds:GetDrawingSurface ()
  local i = 0;
  local j= 0;
  local h = ds.Height
  local w = ds.Width
  local cpixel;
  while (j < h)
  do
    while (i < w)
    do
      cpixel = surface:GetPixel (i, j)
      if (cpixel == -1)
        then
          surface.DrawingColor = 0
      else 
          surface.DrawingColor = C8PaletteMap [cpixel]
        end
      surface:DrawPixel (i, j)
      i = i + 1
    end
    i = 0
    j = j + 1
  end
  surface:Release ()
end

Calin Leafshade

Here are some tricks to simplify your code:

Code: lua

function Colourise (ds)
  local surface = ds:GetDrawingSurface ()
  local h = ds.Height
  local w = ds.Width
  local cpixel;
  for j=0, h-1 do
    for i=0, w-1 do
        cpixel = surface:GetPixel (i, j)
        surface.DrawingColor = C8PaletteMap[cpixel] or 0
        surface:DrawPixel (i, j)
    end
  end
  surface:Release ()
end


See if that crashes. It makes it so that DrawingColor can never be nil.

Denzil Quixode

^^ Yeah, a good suggestion from Calin there. So it looks like Lua's C8PaletteMap has some holes in it, not all palette slots are being set in ProcessPalette().

One thing I notice about ProcessPalette() is that you're taking an array of parameters using palindex = {...} but this array will start from 1 rather than 0, and later code adapted from AGSScript naturally isn't expecting this. So to change it to a 0-based array, you could change the first few lines of ProcessPalette() from:

Code: Lua
function ProcessPalette (...)
  local palindex = {...}
  local paltemp = {...}
  local palsiz = #palindex


...to:

Code: Lua
function ProcessPalette (...)
  local palindex = {}
  local paltemp = {}
  local palsiz = select('#', ...) -- get number of arguments
  for n = 1, palsiz do
    palindex[n - 1] = select(n, ...) -- get nth argument
    paltemp[n - 1] = select(n, ...)
  end


A second thing in ProcessPalette(): it looks like, in the first while-loop, where the original AGSScript version of the function uses ags.palette[palindex[ i ]].r etc. the Lua version uses (the equivalent of) ags.palette[ i ].r

Scavenger

Alright! With Calin and Denzil's amendments to the code, it no longer crashes, and it shows the effect! I fixed the palette[palindex[ i ]].r bit and the colours got more accurate, but they still aren't correct compared to the AGSScript version.

Code: AGS
int paltemp [];
  paltemp = new int [palsiz];
  char i = 0;
  int i2 = 1;
  int upperboundary;
  while (i < palsiz)
  {
    paltemp [i] = (palette[palindex[i]].r +  // This algorithm produces an accurate, fast approximation of
                   palette[palindex[i]].r +  // The luminescence value of each of our palette slots.
                   palette[palindex[i]].r +  //
                   palette[palindex[i]].b +  // This ensures that when we colourise the final product, there 
                   palette[palindex[i]].g +  // will be little to no weird artifacts or lightness shifts.
                   palette[palindex[i]].g +  //
                   palette[palindex[i]].g +  // By using addition and bit shifts instead of multiplication, 
                   palette[palindex[i]].g    // the code will run a lot faster and more efficiently.
                   )>>3;
    i++;
  }
  i = 0;
  while (i < palsiz)
  {
      //Display ("%d , %d", i,  i+1);
      if (i > palsiz -2) upperboundary = 255;
      else upperboundary = (paltemp [i] + paltemp [i + 1])/2;
      //Display ("%d",upperboundary);

      while (i2 < upperboundary)
      {
        _C8palgorithm [i2] = palindex[i];
        i2++;
      }
    i++;
    }
    
    i= 0;
    paltemp = new int [256];
    while (i < 255)
      {
       paltemp [i] = (palette[i].r +  // This algorithm produces an accurate, fast approximation of
                      palette[i].r +  // The luminescence value of each of our palette slots.
                      palette[i].r +  //
                      palette[i].b +  // This ensures that when we colourise the final product, there 
                      palette[i].g +  // will be little to no weird artifacts or lightness shifts.
                      palette[i].g +  //
                      palette[i].g +  // By using addition and bit shifts instead of multiplication, 
                      palette[i].g    // the code will run a lot faster and more efficiently.
                      )>>3;
                      i++;
      }
    i = 0;
    while (i < 255)
      {
       _C8PaletteMap [i] = _C8palgorithm [paltemp[i]];
       i++;
      }


Code: Lua
function ProcessPalette (...)
  local palindex = {}
  local paltemp = {}
  local palsiz = select('#', ...) -- get number of arguments
  for n = 1, palsiz do
     palindex[n - 1] = select(n, ...) -- get nth argument
     paltemp[n - 1] = select(n, ...)
  end
  local i = 0
  local i2 = 1
  local upperboundary = 0
  C8palgorithm = {}
  C8PaletteMap = {}
  C8PaletteMap [0] = C8PaletteMap [0] or {}
  C8palgorithm [0] = C8palgorithm [0] or {}
  while (i < palsiz)
  do
    --paltemp [i] = ags.palette[i].r + ags.palette[i].r + ags.palette[i].r + ags.palette[i].b +  ags.palette[i].g + ags.palette[i].g + ags.palette[i].g + ags.palette[i].g
    paltemp [i] = palette_r [palindex[i]] + palette_r [palindex[i]] + palette_r [palindex[i]] + palette_b [palindex[i]] +  palette_g [palindex[i]] + palette_g [palindex[i]] + palette_g [palindex[i]] + palette_g [palindex[i]]
    paltemp [i] = bit.rshift(paltemp [i],3) 
    i = i + 1
  end
  i = 0
  C8palgorithm = {}
  while (i < palsiz)
  do
      if (i > palsiz - 2) then upperboundary = 255
      else 
        upperboundary = paltemp [i] + paltemp [i + 1]
        upperboundary = upperboundary /2
        end
      
      while (i2 < upperboundary)
      do
        C8palgorithm [i2] = palindex[i]
        i2 = i2 + 1
      end
    i = i + 1
  end
   i= 0
   paltemp = {}
   while (i < 255)
      do
       --paltemp [i] = ags.palette[i].r + ags.palette[i].r + ags.palette[i].r + ags.palette[i].b +  ags.palette[i].g + ags.palette[i].g + ags.palette[i].g + ags.palette[i].g
       paltemp [i] = palette_r [i] + palette_r [i] + palette_r [i] + palette_b [i] +  palette_g [i] + palette_g [i] + palette_g [i] + palette_g [i]
       paltemp [i] = bit.rshift(paltemp [i],3)
       i = i+ 1
      end
    i = 0
    while (i < 255)
      do
       C8PaletteMap [i] = C8palgorithm [paltemp[i]]
       i = i + 1
      end
  
end


Is the problem that Lua is counting from 1 again in the latter part of it?

The effect now looks like this:


The code looks identical, but the effect is very off. I'm not even sure how it's getting those non-blue colours.

ALSO, I'm trying to fix the translucency things, and I've noticed something very odd about the FakeScreen code I'm using:

Code: AGS
while ((i < Game.CharacterCount) || (i < Room.ObjectCount)) {
    if ((i < Game.CharacterCount) && (character[i].Room == player.Room)) surface.DrawCharacter(character[i]);
    if (i < Room.ObjectCount) {
      if (object[i].Visible) surface.DrawObject(object[i]);
      if (object[i].Graphic && object[i].Visible) {
        int scale = GetScalingAt(object[i].X, object[i].Y);
        int ow = (Game.SpriteWidth[object[i].Graphic] * scale) / 100;
        int oh = (Game.SpriteHeight[object[i].Graphic] * scale) / 100;
        if (object[i].IgnoreScaling) {
          ow = Game.SpriteWidth[object[i].Graphic];
          oh = Game.SpriteHeight[object[i].Graphic];
        }
        int ox1 = object[i].X;
        int ox2 = ox1 + ow;
        int j = 0;
        while (j < Game.CharacterCount) {
          if (character[j].Room == player.Room) {
            ViewFrame *frame = Game.GetViewFrame(character[j].View, character[j].Loop, character[j].Frame);
            int cw = (Game.SpriteWidth[frame.Graphic] * character[j].Scaling) / 100;
            int cx1 = character[j].x - (cw / 2);
            if ((((cx1 + cw) >= ox1) && (cx1 <= ox2)) && (character[j].y > object[i].Baseline) && ((character[j].y - character[j].z) >= (object[i].Y - oh))) surface.DrawCharacter(character[j]);
          }
          j++;
        }
      }
    }
    i++;
  }


It doesn't take into account the baseline of the objects, and it only seems to ever draw one object on it's surface. How do I sort objects by their baseline and draw them top-to-bottom, instead of by their object IDs? Otherwise I get stuff like this:



Which makes the effect kind of pointless. (The other object is invisible even when it's Y coordinate is above the translucent sprite. I have no idea what's going on!)

I'm really sorry for being so useless at solving these problems. This must be getting really irritating. x3

Denzil Quixode

#52
Quote from: Scavenger on Tue 03/12/2013 17:07:55
The effect now looks like this:


The code looks identical, but the effect is very off. I'm not even sure how it's getting those non-blue colours.
I think there's something odd going on when you use DrawingColor zero. I tried changing Lua's Colourise() to draw every pixel to zero, and got a lot more of that colourful effect:



Then I tried the same as Calin's except with or 255 instead of or 0:



So it looks like (I'm guessing here) drawing with DrawingColor 0 is adding transparency, punching a "hole" in the image, but what is showing up "behind" it happens to be a lot of weird pixels when Lua does it, but solid black when AGSScript does it. I've got no idea why though.

Scavenger

Yeah, that works! Weird that Lua is doing that with DrawingColor=0. I'm guessing it doesn't show up on the Translucency because Lua doesn't actually write pixels of the 0 colour.

This is a weird bug - does it do the same thing in 16 bit and 32bit with magic pink?

SMF spam blocked by CleanTalk