Example #1
0
void UpdateParticles(float dt, bool generate)
{
	// NOTE: runs on 10 fps

	OpenGLAABox tmpbox = scenebox;
	float center[3];

	if( lightbuffer == 0 )
		return;

	glBindBuffer(GL_SHADER_STORAGE_BUFFER, lightbuffer);
	LightParticle* particles = (LightParticle*)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_WRITE);

	tmpbox.GetCenter(center);

	if( generate )
	{
		int segments = GLISqrt(NUM_LIGHTS);
		float theta, phi;

		OpenGLColor randomcolors[3] =
		{
			OpenGLColor(1, 0, 0, 1),
			OpenGLColor(0, 1, 0, 1),
			OpenGLColor(0, 0, 1, 1)
		};

		for( int i = 0; i < segments; ++i )
		{
			for( int j = 0; j < segments; ++j )
			{
				LightParticle& p = particles[i * segments + j];

				theta = ((float)j / (segments - 1)) * M_PI;
				phi = ((float)i / (segments - 1)) * M_2PI;

				p.previous[0] = center[0];
				p.previous[1] = center[1];
				p.previous[2] = center[2];
				p.previous[3] = 1;

				p.velocity[0] = sinf(theta) * cosf(phi) * 2;
				p.velocity[1] = cosf(theta) * 2;
				p.velocity[2] = sinf(theta) * sinf(phi) * 2;

				p.current[0] = p.previous[0];
				p.current[1] = p.previous[1];
				p.current[2] = p.previous[2];
				p.current[3] = 1;

				p.radius = LIGHT_RADIUS;
				p.color = randomcolors[(i + j) % 3];
			}
		}
	}
	else
	{
		float vx[3], vy[3], vz[3];
		float b[3];
		float A[16], Ainv[16];
		float planes[6][4];
		float denom, energy;
		float toi, besttoi;
		float impulse, noise;
		float (*bestplane)[4];
		bool pastcollision;

		tmpbox.GetPlanes(planes);

		for( int i = 0; i < NUM_LIGHTS; ++i )
		{
			LightParticle& p = particles[i];

			// integrate
			p.previous[0] = p.current[0];
			p.previous[1] = p.current[1];
			p.previous[2] = p.current[2];

			p.current[0] += p.velocity[0] * dt;
			p.current[1] += p.velocity[1] * dt;
			p.current[2] += p.velocity[2] * dt;

			// detect collision
			besttoi = 2;

			b[0] = p.current[0] - p.previous[0];
			b[1] = p.current[1] - p.previous[1];
			b[2] = p.current[2] - p.previous[2];

			for( int j = 0; j < 6; ++j )
			{
				// use radius == 0.5
				denom = GLVec3Dot(b, planes[j]);
				pastcollision = (GLVec3Dot(p.previous, planes[j]) + planes[j][3] < 0.5f);

				if( denom < -1e-4f )
				{
					toi = (0.5f - GLVec3Dot(p.previous, planes[j]) - planes[j][3]) / denom;

					if( ((toi <= 1 && toi >= 0) ||		// normal case
						(toi < 0 && pastcollision)) &&	// allow past collision
						toi < besttoi )
					{
						besttoi = toi;
						bestplane = &planes[j];
					}
				}
			}

			if( besttoi <= 1 )
			{
				// resolve constraint
				p.current[0] = (1 - besttoi) * p.previous[0] + besttoi * p.current[0];
				p.current[1] = (1 - besttoi) * p.previous[1] + besttoi * p.current[1];
				p.current[2] = (1 - besttoi) * p.previous[2] + besttoi * p.current[2];

				impulse = -GLVec3Dot(*bestplane, p.velocity);

				// perturb normal vector
				noise = ((rand() % 100) / 100.0f) * M_PI * 0.333333f - M_PI * 0.166666f; // [-pi/6, pi/6]

				b[0] = cosf(noise + M_PI * 0.5f);
				b[1] = cosf(noise);
				b[2] = 0;

				GLVec3Normalize(vy, (*bestplane));
				GLGetOrthogonalVectors(vx, vz, vy);

				A[0] = vx[0];	A[1] = vy[0];	A[2] = vz[0];	A[3] = 0;
				A[4] = vx[1];	A[5] = vy[1];	A[6] = vz[1];	A[7] = 0;
				A[8] = vx[2];	A[9] = vy[2];	A[10] = vz[2];	A[11] = 0;
				A[12] = 0;		A[13] = 0;		A[14] = 0;		A[15] = 1;

				GLMatrixInverse(Ainv, A);
				GLVec3Transform(vy, b, Ainv);

				energy = GLVec3Length(p.velocity);

				p.velocity[0] += 2 * impulse * vy[0];
				p.velocity[1] += 2 * impulse * vy[1];
				p.velocity[2] += 2 * impulse * vy[2];

				// must conserve energy
				GLVec3Normalize(p.velocity, p.velocity);

				p.velocity[0] *= energy;
				p.velocity[1] *= energy;
				p.velocity[2] *= energy;
			}

#ifdef _DEBUG
			// test if a light fell through
			tmpbox.GetCenter(center);

			if( GLVec3Distance(p.current, center) > tmpbox.Radius() )
				::_CrtDbgBreak();
#endif
		}
	}

	glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
	glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
}
Example #2
0
void Render(float alpha, float elapsedtime)
{
	float tmp[16];
	float texmat[16];
	float view[16];
	float viewinv[16];
	float proj[16];
	float viewproj[16];
	float lightview[16];
	float lightproj[16];
	float lightviewproj[16];

	float globalambient[4]	= { 0.01f, 0.01f, 0.01f, 1.0f };
	float moonlight[]		= { -0.25f, 0.65f, -1, 0 };
	float mooncolor[]		= { 0.6f, 0.6f, 1, 1 };

	float eye[3]			= { 0, 0, 8 };
	float look[3]			= { 0, 0, 0 };
	float up[3]				= { 0, 1, 0 };

	float screensize[2]		= { (float)screenwidth, (float)screenheight };
	float lightclip[2];
	float clipplanes[2];
	float orient[2];

	// setup camera
	cameraangle.smooth(orient, alpha);

	GLMatrixRotationAxis(view, orient[1], 1, 0, 0);
	GLMatrixRotationAxis(tmp, orient[0], 0, 1, 0);
	GLMatrixMultiply(view, view, tmp);

	GLVec3Transform(eye, eye, view);

	GLFitToBox(clipplanes[0], clipplanes[1], eye, look, scenebox);
	GLMatrixPerspectiveFovRH(proj, (60.0f * 3.14159f) / 180.f,  (float)screenwidth / (float)screenheight, clipplanes[0], clipplanes[1]);

	GLMatrixLookAtRH(view, eye, look, up);
	GLMatrixMultiply(viewproj, view, proj);

	// setup moonlight
	GLMatrixInverse(viewinv, view);
	GLVec3Transform(moonlight, moonlight, viewinv);
	GLVec3Normalize(moonlight, moonlight);

	// should be that value in view space (background is fix)
	// but let y stay in world space, so we see shadow
	moonlight[1] = 0.65f;

	GLMatrixViewVector(lightview, moonlight);
	GLFitToBox(lightproj, lightclip, lightview, scenebox);
	GLMatrixMultiply(lightviewproj, lightview, lightproj);

	// render shadow map
	glClearColor(0, 0, 0, 1);

	varianceshadow->SetMatrix("matViewProj", lightviewproj);
	varianceshadow->SetVector("clipPlanes", lightclip);

	shadowmap->Set();
	{
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

		varianceshadow->Begin();
		{
			RenderScene(varianceshadow);
		}
		varianceshadow->End();
	}
	shadowmap->Unset();

	// blur it
	float texelsize[] = { 1.0f / SHADOWMAP_SIZE, 1.0f / SHADOWMAP_SIZE };

	glDepthMask(GL_FALSE);
	glBindTexture(GL_TEXTURE_2D, shadowmap->GetColorAttachment(0));

	boxblur3x3->SetVector("texelSize", texelsize);

	blurredshadow->Set();
	{
		boxblur3x3->Begin();
		{
			screenquad->Draw();
		}
		boxblur3x3->End();
	}
	blurredshadow->Unset();

	glDepthMask(GL_TRUE);

	// STEP 1: z pass
	ambient->SetMatrix("matViewProj", viewproj);
	ambient->SetVector("matAmbient", globalambient);

	framebuffer->Set();
	{
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

		// draw background first
		glDisable(GL_DEPTH_TEST);
		glDepthMask(GL_FALSE);
		glBindTexture(GL_TEXTURE_2D, texture3);

		float scaledx = 1360.0f * (screenheight / 768.0f);
		float scale = screenwidth / scaledx;

		GLMatrixTranslation(tmp, -0.5f, 0, 0);
		GLMatrixScaling(texmat, scale, 1, 1);
		GLMatrixMultiply(texmat, tmp, texmat);

		GLMatrixTranslation(tmp, 0.5f, 0, 0);
		GLMatrixMultiply(texmat, texmat, tmp);

		GLMatrixRotationAxis(tmp, M_PI, 0, 0, 1);
		GLMatrixMultiply(texmat, texmat, tmp);

		basic2D->SetMatrix("matTexture", texmat);
		basic2D->Begin();
		{
			screenquad->Draw();
		}
		basic2D->End();

		glEnable(GL_DEPTH_TEST);
		glDepthMask(GL_TRUE);

		// then fill zbuffer
		ambient->Begin();
		{
			RenderScene(ambient);
		}
		ambient->End();
	}
	framebuffer->Unset();

	// STEP 2: cull lights
	if( lightcull && timeout > DELAY )
	{
		lightcull->SetFloat("alpha", alpha);
		lightcull->SetVector("clipPlanes", clipplanes);
		lightcull->SetVector("screenSize", screensize);
		lightcull->SetMatrix("matProj", proj);
		lightcull->SetMatrix("matView", view);
		lightcull->SetMatrix("matViewProj", viewproj);

		glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, counterbuffer);
		GLuint* counter = (GLuint*)glMapBuffer(GL_ATOMIC_COUNTER_BUFFER, GL_WRITE_ONLY);
	
		*counter = 0;
		glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);

		glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, headbuffer);
		glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, nodebuffer);
		glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, lightbuffer);
		glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, counterbuffer);
		glBindTexture(GL_TEXTURE_2D, framebuffer->GetDepthAttachment());

		lightcull->Begin();
		{
			glDispatchCompute(workgroupsx, workgroupsy, 1);
		}
		lightcull->End();

		glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, 0);
		glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0);
		glBindTexture(GL_TEXTURE_2D, 0);
	}

	// STEP 3: add some moonlight with shadow
	glEnable(GL_BLEND);
	glBlendFunc(GL_ONE, GL_ONE);
	glDepthMask(GL_FALSE);

	framebuffer->Set();

	shadowedlight->SetMatrix("matViewProj", viewproj);
	shadowedlight->SetMatrix("lightViewProj", lightviewproj);
	shadowedlight->SetVector("eyePos", eye);
	shadowedlight->SetVector("lightPos", moonlight);
	shadowedlight->SetVector("lightColor", mooncolor);
	shadowedlight->SetVector("clipPlanes", lightclip);

	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, blurredshadow->GetColorAttachment(0));
	glActiveTexture(GL_TEXTURE0);

	shadowedlight->Begin();
	{
		RenderScene(shadowedlight);
	}
	shadowedlight->End();

	// STEP 4: accumulate lighting
	if( lightaccum && timeout > DELAY )
	{
		lightaccum->SetMatrix("matViewProj", viewproj);
		lightaccum->SetVector("eyePos", eye);
		lightaccum->SetFloat("alpha", alpha);
		lightaccum->SetInt("numTilesX", workgroupsx);

		lightaccum->Begin();
		{
			RenderScene(lightaccum);
		}
		lightaccum->End();
		
		glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, 0);
		glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, 0);
		glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, 0);
	}

	framebuffer->Unset();

	glDisable(GL_BLEND);
	glDepthMask(GL_TRUE);

	// STEP 4: gamma correct
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	glBindTexture(GL_TEXTURE_2D, framebuffer->GetColorAttachment(0));

	gammacorrect->Begin();
	{
		screenquad->Draw();
	}
	gammacorrect->End();

