www.pudn.com > WaveSimulation.rar > Wave.cpp


// Wave.cpp: implementation of the CWave class. 
// 
////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include "Wave.h" 
#include "Bezier.h" 
#include "Terrian.h" 
 
#include  
#include  
#include  
 
////////////////////////////////////////////////////////////////////// 
// Construction/Destruction 
////////////////////////////////////////////////////////////////////// 
 
#define		G  9.8f 
 
 
#define		SMALL 0.0001f 
 
 
CWave::CWave() 
{ 
	memset(m_WaveLineArray,0,sizeof(WaveLine) * (WAVE_ARRAY_SIZE + 1)); 
	memset(&m_Segment1,0,sizeof(WaveSegment)); 
	memset(&m_Segment2,0,sizeof(WaveSegment)); 
	m_zParticleIndex = 0; 
	m_WindSpeed = 6.5f; 
} 
 
CWave::~CWave() 
{ 
 
} 
 
void CWave::Init() 
{ 
	m_rOrbitRadius	= ComOrbitRadius(m_WindSpeed); 
	m_wAngleSpeed	= ComAngleSpeed(m_WindSpeed); 
		m_TPeriod	= 2.0f * PI / m_wAngleSpeed; 
	m_LWaveLength	= G * SQR( m_TPeriod ) / (2 * PI); 
	m_kInf			= (2 * PI) / m_LWaveLength; 
	m_cSpeed		= 2 * PI * m_rOrbitRadius / m_TPeriod; 
} 
 
float CWave::ComOrbitRadius(float windSpeed) 
{ 
	float r = 0.0f; 
	if(windSpeed > SMALL) 
		r = 3.0f * 7.065f * powf(windSpeed,2.5) / 2000.0f ; 
	else 
		r = SMALL; 
	return r; 
} 
 
float CWave::ComAngleSpeed(float windSpeed) 
{ 
	float w; 
	if(windSpeed >	SMALL) 
	    w = sqrtf(2.0f / 30.0f) * G / windSpeed; 
	else 
		w = SMALL; 
 
	return w; 
} 
 
inline float CWave::ComWaveNumber(float x0, float z0, float windSpeed, float deep) 
{ 
	float maxk,k; 
 
	maxk = m_kInf; 
 	if( 2.0f * deep > m_LWaveLength ) 
		k = maxk; 
	else 
	{ 
		if(deep > SMALL) 
			k = sqrtf(maxk / deep); 
		else 
			k = sqrtf(maxk / SMALL); 
 
		 
		float s1,s2; 
		s1 = (m_LWaveLength / 2.0f - deep) / (m_LWaveLength / 2.0f) ; 
		s2 = 1.0f - s1; 
 
		k = s1 * k + s2 * m_kInf; 
	} 
 
	return k ; 
} 
 
void CWave::ComXY(float x, float z,float h, float *t) 
{ 
	float r,d,phaseangle; 
 
	r = m_rOrbitRadius; 
 
	d = ComD(x,z,m_WindSpeed,h); 
	m_aPhaseAngle = d + m_wAngleSpeed * m_tCurrentTime; 
 
	t[0] = z + r * sinf(m_aPhaseAngle); 
 
	float lamda = 0.0f,dz = 0.0f,dt = 0.0f; 
	lamda = 2.0f; 
	dz = (z - t[0]) * 2; 
	dt = m_TPeriod / 100.0f ; 
 
	phaseangle = m_aPhaseAngle + lamda * dz * dt ; 
 
	int n = phaseangle / (2*PI); 
	phaseangle -= n*2*PI; 
	if(phaseangle < 0) 
		phaseangle += 2*PI; 
 
	if( phaseangle < PI/2.0f ) 
		phaseangle *= (phaseangle / (PI/2)); 
 
	t[1] = -r * cosf(phaseangle);			//newy 
	t[0] = z + r * sinf(phaseangle);		//newz 
 
	if(2 * r > h) 
	{ 
		t[1] = -r * cosf(phaseangle) * h /( 2 * r);	//newy 
	} 
} 
 
