123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- #include "metaballs.h"
- #include "game.h"
- #include <QTime>
- // for readability, the screen and texture sizes are hard-coded
- const float SCREEN_WIDTH=640.0f;
- const float SCREEN_HEIGHT=360.0f;
- VGPath path;
- VGPaint paint;
- // images
- VGuint bgImage;
- VGuint centerImage;
- VGuint capsuleImage;
- // temp buffer for transformed path coordinates
- VGubyte moveTo=VG_MOVE_TO_ABS;
- VGubyte lineTo=VG_LINE_TO_ABS;
- VGubyte closePath=VG_CLOSE_PATH;
- /** walltime */
- float t;
- int frames=0;
- #define BALL_COUNT 16
- Ball *balls = 0;
- MetaballSystem *mbs;
- VGPath blobs[BALL_COUNT];
- QVector2D capsulePosition; // where is the medical capsule laying
- float capsulePower;
- /**
- * Loads a PNG file and creates VGImage using the 32bit image data.
- */
- VGImage loadVGImage(QString filename)
- {
- QImage texture;
- if(!texture.load(filename))
- {
- qDebug("loadVGImage cannot load '%s'", filename);
- return VG_INVALID_HANDLE;
- }
- int w=texture.width();
- int h=texture.height();
- if (w<1 || h<1) return VG_INVALID_HANDLE;
- VGImage image = vgCreateImage(VG_sARGB_8888, w, h, VG_IMAGE_QUALITY_FASTER);
- if(image==VG_INVALID_HANDLE)
- {
- VGErrorCode err = vgGetError();
- qDebug("loadVGImage error 0x%x", err);
- }
- vgImageSubData(image, (QRgb*)texture.bits(), w*4, VG_sARGB_8888, 0, 0, w, h);
- return image;
- }
- /**
- *
- * Initialize the game. This is called once when the application is starting up.
- *
- */
- void game_init()
- {
- capsulePower = -1.0f; // capsule is not on the table
- // Create the balls
- balls = new Ball[ BALL_COUNT ];
- // Initialize the balls
- for (int f=0; f<BALL_COUNT; f++) {
- balls[f].pos = QVector2D( (rand() & 255) * SCREEN_WIDTH / 255.0f,(rand() & 255) * SCREEN_HEIGHT / 255.0f);
- balls[f].size = 2+(rand() & 3);
- }
- // Create metaballsystem to control the balls.
- mbs = new MetaballSystem( BALL_COUNT, balls, 2.0, 4*0.0015);
- }
- /**
- *
- * Destroy everything. Will be called when the application is about to exit.
- *
- */
- void game_destroy()
- {
- delete mbs; mbs = 0;
- delete [] balls; balls = 0;
- }
- /**
- *
- * Called after init. And everytime we are coming back from background.
- *
- */
- void game_prepare()
- {
- vgSeti(VG_FILL_RULE, VG_NON_ZERO);
- bgImage = loadVGImage(":/images/bg.png");
- centerImage = loadVGImage(":/images/center.png");
- capsuleImage = loadVGImage(":/images/capsule.png");
- paint = vgCreatePaint();
- vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
- vgSetColor(paint, 0xff00ffff);
- path=vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_ALL);
- vguRect(path, 0, 0, 4, 4);
- for(int i=0;i<mbs->ballCount;i++)
- {
- blobs[i]=vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_ALL);
- }
- }
- /**
- *
- * Called before destroy. And everytime we are going to background.
- *
- */
- void game_release()
- {
- vgDestroyImage( bgImage ); bgImage = VG_INVALID_HANDLE;
- vgDestroyImage( centerImage ); centerImage = VG_INVALID_HANDLE;
- vgDestroyImage( capsuleImage ); capsuleImage = VG_INVALID_HANDLE;
- vgDestroyPath(path);
- vgDestroyPaint(paint);
- for(int i=0;i<mbs->ballCount;i++)
- {
- vgDestroyPath(blobs[i]);
- }
- }
- /**
- *
- * Update. Walltime is the seconds we have been running
- *
- */
- void game_update(float walltime)
- {
- float frameTime = walltime-t;
- if (frameTime>0.1f) frameTime = 0.1f;
- t=walltime;
- QVector2D expos,temp;
- // Update the capsule
- if (capsulePower>=0.0f) {
- for (int f=0; f<BALL_COUNT; f++) {
- temp = balls[f].pos - capsulePosition;
- float invpower = QVector2D::dotProduct(temp, temp)*0.3f;
- invpower = (10000.0f / (invpower+1.0f)) * frameTime * capsulePower;
- if (invpower>1.0f) invpower = 1.0f;
- if (balls[f].size>1.0f) {
- balls[f].size -= (balls[f].size-1.0f) * invpower * 0.1f;
- }
- //if (invpower>10.0f) balls[f].size -= 2;
- //if (balls[f].size<1.0f) balls[f].size = 1.0f;
- balls[f].dir += temp*invpower;
- }
- }
- // Update the balls.
- for (int f=0; f<mbs->ballCount; f++) {
- Ball &b = mbs->balls[f];
- // gravity to other balls.
- for (int g=0; g<mbs->ballCount; g++)
- if (f!=g) {
- Ball &b2 = mbs->balls[g];
- temp = b2.pos - b.pos;
- float invpower = QVector2D::dotProduct(temp,temp)*0.05f;
- //if (invpower<2.0f) invpower = 2.0f;
- temp *= (2000.0f / (invpower*invpower+1.0f))*frameTime;
- b.dir += temp;
- b2.dir -= temp;
- }
- // Some random movement
- b.dir += QVector2D( (float)(rand() & 255)/255.0f-0.5f, (float)(rand() & 255)/255.0f-0.5f ) * frameTime * 500.0f;
- b.size += (10.0f - b.size) * frameTime * 0.05f;
- expos = b.pos;
- temp = (b.dir * frameTime);
- b.pos += temp;
- b.dir -= temp;
- // bound checking
- if (b.pos.x() < 0 || b.pos.x() > SCREEN_WIDTH) {b.dir.setX( -b.dir.x() ); b.pos = expos; }
- if (b.pos.y() < 0 || b.pos.y() > SCREEN_HEIGHT) {b.dir.setY( -b.dir.y() ); b.pos = expos; }
- }
- }
- void debug_point(float x, float y, int color)
- {
- vgLoadIdentity();
- vgTranslate(x, y);
- vgSetColor(paint, color);
- vgSetPaint(paint, VG_FILL_PATH | VG_STROKE_PATH);
- vgDrawPath(path, VG_FILL_PATH | VG_STROKE_PATH);
- vgLoadIdentity();
- }
- void draw_balls()
- {
- float step = 10;
- /*
- # First, track the border for all balls and store
- # it to pos0 and edgePos. The latter will move along the border,
- # pos0 stays at the initial coordinates.
- */
- for(int i=0;i<mbs->ballCount;i++)
- {
- Ball &b = mbs->balls[i];
- //b.pos = QVector2D(sin(t*.2+i*7.1)*200+320,cos(i*4.1+t*.3)*150+180);
- QVector2D p=b.pos;
- b.pos0 = mbs->trackTheBorder(b.pos+QVector2D(0,1));
- b.edgePos = b.pos0;
- b.tracking = true;
- b.sharedBall=-1;
- b.verticeCount=0;
- vgClearPath(blobs[i], VG_PATH_CAPABILITY_APPEND_TO);
- p=b.pos0;
- float xy[2]={p.x(), SCREEN_HEIGHT-p.y()};
- #if 0
- debug_point(xy[0], xy[1], 0xff0000ff);
- #endif
- // vgAppendPathData(blobs[i], 1, &moveTo, xy);
- b.commands[b.verticeCount]=VG_MOVE_TO_ABS;
- b.vertices[b.verticeCount*2]=xy[0];
- b.vertices[b.verticeCount*2+1]=xy[1];
- b.verticeCount++;
- }
- for(int i=0;i<mbs->ballCount;i++)
- {
- for(int c=0;c<100;c++)
- {
- Ball &ball = mbs->balls[i];
- if(!ball.tracking)
- {
- continue;
- }
- //# walk along the tangent, using chosen differential method
- ball.edgePos = mbs->rungeKutta2Tangent(ball.edgePos, step);
- //# correction step towards the border
- mbs->stepOnceTowardsBorder(ball.edgePos);
- //# check if we've gone a full circle or hit some other
- //# edge tracker
- int otherball=-1;
- for(int j=0;j<mbs->ballCount;j++)
- {
- Ball &ob = mbs->balls[j];
- if((i!=j || c>3) && (ob.pos0-ball.edgePos).lengthSquared()<step*step)
- {
- ball.tracking=false;
- otherball=j;
- }
- }
- QVector2D p=ball.edgePos;
- float xy[2]={p.x(), SCREEN_HEIGHT-p.y()};
- ball.commands[ball.verticeCount]=VG_LINE_TO_ABS;
- ball.vertices[ball.verticeCount*2]=xy[0];
- ball.vertices[ball.verticeCount*2+1]=xy[1];
- ball.verticeCount++;
- if(otherball!=-1)
- {
- QVector2D p=balls[otherball].pos0;
- float xy[2]={p.x(), SCREEN_HEIGHT-p.y()};
- ball.commands[ball.verticeCount]=VG_LINE_TO_ABS;
- ball.vertices[ball.verticeCount*2]=xy[0];
- ball.vertices[ball.verticeCount*2+1]=xy[1];
- ball.verticeCount++;
- }
- }
- vgLoadIdentity();
- vgAppendPathData(blobs[i], mbs->balls[i].verticeCount, mbs->balls[i].commands, mbs->balls[i].vertices);
- //vgSetColor(paint, 0xFFFFFF7F);
- vgSetColor(paint, 0x0000007F);
- vgSetPaint(paint, VG_FILL_PATH | VG_STROKE_PATH);
- vgDrawPath(blobs[i], 0*VG_FILL_PATH | VG_STROKE_PATH);
- }
- }
- /**
- *
- * Render everything.
- *
- */
- void game_render()
- {
- // Draw the background
- vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
- vgLoadIdentity();
- vgScale(SCREEN_WIDTH/512.0f, SCREEN_HEIGHT/512.0f );
- vgDrawImage( bgImage );
- // Draw the centers.
- vgScale(1,1);
- for(int i=0;i<mbs->ballCount;i++)
- {
- vgLoadIdentity();
- QVector2D p=mbs->balls[i].pos;
- float scale = 0.3f + mbs->balls[i].size * 0.15f;
- vgTranslate(p.x()-64 * scale, SCREEN_HEIGHT-p.y()-64 * scale);
- vgScale(scale,scale );
- vgDrawImage( centerImage );
- }
- // Render the capsule
- if (capsulePower>=0.0f) {
- vgScale(1,1);
- vgLoadIdentity();
- float scale = -0.6f;
- vgTranslate(capsulePosition.x()-64 * scale, SCREEN_HEIGHT-capsulePosition.y()-64 * scale);
- vgScale(scale,scale );
- vgDrawImage( capsuleImage );
- }
- // center to screen
- vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
- frames++;
- vgSetPaint(paint, VG_STROKE_PATH);
- vgSeti(VG_STROKE_CAP_STYLE, VG_CAP_BUTT);
- vgSetf(VG_STROKE_LINE_WIDTH, 8);
- draw_balls();
- }
- void game_event(int type, float x, float y, float z)
- {
- capsulePosition = QVector2D( x,y );
- switch (type) {
- defualt:
- case Qt::TouchPointPressed:
- capsulePower = 1.0f;
- break;
- case Qt::TouchPointReleased:
- capsulePower = -1.0f;
- }
- }
|