////////////////////////////////////////////////////////////////////// // // Living Entity // // File: livingentity.cpp // Description : CLivingEntity class implementation // // History: // -:Created by Anton Knyazev // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "bvtree.h" #include "geometry.h" #include "singleboxtree.h" #include "cylindergeom.h" #include "spheregeom.h" #include "raybv.h" #include "raygeom.h" #include "rigidbody.h" #include "physicalplaceholder.h" #include "physicalentity.h" #include "geoman.h" #include "physicalworld.h" #include "livingentity.h" inline Vec3_tpl EncodeVec6b(const vectorf &vec) { return Vec3_tpl( float2int(min_safe(100.0f,max_safe(-100.0f,vec.x))*(32767/100.0f)), float2int(min_safe(100.0f,max_safe(-100.0f,vec.y))*(32767/100.0f)), float2int(min_safe(100.0f,max_safe(-100.0f,vec.z))*(32767/100.0f))); } inline vectorf DecodeVec6b(const Vec3_tpl &vec) { return vectorf(vec.x,vec.y,vec.z)*(100.0f/32767); } struct vec4b { short yaw; char pitch; unsigned char len; }; inline vec4b EncodeVec4b(const vectorf &vec) { vec4b res = { 0,0,0 }; if (vec.len2()>sqr(1.0f/512)) { float len = vec.len(); res.yaw = float2int(atan2_tpl(vec.y,vec.x)*(32767/pi)); res.pitch = float2int(asin_tpl(vec.z/len)*(127/pi)); res.len = float2int(min(20.0f,len)*(255/20.0f)); } return res; } inline vectorf DecodeVec4b(const vec4b &vec) { float yaw=vec.yaw*(pi/32767), pitch=vec.pitch*(pi/127), len=vec.len*(20.0f/255); float t = cos_tpl(pitch); return vectorf(t*cos_tpl(yaw),t*sin_tpl(yaw),sin_tpl(pitch))*len; } CLivingEntity::CLivingEntity(CPhysicalWorld *pWorld) : CPhysicalEntity(pWorld) { m_vel.zero(); m_velRequested.zero(); if (pWorld) m_gravity = pWorld->m_vars.gravity; else m_gravity.Set(0,0,-9.81f); m_bFlying = 1; m_bJumpRequested = 0; m_iSimClass = 3; m_dh=m_dhSpeed=m_dhAcc=0; m_stablehTime=1; m_hLatest=0; m_timeFlying=m_minFlyTime = 0; m_nslope.Set(0,0,1); m_mass = 80; m_massinv = 2.0f/80; m_surface_idx = 0; m_slopeSlide = cos_tpl(pi*0.2f); m_slopeJump = m_slopeClimb = cos_tpl(pi*0.3f); m_slopeFall = cos_tpl(pi*0.39f); m_nodSpeed = 60.0f; m_size.Set(0.4f,0.4f,0.6f); m_hCyl = 1.1f; m_hEye = 1.7f; m_hPivot = 0; cylinder cyl; cyl.axis.Set(0,0,1); cyl.center.zero(); cyl.hh = m_size.z; cyl.r = m_size.x; m_CylinderGeom.CreateCylinder(&cyl); m_CylinderGeom.CalcPhysicalProperties(&m_CylinderGeomPhys); m_CylinderGeomPhys.nRefCount = -1; sphere sph; sph.center.Set(0,0,-m_size.z); sph.r = m_size.x; m_SphereGeom.CreateSphere(&sph); sph.center.zero(); sph.r = 0.2f; m_HeadGeom.CreateSphere(&sph); m_hHead = 1.9f; m_parts[0].pPhysGeom = m_parts[0].pPhysGeomProxy = &m_CylinderGeomPhys; m_parts[0].id = 100; m_parts[0].pos.Set(0,0,m_hCyl-m_hPivot); m_parts[0].q.SetIdentity(); m_parts[0].scale = 1.0f; m_parts[0].mass = m_mass; m_parts[0].surface_idx = m_surface_idx; m_parts[0].flags = geom_collides; m_parts[0].flagsCollider = geom_colltype_player; m_parts[0].minContactDist = m_size.z*0.1; m_nParts = 1; m_kInertia = 8; m_kAirControl = 0.1f; m_kAirResistance = 0; m_bSwimming = 0; m_nSensors = 0; m_pSensors = 0; m_pSensorsPoints = 0; m_pSensorsSlopes = 0; m_iSensorsActive = 0; m_pLastGroundCollider = 0; m_iHist=0; int i; m_history = m_history_buf; m_szHistory = sizeof(m_history_buf)/sizeof(m_history_buf[0]); m_actions = m_actions_buf; m_szActions = sizeof(m_actions_buf)/sizeof(m_actions_buf[0]); for(i=0;iRelease(); m_pLastGroundCollider = 0; } } void CLivingEntity::SetGroundCollider(CPhysicalEntity *pCollider) { ReleaseGroundCollider(); if (pCollider && pCollider->m_iSimClass>0) { pCollider->AddRef(); m_pLastGroundCollider = pCollider; } } int CLivingEntity::Awake(int bAwake,int iSource) { if (m_pLastGroundCollider && m_pLastGroundCollider->m_iSimClass==7) ReleaseGroundCollider(); m_bActiveEnvironment = bAwake; return 1; } int CLivingEntity::IsPositionFree(const vectorf *BBox,float newh,const vectorf &newdim) { int i,j,nents; geom_world_data gwd[2]; intersection_params ip; CPhysicalEntity **pentlist; geom_contact *pcontacts; nents = m_pWorld->GetEntitiesAround(BBox[0],BBox[1], pentlist, ent_terrain|ent_static|ent_sleeping_rigid|ent_rigid|ent_living); //m_qrot.getmatrix(gwd[0].R); //gwd[0].R.identity(); //Q2M_IVO gwd[0].R = matrix3x3f(m_qrot); gwd[0].offset = m_pos + gwd[0].R*vectorf(0,0,newh); //(gwd[0].offset = m_pos).z += params->heightCollider-params->heightPivot; ip.vrel_min = 1E10; for(i=0;iGetType()!=PE_LIVING) { for(j=0;jm_nParts;j++) if (pentlist[i]->m_parts[j].flags & geom_colltype_player) { //(pentlist[i]->m_qrot*pentlist[i]->m_parts[j].q).getmatrix(gwd[1].R); //Q2M_IVO gwd[1].R = matrix3x3f(pentlist[i]->m_qrot*pentlist[i]->m_parts[j].q); gwd[1].offset = pentlist[i]->m_pos + pentlist[i]->m_qrot*pentlist[i]->m_parts[j].pos; gwd[1].scale = pentlist[i]->m_parts[j].scale; if (m_CylinderGeom.Intersect(pentlist[i]->m_parts[j].pPhysGeomProxy->pGeom, gwd,gwd+1, &ip, pcontacts)) return 0; } } else { CLivingEntity *pent = (CLivingEntity*)pentlist[i]; if (fabs_tpl((m_pos.z+newh)-(pent->m_pos.z+pent->m_hCyl-pent->m_hPivot)) < newdim.z+pent->m_size.z && (vector2df(m_pos)-vector2df(pent->m_pos)).len2() < sqr(newdim.x+pent->m_size.x)) return 0; } } return 1; } int CLivingEntity::SetParams(pe_params *_params) { int res; vectorf prevpos=m_pos; quaternionf prevq = m_qrot; if (res = CPhysicalEntity::SetParams(_params)) { if (_params->type==pe_params_pos::type_id) { if ((prevpos-m_pos).len2()>sqr(m_size.z*0.01)) { ReleaseGroundCollider(); m_bFlying = 1; m_vel.zero(); m_dh=0; for(int i=0;ibRecalcBounds && m_bActive && !IsPositionFree(m_BBox,m_hCyl-m_hPivot,m_size)) { m_qrot = prevq; ComputeBBox(); return 0; } /*if (m_qrot.s!=prevq.s || m_qrot.v!=prevq.v) { vectorf angles; m_qrot.get_Euler_angles_xyz(angles); m_qrot = quaternionf(angles.z,vectorf(0,0,1)); }*/ //m_BBox[0].z = min(m_BBox[0].z,m_pos.z-m_hPivot); } return res; } if (_params->type==pe_player_dimensions::type_id) { pe_player_dimensions *params = (pe_player_dimensions*)_params; ENTITY_VALIDATE("CLivingEntity:SetParams(player_dimensions)",params); if (is_unused(params->sizeCollider)) params->sizeCollider = m_size; if (is_unused(params->heightCollider)) params->heightCollider = m_hCyl; if (is_unused(params->heightPivot)) params->heightPivot = m_hPivot; if ((params->heightCollider!=m_hCyl || params->sizeCollider.x!=m_size.x || params->sizeCollider.z!=m_size.z) && m_pWorld) { cylinder newdim,prevdim = m_CylinderGeom.m_cyl; newdim.r = params->sizeCollider.x; newdim.hh = params->sizeCollider.z; newdim.center.zero(); newdim.axis.Set(0,0,1); m_CylinderGeom.CreateCylinder(&newdim); vectorf BBox[2]; BBox[0].Set(-params->sizeCollider.x,-params->sizeCollider.x,-params->heightPivot) += m_pos; BBox[1].Set(params->sizeCollider.x,params->sizeCollider.x,params->sizeCollider.z+params->heightCollider-params->heightPivot) += m_pos; if (m_bActive && !IsPositionFree(BBox,params->heightCollider-params->heightPivot,params->sizeCollider)) { m_CylinderGeom.CreateCylinder(&prevdim); return 0; } m_hCyl = params->heightCollider; m_parts[0].pos.Set(0,0,m_hCyl-m_hPivot); m_size.Set(params->sizeCollider.x, params->sizeCollider.x, params->sizeCollider.z); m_CylinderGeom.m_Tree.m_Box.size = m_size; sphere sph; sph.center.Set(0,0,-m_size.z); sph.r = m_size.x; m_SphereGeom.CreateSphere(&sph); m_SphereGeom.m_Tree.m_Box.center = sph.center; m_SphereGeom.m_Tree.m_Box.size = vectorf(sph.r,sph.r,sph.r); } if (!is_unused(params->heightEye)) { if (m_hEye!=params->heightEye) { m_dh += params->heightEye-m_hEye; m_dhSpeed = 4*(params->heightEye-m_hEye); } m_hEye = params->heightEye; m_timeSinceStanceChange = 0; } if (!is_unused(params->heightHead)) m_hHead = params->heightHead; if (!is_unused(params->headRadius)) { sphere sph; sph.center.zero(); sph.r = params->headRadius; m_HeadGeom.CreateSphere(&sph); } m_BBox[0].z = min(m_BBox[0].z,m_pos.z-m_hPivot); if (m_HeadGeom.m_sphere.r>0) m_BBox[1].z = max(m_BBox[1].z,m_pos.z-m_hPivot+m_hHead+m_HeadGeom.m_sphere.r); return 1; } if (_params->type==pe_player_dynamics::type_id) { pe_player_dynamics *params = (pe_player_dynamics*)_params; if (!is_unused(params->kInertia)) m_kInertia = params->kInertia; if (!is_unused(params->kAirControl)) m_kAirControl = params->kAirControl; if (!is_unused(params->kAirResistance)) m_kAirResistance = params->kAirResistance; if (!is_unused(params->gravity)) m_gravity.Set(0,0,-params->gravity); if (!is_unused(params->nodSpeed)) m_nodSpeed = params->nodSpeed; if (!is_unused(params->bSwimming)) m_bSwimming = params->bSwimming; if (!is_unused(params->mass)) { m_mass = params->mass; m_massinv = 2.0f/m_mass; } if (!is_unused(params->surface_idx)) m_surface_idx = m_parts[0].surface_idx = params->surface_idx; if (!is_unused(params->minSlideAngle)) m_slopeSlide = cos_tpl(params->minSlideAngle*(pi/180.0f)); if (!is_unused(params->maxClimbAngle)) m_slopeClimb = cos_tpl(params->maxClimbAngle*(pi/180.0f)); if (!is_unused(params->maxJumpAngle)) m_slopeJump = cos_tpl(params->maxJumpAngle*(pi/180.0f)); if (!is_unused(params->minFallAngle)) m_slopeFall = cos_tpl(params->minFallAngle*(pi/180.0f)); if (!is_unused(params->maxVelGround)) m_maxVelGround = params->maxVelGround; if (!is_unused(params->bActive)) { if (m_bActive+params->bActive*2==2) m_bFlying = 1; m_bActive = params->bActive; } if (!is_unused(params->bNetwork) && params->bNetwork) AllocateExtendedHistory(); return 1; } if (_params->type==pe_params_sensors::type_id) { pe_params_sensors *params = (pe_params_sensors*)_params; if (m_nSensors!=params->nSensors) m_bActiveEnvironment = 1; if (m_pSensors && m_nSensors!=params->nSensors) { delete[] m_pSensors; m_pSensors=0; delete[] m_pSensorsPoints; m_pSensorsPoints=0; delete[] m_pSensorsSlopes; m_pSensorsSlopes=0; } m_nSensors = params->nSensors; if (!m_pSensors && m_nSensors>0) { memset(m_pSensors = new vectorf[m_nSensors],0,m_nSensors*sizeof(vectorf)); m_pSensorsPoints = new vectorf[m_nSensors]; m_pSensorsSlopes = new vectorf[m_nSensors]; } for(int i=0; ipOrigins[i]).len2()>sqr(0.01f)) { m_bActiveEnvironment = 1; (m_pSensorsPoints[i] = m_pos+m_qrot*m_pSensors[i]).z = m_pos.z; m_pSensorsSlopes[i].Set(0,0,1); m_iSensorsActive &= ~(1<pOrigins[i]; } return 1; } return 0; } int CLivingEntity::GetParams(pe_params *_params) { int res; if (res = CPhysicalEntity::GetParams(_params)) return res; if (_params->type==pe_player_dimensions::type_id) { pe_player_dimensions *params = (pe_player_dimensions*)_params; params->heightPivot = m_hPivot; params->sizeCollider = m_size; params->heightCollider = m_hCyl; params->heightEye = m_hEye; params->headRadius = m_HeadGeom.m_sphere.r; params->heightHead = m_hHead; return 1; } if (_params->type==pe_player_dynamics::type_id) { pe_player_dynamics *params = (pe_player_dynamics*)_params; params->kInertia = m_kInertia; params->kAirControl = m_kAirControl; params->gravity = -m_gravity.z; params->nodSpeed = m_nodSpeed; params->bSwimming = m_bSwimming; params->mass = m_mass; params->surface_idx = m_surface_idx; params->minSlideAngle = cry_acosf(m_slopeSlide)*(180.0f/pi); params->maxClimbAngle = cry_acosf(m_slopeClimb)*(180.0f/pi); params->maxJumpAngle = cry_acosf(m_slopeJump)*(180.0f/pi); params->minFallAngle = cry_acosf(m_slopeFall)*(180.0f/pi); params->maxVelGround = m_maxVelGround; params->bActive = m_bActive; params->bNetwork = m_szHistory==SZ_HISTORY; return 1; } if (_params->type==pe_params_sensors::type_id) { pe_params_sensors *params = (pe_params_sensors*)_params; if (m_pSensors && m_nSensors!=params->nSensors) { delete[] m_pSensors; m_pSensors=0; delete[] m_pSensorsPoints; m_pSensorsPoints=0; delete[] m_pSensorsSlopes; m_pSensorsSlopes=0; } m_nSensors = params->nSensors; if (!m_pSensors && m_nSensors>0) { m_pSensors = new vectorf[m_nSensors]; m_pSensorsPoints = new vectorf[m_nSensors]; m_pSensorsSlopes = new vectorf[m_nSensors]; } for(int i=0; ipOrigins[i]; return 1; } return 0; } void CLivingEntity::AllocateExtendedHistory() { int i; if (m_history==m_history_buf) { m_history = new le_history_item[SZ_HISTORY]; for(i=0;itype==pe_status_pos::type_id) { vectorf prevPos = m_pos; if (m_bActive) m_pos += m_deltaPos*(m_timeSmooth*(1/0.3f)); int res = CPhysicalEntity::GetStatus(_status); m_pos = prevPos; return res; } if (CPhysicalEntity::GetStatus(_status)) return 1; if (_status->type==pe_status_living::type_id) { pe_status_living *status = (pe_status_living*)_status; status->bFlying = m_bFlying & m_bActive; status->timeFlying = m_timeFlying; status->camOffset = m_qrot*vectorf(0,0,-m_hPivot+m_hEye-m_dh); int idx,len; float dt; for(idx=m_iHist,len=0,dt=0; len<3 && m_history[idx-1&m_szHistory-1].dt<1E9; idx=idx-1&m_szHistory-1,len++) dt += m_history[idx].dt; if (idx==m_iHist || (status->vel = (m_history[m_iHist].pos-m_history[idx].pos)/dt).len2()>sqr(6.0f)) { status->vel = m_vel; if (m_pLastGroundCollider) status->vel += m_velGround; } status->velUnconstrained = m_vel; status->velRequested = m_velRequested; status->velGround = m_velGround; status->groundHeight = m_hLatest; status->groundSlope = m_nslope; status->groundSurfaceIdx = m_lastGroundSurfaceIdx; status->pGroundCollider = m_pLastGroundCollider; status->iGroundColliderPart = m_iLastGroundColliderPart; status->timeSinceStanceChange = m_timeSinceStanceChange; status->bOnStairs = isneg(0.1f-m_timeOnStairs); return 1; } else if (_status->type==pe_status_sensors::type_id) { pe_status_sensors *status = (pe_status_sensors*)_status; status->pPoints = m_pSensorsPoints; status->pNormals = m_pSensorsSlopes; status->flags = m_iSensorsActive; return m_nSensors; } if (_status->type==pe_status_dynamics::type_id) { pe_status_dynamics *status = (pe_status_dynamics*)_status; status->v = m_vel; if (m_pLastGroundCollider) status->v += m_velGround; float rdt = 1.0f/m_history[m_iHist].dt; status->a = (m_history[m_iHist].v-m_history[m_iHist-1&m_szHistory-1].v)*rdt; status->w = (m_history[m_iHist].q.v-m_history[m_iHist-1&m_szHistory-1].q.v)*2*rdt; status->centerOfMass = (m_BBox[0]+m_BBox[1])*0.5f; status->mass = m_mass; /*for(idx=idx-1&m_szHistory-1; dttime_interval; idx=idx-1&m_szHistory-1) { rdt = 1.0f/m_history[idx].dt; dt += m_history[idx].dt; status->a += (m_history[idx].v-m_history[idx-1&m_szHistory-1].v)*rdt; status->w += (m_history[idx].q.v-m_history[idx-1&m_szHistory-1].q.v)*2*rdt; status->v += m_history[idx].v; } if (dt>0) { rdt = 1.0f/dt; status->v *= dt; status->a *= dt; status->w *= dt; }*/ return 1; } if (_status->type==pe_status_timeslices::type_id) { pe_status_timeslices *status = (pe_status_timeslices*)_status; if (!status->pTimeSlices) return 0; int i,nSlices=0; float dt; if (is_unused(status->time_interval)) { if (m_actions[m_iAction].dt>1E9) return 0; status->time_interval = m_actions[m_iAction].dt; } for(i=m_iHist,dt=status->time_interval; dt>status->precision; dt-=m_history[i].dt,i=i-1&m_szHistory-1); for(i=i+1&m_szHistory-1; i!=(m_iHist+1&m_szHistory-1) && nSlicessz; i=i+1&m_szHistory-1) status->pTimeSlices[nSlices++] = m_history[i].dt; return nSlices; } if (_status->type==pe_status_collisions::type_id) { pe_status_collisions *status = (pe_status_collisions*)_status; if (m_collision.ageage) { status->pHistory[0] = m_collision; if (status->bClearHistory) m_collision.age = 1E10f; return 1; } return 0; } return 0; } int CLivingEntity::Action(pe_action* _action) { int res; if (res = CPhysicalEntity::Action(_action)) return res; if (_action->type==pe_action_move::type_id) { if (!m_bIgnoreCommands) { pe_action_move *action = (pe_action_move*)_action; bool bForceHistory = false; ENTITY_VALIDATE("CLivingEntity:Action(action_move)",action); if (!is_unused(action->dir)) { m_velRequested.zero(); if (m_kAirControl==1) { m_velRequested = m_vel = action->dir; m_bJumpRequested = action->iJump; } else if (!action->iJump) { m_velRequested = action->dir; //if (m_bFlying && m_vel.len2()>0 && !m_bSwimming && !m_pWorld->m_vars.bFlyMode) // m_velRequested -= m_vel*((action->dir*m_vel)/m_vel.len2()); } else { if (action->iJump==2) { m_vel += action->dir; bForceHistory = true; if (action->dir.z>1.0f) { m_minFlyTime=0.2f; m_timeFlying=0; m_bJumpRequested = 1; } } else if (m_bFlying) { if (m_pWorld->m_vars.bFlyMode) m_velRequested.Set(0,0,2); } else if (m_bStuck || !(m_nslope.zdir)).z<0)) { m_vel = action->dir; bForceHistory = true; if (!m_bStuck && m_nslope.ztype==pe_action_impulse::type_id) { pe_action_impulse *action = (pe_action_impulse*)_action; ENTITY_VALIDATE("CLivingEntity:Action(action_impulse)",action); vectorf impulse = action->impulse; if (action->iSource==2) // explosion impulse *= 0.3f; if (action->iApplyTime==0) { //if (!m_bFlying) m_velRequested.zero(); m_vel += impulse*(m_massinv*0.5f); if (m_flags & lef_snap_velocities) m_vel = DecodeVec6b(EncodeVec6b(m_vel)); if (impulse.z*m_massinv*0.5f>1.0f) { m_bFlying=1; m_minFlyTime=0.2f; m_timeFlying=0; } } else { pe_action_move am; am.dir = impulse*(m_massinv*0.5f); am.iJump = 2; Action(&am); } /*if (!m_bFlying && action->impulse.z<0 && m_dhimpulse.z*m_massinv; m_dhAcc = max(sqr(m_dhSpeed)/(m_size.z*2), m_nodSpeed*0.15f); }*/ if (m_kInertia==0) m_timeForceInertia = 5.0f; return 1; } if (_action->type==pe_action_reset::type_id) { m_vel.zero(); m_velRequested.zero(); m_dh = m_dhSpeed = m_stablehTime = 0; m_bFlying = 1; return 1; } return 0; } int CLivingEntity::GetStateSnapshot(CStream &stm,float time_back,int flags) { vectorf pos_prev=m_pos, vel_prev=m_vel, nslope_prev=m_nslope; int bFlying_prev=m_bFlying; float timeFlying_prev=m_timeFlying, minFlyTime_prev=m_minFlyTime, timeUseLowCap_prev=m_timeUseLowCap; quaternionf qrot_prev=m_qrot; CPhysicalEntity *pLastGroundCollider=m_pLastGroundCollider; int iLastGroundColliderPart=m_iLastGroundColliderPart; vectorf posLastGroundColl=m_posLastGroundColl; ReleaseGroundCollider(); if (time_back>0) { AllocateExtendedHistory(); StepBackEx(time_back,false); } stm.WriteNumberInBits(SNAPSHOT_VERSION,4); if (m_pWorld->m_vars.bMultiplayer) { WriteCompressedPos(stm,m_pos,(m_id+m_iSnapshot&31)!=0); if (m_pWorld->m_iTimePhysics!=m_iTimeLastSend) { m_iSnapshot++; m_iTimeLastSend = m_pWorld->m_iTimePhysics; } } else stm.Write(m_pos); if (m_flags & lef_snap_velocities) { if (m_vel.len2()>0) { stm.Write(true); Vec3_tpl v = EncodeVec6b(m_vel); stm.Write(v.x); stm.Write(v.y); stm.Write(v.z); } else stm.Write(false); if (m_velRequested.len2()>0) { stm.Write(true); vec4b vr = EncodeVec4b(m_velRequested); stm.Write(vr.yaw); stm.Write(vr.pitch); stm.Write(vr.len); } else stm.Write(false); } else { if (m_vel.len2()>0) { stm.Write(true); stm.Write(m_vel); } else stm.Write(false); if (m_velRequested.len2()>0) { stm.Write(true); stm.Write(m_velRequested); } else stm.Write(false); } if (m_timeFlying>0) { stm.Write(true); stm.Write((unsigned short)float2int(m_timeFlying*6553.6f)); } else stm.Write(false); unsigned int imft = isneg(0.1f-m_minFlyTime)+isneg(0.35f-m_minFlyTime)+isneg(0.75f-m_minFlyTime); // snap to 0..0.2..0.5..1 stm.WriteNumberInBits(imft,2); stm.Write((bool)(m_bFlying!=0)); /*if (m_pLastGroundCollider) { stm.Write(true); WritePacked(stm, m_pWorld->GetPhysicalEntityId(m_pLastGroundCollider)+1); WritePacked(stm, m_iLastGroundColliderPart); stm.Write((Vec3&)m_posLastGroundColl); } else stm.Write(false);*/ m_pos=pos_prev; m_vel=vel_prev; m_bFlying=bFlying_prev; m_qrot=qrot_prev; m_timeFlying=timeFlying_prev; m_minFlyTime=minFlyTime_prev; m_timeUseLowCap = timeUseLowCap_prev; m_nslope=nslope_prev; SetGroundCollider(pLastGroundCollider); m_iLastGroundColliderPart=iLastGroundColliderPart; m_posLastGroundColl=posLastGroundColl; return 1; } int CLivingEntity::SetStateFromSnapshot(CStream &stm, int flags) { bool bnz,bCompressed; unsigned short tmp; int ver=0; stm.ReadNumberInBits(ver,4); if (ver!=SNAPSHOT_VERSION) return 0; if (flags & ssf_no_update) { vectorf dummy; if (m_pWorld->m_vars.bMultiplayer) ReadCompressedPos(stm,dummy,bCompressed); else stm.Seek(stm.GetReadPos()+sizeof(vectorf)*8); stm.Read(bnz); if (bnz) stm.Seek(stm.GetReadPos()+((m_flags & lef_snap_velocities) ? 6:sizeof(vectorf))*8); stm.Read(bnz); if (bnz) stm.Seek(stm.GetReadPos()+((m_flags & lef_snap_velocities) ? 4:sizeof(vectorf))*8); stm.Read(bnz); if (bnz) stm.Seek(stm.GetReadPos()+sizeof(tmp)*8); stm.Seek(stm.GetReadPos()+3); return 1; } m_posLocal = m_pos + m_deltaPos*(m_timeSmooth*(1/0.3f)); AllocateExtendedHistory(); if (flags & ssf_compensate_time_diff) StepBackEx((m_pWorld->m_iTimePhysics-m_pWorld->m_iTimeSnapshot[0])*m_pWorld->m_vars.timeGranularity,false); else StepBackEx((m_pWorld->m_iTimePhysics-m_pWorld->m_iTimeSnapshot[1])*m_pWorld->m_vars.timeGranularity,true); vectorf pos0=m_pos,vel0=m_vel,velRequested0=m_velRequested; int bFlying0=m_bFlying; if (m_pWorld->m_vars.bMultiplayer) { ReadCompressedPos(stm,m_pos,bCompressed); // if we received compressed pos, and our pos0 compressed is equal to it, assume the server had pos equal to our uncompressed pos0 if (bCompressed && (CompressPos(pos0)-m_pos).len2() v; stm.Read(v.x); stm.Read(v.y); stm.Read(v.z); m_vel = DecodeVec6b(v); } else m_vel.zero(); stm.Read(bnz); if (bnz) { vec4b vr; stm.Read(vr.yaw); stm.Read(vr.pitch); stm.Read(vr.len); m_velRequested = DecodeVec4b(vr); } else m_velRequested.zero(); } else { stm.Read(bnz); if (bnz) stm.Read(m_vel); else m_vel.zero(); stm.Read(bnz); if (bnz) stm.Read(m_velRequested); else m_velRequested.zero(); } stm.Read(bnz); if (bnz) { stm.Read(tmp); m_timeFlying = tmp*(10.0f/65536); } else m_timeFlying = 0; unsigned int imft; stm.ReadNumberInBits(imft,2); static float flytable[] = {0.0f, 0.2f, 0.5f, 1.0f}; m_minFlyTime = flytable[imft]; stm.Read(bnz); m_bFlying = bnz ? 1:0; ReleaseGroundCollider(); m_bJumpRequested = 0; /*stm.Read(bnz); if (bnz) { SetGroundCollider((CPhysicalEntity*)m_pWorld->GetPhysicalEntityById(ReadPackedInt(stm)-1)); m_iLastGroundColliderPart = ReadPackedInt(stm); stm.Read((Vec3&)m_posLastGroundColl); }*/ #ifdef _DEBUG float diff = (m_pos-pos0).len2(); if (diff>sqr(0.0001f) && flags&ssf_compensate_time_diff && m_bActive) m_pWorld->m_pLog->Log("\002Local client desync: %.4f",sqrt_tpl(diff)); #endif if (m_pos.len2()>1E18) { m_pos = pos0; return 1; } float dt_state = (m_pWorld->m_iTimePhysics-m_pWorld->m_iTimeSnapshot[0])*m_pWorld->m_vars.timeGranularity; if (flags & ssf_compensate_time_diff && dt_state>0 && dt_state<3.0f && m_bActive) { #ifdef _DEBUG int iaction0=m_iAction,ihist0=m_iHist; #endif int iaction,nActions,nSteps,bChange; float dt,dt_action; for(dt_action=dt_state,nActions=0; dt_action>0.00001f && nActions0.00001f && nSteps0; nActions--) { iaction = m_iAction+1&m_szActions-1; if (fabsf(dt_action)<0.00001f) dt_action = 0; dt_action += m_actions[iaction].dt; m_actions[iaction].dt = 0; Action(m_actions+iaction); bChange = 1; } for(; dt_action>=0.00001f && nSteps>0; nSteps--) { if (fabsf(dt)<0.00001f) dt = 0; dt += m_history[m_iHist+1&m_szHistory-1].dt; Step(dt); dt_action -= dt; dt = 0; bChange = 1; } } while (nActions+nSteps>0 && bChange); m_bStateReading = 0; } return 1; } float CLivingEntity::ShootRayDown(CPhysicalEntity **pentlist,int nents, const vectorf &pos,vectorf &nslope, float time_interval, bool bUseRotation,bool bUpdateGroundCollider,bool bIgnoreSmallObjects) { int i,j,ibest=-1,jbest,ncont,idbest; //matrix3x3f R; m_qrot.getmatrix(R); //Q2M_IVO matrix3x3f R; box bbox; if (bUseRotation) R = matrix3x3f(m_qrot); else R.SetIdentity(); CRayGeom aray; aray.CreateRay(pos+R*vectorf(0,0,m_hCyl-m_hPivot),R*vectorf(0,0,-m_hCyl-m_size.x),&(R*vectorf(0,0,-1))); geom_world_data gwd; geom_contact *pcontacts; CPhysicalEntity *pPrevCollider=m_pLastGroundCollider; float h=-1E10,maxdim,maxarea; nslope = m_nslope; for(i=nents-1;i>=0;i--) if (pentlist[i]!=this) for(j=0;jm_nParts;j++) if (pentlist[i]->m_parts[j].flags & geom_colltype_player) { if (bIgnoreSmallObjects && (ibest>=0 || pentlist[i]->GetRigidBody(j)->v.len2()>sqr(m_maxVelGround*0.2f))) { pentlist[i]->m_parts[j].pPhysGeomProxy->pGeom->GetBBox(&bbox); maxarea = max(max(bbox.size.x*bbox.size.y, bbox.size.x*bbox.size.z), bbox.size.y*bbox.size.z)*sqr(pentlist[i]->m_parts[j].scale)*4; maxdim = max(max(bbox.size.x,bbox.size.y),bbox.size.z)*pentlist[i]->m_parts[j].scale*2; if (maxaream_qrot*pentlist[i]->m_parts[j].q).getmatrix(gwd.R); //Q2M_IVO gwd.R = matrix3x3f(pentlist[i]->m_qrot*pentlist[i]->m_parts[j].q); gwd.offset = pentlist[i]->m_pos + pentlist[i]->m_qrot*pentlist[i]->m_parts[j].pos; gwd.scale = pentlist[i]->m_parts[j].scale; if ((ncont=pentlist[i]->m_parts[j].pPhysGeomProxy->pGeom->Intersect(&aray, &gwd,0, 0, pcontacts)) && pcontacts[ncont-1].pt.z>h) { h = pcontacts[ncont-1].pt.z; ibest=i; jbest=j; nslope = pcontacts[ncont-1].n; if (pentlist[i]->m_iSimClass==3) { (nslope = pcontacts[ncont-1].pt-pentlist[i]->m_pos).z = 0; if (nslope.len2()>1E-4f) nslope.normalize(); else nslope.Set(1,0,0); nslope*=0.965925826f; nslope.z=0.2588190451f; // make it act like a 75 degrees slope } idbest = pcontacts[ncont-1].id[0]&~pcontacts[ncont-1].id[0]>>31 | pentlist[i]->m_parts[j].surface_idx&pcontacts[ncont-1].id[0]>>31; } } if (bUpdateGroundCollider) { ReleaseGroundCollider(); m_lastGroundSurfaceIdx = -1; if (ibest>=0) { SetGroundCollider(pentlist[ibest]); m_iLastGroundColliderPart = jbest; vectorf coll_origin = pentlist[ibest]->m_pos+pentlist[ibest]->m_qrot*pentlist[ibest]->m_parts[jbest].pos; quaternionf coll_q = pentlist[ibest]->m_qrot*pentlist[ibest]->m_parts[jbest].q; float coll_scale = pentlist[ibest]->m_parts[jbest].scale; m_posLastGroundColl = ((m_pos-coll_origin)*coll_q)/coll_scale; m_lastGroundSurfaceIdx = idbest; if (pPrevCollider!=pentlist[ibest]) AddLegsImpulse(m_gravity*time_interval+m_vel,nslope,true); else AddLegsImpulse(m_gravity*time_interval,nslope,false); } } return h; } void CLivingEntity::AddLegsImpulse(const vectorf &vel, const vectorf &nslope, bool bInstantChange) { int ncoll; CPhysicalEntity **pColliders,*pPrevCollider=m_pLastGroundCollider; pe_status_sample_contact_area ssca; RigidBody *pbody; pe_action_impulse ai; (ssca.ptTest = m_pos).z -= m_hPivot; ssca.dirTest = m_gravity; if (m_pLastGroundCollider && m_flags&lef_push_objects && (unsigned int)m_pLastGroundCollider->m_iSimClass-1u<2u && m_pLastGroundCollider->m_flags & pef_pushable_by_players && ((pbody=m_pLastGroundCollider->GetRigidBody(m_iLastGroundColliderPart))->MinvGetColliders(pColliders))==0 || (ncoll==1 && pColliders[0]==m_pLastGroundCollider)) && !m_pLastGroundCollider->GetStatus(&ssca)) { vectorf vrel = vel; ai.point = ssca.ptTest; if (bInstantChange) vrel -= pbody->v+(pbody->w^ai.point-pbody->pos); matrix3x3f K; K.SetZero(); m_pLastGroundCollider->GetContactMatrix(ai.point,m_iLastGroundColliderPart,K); K(0,0)+=m_massinv*0.5f; K(1,1)+=m_massinv*0.5f; K(2,2)+=m_massinv*0.5f; ai.impulse = nslope*(min(0.0f,nslope*vrel)/(nslope*K*nslope)); m_pLastGroundCollider->Action(&ai); } } void CLivingEntity::RegisterContact(const vectorf& pt,const vectorf& n, CPhysicalEntity *pCollider, int ipart,int idmat) { RigidBody *pbody = pCollider->GetRigidBody(ipart); vectorf v = pbody->v+(pbody->w^pt-pbody->pos); if (m_collision.age<0.5f && v.len2()Minv*m_collision.mass[1]*m_collision.v[1].len2()) return; m_collision.pt = pt; m_collision.n = n; m_collision.v[0] = m_vel; m_collision.v[1] = v; m_collision.mass[0] = m_mass; m_collision.mass[1] = pbody->M; m_collision.age = 0; m_collision.idCollider = m_pWorld->GetPhysicalEntityId(pCollider); m_collision.partid[0] = 100; m_collision.partid[1] = pCollider->m_parts[ipart].id; m_collision.idmat[0] = m_surface_idx; m_collision.idmat[1] = idmat>>31 & pCollider->m_parts[ipart].surface_idx | idmat & ~(idmat>>31); } void CLivingEntity::RegisterUnprojContact(const le_contact &unproj) { if (m_nContacts==m_nContactsAlloc) { le_contact *pContacts = m_pContacts; m_pContacts = new le_contact[m_nContactsAlloc+=4]; memcpy(m_pContacts, pContacts, m_nContacts*sizeof(le_contact)); if (pContacts) delete[] pContacts; } m_pContacts[m_nContacts++] = unproj; AddCollider(unproj.pent); unproj.pent->AddCollider(this); } float CLivingEntity::GetMaxTimeStep(float time_interval) { if (m_timeStepPerformed > m_timeStepFull-0.001f) return time_interval; return min_safe(m_timeStepFull-m_timeStepPerformed,time_interval); } void CLivingEntity::SyncWithGroundCollider(float time_interval) { int i; vectorf newpos; if (m_pLastGroundCollider && m_pLastGroundCollider->m_iSimClass==7) ReleaseGroundCollider(); m_velGround.zero(); if (m_pLastGroundCollider) { i = m_iLastGroundColliderPart; vectorf coll_origin = m_pLastGroundCollider->m_pos+m_pLastGroundCollider->m_qrot*m_pLastGroundCollider->m_parts[i].pos; quaternionf coll_q = m_pLastGroundCollider->m_qrot*m_pLastGroundCollider->m_parts[i].q; float coll_scale = m_pLastGroundCollider->m_parts[i].scale; newpos = coll_q*m_posLastGroundColl*coll_scale+coll_origin; if ((newpos-m_pos).len2() > sqr(time_interval*0.01f)) { if ((newpos-m_pos).len2() > sqr(time_interval*m_maxVelGround)) newpos = m_pos+(newpos-m_pos).normalized()*(m_maxVelGround*time_interval); m_BBox[0]+=(newpos-m_pos); m_BBox[1]+=(newpos-m_pos); m_pos = newpos; } pe_status_dynamics sd; m_pLastGroundCollider->GetStatus(&sd); m_velGround = sd.v; if (m_velGround.len2()>sqr(m_maxVelGround)) m_velGround.normalize() *= m_maxVelGround; } } void CLivingEntity::StartStep(float time_interval) { m_timeStepPerformed = 0; m_timeStepFull = time_interval; m_nContacts = 0; //SyncWithGroundCollider(time_interval); } int CLivingEntity::Step(float time_interval) { if (time_interval<=0) return 1; if (m_timeStepPerformed>m_timeStepFull-0.001f && m_pWorld->m_vars.bMultiplayer+m_bStateReading==0) time_interval = 0.001f; int i,j,imin,jmin,iter,nents,ncont,bWasFlying,bPushOther,bUnprojected,idmat,bFastPhys,bHasFastPhys,iCyl,icnt,nUnproj,bStaticUnproj,bDynUnproj; vectorf pos0,newpos,move(zero),nslope,ncontact,ptcontact,ncontactHist[4],ncontactSum,BBoxInner[2],BBoxOuter[2],velGround; float movelen,tmin,h,hcur,dh,tfirst,vrel,move0,movesum,kInertia=m_timeForceInertia>0 ? 6.0f:m_kInertia; le_contact unproj[8]; CCylinderGeom CylinderGeomOuter,*pCyl[2]; geom_world_data gwd[2]; intersection_params ip; CPhysicalEntity **pentlist; geom_contact *pcontacts; pe_player_dimensions pd; pe_action_impulse ai; pe_status_dynamics sd; matrix3x3f K; ip.bNoAreaContacts = true; SyncWithGroundCollider(time_interval); m_actions[m_iAction].dt += time_interval; bWasFlying = m_bFlying; if (m_vel.z<-m_size.z*200) { m_vel.z = 0; m_pos.z = 100; } m_timeUseLowCap -= time_interval; m_timeSinceStanceChange += time_interval; m_timeSinceImpulseContact += time_interval; m_timeForceInertia = max(0.0f,m_timeForceInertia-time_interval); m_timeStepPerformed += time_interval; m_bUseSphere = 0; if (m_bActive && (!(m_vel.len2()==0 && m_velRequested.len2()==0 && (!m_bFlying || m_gravity.len2()==0) && m_dhSpeed==0 && m_dhAcc==0 && m_qrot.w==m_history[m_iHist-1&m_szHistory-1].q.w && (m_qrot.v-m_history[m_iHist-1&m_szHistory-1].q.v).len2()==0) || m_nslope.z0 || m_bActiveEnvironment)) { FUNCTION_PROFILER( GetISystem(),PROFILE_PHYSICS ); PHYS_ENTITY_PROFILER m_bActiveEnvironment = 0; m_nslope.Set(0,0,1); if (kInertia==0 && !m_bFlying && !m_bJumpRequested || m_pWorld->m_vars.bFlyMode) { m_vel = m_velRequested; m_vel -= m_nslope*(m_nslope*m_vel); } if (!m_pWorld->m_vars.bFlyMode && m_bFlying) //move += m_velRequested*(m_kAirControl*time_interval); if (m_vel*m_velRequested < m_velRequested.len2()*m_kAirControl) m_vel += m_velRequested*(m_kAirControl*time_interval*2); if (m_vel.len2() > sqr(m_pWorld->m_vars.maxVelPlayers)) m_vel.normalize() *= m_pWorld->m_vars.maxVelPlayers; move += m_vel*time_interval; if (m_bFlying && !m_bSwimming && !m_pWorld->m_vars.bFlyMode) move += m_gravity*sqr(time_interval)*0.5f; bUnprojected = 0; if (m_pWorld->m_vars.iCollisionMode!=0 && m_pWorld->m_vars.bFlyMode) { m_pos+=move; m_bFlying=1; m_hLatest=0; ReleaseGroundCollider(); } else { movelen = move.len(); h = (movelen+m_pWorld->m_vars.maxContactGapPlayer)*1.5f; BBoxInner[0] = m_BBox[0]-vectorf(h,h,h+m_size.z*0.1f); BBoxInner[1] = m_BBox[1]+vectorf(h,h,h); h = max(10.0f*time_interval,h); // adds a safety margin of m_size.x width BBoxOuter[0] = m_BBox[0]-vectorf(h,h,h); BBoxOuter[1] = m_BBox[1]+vectorf(h,h,h); nents = m_pWorld->GetEntitiesAround(BBoxOuter[0],BBoxOuter[1], pentlist, ent_terrain|ent_static|ent_sleeping_rigid|ent_rigid|ent_living|ent_independent|ent_triggers|ent_sort_by_mass, this); for(i=j=bHasFastPhys=0,vrel=0; im_BBox[1]-pentlist[i]->m_BBox[0]; if (pentlist[i]->m_iSimClass==2 && pentlist[i]->GetMassInv()*0.4fGetStatus(&sd); vrel = max(vrel,sd.v.len()+sd.w.len()*max(max(sz.x,sz.y),sz.z));; bHasFastPhys |= (bFastPhys = isneg(m_size.x*0.2f-vrel*time_interval)); } else bFastPhys = 0; if (!bFastPhys && !AABB_overlap(pentlist[i]->m_BBox,BBoxInner) && sz.len2()>0) continue; idmat = pentlist[i]->GetType(); if (idmat==PE_SOFT || idmat==PE_ROPE) pentlist[i]->Awake(); else if (pentlist[i]->m_iSimClass<4 && (idmat!=PE_LIVING || /*m_parts[0].flags&*/pentlist[i]->m_parts[0].flags&geom_colltype_player && pentlist[i]!=this))// && pentlist[i]->GetParams(&pdyn) && pdyn.bActive)) { if (pentlist[i]->m_iSimClass==1 && m_timeSinceImpulseContact<0.2f && pentlist[i]->GetMassInv()>0) { int ipart; unsigned int flags; for(ipart=0,flags=0; ipartm_nParts; ipart++) flags |= pentlist[i]->m_parts[ipart].flags; if (flags & geom_colltype_player) pentlist[i]->Awake(); } pentlist[j++] = pentlist[i]; if (idmat==PE_LIVING) ((CLivingEntity*)pentlist[i])->SyncWithGroundCollider(time_interval); } } nents = j; pos0 = m_pos; bStaticUnproj = bDynUnproj = 0; newpos = m_pos+move; h = ShootRayDown(pentlist,nents, newpos,nslope); // first, check if we need unprojection in the initial position //m_qrot.getmatrix(gwd[0].R); //gwd[0].R.identity(); //Q2M_IVO if (sqr(m_qrot.v.x)+sqr(m_qrot.v.y)GetType()!=PE_LIVING || pentlist[i]->m_nParts>1) { int bHeavy = isneg(pentlist[i]->GetMassInv()*0.4f-m_massinv); if (bHasFastPhys && pentlist[i]->m_iSimClass==2 && bHeavy) { pentlist[i]->GetStatus(&sd); vectorf sz = pentlist[i]->m_BBox[1]-pentlist[i]->m_BBox[0]; vrel = max(vrel,sd.v.len()+sd.w.len()*max(max(sz.x,sz.y),sz.z)); bFastPhys = isneg(m_size.x*0.2f-vrel*time_interval); gwd[1].v = sd.v; gwd[1].w = sd.w; gwd[1].centerOfMass = sd.centerOfMass; } else { gwd[1].v.zero(); gwd[1].w.zero(); bFastPhys = 0; } for(iCyl=0; iCyl<=bFastPhys; iCyl++) for(j=0;jm_nParts;j++) if (pentlist[i]->m_parts[j].flags & geom_colltype_player) { //(pentlist[i]->m_qrot*pentlist[i]->m_parts[j].q).getmatrix(gwd[1].R); //Q2M_IVO gwd[1].R = matrix3x3f(pentlist[i]->m_qrot*pentlist[i]->m_parts[j].q); gwd[1].offset = pentlist[i]->m_pos + pentlist[i]->m_qrot*pentlist[i]->m_parts[j].pos; gwd[1].scale = pentlist[i]->m_parts[j].scale; if (pCyl[iCyl]->Intersect(pentlist[i]->m_parts[j].pPhysGeomProxy->pGeom, gwd,gwd+1, &ip, pcontacts)) { if (iCyl==0 && bHeavy) { if (m_pWorld->m_bWorldStep==2) { // this means step induced by rigid bodies moving around // if the entity is rigid, store the contact if (bPushOther = pentlist[i]->m_iSimClass>0 && pentlist[i]->GetMassInv()>0 && pentlist[i]->m_iGroup==m_pWorld->m_iCurGroup) { nUnproj = min(nUnproj+1,sizeof(unproj)/sizeof(unproj[0])); unproj[nUnproj-1].pent = pentlist[i]; unproj[nUnproj-1].ipart = j; unproj[nUnproj-1].pt = pcontacts[0].pt; unproj[nUnproj-1].n = pcontacts[0].n; unproj[nUnproj-1].center = gwd[0].offset; unproj[nUnproj-1].penetration = pcontacts[0].t; } // check previous contacts from this frame, register in entity if there are conflicts for(icnt=0; icntGetMassInv()==0 ? bStaticUnproj:bDynUnproj)++; vectorf offs = pcontacts[0].dir*(pcontacts[0].t+m_pWorld->m_vars.maxContactGapPlayer); offs.z = max(offs.z, h-(m_pos.z-m_hPivot+m_hCyl-m_size.z)); // don't allow unprojection to push cylinder into the ground m_pos += offs; gwd[0].offset += offs; bUnprojected = 1; } if (pentlist[i]->m_iSimClass==2) { K.SetZero(); GetContactMatrix(pcontacts[0].center, 0, K); bPushOther = (m_flags & lef_push_objects) && (pentlist[i]->m_flags & pef_pushable_by_players); if (bPushOther) pentlist[i]->GetContactMatrix(pcontacts[0].center, j, K); ncontact = (gwd[0].offset-pcontacts[0].center).normalized(); if (fabs_tpl(ncontact.z)<0.5f) { ncontact.z=0; ncontact.normalize(); } RigidBody *pbody = pentlist[i]->GetRigidBody(j); vrel = ncontact*(pbody->v+(pbody->w^pcontacts[0].center-pbody->pos)); if (iCyl==0 || vrel*time_interval>m_size.x*0.2f) { vrel -= ncontact*m_vel; ai.impulse = ncontact*(vrel/(ncontact*K*ncontact)); ai.point = pcontacts[0].center; ai.ipart = 0; if (ai.impulse.len2()*sqr(m_massinv) > max(1.0f,sqr(vrel))) ai.impulse.normalize() *= fabs_tpl(vrel)*m_mass*0.5f; m_vel += ai.impulse*m_massinv; /*if (m_vel.z>-5) { m_vel.z = max(0.0f, m_vel.z); m_vel.z += ai.impulse.len()*m_massinv*0.1f; }*/ m_bFlying = 1; m_minFlyTime = 1.0f; m_timeFlying = 0; if (m_kInertia==0) m_timeForceInertia = 5.0f; if (bPushOther) { if (fabs_tpl(pcontacts[0].dir.z)<0.7f) ai.impulse.z = 0; // ignore vertical component - might be semi-random depending on cylinder intersection ai.impulse.Flip(); ai.ipart = j; pentlist[i]->Action(&ai); m_timeSinceImpulseContact = 0; } idmat = pentlist[i]->m_parts[j].surface_idx&pcontacts[0].id[1]>>31 | max(pcontacts[0].id[1],0); RegisterContact(pcontacts[0].pt,ncontact,pentlist[i],j,idmat); } } break; } } } if (bStaticUnproj && bDynUnproj) { m_pos = pos0; // disable unprojection if we are being squashed bStaticUnproj = bDynUnproj = 0; } if (bStaticUnproj+bDynUnproj>0) { // need to recalculate h in case of unprojection newpos = m_pos+move; h = ShootRayDown(pentlist,nents, newpos,nslope); } hcur = newpos.z-m_hPivot; if (h>hcur && hm_slopeClimb || hhcur-m_size.z*0.06f && !m_bFlying && !m_bJumpRequested && !m_bSwimming) { newpos.z = h+m_hPivot; move = newpos-m_pos; movelen = move.len(); } pos0 = m_pos; if (m_bJumpRequested) AddLegsImpulse(-m_vel,m_nslope,true); m_bJumpRequested = 0; m_bStuck = 0; ncontactSum.zero(); if (movelen>m_size.x*1E-4f) { ip.bSweepTest = true; gwd[0].v = move/movelen; //m_pos -= gwd[0].v*m_pWorld->m_vars.maxContactGapPlayer; movelen += m_pWorld->m_vars.maxContactGapPlayer; iter = 0; move0 = movelen; movesum = 0; m_bUseSphere = m_bFlying && !m_pWorld->m_vars.bFlyMode && m_hCyl-m_hPivot>m_size.x*1.05f && h0; do { gwd[0].offset = m_pos + gwd[0].R*m_parts[0].pos; //(gwd[0].offset = m_pos).z += m_parts[0].pos.z; ip.time_interval = movelen+m_pWorld->m_vars.maxContactGapPlayer; tmin = ip.time_interval*2; imin = -1; for(i=0;iGetType()!=PE_LIVING || pentlist[i]->m_nParts>1) { for(j=0;jm_nParts;j++) if (pentlist[i]->m_parts[j].flags & geom_colltype_player) { //(pentlist[i]->m_qrot*pentlist[i]->m_parts[j].q).getmatrix(gwd[1].R); //Q2M_IVO gwd[1].R = matrix3x3f(pentlist[i]->m_qrot*pentlist[i]->m_parts[j].q); gwd[1].offset = pentlist[i]->m_pos + pentlist[i]->m_qrot*pentlist[i]->m_parts[j].pos; gwd[1].scale = pentlist[i]->m_parts[j].scale; if (m_bUseSphere && (ncont = m_SphereGeom.Intersect(pentlist[i]->m_parts[j].pPhysGeomProxy->pGeom, gwd,gwd+1, &ip, pcontacts)) && pcontacts[ncont-1].t0) { tmin = pcontacts[ncont-1].t; ncontact = -pcontacts[ncont-1].n; ptcontact = pcontacts[ncont-1].pt; imin=i; jmin=j; idmat=pentlist[i]->m_parts[j].surface_idx&pcontacts[ncont-1].id[1]>>31 | max(pcontacts[ncont-1].id[1],0); } if((ncont = m_CylinderGeom.Intersect(pentlist[i]->m_parts[j].pPhysGeomProxy->pGeom, gwd,gwd+1, &ip, pcontacts)) && pcontacts[ncont-1].t0 && (!m_bUseSphere || m_timeUseLowCap>0 || pcontacts[ncont-1].n.z>-0.5f || pcontacts[ncont-1].iFeature[0]!=0x20 && pcontacts[ncont-1].iFeature[0]!=0x41)) { tmin = pcontacts[ncont-1].t; ncontact = -pcontacts[ncont-1].n; ptcontact = pcontacts[ncont-1].pt; imin=i; jmin=j; idmat=pentlist[i]->m_parts[j].surface_idx&pcontacts[ncont-1].id[1]>>31 | max(pcontacts[ncont-1].id[1],0); } } } else { pentlist[i]->GetParams(&pd); vector2df dorigin,ncoll,move2d=(vector2df)gwd[0].v; dorigin = newpos-pentlist[i]->m_pos; float kb=dorigin*move2d, kc=dorigin.len2()-sqr(m_size.x+pd.sizeCollider.x), kd=kb*kb-kc, zup0,zdown0,zup1,zdown1; if (kd>=0) { zup0 = (zdown0 = m_pos.z-m_hPivot)+m_hCyl+m_size.z; zup1 = (zdown1 = pentlist[i]->m_pos.z-pd.heightPivot)+pd.heightCollider+pd.sizeCollider.z; kd=sqrt_tpl(kd); tfirst=-kb+kd; ncoll = m_pos+gwd[0].v*tfirst-pentlist[i]->m_pos; if (tfirst>-m_size.x && tfirst=0 && min(zup0+gwd[0].v.z*tfirst,zup1)m_pos; if (tfirst<-m_size.x || min(zup0+gwd[0].v.z*tfirst,zup1)=0) { tfirst=-kb+kd; ncoll = m_pos+gwd[0].v*tfirst-pentlist[i]->m_pos; } if (tfirst>-m_size.x && tfirstmax(zdown0+gwd[0].v.z*tfirst,zdown1)) { tmin = tfirst; ncontact.Set(ncoll.x,ncoll.y,0).normalize(); ptcontact = m_pos+gwd[0].v*tfirst-ncontact*m_size.x; imin=i; jmin=0; idmat=m_surface_idx; } // also check for cap-cap contact if (fabs_tpl(gwd[0].v.z)>m_size.z*1E-5f) { j = sgnnz(gwd[0].v.z); zup0 = m_pos.z-m_hPivot+m_hCyl+m_size.z*j; zdown1 = pentlist[i]->m_pos.z-pd.heightPivot+pd.heightCollider-pd.sizeCollider.z*j; tfirst = zdown1-zup0; if (inrange(tfirst,-m_size.x*gwd[0].v.z,tmin*gwd[0].v.z) && (dorigin*gwd[0].v.z+move2d*tfirst).len2()m_vars.maxContactGapPlayer); m_pos += gwd[0].v*tmin; if (pentlist[imin]->m_iSimClass>0 || ncontact.z>m_slopeClimb || ncontact.z<-m_slopeFall || m_vel.z<-1.0f) { K.SetZero(); GetContactMatrix(ptcontact, 0, K); bPushOther = (m_flags & (pentlist[imin]->GetType()==PE_LIVING ? lef_push_players : lef_push_objects)) && (pentlist[imin]->m_flags & pef_pushable_by_players); if (bPushOther) pentlist[imin]->GetContactMatrix(ptcontact, jmin, K); vrel = ncontact*m_vel; //(ncontact*gwd[0].v)*m_vel.len(); ai.impulse = ncontact; if (pentlist[imin]->GetType()==PE_LIVING) { // make the player slide off when standing on other players vrel -= ncontact*((CLivingEntity*)pentlist[imin])->m_vel; if (ncontact.z>0.95f) { *(vector2df*)&ai.impulse += vector2df(m_pos-pentlist[imin]->m_pos).normalized(); if (inrange(vrel,-1.0f,1.0f)) vrel = -1.0f; } } else { RigidBody *pbody = pentlist[imin]->GetRigidBody(jmin); vrel -= ncontact*(pbody->v+(pbody->w^ptcontact-pbody->pos)); } RegisterContact(ptcontact,ncontact,pentlist[imin],jmin,idmat); ai.impulse *= -vrel*1.01f/(ncontact*K*ncontact); ai.point = ptcontact; ai.ipart = 0; if (ai.impulse.len2()*sqr(m_massinv) > max(1.0f,sqr(vrel))) ai.impulse.normalize() *= fabs_tpl(vrel)*m_mass*0.5f; m_vel += ai.impulse*m_massinv; if (m_kInertia==0) m_timeForceInertia = 5.0f; if (bPushOther) { ai.impulse.Flip(); ai.ipart = jmin; if (fabs_tpl(ncontact.z)<0.7f) ai.impulse.z = 0; // ignore vertical component - might be semi-random depending on cylinder intersection ai.iApplyTime = isneg(pentlist[imin]->m_iSimClass-3)<<1; pentlist[imin]->Action(&ai); if (pentlist[imin]->m_iSimClass<3) m_timeSinceImpulseContact = 0; } } movelen -= tmin; movesum += tmin; gwd[0].v -= ncontact*((gwd[0].v*ncontact)-0.1f); tmin = gwd[0].v.len(); movelen*=tmin; gwd[0].v/=tmin; if (gwd[0].v*move<0) movelen = 0; for(i=0;im_pWorld->m_vars.maxContactGapPlayer*0.1f && ++iter<3); if (movesumsqr(0.4f)*ncontactSum.len2() || ncontactSum.len2()<0.6f)) m_bStuck = 1; if (bUnprojected || !(m_flags & lef_loosen_stuck_checks)) { ip.bSweepTest = false; gwd[0].offset = m_pos + gwd[0].R*m_parts[0].pos; //(gwd[0].offset = m_pos).z += m_parts[0].pos.z; gwd[0].v.Set(0,0,-1); ip.bStopAtFirstTri = true; ip.bNoBorder = true; ip.time_interval = m_size.z*10; for(i=0;iGetType()!=PE_LIVING && pentlist[i]->GetMassInv()*0.4fm_nParts;j++) if (pentlist[i]->m_parts[j].flags & geom_colltype_player) { //(pentlist[i]->m_qrot*pentlist[i]->m_parts[j].q).getmatrix(gwd[1].R); //Q2M_IVO gwd[1].R = matrix3x3f(pentlist[i]->m_qrot*pentlist[i]->m_parts[j].q); gwd[1].offset = pentlist[i]->m_pos + pentlist[i]->m_qrot*pentlist[i]->m_parts[j].pos; gwd[1].scale = pentlist[i]->m_parts[j].scale; if(m_CylinderGeom.Intersect(pentlist[i]->m_parts[j].pPhysGeomProxy->pGeom, gwd,gwd+1, &ip, pcontacts)) { m_pos = pos0; m_vel.zero(); m_timeUseLowCap=1.0f; m_bStuck=1; goto nomove; } } } nomove:; } } if (!m_pLastGroundCollider || m_pLastGroundCollider->GetMassInv()>m_massinv) velGround.zero(); else velGround = m_velGround; m_hLatest = h = ShootRayDown(pentlist,nents, m_pos,nslope, time_interval,false,true); if (nslope.z<0.087f) nslope = m_nslope; else m_nslope = nslope; if (m_bFlying) m_timeFlying += time_interval; int bGroundContact = isneg(m_pos.z-m_hPivot-(h+m_size.z*0.01)); m_bFlying = m_pWorld->m_vars.bFlyMode || m_gravity.z>0 || m_bSwimming || ((bGroundContact^1) && !(m_bStuck || ncontactSum.len2()>0.01f && sqr_signed(ncontactSum.z)>sqr(0.7f)*ncontactSum.len2())); m_bActiveEnvironment = m_bStuck; vectorf last_force(zero); if (m_bFlying) { if (!bWasFlying) m_vel += velGround; if (!m_pWorld->m_vars.bFlyMode) { last_force = m_gravity; if (m_bSwimming) last_force += (m_velRequested-m_vel)*kInertia; if ((m_vel+last_force*time_interval)*m_vel<0) m_vel.zero(); else m_vel += last_force*time_interval; last_force = -m_vel*m_kAirResistance*time_interval; if (last_force.len2()0) m_vel.z=0; m_bFlying = 1; } else if ((!bGroundContact || m_velRequested.len2()>0) && (m_nslope.z>m_slopeClimb || m_vel.z<0)) m_vel -= m_nslope*(m_vel*m_nslope); else m_vel.zero(); } vectorf vel = m_velRequested,g; if (!m_bSwimming) vel -= m_nslope*(vel*m_nslope); last_force = (vel-m_vel)*kInertia; if (m_nslope.z0) g.Set(0,0,-vel.len()*kInertia/sqrt_tpl(1-sqr(m_slopeClimb))); else g = m_gravity; last_force += g-m_nslope*(g*m_nslope); } if ((m_vel+last_force*time_interval)*m_vel<0 && (m_vel+last_force*time_interval)*m_velRequested<=0) m_vel.zero(); else m_vel += last_force*time_interval; if (m_nslope.z0 && last_force.z>0) m_vel.z = 0; if (m_pos.z>pos0.z+m_size.z*0.001f) { /*m_pos=pos0;*/ m_vel.z=0; } } if (m_nslope.zm_size.z*0.01f) { m_dhSpeed = max(m_dhSpeed, dh/m_stablehTime); m_dh += dh; if (m_dh>m_size.z) m_dh=m_size.z; m_stablehTime = 0; if (dh>(m_dhHist[0]+m_dhHist[1])*3) m_timeOnStairs = 0.5f; } else if (m_bFlying) dh = m_dh = m_dhSpeed = 0; m_dhHist[0]=m_dhHist[1]; m_dhHist[1]=dh; m_stablehTime = min(m_stablehTime+time_interval,1.0f); m_timeOnStairs = max(m_timeOnStairs-time_interval,0.0f); m_dhSpeed += m_dhAcc*time_interval; if (m_dhAcc==0 && m_dh*m_dhSpeed<0 || m_dh*m_dhAcc<0 || m_dh*(m_dh-m_dhSpeed*time_interval)<0) m_dh = m_dhSpeed = m_dhAcc = 0; else m_dh -= m_dhSpeed*time_interval; } if (m_HeadGeom.m_sphere.r>0) { ip.bSweepTest = true; gwd[0].offset = m_pos + gwd[0].R*m_parts[0].pos; //(gwd[0].offset = m_pos).z += m_parts[0].pos.z; gwd[0].v = gwd[0].R*vectorf(0,0,1); tmin = ip.time_interval = m_hHead-m_hCyl-min(m_dh,0.0f); for(i=0;im_iSimClass==0) {//pentlist[i]->GetType()!=PE_LIVING && pentlist[i]->GetMassInv()*0.4fm_nParts;j++) if (pentlist[i]->m_parts[j].flags & geom_colltype_player) { //(pentlist[i]->m_qrot*pentlist[i]->m_parts[j].q).getmatrix(gwd[1].R); //Q2M_IVO gwd[1].R = matrix3x3f(pentlist[i]->m_qrot*pentlist[i]->m_parts[j].q); gwd[1].offset = pentlist[i]->m_pos + pentlist[i]->m_qrot*pentlist[i]->m_parts[j].pos; gwd[1].scale = pentlist[i]->m_parts[j].scale; if(m_HeadGeom.Intersect(pentlist[i]->m_parts[j].pPhysGeomProxy->pGeom, gwd,gwd+1, &ip, pcontacts)) tmin = min(tmin,(float)pcontacts[0].t); } } if (m_dhRepositionEntity(this,1); } else if (!m_bActive) { if (m_velRequested.len2()>0) { m_pos += m_velRequested*time_interval; m_BBox[0] += m_velRequested*time_interval; m_BBox[1] += m_velRequested*time_interval; m_pWorld->RepositionEntity(this,1); } ReleaseGroundCollider(); m_iSensorsActive = 0; } //if (m_pWorld->m_vars.bMultiplayer) // m_pos = CompressPos(m_pos); m_iHist = m_iHist+1 & m_szHistory-1; m_history[m_iHist].dt = time_interval; m_history[m_iHist].v = m_vel; m_history[m_iHist].pos = m_pos; m_history[m_iHist].q = m_qrot; m_history[m_iHist].bFlying = m_bFlying; m_history[m_iHist].timeFlying = m_timeFlying; m_history[m_iHist].minFlyTime = m_minFlyTime; m_history[m_iHist].timeUseLowCap = m_timeUseLowCap; m_history[m_iHist].nslope = m_nslope; m_history[m_iHist].idCollider = m_pLastGroundCollider ? m_pWorld->GetPhysicalEntityId(m_pLastGroundCollider) : -2; m_history[m_iHist].iColliderPart = m_iLastGroundColliderPart; m_history[m_iHist].posColl = m_posLastGroundColl; m_timeSmooth = max(0.0f,m_timeSmooth-time_interval); if (m_pWorld->m_bUpdateOnlyFlagged) { m_deltaPos = m_posLocal-m_pos; if (m_deltaPos.len2()sqr(2.0f)) m_deltaPos.zero(); m_timeSmooth = 0.3f; } if (!m_bStateReading && m_flags & pef_custom_poststep) m_pWorld->m_pEventClient->OnPostStep(this,m_pForeignData,m_iForeignData,time_interval); return 1; } void CLivingEntity::StepBackEx(float time_interval, bool bRollbackHistory) { float dt; int idx,nSteps; for(dt=0,idx=m_iHist,nSteps=0; dt+0.00001f=m_szHistory || dt>1E9) return; float a1=dt-time_interval, a0=m_history[idx+1&m_szHistory-1].dt-a1, rdt; if (fabsf(a1)<0.00001f || m_history[idx+1&m_szHistory-1].dt==0) { a1=0; a0=1.0f; } else { rdt = 1.0f/m_history[idx+1&m_szHistory-1].dt; a0*=rdt; a1*=rdt; } m_pos = m_history[idx].pos*a0 + m_history[idx+1&m_szHistory-1].pos*a1; m_vel = m_history[idx].v*a0 + m_history[idx+1&m_szHistory-1].v*a1; //(m_qrot = m_history[idx].q*a0 + m_history[idx+1&m_szHistory-1].q*a1).Normalize(); m_bFlying = m_history[idx].bFlying; m_nslope = m_history[idx].nslope; m_timeFlying = m_history[idx].timeFlying*a0 + m_history[idx+1&m_szHistory-1].timeFlying*a1; m_minFlyTime = m_history[idx].minFlyTime*a0 + m_history[idx+1&m_szHistory-1].minFlyTime*a1; m_timeUseLowCap = m_history[idx].timeUseLowCap*a0 + m_history[idx+1&m_szHistory-1].timeUseLowCap*a1; SetGroundCollider( m_history[idx].idCollider>-2 ? (CPhysicalEntity*)m_pWorld->GetPhysicalEntityById(m_history[idx].idCollider) : 0); m_iLastGroundColliderPart = m_history[idx].iColliderPart; m_posLastGroundColl = m_history[idx].posColl; if (bRollbackHistory) { idx = idx+1&m_szHistory-1; m_history[idx].dt = dt-time_interval; m_history[idx].pos = m_pos; m_history[idx].v = m_vel; //m_history[idx].q = m_qrot; m_history[idx].bFlying = m_bFlying; m_history[idx].nslope = m_nslope; m_history[idx].timeFlying = m_timeFlying; m_history[idx].minFlyTime = m_minFlyTime; m_history[idx].timeUseLowCap = m_timeUseLowCap; m_iHist = idx; } } float CLivingEntity::CalcEnergy(float time_interval) { float E=0; for(int i=0;iGetRigidBody(m_pContacts[i].ipart)->M; return E; } int CLivingEntity::RegisterContacts(float time_interval,int nMaxPlaneContacts) { int i,j; vectorf pt[2]; float h; entity_contact *pcontact; if (m_iSimClass!=7) for(i=0;i0.7f && fabs_tpl(h)>m_size.z*0.9f) { // contact with cap pt[1] += (m_pContacts[i].center-pt[0])*2; pt[1].z = pt[0].z; } else { // contact with side pt[0].z -= h-m_size.z; pt[1].z -= h+m_size.z; } for(j=0;j<2;j++) { if (!(pcontact=(entity_contact*)AllocSolverTmpBuf(sizeof(entity_contact)))) return 0; pcontact->flags = 0; pcontact->pent[0] = m_pContacts[i].pent; pcontact->ipart[0] = m_pContacts[i].ipart; pcontact->pbody[0] = m_pContacts[i].pent->GetRigidBody(m_pContacts[i].ipart); pcontact->pent[1] = this; pcontact->ipart[1] = 0; pcontact->pbody[1] = GetRigidBody(); pcontact->friction = 0; pcontact->vreq = m_pContacts[i].n*min(3.0f,(m_pContacts[i].penetration*10.0f)); pcontact->pt[0] = pcontact->pt[1] = pt[j]; pcontact->n = m_pContacts[i].n; pcontact->K.SetZero(); m_pContacts[i].pent->GetContactMatrix(m_pContacts[i].pt, m_pContacts[i].ipart, pcontact->K); ::RegisterContact(pcontact); } } return 1; } int CLivingEntity::Update(float time_interval, float damping) { for(int i=0;iRemoveCollider(this); m_pColliders[i]->Release(); } m_nColliders = 0; m_nContacts = 0; return 1; } void CLivingEntity::DrawHelperInformation(void (*DrawLineFunc)(float*,float*), int flags) { if (m_bActive) { CPhysicalEntity::DrawHelperInformation(DrawLineFunc, flags); geom_world_data gwd; gwd.R = matrix3x3f(m_qrot); gwd.offset = m_pos + gwd.R*m_parts[0].pos; if (m_bUseSphere) m_SphereGeom.DrawWireframe(DrawLineFunc,&gwd,0); if (m_HeadGeom.m_sphere.r>0) { gwd.offset += gwd.R*vectorf(0,0,m_hHead-m_hCyl-m_hPivot-m_dh); m_HeadGeom.DrawWireframe(DrawLineFunc,&gwd,0); } } } void CLivingEntity::GetMemoryStatistics(ICrySizer *pSizer) { CPhysicalEntity::GetMemoryStatistics(pSizer); if (GetType()==PE_LIVING) pSizer->AddObject(this, sizeof(CLivingEntity)); if (m_nSensors>0) { if (m_pSensors) pSizer->AddObject(m_pSensors, m_nSensors*sizeof(m_pSensors[0])); if (m_pSensorsPoints) pSizer->AddObject(m_pSensorsPoints, m_nSensors*sizeof(m_pSensorsPoints[0])); if (m_pSensorsSlopes) pSizer->AddObject(m_pSensorsSlopes, m_nSensors*sizeof(m_pSensorsSlopes[0])); } }