int idPush::TryTranslatePushEntity( trace_t &results, idEntity *check, idClipModel *clipModel, const int flags, const idVec3 &newOrigin, const idVec3 &move ) { trace_t trace; idVec3 checkMove; idVec3 oldOrigin; idPhysics *physics; physics = check->GetPhysics(); #ifdef TRANSLATIONAL_PUSH_DEBUG bool startsolid = false; if ( physics->ClipContents( clipModel ) ) { startsolid = true; } #endif results.fraction = 1.0f; results.endpos = newOrigin; results.endAxis = clipModel->GetAxis(); memset( &results.c, 0, sizeof( results.c ) ); // always pushed when standing on the pusher if ( physics->IsGroundClipModel( clipModel->GetEntity()->entityNumber, clipModel->GetId() ) ) { // move the entity colliding with all other entities except the pusher itself ClipEntityTranslation( trace, check, NULL, clipModel, move ); // if there is a collision if ( trace.fraction < 1.0f ) { // vector along which the entity is pushed checkMove = move * trace.fraction; // test if the entity can stay at it's partly pushed position by moving the entity in reverse only colliding with pusher ClipEntityTranslation( results, check, clipModel, NULL, -(move - checkMove) ); // 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 { // vector along which the entity is pushed checkMove = move; } } else { // move entity in reverse only colliding with pusher ClipEntityTranslation( results, check, clipModel, NULL, -move ); // if no collision with the pusher then the entity is not pushed by the pusher if ( results.fraction >= 1.0f ) { return PUSH_NO; } // vector along which the entity is pushed checkMove = move * (1.0f - results.fraction); // move the entity colliding with all other entities except the pusher itself ClipEntityTranslation( trace, check, NULL, clipModel, checkMove ); // if there is a collisions if ( trace.fraction < 1.0f ) { results.c.normal = -results.c.normal; results.c.dist = -results.c.dist; // FIXME: try to push the blocking entity as well ? // FIXME: handle sliding along more than one collision plane ? // FIXME: this code has issues, player pushing box into corner in "maps/mre/aaron/test.map" /* oldOrigin = physics->GetOrigin(); // movement still remaining checkMove *= (1.0f - trace.fraction); // project the movement along the collision plane if ( !checkMove.ProjectAlongPlane( trace.c.normal, 0.1f, 1.001f ) ) { return PUSH_BLOCKED; } checkMove *= 1.001f; // move entity from collision point along the collision plane physics->SetOrigin( trace.endpos ); ClipEntityTranslation( trace, check, NULL, NULL, checkMove ); if ( trace.fraction < 1.0f ) { physics->SetOrigin( oldOrigin ); return PUSH_BLOCKED; } checkMove = trace.endpos - oldOrigin; // move entity in reverse only colliding with pusher physics->SetOrigin( trace.endpos ); ClipEntityTranslation( trace, check, clipModel, NULL, -move ); physics->SetOrigin( oldOrigin ); */ if ( trace.fraction < 1.0f ) { return PUSH_BLOCKED; } } } SaveEntityPosition( check ); // translate the entity physics->Translate( checkMove ); #ifdef TRANSLATIONAL_PUSH_DEBUG // set the pusher in the translated position clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), newOrigin, clipModel->GetAxis() ); if ( physics->ClipContents( clipModel ) ) { if ( !startsolid ) { int bah = 1; } } #endif return PUSH_OK; }
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::ClipTranslationalPush Try to push other entities by translating the given entity. ============ */ float idPush::ClipTranslationalPush( trace_t &results, idEntity *pusher, const int flags, const idVec3 &newOrigin, const idVec3 &translation ) { int i, j, numListedEntities; idEntity *curPusher, *ent, *entityList[ MAX_GENTITIES ]; float fraction; bool groundContact, blocked = false; float totalMass; trace_t trace; idVec3 realTranslation, partialTranslation; totalMass = 0.0f; results.fraction = 1.0f; results.endpos = newOrigin; results.endAxis = pusher->GetPhysics()->GetAxis(); memset( results.c, 0, sizeof( results.c ) ); if ( translation == vec3_origin ) { return totalMass; } // clip against all non-pushable physics objects if ( flags & PUSHFL_CLIP ) { numListedEntities = GetPushableEntitiesForTranslation( pusher, pusher, flags, translation, entityList, MAX_GENTITIES ); // disable pushable entities for collision detection for ( i = 0; i < numListedEntities; i++ ) { entityList[i]->GetPhysics()->DisableClip(); } // clip translation pusher->GetPhysics()->ClipTranslation( results, translation, NULL ); // enable pushable entities for ( i = 0; i < numListedEntities; i++ ) { entityList[i]->GetPhysics()->EnableClip(); } if ( results.fraction == 0.0f ) { return totalMass; } realTranslation = results.fraction * translation; } else { realTranslation = translation; } // 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 = GetPushableEntitiesForTranslation( curPusher, pusher, flags, realTranslation, entityList, MAX_GENTITIES ); for ( j = 0; j < numListedEntities; j++ ) { ent = entityList[ j ]; if ( IsFullyPushed( ent ) ) { continue; } if ( !CanPushEntity( ent, curPusher, pusher, flags ) ) { continue; } if ( ent->GetPhysics()->IsGroundEntity( curPusher->entityNumber ) ) { AddEntityToPushedGroup( ent, 1.0f * fraction, false ); } else if ( ClipTranslationAgainstPusher( trace, ent, curPusher, -fraction * realTranslation ) ) { AddEntityToPushedGroup( ent, ( 1.0f - trace.fraction ) * fraction, groundContact ); } } } // save physics states and disable physics objects for collision detection for ( i = 0; i < pushedGroupSize; i++ ) { SaveEntityPosition( pushedGroup[i].ent ); pushedGroup[i].ent->GetPhysics()->DisableClip(); } // clip all pushed physics objects for ( i = 1; i < pushedGroupSize; i++ ) { partialTranslation = realTranslation * pushedGroup[i].fraction; pushedGroup[i].ent->GetPhysics()->ClipTranslation( trace, partialTranslation, 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()->ClipTranslation( results, realTranslation, NULL ); } else { results.fraction = 0.0f; results.endpos = pusher->GetPhysics()->GetOrigin(); results.endAxis = pusher->GetPhysics()->GetAxis(); } } else { // translate all pushed physics objects for ( i = 1; i < pushedGroupSize; i++ ) { partialTranslation = realTranslation * pushedGroup[i].fraction; pushedGroup[i].ent->GetPhysics()->Translate( partialTranslation ); totalMass += pushedGroup[i].ent->GetPhysics()->GetMass(); } // translate the clip models of the pusher for ( i = 0; i < pusher->GetPhysics()->GetNumClipModels(); i++ ) { pusher->GetPhysics()->GetClipModel(i)->Translate( results.fraction * realTranslation ); pusher->GetPhysics()->GetClipModel(i)->Link( gameLocal.clip ); } } return totalMass; }
/* ============ 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; }
int idPush::TryTranslatePushEntity( trace_t &results, idEntity *check, idClipModel *clipModel, const int flags, const idVec3 &newOrigin, const idVec3 &move ) { trace_t trace; idVec3 checkMove; idVec3 oldOrigin; idPhysics* physics = check->GetPhysics(); #ifdef TRANSLATIONAL_PUSH_DEBUG bool startsolid = false; if ( physics->ClipContents( clipModel ) ) { startsolid = true; } #endif results.fraction = 1.0f; results.endpos = newOrigin; results.endAxis = clipModel->GetAxis(); memset( &results.c, 0, sizeof( results.c ) ); // always pushed when standing on the pusher if ( physics->IsGroundClipModel( clipModel->GetEntity()->entityNumber, clipModel->GetId() ) ) { // move the entity colliding with all other entities except the pusher itself // grayman #3029 - if this is an AI with attachments, we have to make them // temporarily non-solid for the translation check. Though weapons don't // impede movement, pauldrons do. if ( check->IsType(idAI::Type) ) { idAI* checkAI = static_cast<idAI*>(check); checkAI->SaveAttachmentContents(); checkAI->SetAttachmentContents(0); ClipEntityTranslation( trace, check, NULL, clipModel, move ); checkAI->RestoreAttachmentContents(); } else { ClipEntityTranslation( trace, check, NULL, clipModel, move ); } // if there is a collision if ( trace.fraction < 1.0f ) { // vector along which the entity is pushed checkMove = move * trace.fraction; // test if the entity can stay at it's partly pushed position by moving the entity in reverse only colliding with pusher ClipEntityTranslation( results, check, clipModel, NULL, -(move - checkMove) ); // 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 { // vector along which the entity is pushed checkMove = move; } } else { // move entity in reverse only colliding with pusher ClipEntityTranslation( results, check, clipModel, NULL, -move ); // if no collision with the pusher then the entity is not pushed by the pusher if ( results.fraction >= 1.0f ) { return PUSH_NO; } // greebo: At this point, the pushes knows that the check entity is in the way // Normally, the pusher tries to rotate the entity to see if the entity itself // is colliding with anything else, but for players, we want to (optionally) skip that. if ((flags & PUSHFL_NOPLAYER) && check->IsType(idPlayer::Type)) { // We are colliding with a player and are not allowed to push it, return BLOCKED results.c.normal = -results.c.normal; results.c.dist = -results.c.dist; return PUSH_BLOCKED; } // vector along which the entity is pushed checkMove = move * (1.0f - results.fraction); // move the entity colliding with all other entities except the pusher itself ClipEntityTranslation( trace, check, NULL, clipModel, checkMove ); // if there is a collisions if ( trace.fraction < 1.0f ) { results.c.normal = -results.c.normal; results.c.dist = -results.c.dist; // FIXME: try to push the blocking entity as well ? // FIXME: handle sliding along more than one collision plane ? // FIXME: this code has issues, player pushing box into corner in "maps/mre/aaron/test.map" /* oldOrigin = physics->GetOrigin(); // movement still remaining checkMove *= (1.0f - trace.fraction); // project the movement along the collision plane if ( !checkMove.ProjectAlongPlane( trace.c.normal, 0.1f, 1.001f ) ) { return PUSH_BLOCKED; } checkMove *= 1.001f; // move entity from collision point along the collision plane physics->SetOrigin( trace.endpos ); ClipEntityTranslation( trace, check, NULL, NULL, checkMove ); if ( trace.fraction < 1.0f ) { physics->SetOrigin( oldOrigin ); return PUSH_BLOCKED; } checkMove = trace.endpos - oldOrigin; // move entity in reverse only colliding with pusher physics->SetOrigin( trace.endpos ); ClipEntityTranslation( trace, check, clipModel, NULL, -move ); physics->SetOrigin( oldOrigin ); */ if ( trace.fraction < 1.0f ) { return PUSH_BLOCKED; } } } SaveEntityPosition( check ); // translate the entity physics->Translate( checkMove ); #ifdef TRANSLATIONAL_PUSH_DEBUG // set the pusher in the translated position clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), newOrigin, clipModel->GetAxis() ); if ( physics->ClipContents( clipModel ) ) { if ( !startsolid ) { int bah = 1; } } #endif return PUSH_OK; }