Roto Zoomer - The Source!
February 13th, 2009
Happy Friday the 13th! If you are looking for source code to a roto zoomer (as I was last weekend) then this is actually your lucky day! The following may have to be massaged a bit for your system - rotoZoom gets passed in a sprite structure that I use for my games - but it's pretty straight forward stuff and weighs in under 300 lines. Let me know if you make it better as I would like a copy!
RotoZoomer.h
#ifndef ROTO_ZOOMER_H
#define ROTO_ZOOMER_H
typedef enum
{
RFX_SIMPLE, //no mod's, blit all non-transparent
RFX_ALPHA_BLEND, //alpha blend w/background
RFX_ALPHA_ACCEPT, //for sprite building, just set alpha from texture, no bg blend
}RFX;
typedef struct
{
F32 x, y;
}Pt;
typedef struct
{
Pt p[4];
}Quad;
typedef struct
{
F32 tx, ty;
S32 x;
}Tpt;
extern void rotZoomQuad( Quad *q,
S32 _x,
S32 _y,
SprEntry *tex,
F32 rot,
F32 zoom
);
extern void rotoZoom( U32 *dest,
U32 dest_wd,
S32 _x,
S32 _y,
SprEntry *tex,
F32 rot,
F32 zoom,
RFX render_mods
);
extern void rotoZoomerTest(void);
#endif
RotoZoomer.cpp
#include
#include
#include
#include
#include
#include
#include
#include "blit.h"
#include "rotoZoomer.h"
#include "main.h"
static Tpt min[SCRN_HT], max[SCRN_HT];
static Bool initted[SCRN_HT];
S32 minY( Quad *q )
{
S32 i,y=q->p[0].y;
for(i=1;i<4;i++)
if( q->p[i].y < y )
y = q->p[i].y;
return y;
}
S32 maxY( Quad *q )
{
S32 i,y=q->p[0].y;
for(i=1;i<4;i++)
if( q->p[i].y > y )
y = q->p[i].y;
return y;
}
void traverse( Pt *p0,
Pt *p1,
F32 tx0,
F32 ty0,
F32 tx1,
F32 ty1
)
{
F32 pdx, pdy, tdx, tdy, len;
F32 fy = p0->y;
F32 fx = p0->x;
F32 tx = tx0;
F32 ty = ty0;
len = (S32)(p1->y - p0->y);
if( len < 0 )
len = -len;
pdx = (p1->x - p0->x) / len;
pdy = (p1->y - p0->y) / len;
tdx = (tx1 - tx0) / len;
tdy = (ty1 - ty0) / len;
S32 dy, y = (S32)fy;
if( pdy < 0 )
dy = -1;
else dy = 1;
do
{
if( y >=0 && y < SCRN_HT )
{
if( fx < 0 || fx >=800)
{
int halt = 1;
}
if( !initted[y] )
{
min[y].x = fx;
min[y].tx = tx;
min[y].ty = ty;
max[y].x = fx;
max[y].tx = tx;
max[y].ty = ty;
initted[y] = TRUE;
}
else
{
initted[y]++;
if( fx < min[y].x )
{
min[y].x = fx;
min[y].tx = tx;
min[y].ty = ty;
}
else if( fx > max[y].x )
{
max[y].x = fx;
max[y].tx = tx;
max[y].ty = ty;
}
}
}
tx += tdx; ty += tdy;
fx += pdx; y+=dy;
}while(len--);
}
void polyRender( U32 *dest,
U32 dest_wd,
Quad *q,
SprEntry *tex,
RFX rfx
)
{
S32 y0, y1, y, ht;
y0 = minY(q);
y1 = maxY(q);
ht = y1-y0;
memSet( &initted[y0], 0, ht * sizeof(S32));
//traverse goes over the edge of each line and collects end points in an array
traverse( &q->p[0], &q->p[1], 0,0, tex->wd, 0 );
traverse( &q->p[1], &q->p[2], tex->wd,0, tex->wd, tex->ht );
traverse( &q->p[2], &q->p[3], tex->wd, tex->ht, 0, tex->ht );
traverse( &q->p[3], &q->p[0], 0,tex->ht, 0, 0 );
for(y=y0;y= CLIP_WDW_TOP && ypix;
S32 len, x = min[y].x;
len = max[y].x - min[y].x; //length of pixel run.
tdx = (max[y].tx - min[y].tx)/(F32)len;
tdy = (max[y].ty - min[y].ty)/(F32)len;
tx = (U32)min[y].tx;
ty = (U32)min[y].ty;
while(len-- && x < CLIP_WDW_RIGHT)
{
if( x++ >= CLIP_WDW_LEFT )
if( tx >= 0 && ty >= 0 && tx < tex->wd && ty < tex->ht )
{
U32 pix = *(tp + (U32)ty * tex->wd + (U32)tx);
if( rfx == RFX_SIMPLE )
{
if( pix & 0xff000000 ) //no alpha blend
*base = pix;
}
else if( rfx == RFX_ALPHA_BLEND )
{
U32 alpha = (pix & 0xff000000) >> 24;
U32 r, bg_r, g, bg_g, b, bg_b, bg = *base;
r = pix & 0x000000ff;
bg_r = bg & 0x000000ff;
r *= ( alpha);
bg_r *= ( 255 - alpha );
r = ( r + bg_r + 1 ) >> 8;
g = ( pix & 0x0000ff00 ) >> 8;
bg_g = ( bg & 0x0000ff00 ) >> 8;
g *= ( alpha);
bg_g *= ( 255 - alpha );
g = ( g + bg_g + 1 ) >> 8;
b = ( pix & 0x00ff0000 ) >> 16;
bg_b = ( bg & 0x00ff0000 ) >> 16;
b *= ( alpha);
bg_b *= ( 255 - alpha );
b = ( b + bg_b + 1 ) >> 8;
if( r > 255 )
r = 255;
else if( r < 0 )
r = 0;
if( g > 255 )
g = 255;
else if( g < 0 )
g = 0;
if( b > 255 )
b = 255;
else if( b < 0 )
b = 0;
*base = r | ( g << 8 ) | ( b << 16 );
}
else if( rfx == RFX_ALPHA_ACCEPT ) //take alpha channel
{ //from texture, useful for pre-building sprites.
*base = pix;
}
}
base++;
tx += tdx;
ty += tdy;
}
}
}
void rotZoomQuad( Quad *q,
S32 _x,
S32 _y,
SprEntry *tex,
F32 rot,
F32 zoom
)
{
q->p[0].x = tex->hot_x; //ul
q->p[0].y = tex->hot_y;
q->p[1].x = q->p[0].x + tex->wd; //ur
q->p[1].y = q->p[0].y;
q->p[2].x = q->p[1].x; //dr
q->p[2].y = q->p[1].y + tex->ht;
q->p[3].x = q->p[0].x; //dl
q->p[3].y = q->p[2].y;
S32 i;
for(i=0;i<4;i++)
{
F32 x, y;
x = q->p[i].x;
y = q->p[i].y;
q->p[i].x = _x + (zoom * (x * cos(rot) - y * sin(rot)));
q->p[i].y = _y + (zoom * (x * sin(rot) + y * cos(rot)));
}
}
void rotoZoom( U32 *dest,
U32 dest_wd,
S32 _x,
S32 _y,
SprEntry *tex,
F32 rot,
F32 zoom,
RFX render_mods
)
{
Quad q;
rotZoomQuad( &q, _x, _y, tex, rot, zoom );
//quad has been transformed and scaled, now texture render the quad.
polyRender( dest, dest_wd, &q, tex, render_mods );
}
Well, that's it. I've been using it for several days and it seems solid. I've noticed a little artifacting when rotating small text strings and if I find a way to make it better then I will update the source.
Have a good weekend,
L.
prev
blog home
next
No comments have been provided.