ENIGMA Forums

Contributing to ENIGMA => Function Peer Review => Topic started by: TheExDeus on September 25, 2010, 12:58:45 pm

Title: GML: draw_sprite_tiled_area + draw_sprite_tiled_area_ext (Custom, not in GM)
Post by: TheExDeus on September 25, 2010, 12:58:45 pm
Notice: These functions are not in GM. They are just additional functions that could be useful to add. What they do is that they tile a sprite over selected region (which is a rectangle with x1,y1 top left corner and x2,y2 bottom left). It automatically crops the image as needed and it works with any size sprite. They are based on GMLScripts script http://www.gmlscripts.com/script/draw_sprite_tiled_area (http://www.gmlscripts.com/script/draw_sprite_tiled_area) by EyeGuy.

Function: draw_sprite_tiled_area(int spr,int subimg,double x,double y,double x1,double y1,double x2,double y2);

GSsprite.h:
Code: [Select]
int draw_sprite_tiled_area(int spr,int subimg,double x,double y,double x1,double y1,double x2,double y2);
GSsprite.cpp:
Code: [Select]
int draw_sprite_tiled_area(int spr,int subimg,double x,double y,double x1,double y1,double x2,double y2)
{
  enigma::sprite *spr2d = enigma::spritestructarray[spr];
  if (!spr2d)
    return -1;

  if (enigma::cur_bou_tha_noo_sho_eve_cha_eve != spr2d->texturearray[subimg % spr2d->subcount])
  {
    glBindTexture(GL_TEXTURE_2D,spr2d->texturearray[subimg % spr2d->subcount]);
    enigma::cur_bou_tha_noo_sho_eve_cha_eve = spr2d->texturearray[subimg % spr2d->subcount];
  }


  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);


  glPushAttrib(GL_CURRENT_BIT);
    glColor4f(1,1,1,1);

    const float tbx=spr2d->texbordx,tby=spr2d->texbordy;
    float sw,sh,i,j,jj,left,top,width,height,X,Y;
    sw = spr2d->width;
    sh = spr2d->height;

    i = x1-(fmod(x1,sw) - fmod(x,sw)) - sw*(fmod(x1,sw)<fmod(x,sw));
    j = y1-(fmod(y1,sh) - fmod(y,sh)) - sh*(fmod(y1,sh)<fmod(y,sh));
    jj = j;

    glBegin(GL_QUADS);
    for(i=i; i<=x2; i+=sw) {
        for(j=j; j<=y2; j+=sh) {

            if(i <= x1) left = x1-i;
            else left = 0;
            X = i+left;

            if(j <= y1) top = y1-j;
            else top = 0;
            Y = j+top;

            if(x2 <= i+sw) width = ((sw)-(i+sw-x2)+1)-left;
            else width = sw-left;

            if(y2 <= j+sh) height = ((sh)-(j+sh-y2)+1)-top;
            else height = sh-top;

            glTexCoord2f(left/sw*tbx,top/sh*tby);
            glVertex2f(X,Y);
            glTexCoord2f((left+width)/sw*tbx,top/sh*tby);
            glVertex2f(X+width,Y);
            glTexCoord2f((left+width)/sw*tbx,(top+height)/sh*tby);
            glVertex2f(X+width,Y+height);
            glTexCoord2f(left/sw*tbx,(top+height)/sh*tby);
            glVertex2f(X,Y+height);
        }
        j = jj;
    }
    glEnd();

    glPopAttrib();
    return 0;
}

Function: draw_sprite_tiled_area_ext(int spr,int subimg,double x,double y,double x1,double y1,double x2,double y2, double xscale, double yscale, int color, double alpha);