float CWave::ComD(float x0, float z0, float windSpeed, float h) 
{ 
	return z0 / 3.0f; 
} 
 
 
void CWave::ComputeWave(float x,float dtimet) 
{ 
	m_tCurrentTime += dtimet; 
 
	float t[2]; 
	for(int  i = 0; i < WAVE_ARRAY_SIZE; i++) 
	{ 
		float deep ; 
 
		if(i <= 100) 
			deep = i / 50.0f; 
		else if(i < 200) 
 
			deep = i / 10.0f - 8.0f; 
		else 
			deep = 200/ 10.0f - 8.0f; 
 
		ComXY(x,i / 6.0f, deep,t); 
		m_WaveLineArray[i].x = x; 
		m_WaveLineArray[i].y = t[1]; 
		m_WaveLineArray[i].z = t[0]; 
	} 
 
	int nperiod = m_tCurrentTime / m_TPeriod; 
	float periodlife = (m_tCurrentTime - nperiod * m_TPeriod) / m_TPeriod ;	//0~~1 
 
	for(i = WAVE_ARRAY_SIZE; i >= 0; i--) 
		m_WaveLineArray[i].y *= (0.2f + sinf(periodlife * PI) ) / 1.2f; 
 
	 
	if(m_Segment1.CurrentState == WS_STATE_NORMAL) 
	{ 
		SearchStartEnd(m_Segment1.StartIndex,m_Segment1.EndIndex); 
		m_Segment1.RemainTime =m_Segment1.StageTime = m_TPeriod; 
		m_Segment1.CurrentState = WS_STATE_CURVE; 
	} 
	ComputeCurve(m_Segment1,dtimet,x); 
 
	if(m_Segment2.CurrentState != WS_STATE_NORMAL) 
		ComputeCurve(m_Segment2,dtimet,x); 
 
	for(i = WAVE_ARRAY_SIZE; i >= 0; i--) 
	{ 
		float deep ; 
		deep = -g_cTerrian.GetPositionHeight(x,m_WaveLineArray[i].z); 
		if(deep > m_WaveLineArray[i].y && m_WaveLineArray[i].z <= 5.0f ) 
		m_WaveLineArray[i].y = deep + 0.01f; 
	} 
 
	MoveParticle(dtimet); 
 
} 
 
void CWave::SearchStartEnd(int &sI, int &eI) 
{ 
	int changenumber = 0; 
	int presign = m_WaveLineArray[WAVE_ARRAY_SIZE].y > 0 ? 1 : -1;//前一个的符号 
 
	for(int i = WAVE_ARRAY_SIZE; i >= 0; i--) 
	{ 
		int cursign = m_WaveLineArray[i].y > 0 ? 1 : -1;	//当前符号 
 
		cursign *= presign; 
		if(cursign < 0) //变号 
		{ 
			if(presign < 0)//由-变为+  1, 
			{ 
				changenumber = 1; 
				sI = i; 
			}//由+变为-  -1 
			if(presign > 0 && changenumber > 0 ) 
			{ 
				eI = i; 
				return; 
			} 
			presign = -presign;	 
		} 
	} 
} 
 
