Sprite Deformation

Started by Calin Leafshade, Mon 28/12/2009 07:49:33

Previous topic - Next topic

Calin Leafshade

Is it possible to deform a sprite in the current AGS?

So like you can like set the four corners of a sprite to be independent points?

I guess this is hackable since you have direct control over a drawing surface but im guessing that would be slow.

Jim Reed

It's not possible directly, but SteveMcCrea in his ags 3d module did it. Maybe try PM him.
I put it on the next version wishlist back there somwhere. But as CJ said, he's quite busy, so I don't expect to be implented anytime soon.

GarageGothic

I wrote some deformation scripts that allow you to skew a sprite vertically, horizontally or both. I think there's also code to change the corner coordinates. It works pretty well, but yeah, it does use DrawingSurface commands so I wouldn't recommend trying it on huge sprites or multiple sprites every loop in the rep_exec.

Jim Reed

If it's not a problem, I'd very much like to get my hands on that code, GarageGothic.
Would you be kind enough and pass it along?

Calin Leafshade


GarageGothic

#5
Alright, I don't really have time to check if everything's working, but I think it should. Here's the code and a few helper functions, it's the bottom function you have to run to achieve the effect you want. I remember now the reason I took a break from finishing this code - I simply couldn't come up with the best way of returning the offset coordinates for base/center/corners. I'll get back to it eventually, I think I'll simply save them in global ints that get updated with each deformation.

Please let me know if there are any problems with the code. The main thing I remember lacking is optional rotation of the sprite before the transformation.

Code: ags

#sectionstart AbsInt(int number)
function AbsInt(int number) {
  if (number < 0) number = -number;
  return number;
  }
#sectionend AbsInt(int number)


#sectionstart AbsFloat(float number)
float AbsFloat(float number) {
  if (number < 0.0) number = -number;
  return number;
  }
#sectionend AbsFloat(float number)

#sectionstart MaxIntValue2(int value1, int value2)
function MaxIntValue2(int value1, int value2) { //may be slightly faster than the 9-parameter version
  if (value1 >= value2) return value1;
  else return value2;
  }
#sectionend MaxIntValue2(int value1, int value2)


#sectionstart MinIntValue2(int value1, int value2)
function MinIntValue2(int value1, int value2) { //may be slightly faster than the 9-parameter version
  if (value1 < value2) return value1;
  else return value2;
  }
#sectionend MinIntValue2(int value1, int value2)


#sectionstart SkewVerticalAdvanced(this DynamicSprite*, int yoffset1,  int yoffset2, int height1,  int height2)
function SkewVerticalAdvanced(this DynamicSprite*, int yoffset1,  int yoffset2, int height1,  int height2) {
  int width = this.Width;
  int height = this.Height;
  int yoffsetdifference = yoffset2-yoffset1;
  int heightdifference = height2-height1;
  DynamicSprite* origsprite = DynamicSprite.CreateFromExistingSprite(this.Graphic, true);
  this.ChangeCanvasSize(width, MaxIntValue2(AbsInt(height1), AbsInt(height2)) + MaxIntValue2(AbsInt(yoffset1), AbsInt(yoffset2)), 0, 0);
  DrawingSurface* drawsurface = this.GetDrawingSurface();
  DrawingSurface* readsurface = origsprite.GetDrawingSurface();//drawsurface.CreateCopy();
  drawsurface.Clear();
  int counter;
  while (counter < width) {
    DynamicSprite* tempsprite = DynamicSprite.CreateFromDrawingSurface(readsurface, counter, 0, 1, height);
    int drawoffset = yoffset1+((yoffsetdifference*counter)/width);
    int drawheight = height1+((heightdifference*counter)/width);
    drawsurface.DrawImage(counter, drawoffset, tempsprite.Graphic, 0, 1,  drawheight);
    counter++;//++;
    }
  drawsurface.Release();
  readsurface.Release();
  origsprite.Delete();
  }
#sectionend SkewVerticalAdvanced(this DynamicSprite*, int yoffset1,  int yoffset2, int height1,  int height2)


