OpenGL: 水果忍者刀锋效果

http://www.cnblogs.com/lghost/archive/2012/05/05/2484888.html

 

修复版1

修复版2

之前在视频中许诺过五一之后写个关于这个刀锋实现的文章的,但发现最近真的很忙。。。。所以。。。先放出到现在为止的源代码,具体实现思路和方法以后再补上。

Ninja.cpp

#include <Windows.h>
#include <GL/glut.h>
#include "Blade.h"

#ifndef _LINUX_
#include <windows.h>
void mySleep(int millisecond) {Sleep(millisecond);};
#else
#include <unistd.h>
void mySleep(int millisecond) {sleep((float)millisecond/1000.0);};
#endif

Blade myBlade;

const int sleepTime = 1;

void display(void )
{
	glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT);

	myBlade.drawBlade();

	glutSwapBuffers();
}

void mouseButton(int btn, int state, int x, int y)
{
	myBlade.mouse2BladeState(btn, state, x, y);
	//if (btn == GLUT_RIGHT_BUTTON)
	//    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}

void reshape(int w, int h)
{
	myBlade.winWidth = w;
	myBlade.winHeight = h;
}

void mouseMove(int x, int y)
{
	myBlade.mousePosX = x;
	myBlade.mousePosY = y;
}

void idle(void)
{
	mySleep(sleepTime);

	myBlade.sampleFadeBlade();
}
int main(int argc, char *argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(100, 100);
	glutInitWindowSize(800, 600);
	glutCreateWindow("Ninjia blade");

	gluOrtho2D(0.0f, 1.0f, 0.0f, 1.0f);
	myBlade.recordOrthoData(0.0f, 1.0f, 0.0f, 1.0f);
	//gluOrtho2D(0.0f, -1.0f, -2.0f, 1.0f);
	//myBlade.recordOrthoData(0.0f, -1.0f, -2.0f, 1.0f);
	glEnable(GL_LINE_SMOOTH);

	glEnable(GL_CULL_FACE);
	glFrontFace(GL_CCW);

	glutDisplayFunc(&display);
	glutMouseFunc(&mouseButton);
	glutReshapeFunc(&reshape);
	glutMotionFunc(&mouseMove);
	glutIdleFunc(&idle);

	glutMainLoop();
	return 0;
}

Blade.h

#ifndef _BLADE_H_
#define _BLADE_H_

#include <GL/glut.h>
#include <time.h>

class Blade
{
public :
	int winWidth, winHeight;
	GLint mousePosX, mousePosY;

	Blade();
	~Blade();

	void drawBlade(void );
	void sampleFadeBlade(void);
	void mouse2BladeState(int btn, int state, int x, int y);

	void recordOrthoData(GLfloat left, GLfloat right, GLfloat bottom, GLfloat up);

private :
	static const int MAXTIMER = 10;
	static const int vertexArrHead = 0;
	static const int vertexArrTail = 20;
	static const int POINTTHRESHOLD = 16;
	static const GLint sampleTimerFlag = 0;
	static const GLint fadeTimerFlag = 1;

	int vertexArrStartPos;
	//int vertexArrEndPos;

	GLint pointCount;
	GLint drawFlag;
	GLint mouseDownFlag;    
	GLfloat bodyVertex[vertexArrTail][2];
	GLfloat uniformSideVertex[vertexArrTail][2][2];
	GLfloat outerSideVertex[vertexArrTail][2][2];
	GLfloat innerSideVertex[vertexArrTail][2][2];
	GLfloat bladeThreshold;

	clock_t start[MAXTIMER], finish[MAXTIMER];
	GLfloat sampleInterval;
	GLfloat fadeInterval;

	GLfloat orthoLeft;
	GLfloat orthoRight;
	GLfloat orthoBottom;
	GLfloat orthoUp;

	void init(void);
	void calcSideVertex(GLfloat *startVertex, GLfloat *endVertex, GLfloat returnSideVertex[][2], GLfloat sideWidth, GLfloat end2StartRatio);
	void vertexSampling();
	GLint isTimeUp(GLint timerFlag, GLfloat timeInterval);
	void vertexFading(void );
	void recalculateBladeWidth(void );
};

#endif

Blade.cpp

#include "Blade.h"
#include <GL/glut.h>
#include <math.h>
//#include <stdio.h>
#include <time.h> 
//#include <stdlib.h>

Blade::Blade()
{
	winWidth = 0;
	winHeight = 0;
	mousePosX = 0;
	mousePosY = 0;

	vertexArrStartPos = 0;
	//vertexArrEndPos = 0;

	pointCount = 0;
	drawFlag = 0;
	mouseDownFlag = 0;    

	sampleInterval = 0.015f;
	fadeInterval = 0.02f;

	bladeThreshold = 0.032f;

	init();
}

Blade::~Blade()
{}

void Blade::calcSideVertex(GLfloat *startVertex, GLfloat *endVertex, GLfloat returnSideVertex[][2], GLfloat sideWidth, GLfloat end2StartRatio)
{
	GLfloat tmpAngle = 0.0;
	GLfloat tmpVertex[2] = {0.0f, 0.0f};
	GLfloat tmpSideVertex[2][2] = {{0.0f, 0.0f}, {0.0f, 0.0f}};
	GLfloat tmpResult = 0.0;

	tmpAngle = atan((endVertex[1] - startVertex[1])/(endVertex[0] - startVertex[0]));

	tmpVertex[0] = endVertex[0]-(endVertex[0]-startVertex[0])*end2StartRatio;
	tmpVertex[1] = endVertex[1]-(endVertex[1]-startVertex[1])*end2StartRatio;

	if (endVertex[0] > startVertex[0]) {
		tmpSideVertex[0][0] = tmpVertex[0] + (sideWidth * sin(tmpAngle));
		tmpSideVertex[0][1] = tmpVertex[1] - (sideWidth * cos(tmpAngle));
		tmpSideVertex[1][0] = tmpVertex[0] - (sideWidth * sin(tmpAngle));
		tmpSideVertex[1][1] = tmpVertex[1] + (sideWidth * cos(tmpAngle));
	} else {
		tmpSideVertex[0][0] = tmpVertex[0] - (sideWidth * sin(tmpAngle));
		tmpSideVertex[0][1] = tmpVertex[1] + (sideWidth * cos(tmpAngle));
		tmpSideVertex[1][0] = tmpVertex[0] + (sideWidth * sin(tmpAngle));
		tmpSideVertex[1][1] = tmpVertex[1] - (sideWidth * cos(tmpAngle));
	}

	//判断点在向量的左边还是右边
	tmpResult = (startVertex[0] - tmpSideVertex[0][0])*(endVertex[1] - tmpSideVertex[0][1]) - (startVertex[1] - tmpSideVertex[0][1])*(endVertex[0] - tmpSideVertex[0][0]);
	if (tmpResult > 0.0f) {
		returnSideVertex[0][0] = tmpSideVertex[1][0];
		returnSideVertex[0][1] = tmpSideVertex[1][1];
		returnSideVertex[1][0] = tmpSideVertex[0][0];
		returnSideVertex[1][1] = tmpSideVertex[0][1];
	} else {
		returnSideVertex[0][0] = tmpSideVertex[0][0];
		returnSideVertex[0][1] = tmpSideVertex[0][1];
		returnSideVertex[1][0] = tmpSideVertex[1][0];
		returnSideVertex[1][1] = tmpSideVertex[1][1];
	}
}

void Blade::init(void)
{
	int i = 0;

	for (i = 0; i < vertexArrTail; i++)    {
		bodyVertex[i][0] = 0.0f;
		bodyVertex[i][1] = 0.0f;

		uniformSideVertex[i][0][0] = 0.0f;
		uniformSideVertex[i][0][1] = 0.0f;
		uniformSideVertex[i][1][0] = 0.0f;
		uniformSideVertex[i][1][1] = 0.0f;

		outerSideVertex[i][0][0] = 0.0f;
		outerSideVertex[i][0][1] = 0.0f;
		outerSideVertex[i][1][0] = 0.0f;
		outerSideVertex[i][1][1] = 0.0f;

		innerSideVertex[i][0][0] = 0.0f;
		innerSideVertex[i][0][1] = 0.0f;
		innerSideVertex[i][1][0] = 0.0f;
		innerSideVertex[i][1][1] = 0.0f;        
	}
}

void Blade::vertexSampling()
{
	int tmpIndex1 = 0, tmpIndex2 = 0;
	GLfloat uniformX = 0.0f, uniformY = 0.0f;
	GLfloat realX = 0.0f, realY = 0.0f;
	drawFlag = 0;
	pointCount++;

	uniformX = (GLfloat)mousePosX / (GLfloat)winWidth;
	uniformY = (GLfloat)(winHeight - mousePosY) / (GLfloat)winHeight;
	realX = uniformX*(orthoRight - orthoLeft) + orthoLeft;
	realY = uniformY*(orthoUp - orthoBottom) + orthoBottom;
	if (pointCount == 0) {
		bodyVertex[vertexArrStartPos][0] = realX;
		bodyVertex[vertexArrStartPos][1] = realY;
	} else {
		tmpIndex1 = (vertexArrStartPos + pointCount - 1)%vertexArrTail;
		tmpIndex2 = (tmpIndex1 + 1)%vertexArrTail;

		bodyVertex[tmpIndex2][0] = realX;
		bodyVertex[tmpIndex2][1] = realY;

		calcSideVertex(bodyVertex[tmpIndex1], bodyVertex[tmpIndex2], uniformSideVertex[tmpIndex2], bladeThreshold, 0.4);
	}    
	if (pointCount >= 3)
		drawFlag = 1;
	//vertexArrEndPos++;
	//if (vertexArrEndPos >= vertexArrTail)
	//    vertexArrEndPos -= vertexArrTail;
}

GLint Blade::isTimeUp(GLint timerFlag, GLfloat timeInterval)
{
	double duration = 0.0;

	finish[timerFlag] = clock();
	duration = (double)(finish[timerFlag] - start[timerFlag]) / CLOCKS_PER_SEC;
	if (duration >= timeInterval) {
		start[timerFlag] = clock();
		return 1;
	}
	return 0;
}

void Blade::vertexFading()
{
	vertexArrStartPos++;
	if (vertexArrStartPos >= vertexArrTail)
		vertexArrStartPos -= vertexArrTail;
	pointCount--;
	if (pointCount <= 2)
		drawFlag = 0;
}

void Blade::recalculateBladeWidth(void )
{
	int i = 0;    
	int tmpIndex = 0;
	GLfloat ratio = 0.0f;
	GLfloat tmp[4] = {0.0f, 0.0f, 0.0f, 0.0f};

	//reclaculate the blade width
	for (i = 1; i < pointCount - 1; i++) {
		tmpIndex = (vertexArrStartPos + i)%vertexArrTail;
		tmp[0] = uniformSideVertex[tmpIndex][0][0] + uniformSideVertex[tmpIndex][1][0];
		tmp[1] = uniformSideVertex[tmpIndex][0][0] - uniformSideVertex[tmpIndex][1][0];
		tmp[2] = uniformSideVertex[tmpIndex][0][1] + uniformSideVertex[tmpIndex][1][1];
		tmp[3] = uniformSideVertex[tmpIndex][0][1] - uniformSideVertex[tmpIndex][1][1];
		if (i == (pointCount-2))
			ratio = 1.0;
		else
			ratio = (GLfloat)i/(GLfloat)pointCount;

		outerSideVertex[tmpIndex][0][0] = 0.5*(tmp[0] + ratio*tmp[1]);
		outerSideVertex[tmpIndex][0][1] = 0.5*(tmp[2] + ratio*tmp[3]);
		outerSideVertex[tmpIndex][1][0] = 0.5*(tmp[0] - ratio*tmp[1]);
		outerSideVertex[tmpIndex][1][1] = 0.5*(tmp[2] - ratio*tmp[3]);

		innerSideVertex[tmpIndex][0][0] = 0.5*(tmp[0] + ratio*2.0/3.0*tmp[1]);
		innerSideVertex[tmpIndex][0][1] = 0.5*(tmp[2] + ratio*2.0/3.0*tmp[3]);
		innerSideVertex[tmpIndex][1][0] = 0.5*(tmp[0] - ratio*2.0/3.0*tmp[1]);
		innerSideVertex[tmpIndex][1][1] = 0.5*(tmp[2] - ratio*2.0/3.0*tmp[3]);
	}
}



void Blade::drawBlade()
{
	int i = 0;

	//printf("%d\n", pointCount);
	//printf("%d\n", vertexArrStartPos);
	//printf("Flag:%d\n\n", drawFlag);

	if (drawFlag == 0){
		return;
	}

	/*****--------------------------------------*****/
	//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	//glPolygonMode(GL_FRONT, GL_LINE);
	/*****--------------------------------------*****/
	//draw the outer blade
	glColor4f(0.2f, 0.2f, 0.9f, 0.5f);
	glBegin(GL_POLYGON);
	glVertex2fv(bodyVertex[vertexArrStartPos]);
	glVertex2fv(outerSideVertex[(vertexArrStartPos+1)%vertexArrTail][0]);
	glVertex2fv(outerSideVertex[(vertexArrStartPos+1)%vertexArrTail][1]);
	glEnd();
	for (i = 1; i < pointCount - 2; i++)    {
		glBegin(GL_POLYGON);
		glVertex2fv(outerSideVertex[(vertexArrStartPos + i)%vertexArrTail][0]);
		glVertex2fv(outerSideVertex[(vertexArrStartPos + i + 1)%vertexArrTail][0]);
		glVertex2fv(outerSideVertex[(vertexArrStartPos + i)%vertexArrTail][1]);
		glEnd();
		glBegin(GL_POLYGON);
		glVertex2fv(outerSideVertex[(vertexArrStartPos + i + 1)%vertexArrTail][1]);
		glVertex2fv(outerSideVertex[(vertexArrStartPos + i)%vertexArrTail][1]);
		glVertex2fv(outerSideVertex[(vertexArrStartPos + i + 1)%vertexArrTail][0]);
		glEnd();
	}
	glBegin(GL_POLYGON);
	glVertex2fv(outerSideVertex[(vertexArrStartPos + pointCount - 2)%vertexArrTail][0]);        
	glVertex2fv(bodyVertex[(vertexArrStartPos + pointCount - 1)%vertexArrTail]);
	glVertex2fv(outerSideVertex[(vertexArrStartPos + pointCount - 2)%vertexArrTail][1]);
	glEnd();


	//draw the inner blade
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	glBegin(GL_POLYGON);
	glVertex2fv(bodyVertex[vertexArrStartPos]);
	glVertex2fv(innerSideVertex[(vertexArrStartPos+1)%vertexArrTail][0]);
	glVertex2fv(innerSideVertex[(vertexArrStartPos+1)%vertexArrTail][1]);
	glEnd();

	for (i = 1; i < pointCount - 2; i++)    {
		glBegin(GL_POLYGON);
		glVertex2fv(innerSideVertex[(vertexArrStartPos + i)%vertexArrTail][0]);
		glVertex2fv(innerSideVertex[(vertexArrStartPos + i + 1)%vertexArrTail][0]);
		glVertex2fv(innerSideVertex[(vertexArrStartPos + i)%vertexArrTail][1]);
		glEnd();
		glBegin(GL_POLYGON);
		glVertex2fv(innerSideVertex[(vertexArrStartPos + i + 1)%vertexArrTail][1]);
		glVertex2fv(innerSideVertex[(vertexArrStartPos + i)%vertexArrTail][1]);
		glVertex2fv(innerSideVertex[(vertexArrStartPos + i + 1)%vertexArrTail][0]);
		glEnd();
	}
	glBegin(GL_POLYGON);
	glVertex2fv(innerSideVertex[(vertexArrStartPos + pointCount - 2)%vertexArrTail][0]);        
	glVertex2fv(bodyVertex[(vertexArrStartPos + pointCount - 1)%vertexArrTail]);
	glVertex2fv(innerSideVertex[(vertexArrStartPos + pointCount - 2)%vertexArrTail][1]);
	glEnd();
}

void Blade::sampleFadeBlade(void)
{
	if (mouseDownFlag == 1) {
		sampleInterval = (pointCount <= 2 ? 0.005 : 0.015);

		if (isTimeUp(sampleTimerFlag, sampleInterval) == 1) {
			vertexSampling();
			recalculateBladeWidth();
			glutPostRedisplay();
		}
	}

	if (pointCount > 0) {
		fadeInterval = (pointCount >= POINTTHRESHOLD ? 0.0 : 0.016);
		fadeInterval = (mouseDownFlag == 0 ? 0.0 : fadeInterval);
		//printf("%f\n", fadeInterval);

		if (isTimeUp(fadeTimerFlag, fadeInterval) == 1) {
			vertexFading();
			recalculateBladeWidth();
			glutPostRedisplay();
		}
	}
}

void Blade::mouse2BladeState(int btn, int state, int x, int y)
{
	if (btn == GLUT_LEFT_BUTTON) {
		if (state == GLUT_DOWN) {            
			mousePosX = x;
			mousePosY = y;
			mouseDownFlag = 1;

			start[sampleTimerFlag] = clock();
			start[fadeTimerFlag] = clock();

			pointCount = 0;
		} else if (state == GLUT_UP) {
			mouseDownFlag = 0;
		}
	}
}

void Blade::recordOrthoData(GLfloat left, GLfloat right, GLfloat bottom, GLfloat up)
{
	orthoLeft = left;
	orthoRight = right;
	orthoBottom = bottom;
	orthoUp = up;
}


 

只要将以上三个文件放到同一个工程编译即可。

程序入口(main函数)在Ninjia.cpp文件中定义。

 

对了!要感谢这篇文章的作者,他给了我思路。其实,我的工作也大概是将他的翻译成OpenGL版本并且再做点修改而已。

http://www.blogjava.net/oathleo/archive/2011/09/23/android_Fruit_Ninja.html


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值