void CWave::ComputeCurve(WaveSegment &WS,float dtimet,float x) 
{ 
	switch(WS.CurrentState) 
	{ 
	case WS_STATE_CURVE: 
		DealWithCurveState(WS,dtimet,x); 
		break; 
	case WS_STATE_SURGE: 
		DealWithSurgeState(WS,dtimet,x); 
		break; 
	case WS_STATE_RETREATE: 
		DealWithRetreateState(WS,dtimet,x); 
		break; 
	default: 
		break; 
	} 
} 
//x 用于产生粒子 
void CWave::DealWithCurveState(WaveSegment &WS,float dtimet,float x) 
{ 
	if(WS.RemainTime < dtimet) 
		WS.RemainTime = 0; 
	else 
		WS.RemainTime -= dtimet; 
 
	///////////modify the offset 
	RepositionStartEnd(WS.StartIndex, WS.EndIndex); 
	float periodlife = ( WS.StageTime - WS.RemainTime) / WS.StageTime; 
 
	//Init Bezier arg 
	float x1,y1,x2,y2,x3,y3,x4,y4; 
	float slopek1,slopek4; 
	float t = 0.0f ,result[2]; 
 
	int mI; 
	mI = (WS.StartIndex + WS.EndIndex) / 2; 
 
	x1 = m_WaveLineArray[WS.StartIndex].z; 
	y1 = m_WaveLineArray[WS.StartIndex].y; 
 
	slopek1 = ( m_WaveLineArray[WS.StartIndex].y - m_WaveLineArray[WS.StartIndex + 1].y  ) /  
				( m_WaveLineArray[WS.StartIndex + 1].z - m_WaveLineArray[WS.StartIndex].z ); 
 
	x4 = m_WaveLineArray[mI].z - periodlife * 3.5f * m_rOrbitRadius; 
	y4 = m_WaveLineArray[mI].y + (1.0f + periodlife) * m_rOrbitRadius * sinf( (3.0f / 2.0f) * PI  * periodlife); 
 
	slopek4 = 2 * cosf(PI / 2.0f + periodlife * PI * 6 / 7); 
 
	x2 = 0.80f * x1 + 0.20f * x4; 
	y2 = y1 + slopek1 * (x1 - x2); 
 
	x3 = 0.20f * x1 + 0.80f * x4; 
	y3 = y4 + slopek4 * (x4 - x3); 
 
 
	CBezier backBezier; 
	backBezier.Init(x1,y1,x2,y2,x3,y3,x4,y4); 
 
 
	int indexnum = abs(WS.StartIndex - mI); 
 
	for(int index = 0; index <= indexnum; index++) 
	{ 
		backBezier.ComputeBezier( (float)index / indexnum,result); 
		m_WaveLineArray[WS.StartIndex - index].z = result[0]; 
		m_WaveLineArray[WS.StartIndex - index].y = result[1]; 
	} 
 
 
	/////////front side  
	float slopek42 = slopek4 / 3.0f; 
	float x5,y5,x6,y6,x7,y7; 
 
	x7 = m_WaveLineArray[WS.EndIndex].z; 
	y7 = m_WaveLineArray[WS.EndIndex].y; 
	float slopek7 = ( m_WaveLineArray[WS.EndIndex - 1].y - m_WaveLineArray[WS.EndIndex].y  ) /  
				( m_WaveLineArray[WS.EndIndex - 1].z - m_WaveLineArray[WS.EndIndex].z ); 
 
	x5 = x4 - cosf(PI / 3 + PI * periodlife) * m_rOrbitRadius * 1.0f; 
	y5 = y4 - slopek42 * (x5 - x4); 
 
	x6 = x7 + m_rOrbitRadius * (1.2f - periodlife); 
	y6 = y7 - slopek7 * (x7 -x6); 
 
	CBezier frontBezier; 
	frontBezier.Init(x4,y4,x5,y5,x6,y6,x7,y7); 
 
	t = 0.0f ; 
	indexnum = abs(WS.EndIndex - mI); 
	for(index = 0; index <=  indexnum; index++) 
	{ 
		frontBezier.ComputeBezier( (float)index / indexnum,result); 
		m_WaveLineArray[mI - index].z = result[0]; 
		m_WaveLineArray[mI - index].y = result[1]; 
	} 
 
 
	//enter to next stage 
	if(WS.RemainTime < 0.00000001 * m_TPeriod) 
	{ 
		WS.CurrentState = WS_STATE_SURGE;\ 
		WS.RemainTime = 0.0f; 
		WS.StageTime = 1.0f; 
	} 
	/////////////判断是否需要涌向岸边 
	int tempIndex = WS.EndIndex - 1; 
	while(tempIndex >= 1 ) 
	{ 
		if(m_WaveLineArray[tempIndex].z >= x4 && m_WaveLineArray[tempIndex - 1].z < x4) 
			break; 
		else 
			tempIndex--; 
	} 
	if(m_WaveLineArray[tempIndex].y >= y4 ) 
	{ 
		WS.EndIndex = tempIndex; 
 
		WS.CurrentState = WS_STATE_SURGE; 
		WS.FitLength = WS.StartIndex - WS.EndIndex; 
		WS.ScaleIndex = WS.EndIndex; 
 
//		///////////////////////产生粒子 
		for(int i = 0; i < 80; i++) 
		{ 
			CreateParticle(x,y4,x4,true); 
		} 
	} 
} 
 