#sectionstart SkewHorizontalAdvanced(this DynamicSprite*, int xoffset1,  int xoffset2, int width1,  int width2) 
function SkewHorizontalAdvanced(this DynamicSprite*, int xoffset1,  int xoffset2, int width1,  int width2) {
  int width = this.Width;
  int height = this.Height;
  int xoffsetdifference = xoffset2-xoffset1;
  int widthdifference = width2-width1;
  DynamicSprite* origsprite = DynamicSprite.CreateFromExistingSprite(this.Graphic, true);
  this.ChangeCanvasSize(MaxIntValue2(AbsInt(width1), AbsInt(width2)) + MaxIntValue2(AbsInt(xoffset1), AbsInt(xoffset2)), height, 0, 0);
  DrawingSurface* drawsurface = this.GetDrawingSurface();
  DrawingSurface* readsurface = origsprite.GetDrawingSurface();//drawsurface.CreateCopy();
  drawsurface.Clear();
  int counter;
  while (counter < height) {
    DynamicSprite* tempsprite = DynamicSprite.CreateFromDrawingSurface(readsurface, 0, counter, width, 1);
    int drawoffset = xoffset1+((xoffsetdifference*counter)/height);
    int drawwidth = width1+((widthdifference*counter)/height);
    drawsurface.DrawImage(drawoffset, counter, tempsprite.Graphic, 0, drawwidth, 1);
    counter++;
    }
  drawsurface.Release();
  readsurface.Release();
  origsprite.Delete();
  }
#sectionend SkewHorizontalAdvanced(this DynamicSprite*, int xoffset1,  int xoffset2, int width1,  int width2) 


#sectionstart SkewVertical(this DynamicSprite*, int yoffset)
function SkewVertical(this DynamicSprite*, int yoffset) {  //Possibly faster than the advanced version as scale is not needed
  if (yoffset == 0) return;
  int width = this.Width;
  int height = this.Height;
  this.ChangeCanvasSize(width, height+AbsInt(yoffset), 0, 0);
  DrawingSurface* drawsurface = this.GetDrawingSurface();
  int counter;
  while (counter < width) {
    DynamicSprite* tempsprite = DynamicSprite.CreateFromDrawingSurface(drawsurface, counter, 0, 1, height);
    drawsurface.DrawingColor = COLOR_TRANSPARENT;
    drawsurface.DrawLine(counter, 0, counter, height);
    if (yoffset < 0) drawsurface.DrawImage(counter, ((yoffset*counter)/width)-yoffset, tempsprite.Graphic);
    else drawsurface.DrawImage(counter, (yoffset*counter)/width, tempsprite.Graphic);
    counter++;
    }
  drawsurface.Release();
  //return mysprite;
  }
#sectionend SkewVertical(this DynamicSprite*, int yoffset)


#sectionstart SkewHorizontal(this DynamicSprite*, int xoffset)
function SkewHorizontal(this DynamicSprite*, int xoffset) { //Possibly faster than the advanced version as scale is not needed
  if (xoffset == 0) return;
  int width = this.Width;
  int height = this.Height;
  this.ChangeCanvasSize(width+AbsInt(xoffset), height, 0, 0);
  DrawingSurface* drawsurface = this.GetDrawingSurface();
  int counter;
  while (counter < height) {
    DynamicSprite* tempsprite = DynamicSprite.CreateFromDrawingSurface(drawsurface, 0, counter, width, 1);
    drawsurface.DrawingColor = COLOR_TRANSPARENT;
    drawsurface.DrawLine(0, counter, width, counter);
    if (xoffset < 0) drawsurface.DrawImage(((xoffset*counter)/height)-xoffset, counter, tempsprite.Graphic);
    else drawsurface.DrawImage((xoffset*counter)/height, counter, tempsprite.Graphic);
    counter++;
    }
  drawsurface.Release();
  //return mysprite;
  }
#sectionend SkewHorizontal(this DynamicSprite*, int xoffset)


#sectionstart Skew(this DynamicSprite*, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)
function Skew(this DynamicSprite*, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
  this.SkewHorizontalAdvanced(x1, x4, x2-x1, x3-x4);
  this.SkewVerticalAdvanced(y1, y2, y4-y1, y3-y2);
  }
#sectionend Skew(this DynamicSprite*, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)


Edit: I must admit I don't remember which corners are controlled by which parameters (i.e. x1,y1 and x2,y2 are probably the top left and top right corner, but I'm not sure if x3,y3 is bottom left corner - sorting the coordinate pairs in rows - or it could be bottom right corner - going clockwise around the sprite). Just play around with it.

Jim Reed

Thank you very much GarageGothic! Will give it a twist tomorrow.

SMF spam blocked by CleanTalk