PDA

View Full Version : ISEFfects - transformation demo



Admin
04-04-2009, 12:17 PM
here is the source I used to create the effects in this video:
http://www.youtube.com/watch?v=12JJ3yZDpO0&feature=player_embedded

the whole animation is a series of ISEffects transformations of the album cover, rendered to an AVI file.

i use Windows Movie Maker to combine the AVI and the audio for the song.

the first bit of code is a custom ISEffects transformation. it's a simple point attractor:


struct particle
{
typedef enum {linear, square, log, cos, sin, cost} attractMode;

particle()
{
}

double x, y;
double mag;
double maxDist;
double fK;
attractMode mode;

};

struct partInfo
{
std::vector<particle> particles;
UINT w, h;
double fCenterX, fCenterY;
};

// our custom XForm.
BOOL CALLBACK ParticleXForm(const double wx, const double wy,
double *x, double *y,
const ISE_CALLBACK_DATA uUserData)
{
partInfo *pInfo = (partInfo*)uUserData;

double vx=0, vy=0;

for (std::vector<particle>::iterator it = pInfo->particles.begin();
it != pInfo->particles.end();
it++)
{
if ((*it).maxDist==0)
{
continue;
}

double offX=0, offY=0;

double dx = wx - (*it).x, dy = wy - (*it).y;

double fDist = sqrt(dx * dx + dy * dy);

double mag = 0;

switch ((*it).mode)
{
case particle::linear:
if (fDist <= (*it).maxDist)
mag = 1.0 - fDist / (*it).maxDist;
break;

// ... other modes removed to keep the sample short...
}

mag *= (*it).mag;

// new point lies this far out on the line
double fNewDist = mag;

// find the angle that the input points make with the +X axis
if (dx != 0)
{
double fAngle = atan((double)dy / (double)dx);

double nY = sin(fAngle) * fNewDist, nX = cos(fAngle) * fNewDist;

// handle negative quadrants
if (dx >= 0)
{
offX = nX; offY = nY;
}
else
{
offX = -nX; offY = -nY;
}
}
else
{
// on y axis
offX = 0;

if (dy < 0)
offY = -fNewDist;
else
offY = fNewDist ;
}

vx+=offX;
vy+=offY;
}

*x = wx + vx;
*y = wy + vy;

return TRUE;
}
and here's the main function:



#include "ISource.h"
#include "ISEffects.h"
#include <io.h>
#include <iostream>
#include "math.h"

...

