www.pudn.com > softbody2d.zip > csoftbody2d_mouse.c


#include  
#include  
#include  
#include  
 
#define SCRSIZE 7			// screen size factor 
#define NUMP 20				// number of balls points 
#define NUMS (NUMP+1)		// number of springs 
#define MASS 1.0f				// default point mass 
#define BALLRADIUS 0.516f		// default radius of the ball 
#define KS 755.0f				// spring constant 
#define KD 35.0f				// spring damping constant 
#define GY -10.0f 
#define DT 0.005f 
#define FINAL_PRESSURE 45.0f 
 
/* Mouse */ 
 
#define windW 380 
#define windH 380 
 
int mousedown = 0; 
float xMouse, yMouse; 
int closest_i=1;				// point closest to mouse index 
 
/* Structure of the point */ 
typedef struct 
{ 
	float		x,y;            		// position 
	float		vx,vy;			// velocity 
	float		fx,fy;			// force accumulator 
} CPoint2d; 
 
/* Structure of the springs */ 
typedef struct 
{ 
  	int i,j;					// points indexes 
   	float length;				// rest length 
	float	nx,ny;            			// normal vector    
} CSpring; 
 
   
/* There we will keep an object */ 
float Pressure = 0; 
CPoint2d myPoints[NUMP+1]; 
CSpring mySprings[NUMS+1]; 
 
  
/* Add new spring */ 
void AddSpring(int pi, int i, int j) 
{ 
	mySprings[pi].i = i;	mySprings[pi].j = j; 
	mySprings[pi].length = sqrt( 	(myPoints[ i ].x - myPoints[ j ].x)*(myPoints[ i ].x - myPoints[ j ].x) 
						+ 	(myPoints[ i ].y - myPoints[ j ].y)*(myPoints[ i ].y - myPoints[ j ].y) ); 
} 
 
/* Create Ball Object (points & springs) */ 
void CreateBall(void) 
{ 
	int i; 
	 for(i=1 ; i <= NUMP ; ++i)		// create points 
	 { 
		myPoints[i].x = BALLRADIUS * sin( i * (2.0 * 3.14) /  NUMP  ); 
		myPoints[i].y = BALLRADIUS * cos(i * (2.0 * 3.14) /  NUMP  ) + SCRSIZE/2; 
	 } 
	 for(i=1 ; i <= NUMP ; ++i)		// create springs 
	   	AddSpring(i,i,i+1);    
   	AddSpring(i-1,i-1,1); 
} 
 
/* Reshape Window */ 
void Reshape(int width, int height) 
{ 
    glViewport(0, 0, width, height); 
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 
    gluOrtho2D(-SCRSIZE, SCRSIZE, -SCRSIZE, SCRSIZE); 
    glMatrixMode(GL_MODELVIEW); 
} 
 