GSsprite.h:
Code: [Select]
int draw_sprite_tiled_area_ext(int spr,int subimg,double x,double y,double x1,double y1,double x2,double y2, double xscale, double yscale, int color, double alpha);
GSsprite.cpp:
Code: [Select]
int draw_sprite_tiled_area_ext(int spr,int subimg,double x,double y,double x1,double y1,double x2,double y2, double xscale, double yscale, int color, double alpha)
{
  enigma::sprite *spr2d = enigma::spritestructarray[spr];
  if (!spr2d)
    return -1;

  if (enigma::cur_bou_tha_noo_sho_eve_cha_eve != spr2d->texturearray[subimg % spr2d->subcount])
  {
    glBindTexture(GL_TEXTURE_2D,spr2d->texturearray[subimg % spr2d->subcount]);
    enigma::cur_bou_tha_noo_sho_eve_cha_eve = spr2d->texturearray[subimg % spr2d->subcount];
  }


  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);


  glPushAttrib(GL_CURRENT_BIT);
    glColor4ub(__GETR(color),__GETG(color),__GETB(color),char(alpha*255));

    const float tbx=spr2d->texbordx,tby=spr2d->texbordy;
    float sw,sh,i,j,jj,left,top,width,height,X,Y;
    sw = spr2d->width*xscale;
    sh = spr2d->height*yscale;

    i = x1-(fmod(x1,sw) - fmod(x,sw)) - sw*(fmod(x1,sw)<fmod(x,sw));
    j = y1-(fmod(y1,sh) - fmod(y,sh)) - sh*(fmod(y1,sh)<fmod(y,sh));
    jj = j;

    glBegin(GL_QUADS);
    for(i=i; i<=x2; i+=sw) {
        for(j=j; j<=y2; j+=sh) {

            if(i <= x1) left = x1-i;
            else left = 0;
            X = i+left;

            if(j <= y1) top = y1-j;
            else top = 0;
            Y = j+top;

            if(x2 <= i+sw) width = ((sw)-(i+sw-x2)+1)-left;
            else width = sw-left;

            if(y2 <= j+sh) height = ((sh)-(j+sh-y2)+1)-top;
            else height = sh-top;

            glTexCoord2f(left/sw*tbx,top/sh*tby);
            glVertex2f(X,Y);
            glTexCoord2f((left+width)/sw*tbx,top/sh*tby);
            glVertex2f(X+width,Y);
            glTexCoord2f((left+width)/sw*tbx,(top+height)/sh*tby);
            glVertex2f(X+width,Y+height);
            glTexCoord2f(left/sw*tbx,(top+height)/sh*tby);
            glVertex2f(X,Y+height);
        }
        j = jj;
    }
    glEnd();

    glPopAttrib();
    return 0;
}

Added attachments show:
First image: draw_sprite_tiled_area(sprite0,0,0,0,75,75,375,375);
Second image: draw_sprite_tiled_area_ext(sprite0,0,0,0,75,75,375,375,0.5,0.5,c_yellow,0.7);

You can see some artifacts when scaled down, but I guess it is normal.
Title: Re: GML: draw_sprite_tiled_area + draw_sprite_tiled_area_ext (Custom, not in GM)
Post by: Josh @ Dreamland on October 16, 2010, 01:38:32 pm
I'm not sure what to do with the licensing for these. I can see these being useful, though. Is any license associated with them on that site?
Title: Re: GML: draw_sprite_tiled_area + draw_sprite_tiled_area_ext (Custom, not in GM)
Post by: IsmAvatar on October 16, 2010, 02:16:10 pm
The scripts on that site are unlicensed and free use, but copyrighted. That means that technically you can steal them and license them however you'd like. However, it would be common courtesy to try to contact the author first and make sure that they are ok with you relicensing them to a GPL compatible license (such as MIT, or GPL)
Title: Re: GML: draw_sprite_tiled_area + draw_sprite_tiled_area_ext (Custom, not in GM)
Post by: TheExDeus on October 16, 2010, 04:58:57 pm
They are free to use, modify and redistribute, so there is really no license involved. You don't even have to give credit, but you could if you were kind enough. :D I do understand the math behind all this, so I could make them from scratch, but they would look almost exactly like these. Its just that there are aren't so many ways to invent the wheel... I could make them more messy and less optimized if you want. :D Then they would be custom made for sure. And I am pretty sure these can't be optimized any more, because gmlscript.com usually hold only the most optimized code. Thou here we could change float to int, thus creating an (substantial?) speed increase, because I don't see why width, height or position should be decimal.. Thou you have made all positions and sizes in all functions floats for some reason. I know that GM allows x and y to be a decimal, but I don't get why. You cant have something 1.5 pixels from somewhere. Its a lot better if is 1 or 2 pixels.

Hope you understand this post. It seems I have written a lot and said so little.
Title: Re: GML: draw_sprite_tiled_area + draw_sprite_tiled_area_ext (Custom, not in GM)
Post by: Josh @ Dreamland on October 17, 2010, 08:23:37 am
Well, that's a mistake in itself. You could have the most outstandingly efficient piece of GML fathomable, and on the optimization scale, it'd be a steaming pile of shit and rusted bolts compared to the equivalent in a real language. Not just because GM's interpreter is slow, but because GM's pointerless, classless system makes you do all sorts of running around to get something simple done. Compare a C Brainfuck interpreter to a JavaScript one. There's no comparison.

But yes, your purpose is legal, then; as Ism said, it's just common courtesy to let him know we're using them. (Even though I don't find anything particularly insightful about his code, it's the idea that counts). But, seeing as that site is full of people taking such scripts and running with them without credit, I suppose it'd only be annoying if everyone that found use for them emailed him. So do what you like.

Anyway, yes, I'm a proponent of integral coordinates. Except one thing:
All vars are double. To call your function, they are cast as int.
You then invoke glTexCoord2f, casting them back to float.