void CWave::DealWithSurgeState(WaveSegment &WS,float dtimet,float x) 
{ 
	RepositionStartEnd(WS.StartIndex, WS.EndIndex); 
	float x1,y1,x2,y2,x3,y3,x4,y4; 
	float slopek1,slopek4; 
	if(WS.EndIndex > 0)	//保持波形不变前进 
	{ 
		 
		float fitlife; 
 
		//上一段末尾时间的生命期 
		fitlife = WS.RemainTime / WS.StageTime; 
		x1 = m_WaveLineArray[WS.StartIndex].z; 
		y1 = m_WaveLineArray[WS.StartIndex].y; 
		 
		slopek1 = ( m_WaveLineArray[WS.StartIndex].y - m_WaveLineArray[WS.StartIndex + 1].y  ) /  
						( m_WaveLineArray[WS.StartIndex + 1].z - m_WaveLineArray[WS.StartIndex].z ); 
		x4 = m_WaveLineArray[WS.EndIndex].z; 
		y4 = m_WaveLineArray[WS.EndIndex].y; 
 
		slopek4 = 2 * cosf(PI / 2.0f + ( (float)WS.EndIndex / WS.ScaleIndex ) * fitlife * PI * 6 / 7); 
		x2 = 0.80f * x1 + 0.20f * x4; 
		y2 = y1 + slopek1 * (x1 - x2); 
 
		x3 = 0.20f * x1 + 0.80f * x4; 
		y3 = y4 + slopek4 * (x4 - x3); 
	 
		CBezier surgeBezier; 
		surgeBezier.Init(x1,y1,x2,y2,x3,y3,x4,y4); 
 
		int indexnum = abs(WS.EndIndex - WS.StartIndex); 
		float result[2]; 
 
		for(int index = 0; index <= indexnum; index++) 
		{ 
			surgeBezier.ComputeBezier( (float)index / indexnum,result); 
			m_WaveLineArray[WS.StartIndex - index].z = result[0]; 
			m_WaveLineArray[WS.StartIndex - index].y = result[1]; 
		} 
	} 
	else	//到岸边时 
	{ 
		if(WS.StartIndex > 0)//直到sI变为0,波浪一直向前涌 
		{ 
			int i = 0 ; 
			float plife = (float)(WS.FitLength - WS.StartIndex) / WS.FitLength; 
 
			SearchStartEnd(m_Segment2.StartIndex,m_Segment2.EndIndex); 
			i = (m_Segment2.StartIndex + m_Segment2.EndIndex) / 2; 
 
 
			//position :from i to 0; 
			//time: from 0 to 1; 
			x1 = 0.0f; 
			y1 = 0.0f; 
			slopek1 = 0.0f; 
			x4 = 1.0f; 
			y4 = -sinf(plife * PI) * m_rOrbitRadius * 0.5f; 
 
			slopek4 = plife; 
 
			x2 = 0.75f * x1 + 0.25f * x4; 
			y2 = 0.0f; 
			x3 = 0.25f * x1 + 0.75f * x4; 
			y3 = y4 - slopek4 * (x3 - x4); 
 
			CBezier beachBezier; 
			beachBezier.Init(x1,y1,x2,y2,x3,y3,x4,y4); 
 
			float result[2]; 
			for(int j = i; j >=0 ; j--) 
			{ 
				beachBezier.ComputeBezier( (float)(i - j) / i,result); 
				m_WaveLineArray[j].z += result[1]; 
			} 
		} 
		else 
		{ 
			WS.CurrentState = WS_STATE_RETREATE; 
			WS.RemainTime = WS.StageTime = m_TPeriod * 0.8f; 
			m_Segment2.CurrentState = WS_STATE_CURVE; 
			m_Segment2.RemainTime = m_Segment2.StageTime = m_TPeriod; 
		} 
	} 
	/////////////////////// 
	for(int i = 0; i < 45 ;i++ ) 
	{ 
		float y,z; 
		int zIndex = 0; 
		zIndex = WS.EndIndex; 
		z = m_WaveLineArray[zIndex].z; 
		y = ParticalYFromZ(z); 
 
		CreateParticle(x,y,z,false); 
	} 
 
} 
 