/* Force Calculation Subroutine */ 
void AccumulateForces(void) 
{ 
	int i; 
   	float x1,x2,y1,y2;		// positions of spring points p1, p2 
	float r12d;			// length of p1 - p2 vector 
	float vx12;			// vx1 - vx2 
	float vy12;			// vy1 - vy2 
	float f;				// hooke force value 
	float Fx,Fy;			// force vector 
	float volume=0;			// volume of the body 
	float pressurev;		// pressure force value 
 
	/* gravity */ 
	for(i=1 ; i <= NUMP ; ++i) 
	{ 
		myPoints[i].fx = 0; 
		myPoints[i].fy = MASS * GY * (Pressure - FINAL_PRESSURE >= 0); 
 
		/* user clicked (mouse spring) */ 
		if(i==closest_i)		// closest point only 
		if(mousedown)			// if user clicked 
		{ 
			x1 = myPoints[ i ].x;		// get points coords. 
			y1 = myPoints[ i ].y; 
			x2 = xMouse; 
			y2 = yMouse; 
 
			r12d = sqrt ( (x1 - x2) *(x1 - x2)  +  (y1 - y2) * (y1 - y2) ); 
															// square 
															// root  of the distance 
			f = (r12d -  2.2) * 22 + (myPoints[ i ].vx * (x1 - x2) + myPoints[ i ].y * (y1 - y2)) * 54 / r12d; 
 
			// calculate spring force 
			Fx = ((x1 - x2) / r12d ) * f; 
			Fy = ((y1 - y2) / r12d ) * f; 
 
			// accumulate force 
			myPoints[i].fx -= Fx; 
			myPoints[i].fy -= Fy; 
 
		} 
	} 
 
	/* spring force */ 
	 for(i=1 ; i <= NUMS ; ++i) 
	 { 
			x1 = myPoints[ mySprings[i].i ].x;	y1 = myPoints[ mySprings[i].i ].y; 
			x2 = myPoints[ mySprings[i].j ].x;	y2 = myPoints[ mySprings[i].j ].y; 
			r12d = sqrt ( (x1 - x2) *(x1 - x2)  +  (y1 - y2) * (y1 - y2) );	// square 
															// root  of the distance 
			if(r12d != 0) 
			{ 
				vx12 = myPoints[ mySprings[i].i ].vx - myPoints[ mySprings[i].j ].vx; 
				vy12 = myPoints[ mySprings[i].i ].vy - myPoints[ mySprings[i].j ].vy; 
 
				f = (r12d -  mySprings[i].length) * KS + (vx12 * (x1 - x2) + vy12 * (y1 - y2)) * KD / r12d; 
 
				Fx = ((x1 - x2) / r12d ) * f; 
				Fy = ((y1 - y2) / r12d ) * f; 
 
				myPoints[ mySprings[i].i  ].fx -= Fx; 
				myPoints[ mySprings[i].i  ].fy -= Fy; 
 
				myPoints[  mySprings[i].j  ].fx += Fx; 
				myPoints[  mySprings[i].j  ].fy += Fy; 
 
			} 
 
			/* Calculate normal vectors to springs */ 
			mySprings[i].nx =  (y1 - y2) / r12d; 
			mySprings[i].ny =  -(x1 - x2) / r12d; 
	} 
	 
	/* pressure force */ 
	/* Calculate Volume of the Ball (Gauss Theorem) */ 
	for(i=1 ; i<=NUMS-1 ; i++) 
	{ 
		x1 = myPoints[ mySprings[i].i ].x;	y1 = myPoints[ mySprings[i].i ].y; 
		x2 = myPoints[ mySprings[i].j ].x;	y2 = myPoints[ mySprings[i].j ].y; 
		r12d = sqrt ( (x1 - x2) *(x1 - x2)  +  (y1 - y2) * (y1 - y2) );	// square 
														// root  of the distance 
		volume += 0.5 * fabs(x1 - x2) * fabs(mySprings[i].nx) * (r12d); 
	} 
	for(i=1 ; i<=NUMS-1 ; i++) 
	{ 
		x1 = myPoints[ mySprings[i].i ].x;	y1 = myPoints[ mySprings[i].i ].y; 
		x2 = myPoints[ mySprings[i].j ].x;	y2 = myPoints[ mySprings[i].j ].y; 
		r12d = sqrt ( (x1 - x2) *(x1 - x2)  +  (y1 - y2) * (y1 - y2) );	// square 
														// root  of the distance 
		pressurev = r12d * Pressure * (1.0f/volume); 
		myPoints[ mySprings[i].i ].fx += mySprings[ i ].nx * pressurev; 
		myPoints[ mySprings[i].i ].fy += mySprings[ i ].ny * pressurev; 
		myPoints[ mySprings[i].j ].fx += mySprings[ i ].nx * pressurev; 
		myPoints[ mySprings[i].j ].fy += mySprings[ i ].ny * pressurev; 
	} 
   
} 
 
 
 
 
/** 
 * Euler Integrator 
 */ 
 
void IntegrateEuler() 
{ 
	int i; 
 	float dry,drx;			// dr for Euler integration 
	 
	/* Euler Integrator (second Newton's law) */ 
 
	 for(i=1 ; i <= NUMP ; ++i) 
	 { 
	   	/* x */ 
		myPoints[i].vx = myPoints[i].vx + ( myPoints[i].fx / MASS )* DT; 
		drx = myPoints[i].vx * DT; 
		 
		/* Boundaries  X */ 
		if(myPoints[i].x + drx < -SCRSIZE) 
		{ 
			drx = -SCRSIZE - myPoints[ i ].x; 
		   	myPoints[i].vx = - 0.1 *myPoints[i].vx; 
		   	myPoints[i].vy = 0.95 *myPoints[i].vy; 
		}else 
		/* Boundaries  X */ 
		if(myPoints[i].x + drx > SCRSIZE) 
		{ 
			drx = SCRSIZE - myPoints[ i ].x; 
		   	myPoints[i].vx = - 0.1 *myPoints[i].vx; 
		   	myPoints[i].vy = 0.95 *myPoints[i].vy; 
		} 
 
		myPoints[i].x = myPoints[i].x + drx; 
 
	   	/* y */ 
		myPoints[i].vy = myPoints[i].vy + myPoints[i].fy * DT; 
		dry = myPoints[i].vy * DT; 
 
		/* Boundaries  Y */ 
		if(myPoints[i].y + dry < -SCRSIZE) 
		{ 
			dry = -SCRSIZE - myPoints[ i ].y; 
		   	myPoints[i].vy = - 0.1 *myPoints[i].vy; 
		   	myPoints[i].vx = 0.95 *myPoints[i].vx; 
		} 
		/* Boundaries  Y */ 
		if(myPoints[i].y + dry > SCRSIZE) 
		{ 
			dry = SCRSIZE - myPoints[ i ].y; 
		   	myPoints[i].vy = - 0.1 *myPoints[i].vy; 
		} 
 
		myPoints[i].y = myPoints[i].y + dry; 
 
 
		/* fast chek if outside */ 
		if(myPoints[i].x > SCRSIZE) 
			myPoints[i].x = SCRSIZE; 
		if(myPoints[i].y > SCRSIZE) 
			myPoints[i].y = SCRSIZE; 
		if(myPoints[i].x < -SCRSIZE) 
			myPoints[i].x = -SCRSIZE; 
		if(myPoints[i].y < -SCRSIZE) 
			myPoints[i].y = -SCRSIZE; 
 
	 } 
 
   
} 
      