#ifdef _DEBUG
	// check errors
	GLenum err = glGetError();

	if( err != GL_NO_ERROR )
		std::cout << "Error\n";
#endif

	SwapBuffers(hdc);
	mousedx = mousedy = 0;
}
void FPSCamera::Update(float dt)
{
	float forward[3], right[3], up[3];
	float diff[3];
	float movedir[2] = { 0, 0 };

	// rotate
	targetangles[1] = GLClamp(targetangles[1], -GL_HALF_PI, GL_HALF_PI);

	diff[0] = (targetangles[0] - anglecurve.curr[0]) * dt * ROTATIONAL_INVINTERTIA;
	diff[1] = (targetangles[1] - anglecurve.curr[1]) * dt * ROTATIONAL_INVINTERTIA;
	diff[2] = 0;

	anglecurve.extend(diff);
	
	// move
	GLVec3Set(diff, 0, 0, 0);

	if( state & State_Moving )
	{
		if( state & State_Left )
			movedir[0] = -1;

		if( state & State_Right )
			movedir[0] = 1;

		if( state & State_Forward )
			movedir[1] = 1;

		if( state & State_Backward )
			movedir[1] = -1;

		GetViewVectors(forward, right, up);

		if( forward[1] > 0.98f )
			GLVec3Set(forward, -up[0], -up[1], -up[2]);

		if( forward[1] < -0.98f )
			GLVec3Set(forward, up[0], up[1], up[2]);

		forward[1] = right[1] = 0;

		GLVec3Scale(forward, forward, movedir[1]);
		GLVec3Scale(right, right, movedir[0]);
		GLVec3Add(diff, forward, right);

		GLVec3Normalize(diff, diff);
		GLVec3Scale(diff, diff, MOVEMENT_SPEED);
	}

	// update body (NOTE: don't even try it with physics)
	CollisionData	data;
	RigidBody*		groundbody = 0;
	float			groundplane[4];
	float			hitparams[4];
	float			hitpos[3];
	float			prevpos[3];
	float			vel[3];
	float			deltavel[3] = { 0, 0, 0 };
	float			down[3] = { 0, -1, 0 };
	bool			wasonground = isonground;

	GLVec3Assign(prevpos, body->GetPosition());

	if( wasonground )
		body->SetVelocity(diff);
	
	body->Integrate(dt);

	// look for ground first
	groundbody = collworld->RayIntersect(hitparams, prevpos, down);
	isonground = false;

	if( groundbody && hitparams[3] >= 0 ) // && hitparams[1] > 0.64f
	{
		GLVec3Mad(hitpos, prevpos, down, hitparams[3]);
		GLPlaneFromRay(groundplane, hitpos, hitparams);
		GLVec3Subtract(vel, body->GetPosition(), prevpos);

		hitparams[3] = (CAMERA_RADIUS - groundplane[3] - GLVec3Dot(hitparams, prevpos)) / GLVec3Dot(hitparams, vel);

		if( hitparams[3] > -0.1f && hitparams[3] < 1.0f )
		{
			// resolve position
			body->ResolvePenetration(hitparams[3] * dt);
			isonground = true;

			// resolve velocity and integrate
			float length = GLVec3Length(diff);
			float cosa = GLVec3Dot(diff, hitparams);

			GLVec3Mad(diff, diff, hitparams, -cosa);

			if( fabs(length) > 1e-5f )
				GLVec3Scale(diff, diff, length / GLVec3Length(diff));

			body->SetVelocity(diff);
			body->IntegratePosition(dt);
		}
	}

	body->GetVelocity(vel);

	// now test every other object
	collworld->DetectCollisions(data, body);

	for( size_t i = 0; i < data.contacts.size(); ++i )
	{
		const Contact& contact = data.contacts[i];

		if( contact.body2 == groundbody )
			continue;

		if( contact.depth > 0 )
		{
			body->ResolvePenetration(contact);

			float rel_vel = GLVec3Dot(contact.normal, vel);
			float impulse = rel_vel;

			GLVec3Mad(deltavel, deltavel, contact.normal, -impulse);
		}
	}

	GLVec3Add(vel, vel, deltavel);
	body->SetVelocity(vel);
}