I do the same thing; I've been waiting for someone to help me decide what to do about it. I think at this point swapping them all for 2i would be our best bet.
Title: Re: GML: draw_sprite_tiled_area + draw_sprite_tiled_area_ext (Custom, not in GM)
Post by: IsmAvatar on October 17, 2010, 09:07:32 am
Quote
But, seeing as that site is full of people taking such scripts and running with them without credit, I suppose it'd only be annoying if everyone that found use for them emailed him. So do what you like.
Yes, but it's not every day that someone requests permission to relicense/license their code.
Title: Re: GML: draw_sprite_tiled_area + draw_sprite_tiled_area_ext (Custom, not in GM)
Post by: Josh @ Dreamland on October 17, 2010, 12:43:33 pm
Good point. Maybe I'll drop him a letter later.
Title: Re: GML: draw_sprite_tiled_area + draw_sprite_tiled_area_ext (Custom, not in GM)
Post by: Brett on October 19, 2010, 07:34:48 pm
Thou here we could change float to int, thus creating an (substantial?) speed increase, because I don't see why width, height or position should be decimal.. Thou you have made all positions and sizes in all functions floats for some reason. I know that GM allows x and y to be a decimal, but I don't get why. You cant have something 1.5 pixels from somewhere. Its a lot better if is 1 or 2 pixels.
GM allows X,Y to be decimals because you can modify the view so that those little differences matter. GM has 3D support, and all 2D functions work in 3D as well so the decimals matter when the view is transformed.



[EDIT]
Not sure if you can do this in OpenGL (I've only used DX) but you can get DX to do this automatically for you. Simply turn texture repeating on and then put a vertex at the 4 corners with texture cords changed.
[/EDIT]

[EDIT2]
Something like this? (Never used OGL before, so it needs debugging)

.H file
Code: [Select]
int draw_sprite_tiled_area(int spr, double X, double Y, int x1, int y1, int x2, int y2);
.CPP file
Code: [Select]
//int spr, double x, double y, int x1, int y1, int x2, int y2
//Draws a rectangle between [x1,y1] and [x2,y2] with the sprite offset by X and Y (both 0-1)
int draw_sprite_tiled_area(int spr, double X, double Y, int x1, int y1, int x2, int y2){
  enigma::sprite *spr2d = enigma::spritestructarray[spr];
  //Non existant sprite
  if (!spr2d) return -1;
  //Some error? No idea, but it is used in the above example too.
  if (enigma::cur_bou_tha_noo_sho_eve_cha_eve != spr2d->texturearray[subimg % spr2d->subcount]){
    glBindTexture(GL_TEXTURE_2D,spr2d->texturearray[subimg % spr2d->subcount]);
    enigma::cur_bou_tha_noo_sho_eve_cha_eve = spr2d->texturearray[subimg % spr2d->subcount];
  }
  const float tbx=spr2d->texbordx;
  const float tby=spr2d->texbordy;
  const float sw = spr2d->width;
  const float sh = spr2d->height;
  //Texture repeat ON
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
  glPushAttrib(GL_CURRENT_BIT);
  glColor4f(1,1,1,1);
  //Draw the quad
  glBegin(GL_QUADS);
       glTexCoord2f(x1/sw*tbx+X,y1/sh*tby+Y);
       glVertex2f(x1,y1);
       glTexCoord2f(x2/sw*tbx+X,y1/sh*tby+Y);
       glVertex2f(x2,y1);
       glTexCoord2f(x2/sw*tbx+X,y2/sh*tby+Y);
       glVertex2f(x1,y2);
       glTexCoord2f(x1/sw*tbx+X,y2/sh*tby+Y);
       glVertex2f(x2,y2);
  //End drawing
  glEnd();
  glPopAttrib();
  return 0;
}

No credit required and No license attached - do whatever you want with it (if it works). Derived from the code above
[/EDIT2]
Title: Re: GML: draw_sprite_tiled_area + draw_sprite_tiled_area_ext (Custom, not in GM)
Post by: RetroX on October 20, 2010, 02:30:28 pm
If numbers are stored as doubles, and passed as doubles, why are you using glVertex2f instead of glVertex2d?
Title: Re: GML: draw_sprite_tiled_area + draw_sprite_tiled_area_ext (Custom, not in GM)
Post by: Josh @ Dreamland on October 20, 2010, 08:07:51 pm
Brett--
You are assuming all power-of-two textures. I see you multiplied tbx and tby by the necessary factor. Those variables, however, are only 1 (meaning the edge of the sprite) when the sprite is a power of two across both dimensions. I proposed such a fix after the check for each against 1, but no one has implemented it, yet.

Also, can you elaborate on a circumstance where doubles would be necessary for X and Y for the sake of accuracy? I'm not sure if a highly zoomed in view would produce such a case or not; can you test that for me? (I realize that many GL demonstrations have axises from -1 to 1, but I am not sure what GM's behavior would be in that case.)

RetroX--
No good reason. Originally I was using floats for x and y, but then I changed to doubles, and now I'm using int. I never updated the code, though, because I was waiting for someone like Brett to justify why they should be doubles.
Title: Re: GML: draw_sprite_tiled_area + draw_sprite_tiled_area_ext (Custom, not in GM)
Post by: TheExDeus on October 21, 2010, 01:49:04 pm
So whens the new Rep coming? I want to study a code a bit and maybe, in time, I could add more advanced things (thinking about paths). Just want to have the newest rep before I do so.