/* Idle function */ 
void Idle(void) 
{ 
	AccumulateForces(); 
	IntegrateEuler(); 
  /** 
	  * Update Pressure (pump an air into the ball) 
	  */ 
	  if(Pressure < FINAL_PRESSURE) 
	  { 
		Pressure += FINAL_PRESSURE/300.0f; 
	   	printf("Pressure = %4.4f\n",Pressure); 
	  }	 
	glutPostRedisplay(); 
} 
 
 
/* Visualization */ 
void Draw(void) 
{ 
	int i; 
	glClearColor(1,1,1,0); 
	glClear(GL_COLOR_BUFFER_BIT); 
	glBegin(GL_QUADS); 
		for(i = 1 ; i <= NUMS-1 ; i++) 
		{ 
			glColor3f(0.8,0.4,0.4); 
			glVertex2f(myPoints[ mySprings[i].i ].x,myPoints[ mySprings[i].i ].y); 
			glVertex2f(myPoints[ mySprings[i].j ].x,myPoints[ mySprings[i].j ].y); 
 
			glVertex2f(myPoints[ NUMP - mySprings[i].i +1].x,myPoints[ NUMP - mySprings[i].i + 1].y); 
			glVertex2f(myPoints[ NUMP - mySprings[i].j +1].x,myPoints[ NUMP - mySprings[i].j + 1].y); 
		} 
	glEnd(); 
 
	if(mousedown) 
	{ 
	  glColor3f(0,0,0); 
	  glBegin(GL_LINES); 
		glVertex2f(xMouse,yMouse); 
   	   	glVertex2f(myPoints[closest_i].x,myPoints[closest_i].y); 
      glEnd(); 
	} 
 
	glutSwapBuffers();  
} 
 
/* Find point in the model which is closest to mouse click point */ 
 
void FindClosestPoint(void) 
{ 
	float dmin; 
	float mousepointd; 
	int i; 
 
	// find closest point 
	dmin = sqrt(pow(myPoints[closest_i].x - xMouse,2) + pow(myPoints[closest_i].y - yMouse,2)); 
 
	for(i=1 ; i <= NUMP ; ++i) 
	{ 
		mousepointd = sqrt(	pow(myPoints[i].x - xMouse,2) + 
							pow(myPoints[i].y - yMouse,2)); 
		if(mousepointd < dmin) 
		{ 
			dmin = mousepointd; 
			closest_i = i; 
		} 
	} 
} 
 
/* Clicked mouse */ 
void Mouse (int button, int state, int x, int y) 
{ 
	 
	if (button == GLUT_LEFT_BUTTON) 
	{ 
		if (state == GLUT_DOWN) 
		{ 
			mousedown = 1; 
 
			xMouse = SCRSIZE * 2.0 * ((float)x/(float)windW - 0.5); 
			yMouse = -SCRSIZE * 2.0 * ((float)y/(float)windH - 0.5); 
 
		} 
		else if (state == GLUT_UP) 
		{ 
			FindClosestPoint(); 
	       	mousedown = 0; 
	  	} 
     } 
} 
 
/* Mouse Motion */ 
void Motion (int x, int y) 
{ 
	if (mousedown) 
	{ 
		xMouse = SCRSIZE * 2.0 * ((float)x/(float)windW - 0.5); 
		yMouse = -SCRSIZE * 2.0 * ((float)y/(float)windH - 0.5); 
		glutPostRedisplay(); 
 	} 
} 
 
/* Main Function */ 
int main(void) 
{ 
	glutInitWindowPosition( 150, 150 ); glutInitWindowSize( windW, windH ); 
	glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE ); 
	glutCreateWindow("Soft Body 2D v1.0 by Maciej Matyka"); 
 
	CreateBall(); 
 
	glutReshapeFunc(Reshape); 
	glutDisplayFunc(Draw); 
	glutIdleFunc(Idle); 
    glutMouseFunc(Mouse); 
    glutMotionFunc(Motion); 
 
	glutMainLoop(); 
 
	return 0; 
}