void idSimpleWindow::SetupTransforms( float x, float y ) { static idMat3 trans; static idVec3 org; trans.Identity(); org.Set( origin.x + x, origin.y + y, 0 ); if( rotate ) { static idRotation rot; static idVec3 vec( 0, 0, 1 ); rot.Set( org, vec, rotate ); trans = rot.ToMat3(); } static idMat3 smat; smat.Identity(); if( shear.x() || shear.y() ) { smat[0][1] = shear.x(); smat[1][0] = shear.y(); trans *= smat; } if( !trans.IsIdentity() ) { dc->SetTransformInfo( org, trans ); } }
/* ================ idPhysics_Static::Rotate ================ */ void idPhysics_Static::Rotate( const idRotation& rotation, int id ) { idVec3 masterOrigin; idMat3 masterAxis; current.origin *= rotation; current.axis *= rotation.ToMat3(); if( hasMaster ) { self->GetMasterPosition( masterOrigin, masterAxis ); current.localAxis *= rotation.ToMat3(); current.localOrigin = ( current.origin - masterOrigin ) * masterAxis.Transpose(); } else { current.localAxis = current.axis; current.localOrigin = current.origin; } if( clipModel ) { clipModel->Link( gameLocal.clip, self, 0, current.origin, current.axis ); } }
/* ================ idPhysics_StaticMulti::Rotate ================ */ void idPhysics_StaticMulti::Rotate( const idRotation& rotation, int id ) { int i; idVec3 masterOrigin; idMat3 masterAxis; if( id >= 0 && id < clipModels.Num() ) { current[id].origin *= rotation; current[id].axis *= rotation.ToMat3(); if( hasMaster ) { self->GetMasterPosition( masterOrigin, masterAxis ); current[id].localAxis *= rotation.ToMat3(); current[id].localOrigin = ( current[id].origin - masterOrigin ) * masterAxis.Transpose(); } else { current[id].localAxis = current[id].axis; current[id].localOrigin = current[id].origin; } if( clipModels[id] ) { clipModels[id]->Link( gameLocal.clip, self, id, current[id].origin, current[id].axis ); } } else if( id == -1 ) { for( i = 0; i < clipModels.Num(); i++ ) { current[i].origin *= rotation; current[i].axis *= rotation.ToMat3(); if( hasMaster ) { self->GetMasterPosition( masterOrigin, masterAxis ); current[i].localAxis *= rotation.ToMat3(); current[i].localOrigin = ( current[i].origin - masterOrigin ) * masterAxis.Transpose(); } else { current[i].localAxis = current[i].axis; current[i].localOrigin = current[i].origin; } if( clipModels[i] ) { clipModels[i]->Link( gameLocal.clip, self, i, current[i].origin, current[i].axis ); } } } }
/* ============ idBounds::FromPointRotation Most tight bounds for the rotational movement of the given point. ============ */ void idBounds::FromPointRotation( const idVec3 &point, const idRotation &rotation ) { float radius; if ( idMath::Fabs( rotation.GetAngle() ) < 180.0f ) { (*this) = BoundsForPointRotation( point, rotation ); } else { radius = ( point - rotation.GetOrigin() ).Length(); // FIXME: these bounds are usually way larger b[0].Set( -radius, -radius, -radius ); b[1].Set( radius, radius, radius ); } }
/* ================ idPhysics::Rotate ================ */ void idPhysics_RigidBody::Rotate( const idRotation &rotation, int id ) { idVec3 masterOrigin; idMat3 masterAxis; current.i.orientation *= rotation.ToMat3(); current.i.position *= rotation; if( hasMaster ) { self->GetMasterPosition( masterOrigin, masterAxis ); current.localAxis *= rotation.ToMat3(); current.localOrigin = ( current.i.position - masterOrigin ) * masterAxis.Transpose(); } else { current.localAxis = current.i.orientation; current.localOrigin = current.i.position; } clipModel->Link( gameLocal.clip, self, clipModel->GetId(), current.i.position, current.i.orientation ); Activate(); }
/* ================ idPhysics_Monster::Rotate ================ */ void idPhysics_Monster::Rotate( const idRotation &rotation, int id ) { idVec3 masterOrigin; idMat3 masterAxis; current.origin *= rotation; if( masterEntity ) { self->GetMasterPosition( masterOrigin, masterAxis ); current.localOrigin = ( current.origin - masterOrigin ) * masterAxis.Transpose(); } else { current.localOrigin = current.origin; } clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() * rotation.ToMat3() ); Activate(); }
/* ============ idClip::Rotation ============ */ bool idClip::Rotation( trace_t &results, const idVec3 &start, const idRotation &rotation, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) { int i, num; idClipModel *touch, *clipModelList[MAX_GENTITIES]; idBounds traceBounds; trace_t trace; const idTraceModel *trm; trm = TraceModelForClipModel( mdl ); if( !passEntity || passEntity->entityNumber != ENTITYNUM_WORLD ) { // test world idClip::numRotations++; collisionModelManager->Rotation( &results, start, rotation, trm, trmAxis, contentMask, 0, vec3_origin, mat3_default ); results.c.entityNum = results.fraction != 1.0f ? ENTITYNUM_WORLD : ENTITYNUM_NONE; if( results.fraction == 0.0f ) { return true; // blocked immediately by the world } } else { memset( &results, 0, sizeof( results ) ); results.fraction = 1.0f; results.endpos = start; results.endAxis = trmAxis * rotation.ToMat3(); } if( !trm ) { traceBounds.FromPointRotation( start, rotation ); } else { traceBounds.FromBoundsRotation( trm->bounds, start, trmAxis, rotation ); } num = GetTraceClipModels( traceBounds, contentMask, passEntity, clipModelList ); for( i = 0; i < num; i++ ) { touch = clipModelList[i]; if( !touch ) { continue; } // no rotational collision with render models if( touch->renderModelHandle != -1 ) { continue; } idClip::numRotations++; collisionModelManager->Rotation( &trace, start, rotation, trm, trmAxis, contentMask, touch->Handle(), touch->origin, touch->axis ); if( trace.fraction < results.fraction ) { results = trace; results.c.entityNum = touch->entity->entityNumber; results.c.id = touch->id; if( results.fraction == 0.0f ) { break; } } } return ( results.fraction < 1.0f ); }
/* ================ BoundsForPointRotation only for rotations < 180 degrees ================ */ idBounds BoundsForPointRotation( const idVec3 &start, const idRotation &rotation ) { int i; float radiusSqr; idVec3 v1, v2; idVec3 origin, axis, end; idBounds bounds; end = start * rotation; axis = rotation.GetVec(); origin = rotation.GetOrigin() + axis * ( axis * ( start - rotation.GetOrigin() ) ); radiusSqr = ( start - origin ).LengthSqr(); v1 = ( start - origin ).Cross( axis ); v2 = ( end - origin ).Cross( axis ); for ( i = 0; i < 3; i++ ) { // if the derivative changes sign along this axis during the rotation from start to end if ( ( v1[i] > 0.0f && v2[i] < 0.0f ) || ( v1[i] < 0.0f && v2[i] > 0.0f ) ) { if ( ( 0.5f * (start[i] + end[i]) - origin[i] ) > 0.0f ) { bounds[0][i] = Min( start[i], end[i] ); bounds[1][i] = origin[i] + idMath::Sqrt( radiusSqr * ( 1.0f - axis[i] * axis[i] ) ); } else { bounds[0][i] = origin[i] - idMath::Sqrt( radiusSqr * ( 1.0f - axis[i] * axis[i] ) ); bounds[1][i] = Max( start[i], end[i] ); } } else if ( start[i] > end[i] ) { bounds[0][i] = end[i]; bounds[1][i] = start[i]; } else { bounds[0][i] = start[i]; bounds[1][i] = end[i]; } } return bounds; }
/* ============ idBounds::FromBoundsRotation Most tight bounds for the rotational movement of the given bounds. ============ */ void idBounds::FromBoundsRotation( const idBounds &bounds, const idVec3 &origin, const idMat3 &axis, const idRotation &rotation ) { int i; float radius; idVec3 point; idBounds rBounds; if ( idMath::Fabs( rotation.GetAngle() ) < 180.0f ) { (*this) = BoundsForPointRotation( bounds[0] * axis + origin, rotation ); for ( i = 1; i < 8; i++ ) { point[0] = bounds[(i^(i>>1))&1][0]; point[1] = bounds[(i>>1)&1][1]; point[2] = bounds[(i>>2)&1][2]; (*this) += BoundsForPointRotation( point * axis + origin, rotation ); } }
/* ================ rvPhysics_Particle::Rotate( ================ */ void rvPhysics_Particle::Rotate( const idRotation &rotation, int id ) { idVec3 masterOrigin; idMat3 masterAxis; current.origin *= rotation; if ( hasMaster ) { self->GetMasterPosition( masterOrigin, masterAxis ); current.localOrigin = ( current.origin - masterOrigin ) * masterAxis.Transpose(); } else { current.localOrigin = current.origin; } // RAVEN BEGIN // ddynerman: multiple clip worlds clipModel->Link( self, 0, current.origin, clipModel->GetAxis() * rotation.ToMat3() ); // RAVEN END Activate(); }
/* ============ idClip::Motion ============ */ bool idClip::Motion( trace_t &results, const idVec3 &start, const idVec3 &end, const idRotation &rotation, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) { int i, num; idClipModel *touch, *clipModelList[MAX_GENTITIES]; idVec3 dir, endPosition; idBounds traceBounds; float radius; trace_t translationalTrace, rotationalTrace, trace; idRotation endRotation; const idTraceModel *trm; assert( rotation.GetOrigin() == start ); if ( TestHugeTranslation( results, mdl, start, end, trmAxis ) ) { return true; } if ( mdl != NULL && rotation.GetAngle() != 0.0f && rotation.GetVec() != vec3_origin ) { // if no translation if ( start == end ) { // pure rotation return Rotation( results, start, rotation, mdl, trmAxis, contentMask, passEntity ); } } else if ( start != end ) { // pure translation return Translation( results, start, end, mdl, trmAxis, contentMask, passEntity ); } else { // no motion results.fraction = 1.0f; results.endpos = start; results.endAxis = trmAxis; return false; } trm = TraceModelForClipModel( mdl ); radius = trm->bounds.GetRadius(); if ( !passEntity || passEntity->entityNumber != ENTITYNUM_WORLD ) { // translational collision with world idClip::numTranslations++; collisionModelManager->Translation( &translationalTrace, start, end, trm, trmAxis, contentMask, 0, vec3_origin, mat3_default ); translationalTrace.c.entityNum = translationalTrace.fraction != 1.0f ? ENTITYNUM_WORLD : ENTITYNUM_NONE; } else { memset( &translationalTrace, 0, sizeof( translationalTrace ) ); translationalTrace.fraction = 1.0f; translationalTrace.endpos = end; translationalTrace.endAxis = trmAxis; } if ( translationalTrace.fraction != 0.0f ) { traceBounds.FromBoundsRotation( trm->bounds, start, trmAxis, rotation ); dir = translationalTrace.endpos - start; for ( i = 0; i < 3; i++ ) { if ( dir[i] < 0.0f ) { traceBounds[0][i] += dir[i]; } else { traceBounds[1][i] += dir[i]; } } num = GetTraceClipModels( traceBounds, contentMask, passEntity, clipModelList ); for ( i = 0; i < num; i++ ) { touch = clipModelList[i]; if ( !touch ) { continue; } if ( touch->renderModelHandle != -1 ) { idClip::numRenderModelTraces++; TraceRenderModel( trace, start, end, radius, trmAxis, touch ); } else { idClip::numTranslations++; collisionModelManager->Translation( &trace, start, end, trm, trmAxis, contentMask, touch->Handle(), touch->origin, touch->axis ); } if ( trace.fraction < translationalTrace.fraction ) { translationalTrace = trace; translationalTrace.c.entityNum = touch->entity->entityNumber; translationalTrace.c.id = touch->id; if ( translationalTrace.fraction == 0.0f ) { break; } } } } else { num = -1; } endPosition = translationalTrace.endpos; endRotation = rotation; endRotation.SetOrigin( endPosition ); if ( !passEntity || passEntity->entityNumber != ENTITYNUM_WORLD ) { // rotational collision with world idClip::numRotations++; collisionModelManager->Rotation( &rotationalTrace, endPosition, endRotation, trm, trmAxis, contentMask, 0, vec3_origin, mat3_default ); rotationalTrace.c.entityNum = rotationalTrace.fraction != 1.0f ? ENTITYNUM_WORLD : ENTITYNUM_NONE; } else { memset( &rotationalTrace, 0, sizeof( rotationalTrace ) ); rotationalTrace.fraction = 1.0f; rotationalTrace.endpos = endPosition; rotationalTrace.endAxis = trmAxis * rotation.ToMat3(); } if ( rotationalTrace.fraction != 0.0f ) { if ( num == -1 ) { traceBounds.FromBoundsRotation( trm->bounds, endPosition, trmAxis, endRotation ); num = GetTraceClipModels( traceBounds, contentMask, passEntity, clipModelList ); } for ( i = 0; i < num; i++ ) { touch = clipModelList[i]; if ( !touch ) { continue; } // no rotational collision detection with render models if ( touch->renderModelHandle != -1 ) { continue; } idClip::numRotations++; collisionModelManager->Rotation( &trace, endPosition, endRotation, trm, trmAxis, contentMask, touch->Handle(), touch->origin, touch->axis ); if ( trace.fraction < rotationalTrace.fraction ) { rotationalTrace = trace; rotationalTrace.c.entityNum = touch->entity->entityNumber; rotationalTrace.c.id = touch->id; if ( rotationalTrace.fraction == 0.0f ) { break; } } } } if ( rotationalTrace.fraction < 1.0f ) { results = rotationalTrace; } else { results = translationalTrace; results.endAxis = rotationalTrace.endAxis; } results.fraction = Max( translationalTrace.fraction, rotationalTrace.fraction ); return ( translationalTrace.fraction < 1.0f || rotationalTrace.fraction < 1.0f ); }
int idPush::TryRotatePushEntity( trace_t &results, idEntity *check, idClipModel *clipModel, const int flags, const idMat3 &newAxis, const idRotation &rotation ) { trace_t trace; idVec3 rotationPoint; idRotation newRotation; float checkAngle; idPhysics *physics; physics = check->GetPhysics(); #ifdef ROTATIONAL_PUSH_DEBUG bool startsolid = false; if ( physics->ClipContents( clipModel ) ) { startsolid = true; } #endif results.fraction = 1.0f; results.endpos = clipModel->GetOrigin(); results.endAxis = newAxis; memset( &results.c, 0, sizeof( results.c ) ); // always pushed when standing on the pusher if ( physics->IsGroundClipModel( clipModel->GetEntity()->entityNumber, clipModel->GetId() ) ) { // rotate the entity colliding with all other entities except the pusher itself ClipEntityRotation( trace, check, NULL, clipModel, rotation ); // if there is a collision if ( trace.fraction < 1.0f ) { // angle along which the entity is pushed checkAngle = rotation.GetAngle() * trace.fraction; // test if the entity can stay at it's partly pushed position by rotating // the entity in reverse only colliding with pusher newRotation.Set( rotation.GetOrigin(), rotation.GetVec(), -(rotation.GetAngle() - checkAngle) ); ClipEntityRotation( results, check, clipModel, NULL, newRotation ); // if there is a collision if ( results.fraction < 1.0f ) { // FIXME: try to push the blocking entity as well or try to slide along collision plane(s)? results.c.normal = -results.c.normal; results.c.dist = -results.c.dist; // the entity will be crushed between the pusher and some other entity return PUSH_BLOCKED; } } else { // angle along which the entity is pushed checkAngle = rotation.GetAngle(); } // point to rotate entity bbox around back to axial rotationPoint = physics->GetOrigin(); } else { // rotate entity in reverse only colliding with pusher newRotation = rotation; newRotation.Scale( -1 ); // ClipEntityRotation( results, check, clipModel, NULL, newRotation ); // if no collision with the pusher then the entity is not pushed by the pusher if ( results.fraction >= 1.0f ) { #ifdef ROTATIONAL_PUSH_DEBUG // set pusher into final position clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), newAxis ); if ( physics->ClipContents( clipModel ) ) { if ( !startsolid ) { int bah = 1; } } #endif return PUSH_NO; } // get point to rotate bbox around back to axial rotationPoint = results.c.point; // angle along which the entity will be pushed checkAngle = rotation.GetAngle() * (1.0f - results.fraction); // rotate the entity colliding with all other entities except the pusher itself newRotation.Set( rotation.GetOrigin(), rotation.GetVec(), checkAngle ); ClipEntityRotation( trace, check, NULL, clipModel, newRotation ); // if there is a collision if ( trace.fraction < 1.0f ) { // FIXME: try to push the blocking entity as well or try to slide along collision plane(s)? results.c.normal = -results.c.normal; results.c.dist = -results.c.dist; // the entity will be crushed between the pusher and some other entity return PUSH_BLOCKED; } } SaveEntityPosition( check ); newRotation.Set( rotation.GetOrigin(), rotation.GetVec(), checkAngle ); // NOTE: this code prevents msvc 6.0 & 7.0 from screwing up the above code in // release builds moving less floats than it should static float shit = checkAngle; #pragma unused(shit) newRotation.RotatePoint( rotationPoint ); // rotate the entity physics->Rotate( newRotation ); // set pusher into final position clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), newAxis ); #ifdef ROTATIONAL_PUSH_DEBUG if ( physics->ClipContents( clipModel ) ) { if ( !startsolid ) { int bah = 1; } } #endif // if the entity uses actor physics if ( physics->IsType( idPhysics_Actor::Type ) ) { // rotate the collision model back to axial if ( !RotateEntityToAxial( check, rotationPoint ) ) { // don't allow rotation if the bbox is no longer axial return PUSH_BLOCKED; } } #ifdef ROTATIONAL_PUSH_DEBUG if ( physics->ClipContents( clipModel ) ) { if ( !startsolid ) { int bah = 1; } } #endif // if the entity is an actor using actor physics if ( check->IsType( idActor::Type ) && physics->IsType( idPhysics_Actor::Type ) ) { // if the entity is standing ontop of the pusher if ( physics->IsGroundClipModel( clipModel->GetEntity()->entityNumber, clipModel->GetId() ) ) { // rotate actor view idActor *actor = static_cast<idActor *>(check); idAngles delta = actor->GetDeltaViewAngles(); delta.yaw += newRotation.ToMat3()[0].ToYaw(); actor->SetDeltaViewAngles( delta ); } } return PUSH_OK; }
/* ============ idPush::ClipRotationalPush Try to push other entities by rotating the given entity. ============ */ float idPush::ClipRotationalPush( trace_t &results, idEntity *pusher, const int flags, const idMat3 &newAxis, const idRotation &rotation ) { int i, j, numListedEntities; idEntity *curPusher, *ent, *entityList[ MAX_GENTITIES ]; float fraction; bool groundContact, blocked = false; float totalMass; trace_t trace; idRotation realRotation, partialRotation; idMat3 oldAxis; totalMass = 0.0f; results.fraction = 1.0f; results.endpos = pusher->GetPhysics()->GetOrigin(); results.endAxis = newAxis; memset( results.c, 0, sizeof( results.c ) ); if ( !rotation.GetAngle() ) { return totalMass; } // clip against all non-pushable physics objects if ( flags & PUSHFL_CLIP ) { numListedEntities = GetPushableEntitiesForRotation( pusher, pusher, flags, rotation, entityList, MAX_GENTITIES ); // disable pushable entities for collision detection for ( i = 0; i < numListedEntities; i++ ) { entityList[i]->GetPhysics()->DisableClip(); } // clip rotation pusher->GetPhysics()->ClipRotation( results, rotation, NULL ); // enable pushable entities for ( i = 0; i < numListedEntities; i++ ) { entityList[i]->GetPhysics()->EnableClip(); } if ( results.fraction == 0.0f ) { return totalMass; } realRotation = results.fraction * rotation; } else { realRotation = rotation; } // put the pusher in the group of pushed physics objects pushedGroup[0].ent = pusher; pushedGroup[0].fraction = 1.0f; pushedGroup[0].groundContact = true; pushedGroup[0].test = true; pushedGroupSize = 1; // get all physics objects that need to be pushed for ( i = 0; i < pushedGroupSize; ) { if ( !pushedGroup[i].test ) { i++; continue; } pushedGroup[i].test = false; curPusher = pushedGroup[i].ent; fraction = pushedGroup[i].fraction; groundContact = pushedGroup[i].groundContact; i = 0; numListedEntities = GetPushableEntitiesForRotation( curPusher, pusher, flags, realRotation, entityList, MAX_GENTITIES ); for ( j = 0; j < numListedEntities; j++ ) { ent = entityList[ j ]; if ( IsFullyPushed( ent ) ) { continue; } if ( ent->GetPhysics()->IsGroundEntity( curPusher->entityNumber ) ) { AddEntityToPushedGroup( ent, 1.0f * fraction, false ); } else if ( ClipRotationAgainstPusher( trace, ent, curPusher, -fraction * realRotation ) ) { AddEntityToPushedGroup( ent, ( 1.0f - trace.fraction ) * fraction, groundContact ); } } } // save physics states and disable physics objects for collision detection for ( i = 1; i < pushedGroupSize; i++ ) { SaveEntityPosition( pushedGroup[i].ent ); pushedGroup[i].ent->GetPhysics()->DisableClip(); } // clip all pushed physics objects for ( i = 1; i < pushedGroupSize; i++ ) { partialRotation = realRotation * pushedGroup[i].fraction; pushedGroup[i].ent->GetPhysics()->ClipRotation( trace, partialRotation, NULL ); if ( trace.fraction < 1.0f ) { blocked = true; break; } } // enable all physics objects for collision detection for ( i = 1; i < pushedGroupSize; i++ ) { pushedGroup[i].ent->GetPhysics()->EnableClip(); } // push all or nothing if ( blocked ) { if ( flags & PUSHFL_CLIP ) { pusher->GetPhysics()->ClipRotation( results, realRotation, NULL ); } else { results.fraction = 0.0f; results.endpos = pusher->GetPhysics()->GetOrigin(); results.endAxis = pusher->GetPhysics()->GetAxis(); } } else { // rotate all pushed physics objects for ( i = 1; i < pushedGroupSize; i++ ) { partialRotation = realRotation * pushedGroup[i].fraction; pushedGroup[i].ent->GetPhysics()->Rotate( partialRotation ); totalMass += pushedGroup[i].ent->GetPhysics()->GetMass(); } // rotate the clip models of the pusher for ( i = 0; i < pusher->GetPhysics()->GetNumClipModels(); i++ ) { pusher->GetPhysics()->GetClipModel(i)->Rotate( realRotation ); pusher->GetPhysics()->GetClipModel(i)->Link( gameLocal.clip ); pusher->GetPhysics()->GetClipModel(i)->Enable(); } // rotate any actors back to axial for ( i = 1; i < pushedGroupSize; i++ ) { // if the entity is using actor physics if ( pushedGroup[i].ent->GetPhysics()->IsType( idPhysics_Actor::Type ) ) { // rotate the collision model back to axial if ( !RotateEntityToAxial( pushedGroup[i].ent, pushedGroup[i].ent->GetPhysics()->GetOrigin() ) ) { // don't allow rotation if the bbox is no longer axial results.fraction = 0.0f; results.endpos = pusher->GetPhysics()->GetOrigin(); results.endAxis = pusher->GetPhysics()->GetAxis(); } } } } return totalMass; }
/* ============ idPush::ClipRotationalPush Try to push other entities by moving the given entity. ============ */ float idPush::ClipRotationalPush( trace_t &results, idEntity *pusher, const int flags, const idMat3 &newAxis, const idRotation &rotation ) { int i, listedEntities, res; idEntity *check, *entityList[ MAX_GENTITIES ]; idBounds bounds, pushBounds; idRotation clipRotation; idMat3 clipAxis, oldAxis; trace_t pushResults; bool wasEnabled; float totalMass; idClipModel *clipModel; clipModel = pusher->GetPhysics()->GetClipModel(); totalMass = 0.0f; results.fraction = 1.0f; results.endpos = clipModel->GetOrigin(); results.endAxis = newAxis; memset( &results.c, 0, sizeof( results.c ) ); if ( !rotation.GetAngle() ) { return totalMass; } // get bounds for the whole movement bounds = clipModel->GetBounds(); if ( bounds[0].x >= bounds[1].x ) { return totalMass; } pushBounds.FromBoundsRotation( bounds, clipModel->GetOrigin(), clipModel->GetAxis(), rotation ); wasEnabled = clipModel->IsEnabled(); // make sure we don't get the pushing clip model in the list clipModel->Disable(); listedEntities = gameLocal.clip.EntitiesTouchingBounds( pushBounds, -1, entityList, MAX_GENTITIES ); // discard entities we cannot or should not push listedEntities = DiscardEntities( entityList, listedEntities, flags, pusher ); if ( flags & PUSHFL_CLIP ) { // can only clip movement of a trace model assert( clipModel->IsTraceModel() ); // disable to be pushed entities for collision detection for ( i = 0; i < listedEntities; i++ ) { entityList[i]->GetPhysics()->DisableClip(); } gameLocal.clip.Rotation( results, clipModel->GetOrigin(), rotation, clipModel, clipModel->GetAxis(), pusher->GetPhysics()->GetClipMask(), NULL ); // enable to be pushed entities for collision detection for ( i = 0; i < listedEntities; i++ ) { entityList[i]->GetPhysics()->EnableClip(); } if ( results.fraction == 0.0f ) { if ( wasEnabled ) { clipModel->Enable(); } return totalMass; } clipRotation = rotation * results.fraction; clipAxis = results.endAxis; } else { clipRotation = rotation; clipAxis = newAxis; } // we have to enable the clip model because we use it during pushing clipModel->Enable(); // save pusher old position oldAxis = clipModel->GetAxis(); // try to push all the entities for ( i = 0; i < listedEntities; i++ ) { check = entityList[ i ]; idPhysics *physics = check->GetPhysics(); // disable the entity for collision detection physics->DisableClip(); res = TryRotatePushEntity( pushResults, check, clipModel, flags, clipAxis, clipRotation ); // enable the entity for collision detection physics->EnableClip(); // if the entity is pushed if ( res == PUSH_OK ) { // set the pusher in the rotated position clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), newAxis ); // the entity might be pushed off the ground physics->EvaluateContacts(); // put pusher back in old position clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), oldAxis ); // wake up this object check->ApplyImpulse( clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), vec3_origin ); // add mass of pushed entity totalMass += physics->GetMass(); } // if the entity is not blocking if ( res != PUSH_BLOCKED ) { continue; } // if the blocking entity is a projectile if ( check->IsType( idProjectile::Type ) ) { check->ProcessEvent( &EV_Explode ); continue; } // if blocking entities should be crushed if ( flags & PUSHFL_CRUSH ) { check->Damage( clipModel->GetEntity(), clipModel->GetEntity(), vec3_origin, "damage_crush", 1.0f, CLIPMODEL_ID_TO_JOINT_HANDLE( pushResults.c.id ) ); continue; } // if the entity is an active articulated figure and gibs if ( check->IsType( idAFEntity_Base::Type ) && check->spawnArgs.GetBool( "gib" ) ) { if ( static_cast<idAFEntity_Base *>(check)->IsActiveAF() ) { check->ProcessEvent( &EV_Gib, "damage_Gib" ); } } // blocked results = pushResults; results.fraction = 0.0f; results.endAxis = clipModel->GetAxis(); results.endpos = clipModel->GetOrigin(); results.c.entityNum = check->entityNumber; results.c.id = 0; if ( !wasEnabled ) { clipModel->Disable(); } return totalMass; } if ( !wasEnabled ) { clipModel->Disable(); } return totalMass; }
void idCollisionModelManagerLocal::Rotation( trace_t *results, const idVec3 &start, const idRotation &rotation, const idTraceModel *trm, const idMat3 &trmAxis, int contentMask, cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) { idVec3 tmp; float maxa, stepa, a, lasta; assert( ((byte *)&start) < ((byte *)results) || ((byte *)&start) > (((byte *)results) + sizeof( trace_t )) ); assert( ((byte *)&trmAxis) < ((byte *)results) || ((byte *)&trmAxis) > (((byte *)results) + sizeof( trace_t )) ); memset( results, 0, sizeof( *results ) ); // if special position test if ( rotation.GetAngle() == 0.0f ) { idCollisionModelManagerLocal::ContentsTrm( results, start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis ); return; } #ifdef _DEBUG bool startsolid = false; // test whether or not stuck to begin with if ( cm_debugCollision.GetBool() ) { if ( !entered ) { entered = 1; // if already messed up to begin with if ( idCollisionModelManagerLocal::Contents( start, trm, trmAxis, -1, model, modelOrigin, modelAxis ) & contentMask ) { startsolid = true; } entered = 0; } } #endif if ( rotation.GetAngle() >= 180.0f || rotation.GetAngle() <= -180.0f) { if ( rotation.GetAngle() >= 360.0f ) { maxa = 360.0f; stepa = 120.0f; // three steps strictly < 180 degrees } else if ( rotation.GetAngle() <= -360.0f ) { maxa = -360.0f; stepa = -120.0f; // three steps strictly < 180 degrees } else { maxa = rotation.GetAngle(); stepa = rotation.GetAngle() * 0.5f; // two steps strictly < 180 degrees } for ( lasta = 0.0f, a = stepa; fabs( a ) < fabs( maxa ) + 1.0f; lasta = a, a += stepa ) { // partial rotation idCollisionModelManagerLocal::Rotation180( results, rotation.GetOrigin(), rotation.GetVec(), lasta, a, start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis ); // if there is a collision if ( results->fraction < 1.0f ) { // fraction of total rotation results->fraction = (lasta + stepa * results->fraction) / rotation.GetAngle(); return; } } results->fraction = 1.0f; return; } idCollisionModelManagerLocal::Rotation180( results, rotation.GetOrigin(), rotation.GetVec(), 0.0f, rotation.GetAngle(), start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis ); #ifdef _DEBUG // test for missed collisions if ( cm_debugCollision.GetBool() ) { if ( !entered ) { entered = 1; // if the trm is stuck in the model if ( idCollisionModelManagerLocal::Contents( results->endpos, trm, results->endAxis, -1, model, modelOrigin, modelAxis ) & contentMask ) { trace_t tr; // test where the trm is stuck in the model idCollisionModelManagerLocal::Contents( results->endpos, trm, results->endAxis, -1, model, modelOrigin, modelAxis ); // re-run collision detection to find out where it failed idCollisionModelManagerLocal::Rotation( &tr, start, rotation, trm, trmAxis, contentMask, model, modelOrigin, modelAxis ); } entered = 0; } } #endif }
/* ============ idPush::ClipRotationalPush Try to push other entities by moving the given entity. ============ */ float idPush::ClipRotationalPush( trace_t &results, idEntity *pusher, const int flags, const idMat3 &newAxis, const idRotation &rotation, idClipModel * clipModel ) { int i, listedEntities, res; idEntity *check, *entityList[ MAX_GENTITIES ]; idBounds bounds, pushBounds; idRotation clipRotation; idMat3 clipAxis, oldAxis; trace_t pushResults; bool wasEnabled; float totalMass; totalMass = 0.0f; results.fraction = 1.0f; results.endpos = clipModel->GetOrigin(); results.endAxis = newAxis; memset( &results.c, 0, sizeof( results.c ) ); if ( !rotation.GetAngle() ) { return totalMass; } // get bounds for the whole movement bounds = clipModel->GetBounds(); if ( bounds[0].x >= bounds[1].x ) { return totalMass; } pushBounds.FromBoundsRotation( bounds, clipModel->GetOrigin(), clipModel->GetAxis(), rotation ); wasEnabled = clipModel->GetContents() != 0; // make sure we don't get the pushing clip model in the list clipModel->Disable(); listedEntities = gameLocal.clip.EntitiesTouchingBounds( pushBounds, -1, entityList, MAX_GENTITIES ); // discard entities we cannot or should not push listedEntities = DiscardEntities( entityList, listedEntities, flags, pusher, clipModel->GetRealContents() ); if ( flags & PUSHFL_CLIP ) { // can only clip movement of a trace model assert( clipModel->IsTraceModel() ); // disable to be pushed entities for collision detection for ( i = 0; i < listedEntities; i++ ) { entityList[ i ]->DisableClip( false ); } gameLocal.clip.Rotation( CLIP_DEBUG_PARMS results, clipModel->GetOrigin(), rotation, clipModel, clipModel->GetAxis(), pusher->GetPhysics()->GetClipMask(), NULL ); // enable to be pushed entities for collision detection for ( i = 0; i < listedEntities; i++ ) { entityList[ i ]->EnableClip(); } if ( results.fraction == 0.0f ) { if ( wasEnabled ) { clipModel->Enable(); } return totalMass; } clipRotation = rotation * results.fraction; clipAxis = results.endAxis; } else { clipRotation = rotation; clipAxis = newAxis; } // we have to enable the clip model because we use it during pushing clipModel->Link( gameLocal.clip ); // save pusher old position oldAxis = clipModel->GetAxis(); // try to push all the entities for ( i = 0; i < listedEntities; i++ ) { check = entityList[ i ]; idPhysics *physics = check->GetPhysics(); // disable the entity for collision detection check->DisableClip( false ); res = TryRotatePushEntity( pushResults, check, clipModel, flags, clipAxis, clipRotation ); // enable the entity for collision detection check->EnableClip(); // if the entity is pushed if ( res == PUSH_OK ) { // set the pusher in the rotated position clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), newAxis ); // the entity might be pushed off the ground physics->EvaluateContacts( CLIP_DEBUG_PARMS_ONLY ); // put pusher back in old position clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), oldAxis ); // wake up this object check->ApplyImpulse( clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), vec3_origin ); // add mass of pushed entity totalMass += physics->GetMass(); } // if the entity is not blocking if ( res != PUSH_BLOCKED ) { continue; } // if blocking entities should be crushed if ( flags & PUSHFL_CRUSH ) { check->Damage( clipModel->GetEntity(), clipModel->GetEntity(), vec3_origin, DAMAGE_FOR_NAME( "damage_crush" ), 1.0f, &pushResults ); continue; } // blocked results = pushResults; results.fraction = 0.0f; results.endAxis = clipModel->GetAxis(); results.endpos = clipModel->GetOrigin(); results.c.entityNum = check->entityNumber; results.c.id = 0; if ( !wasEnabled ) { clipModel->Disable(); } return totalMass; } if ( !wasEnabled ) { clipModel->Disable(); } return totalMass; }