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); }
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); }