void CWave::DealWithRetreateState(WaveSegment &WS,float dtimet,float x) 
{ 
	if(WS.RemainTime < dtimet) 
		WS.RemainTime = 0; 
	else 
		WS.RemainTime -= dtimet; 
 
	////////////compute 
	float plife = 1.0f - WS.RemainTime / WS.StageTime; 
 
	float x1,y1,x2,y2,x3,y3,x4,y4; 
	float slopek1,slopek4; 
	float result[2]; 
	int i = m_Segment2.EndIndex; 
 
	x1 = 0.0f; 
	y1 = 0.0f; 
	slopek1 = 10.0f; 
	x4 = 1.0f; 
	y4 = sinf(plife * PI) * m_rOrbitRadius * 4.0f; 
				 
	slopek4 = plife; 
 
	x2 = 0.75f * x1 + 0.25f * x4; 
	y2 = 0.0f; 
	x3 = 0.25f * x1 + 0.75f * x4; 
	y3 = y4 - slopek4 * (x3 - x4); 
 
	CBezier beachBezier; 
	beachBezier.Init(x1,y1,x2,y2,x3,y3,x4,y4); 
 
	for(int j = i; j >=0 ; j--) 
	{ 
		beachBezier.ComputeBezier( (float)(i - j) / i,result); 
		m_WaveLineArray[j].z += result[1] * ( 1.0f - plife); 
	} 
 
 
	if(WS.RemainTime < 0.00000001 * m_TPeriod) 
	{ 
		memcpy(&m_Segment1,&m_Segment2,sizeof(WaveSegment)); 
		memset(&m_Segment2,0,sizeof(WaveSegment)); 
		DealWithCurveState(m_Segment1,dtimet,x); 
	} 
 
		/////////////////////// 
	for(i = 0; i < 25 ;i++ ) 
	{ 
		float y,z; 
		int zIndex = 0; 
		int tempindex = m_Segment2.EndIndex / 4; 
 
		tempindex = tempindex <= 0 ? 1 : tempindex; 
		zIndex = rand() % tempindex; 
 
		z = m_WaveLineArray[zIndex].z; 
		y = ParticalYFromZ(z); 
 
		CreateParticle(x,y,z,false); 
	} 
} 
 
 
void CWave::RepositionStartEnd(int &sI, int &eI) 
{ 
	while(sI > 0 && m_WaveLineArray[sI].y < 0) 
		sI--; 
 
	while(eI > 0 && m_WaveLineArray[eI].y > 0) 
		eI--;	 
} 
 
 
void CWave::MoveParticle(float timedt) 
{ 
	int endIndex = m_Particle.m_iNextParticle; 
	for(int i = 0; i < endIndex; i++) 
	{ 
		FoamPartical *pParticle = &m_Particle.m_ParticalPool[i]; 
 
		pParticle->z -= timedt * pParticle->hv ; 
		pParticle->y += timedt * pParticle->vv ; 
		//更新速度 
		pParticle->vv -= timedt * G / 6.0f; 
 
		if(pParticle->z < m_WaveLineArray[1].z) 
		{ 
			pParticle->z = m_WaveLineArray[1].z; 
			pParticle->hv = -pParticle->hv; 
		} 
 
		float Interploatey = ParticalYFromZ(pParticle->z); 
		if(pParticle->y < Interploatey) 
			pParticle->y = Interploatey + 0.001f; 
 
 
		//更新生命值 
		pParticle->life -=  timedt; 
	} 
	m_Particle.Refresh(); 
} 
 
 
float CWave::ParticalYFromZ(float z) 
{ 
	while(m_zParticleIndex < WAVE_ARRAY_SIZE - 1 && m_WaveLineArray[m_zParticleIndex].z <= z) 
		m_zParticleIndex++; 
 
	while(m_zParticleIndex >= 0 && m_WaveLineArray[m_zParticleIndex].z > z) 
		m_zParticleIndex--; 
 
	float distance = m_WaveLineArray[m_zParticleIndex + 1].z - m_WaveLineArray[m_zParticleIndex ].z; 
	float scale = (z - m_WaveLineArray[m_zParticleIndex].z) / distance; 
	 
	float result = 0.0f; 
	float y1 = m_WaveLineArray[m_zParticleIndex].y; 
	float y2 = m_WaveLineArray[m_zParticleIndex + 1].y; 
	result = (1.0f - scale) * y1 + scale * y2; 
 
	return result; 
} 
 
FoamPartical* CWave::CreateParticle(float x, float y, float z,bool state) 
{ 
	FoamPartical * newparticle = NULL; 
	newparticle = m_Particle.GenerateFoam(); 
 
	if(newparticle != NULL) 
	{ 
		newparticle->x = x + (rand() % 50) / 50.0f - 0.5f; 
		newparticle->y = y; 
		newparticle->z = z +  (rand() % 50) / 100.0f - 0.25f; 
 
		if(state) 
		{ 
			newparticle->vv = (rand() % 150)  / 100.0f + 1.2f; 
			newparticle->hv =  1.5f * m_cSpeed * ( 1.0f + (rand() % 50) / 100.0f); 
		} 
		else 
		{ 
			newparticle->vv = 0.0f; 
			newparticle->hv = m_cSpeed * ( 1.0f + (rand() % 50) / 100.0f); 
		} 
 
		newparticle->size = rand() % 4; 
		if(newparticle->size == 0) 
			newparticle->size = 1; 
 
		newparticle->life = (rand() % 250) / 30.0f; 
	} 
	return newparticle; 
} 
 
void  CWave::CompTextCor(WaveLine &wl,float &textcorx,float &textcory) 
{ 
	float x = wl.x + 10.0f; 
	float y = wl.z + 10.0f; 
 
	int nx,ny; 
	nx = int(x / 20); 
	ny = int(y / 20); 
 
	x = x - nx * 20.0f; 
	y = y - ny * 20.0f; 
 
	x = x < 0 ? x + 20.0f : x; 
	y = y < 0 ? y + 20.0f : y; 
 
	textcorx = x / 20.0f; 
	textcory = y / 20.0f; 
}