void WarpIt()
{
bool bAVI = true;

int frame_rate = 20; // 20 per second

// the song is 4 minutes and 4 seconds long.
int totalFrames = frame_rate * (60 * 4 + 4);// 4:04 = 4880 frames;
//int totalFrames = frame_rate * 10; // 10 sec demo, useful for testing

char *lpFilename = "c:\\temp\\warp_out.avi";

if (_access(lpFilename, 0) == 0)
{
if (!DeleteFile(lpFilename))
{
AfxMessageBox("Can't delete target. (Is it open in WMP?");
return;
}
}

::CoInitialize(NULL);
PAVIFILE pfile = NULL;
PAVISTREAM ps = NULL, psCompressed = NULL;

// get a DC so we can draw frames as they're rendered
CClientDC dc(this);

// output image size
UINT32 w = 400;
UINT32 h = 300;

// read the source image
UINT32 bw, bh;
HISSRC hSrc = IS40_OpenFileSource(DPIX"bizcover.bmp");
HGLOBAL hImgDIB = IS40_ReadImage(hSrc, &bw, &bh, 2, 0);
IS40_CloseSource(hSrc);


BITMAPINFOHEADER * pInBMIH = (BITMAPINFOHEADER *)hImgDIB;

UINT32 uBitCount;
IS40_DIBBitCount(pInBMIH, &uBitCount);

ASSERT(uBitCount==24);
if (uBitCount!=24) return;


BYTE *pInDIBPix = IS40_DIBPixelStart(pInBMIH);

UINT32 inRowStride = IS40_GetDIBRowStride(bw, 24);
UINT32 srcRowStride = IS40_GetDIBRowStride(w, 24);

UINT dibSize = IS40_GetISDIBSize(w, h, 24, 0);

// resized input
BYTE *pSrcDIB = new BYTE[dibSize];
BITMAPINFOHEADER * pSrcBMIH = (BITMAPINFOHEADER *)pSrcDIB;
pSrcBMIH->biBitCount = 24;
pSrcBMIH->biClrImportant = 0;
pSrcBMIH->biClrUsed = 0;
pSrcBMIH->biCompression = BI_RGB;
pSrcBMIH->biHeight = h;
pSrcBMIH->biPlanes = 1;
pSrcBMIH->biSize = sizeof(BITMAPINFOHEADER);
pSrcBMIH->biSizeImage = dibSize;
pSrcBMIH->biWidth = w;
pSrcBMIH->biXPelsPerMeter = 10000;
pSrcBMIH->biYPelsPerMeter = 10000;

BYTE *pSrcPix = IS40_DIBPixelStart(pSrcBMIH);

// output
BYTE *pOutDIB = new BYTE[dibSize];
BITMAPINFOHEADER * pOutBMIH = (BITMAPINFOHEADER *)pOutDIB;
pOutBMIH->biBitCount = 24;
pOutBMIH->biClrImportant = 0;
pOutBMIH->biClrUsed = 0;
pOutBMIH->biCompression = BI_RGB;
pOutBMIH->biHeight = h;
pOutBMIH->biPlanes = 1;
pOutBMIH->biSize = sizeof(BITMAPINFOHEADER);
pOutBMIH->biSizeImage = dibSize;
pOutBMIH->biWidth = w;
pOutBMIH->biXPelsPerMeter = 0;
pOutBMIH->biYPelsPerMeter = 0;

BYTE *pOutPix = IS40_DIBPixelStart(pOutBMIH);

if (bAVI)
{
AVI_Init();

AVI_FileOpenWrite(&pfile, lpFilename);

AVI_CreateStream(pfile, &ps, frame_rate,
(unsigned long)0,// lpbi->biSizeImage,
(int) pSrcBMIH->biWidth,
(int) pSrcBMIH->biHeight);

AVI_SetOptions(&ps, &psCompressed, pSrcBMIH);

if (!psCompressed) return;
}

// init our Perlin noise source
HISEPERLIN perlin = ISE40_InitializePerlinNoise3D(0);
BYTE *perlinData = new BYTE[w * h];
BYTE *perlinData2 = new BYTE[w * h];

int iFrame = 0;

int z = 0;

double pi = acos(-1);

BYTE *pInStart = pInDIBPix;
IS40_ResizeImage(pInStart, bw, bh, inRowStride,
pSrcPix, w, h, srcRowStride, 3, 0, 0);

memcpy(pOutPix, pSrcPix, h * srcRowStride);

// loop over the total number of frames.
for (z=0;z<totalFrames;z++)
{
ISE40_ClearAllTransforms();

double dz = (double)z;
double totalDone = dz / (totalFrames - 1);

///////////////////////////////////////////////////////////////////

// first effect is some warping, using a
// Perlin noise image as the input to a Bump XForm
int part1Start = 0;

// it will run for the first 1/3 of the animation
int part1End = part1Start + totalFrames / 3;
int part1Size = part1End - part1Start;

if (z >= part1Start && z < part1End)
{
double pz = z - part1Start;
double partDone = pz / (double)(part1Size - 1.);
double fScale = 0.15;
double fZoomScale = part1Size/fScale;
double fZoomSpeed = .5;
double fFeatureSpeed = .01;
double fz = fScale - pz / fZoomScale;
double zpos = 0.001 + pz / 50.;
ISE40_GetPerlinNoise3DSliceBYTE(perlin, perlinData, w, h, w,
fz * fZoomSpeed, zpos * partDone);

// add some high-frequency noise, too
double freq = 2;
for (; freq >= 1. ; freq /= 2)
{
ISE40_GetPerlinNoise3DSliceBYTE(perlin, perlinData2, w, h, w,
freq * fz * fZoomSpeed, zpos * fFeatureSpeed);

ISE40_AddBumpMapDisplacementTransform(w, h,
20. * partDone, perlinData2, w, h);
}

ISE40_AddBumpMapDisplacementTransform(w, h,
50. * partDone, perlinData, w, h);
}

///////////////////////////////////////////////////////////////////

// the next effect, comes in 5 seconds before the end of the first effect
int part2Start = part1End - frame_rate * 5;
int part2End = part2Start + totalFrames / 3; // runs for 1/3 the total time
int part2Size = part2End - part2Start;

// the next two effects use the custom "particle" XForm,
// which is a simple point attractor. but we're using it backwards,
// sortof. instead of attracting, it's pulling image data from the
// outside of the image and shoving it up through the middle,
// turning the image inside out.
partInfo pia;
pia.particles.clear();
particle p0;

if (z >= part2Start && z < part2End)
{
double pz = z - part2Start;
double partDone = pz / (double)(part2Size - 1.);

// vary the strength of the attractor over the lifetime of the effect
double f = 1 + (.5 - cos(partDone * pi) / 2.0);

p0.x = w/2;
p0.y = h/2;
p0.mag = 200 * (1.0 - f);

p0.fK = 1;
p0.maxDist = 400;
p0.mode = particle::linear;
pia.particles.push_back(p0);

ISE40_AddUserTransform(w,h,ParticleXForm, (UINT32)&pia);
}

///////////////////////////////////////////////////////////////////

// the next effect is basically the previous one running in
// reverse - sucking all the image data back in.
int part3Start = part2End;
int part3End = part3Start + totalFrames / 3;
int part3Size = part3End - part3Start;

if (z >= part3Start && z < part3End)
{
double pz = z - part3Start;
double partDone = pz / (double)(part3Size - 1.);

double f = 2. - partDone;
ASSERT(f >= 0);

p0.x = w/2;
p0.y = h/2;
p0.mag = 200 * (1.0 - f);
p0.fK = 1;
p0.maxDist = 400;
p0.mode = particle::linear;
pia.particles.push_back(p0);

ISE40_AddUserTransform(w,h,ParticleXForm, (UINT32)&pia);
}

///////////////////////////////////////////////////////////////////

// this effect comes in part way through the previous one. it's a
// spin XForm, which will vary in direction and intensity over the
// effect's lifetime.
int part4Start = part2End + part3Size / 3;
int part4End = totalFrames - (frame_rate * 4);
int part4Size = part4End - part4Start;

if (z >= part4Start && z < part4End)
{
double pz = z - part4Start;
double partDone = pz / (double)(part4Size - 1.);

double sta = 45.0 * sin(2.0 * pi * partDone);
double rad = 300 * sin(2.0 * pi * partDone);

ISE40_AddSpinTransform(w, h, rad, sta, w/2, h/2);
}

///////////////////////////////////////////////////////////////////

// and finally, the last 4 seconds gets the dissolve. this is a
// "random" XForm which increases in intensity over the for seconds.
int part5Start = totalFrames - (frame_rate * 4);
int part5End = totalFrames - frame_rate / 2;
int part5Size = part5End - part5Start;

if (z >= part5Start && z < part5End)
{
double pz = z - part5Start;
double partDone = pz / (double)(part5Size - 1.);

double sta = w * partDone;

ISE40_AddRandomTransform(w,h,sta);
}

///////////////////////////////////////////////////////////////////

// perform whatever transforms we have lined-up
UINT32 uMode = 2;
ISE40_TransformProcess(pSrcPix, w, h - 20, srcRowStride,
pOutPix, srcRowStride, 3, uMode | (1<<3), RGB(0,0,0));

// draw the image
IS40_StretchDrawDIB(dc.m_hDC, pOutBMIH, 0, 0, w, h);

// add the AVI frame
if (bAVI) AVI_AddFrame(psCompressed, iFrame++, pOutBMIH);

// monitor the progress
char buf[10];
sprintf(buf, "&#37;d\n", z);
OutputDebugString(buf);
}

// add the final frame
if (bAVI) AVI_AddFrame(psCompressed, iFrame++, pSrcBMIH);

// clean up.
ISE40_DestroyPerlinNoise3D(perlin);

GlobalFree(hImgDIB);

// The end...
if (bAVI)
{
AVI_CloseStream(ps, psCompressed, NULL);

AVI_CloseFile(pfile);

AVI_Exit();
}


delete [] pOutDIB;
delete [] pSrcDIB;

delete [] perlinData;
delete [] perlinData2;
}