////////////////////////////////////////////////////////////////////// // // Particle Entity // // File: particleentity.cpp // Description : CParticleEntity class implementation // // History: // -:Created by Anton Knyazev // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "bvtree.h" #include "geometry.h" #include "singleboxtree.h" #include "raybv.h" #include "raygeom.h" #include "intersectionchecks.h" #include "rigidbody.h" #include "physicalplaceholder.h" #include "physicalentity.h" #include "geoman.h" #include "physicalworld.h" #include "particleentity.h" CParticleEntity::CParticleEntity(CPhysicalWorld *pWorld) : CPhysicalEntity(pWorld) { m_gravity = pWorld->m_vars.gravity; m_mass=0.2f; m_dim=m_dimLying=0.05f; m_rdim=20.0f; m_heading.Set(1,0,0); m_vel.zero(); m_wspin.zero(); m_waterGravity = m_gravity*0.8f; m_kAirResistance = 0; m_kWaterResistance = 0.5f; m_accThrust = 0; m_kAccLift = 0; m_qspin.SetIdentity(); m_surface_idx = 0; m_dirdown.Set(0,0,-1); m_normal.Set(0,0,1); m_flags = 0; m_iSimClass = 4; for(int i=0;i0 && m_pWorld) m_pWorld->GetGeomManager()->UnregisterGeometry(m_parts[0].pPhysGeom); m_nParts = 0; } int CParticleEntity::SetParams(pe_params *_params) { int res; if (res = CPhysicalEntity::SetParams(_params)) { if (_params->type==pe_params_flags::type_id) { pe_params_particle pp; SetParams(&pp); } return res; } if (_params->type==pe_params_particle::type_id) { pe_params_particle *params = (pe_params_particle*)_params; ENTITY_VALIDATE("CParticleEntity:SetParams(pe_params_particle)",params); if (!is_unused(params->mass)) m_mass = params->mass; if (!is_unused(params->size)) { m_rdim = 1.0f/(m_dim = params->size*0.5f); m_dimLying = params->size*0.5f; } if (!is_unused(params->thickness)) m_dimLying = params->thickness*0.5f; if (!is_unused(params->kAirResistance)) m_kAirResistance = params->kAirResistance; if (!is_unused(params->kWaterResistance)) m_kWaterResistance = params->kWaterResistance; if (!is_unused(params->accThrust)) m_accThrust = params->accThrust; if (!is_unused(params->accLift) && !is_unused(params->velocity)) m_kAccLift = params->velocity!=0 ? params->accLift/fabs_tpl(params->velocity):0; if (!is_unused(params->heading)) m_heading = params->heading; if (!is_unused(params->velocity)) m_vel = m_heading*params->velocity; if (!is_unused(params->wspin)) m_wspin = params->wspin; if (!is_unused(params->gravity)) { m_gravity = params->gravity; if (m_gravity.len2()>0) (m_dirdown=m_gravity).normalize(); else m_dirdown.Set(0,0,-1); } if (!is_unused(params->waterGravity)) m_waterGravity = params->waterGravity; if (!is_unused(params->normal)) m_normal = params->normal; if (!is_unused(params->q0)) m_qspin = params->q0; if (!is_unused(params->minBounceVel)) m_minBounceVel = params->minBounceVel; if (!is_unused(params->surface_idx)) m_surface_idx = params->surface_idx; if (!is_unused(params->flags)) m_flags = params->flags; if (m_flags & particle_traceable) { if (m_ig[0].x==-3) m_ig[0].x=m_ig[1].x=m_ig[0].y=m_ig[1].y = -2; if (m_pos.len2()>0) m_pWorld->RepositionEntity(this,1); } else { if (m_ig[0].x!=-3) { m_pWorld->DetachEntityGridThunks(this); m_ig[0].x=m_ig[1].x=m_ig[0].y=m_ig[1].y = -3; } } if (!is_unused(params->pColliderToIgnore)) m_pColliderToIgnore = (CPhysicalEntity*)params->pColliderToIgnore; if (!is_unused(params->iPierceability)) m_iPierceability = params->iPierceability; if (!(m_flags & particle_constant_orientation)) { if (!(m_flags & particle_no_path_alignment)) { vectorf dirbuf[3]; dirbuf[0] = m_dirdown^m_heading; dirbuf[1] = m_heading; // if (dirbuf[0].len2()<0.01f) dirbuf[0] = m_heading.orthogonal(); if (dirbuf[0].len2()<0.01f) dirbuf[0].SetOrthogonal(m_heading); dirbuf[0].normalize(); dirbuf[2] = dirbuf[0]^dirbuf[1]; m_qrot = quaternionf((matrix3x3CMf&)dirbuf[0])*m_qspin; } else m_qrot = m_qspin; } m_BBox[0] = m_pos-vectorf(m_dim,m_dim,m_dim); m_BBox[1] = m_pos+vectorf(m_dim,m_dim,m_dim); return 1; } if (_params->type==pe_params_buoyancy::type_id) { pe_params_buoyancy *params = (pe_params_buoyancy*)_params; if (!is_unused(params->waterDensity)) m_waterDensity = params->waterDensity; if (!is_unused(params->waterPlane.n)) m_waterPlane.n = params->waterPlane.n; if (!is_unused(params->waterPlane.origin)) m_waterPlane.origin = params->waterPlane.origin; return 1; } return 0; } int CParticleEntity::GetParams(pe_params *_params) { if (CPhysicalEntity::GetParams(_params)) return 1; if (_params->type==pe_params_particle::type_id) { pe_params_particle *params = (pe_params_particle*)_params; params->mass = m_mass; params->size = m_dim*2.0f; params->thickness = m_dimLying*2.0f; params->heading = m_heading; params->velocity = m_vel.len(); params->wspin = m_wspin; params->gravity = m_gravity; params->normal = m_normal; params->kAirResistance = m_kAirResistance; params->accThrust = m_accThrust; params->accLift = params->velocity*m_kAccLift; params->q0 = m_qspin; params->surface_idx = m_surface_idx; params->flags = m_flags; params->pColliderToIgnore = m_pColliderToIgnore; params->iPierceability = m_iPierceability; return 1; } if (_params->type==pe_params_buoyancy::type_id) { pe_params_buoyancy *params = (pe_params_buoyancy*)_params; params->waterDensity = m_waterDensity; params->waterDamping = 0; params->waterPlane = m_waterPlane; params->waterFlow.zero(); params->waterResistance = 0; params->waterEmin = 0; return 1; } return 0; } int CParticleEntity::GetStateSnapshot(CStream &stm, float time_back, int flags) { stm.WriteNumberInBits(SNAPSHOT_VERSION, 4); if (m_pWorld->m_vars.bMultiplayer) { if (!IsAwake()) { if (m_sleepTime>5.0f) stm.Write(false); else { stm.Write(true); stm.Write(m_pos); stm.Write(false); } } else { stm.Write(true); stm.Write(m_pos); stm.Write(true); stm.Write(m_vel); if (!m_bSliding) stm.Write(false); else { stm.Write(true); stm.Write(asin_tpl(m_slide_normal.z)); stm.Write(atan2_tpl(m_slide_normal.y,m_slide_normal.x)); } } } else { stm.Write(m_pos); stm.Write(m_vel); if (!m_bSliding) stm.Write(false); else { stm.Write(true); stm.Write(asin_tpl(m_slide_normal.z)); stm.Write(atan2_tpl(m_slide_normal.y,m_slide_normal.x)); } } /*if (m_qspin.v.len2()<0.01*0.01) stm.Write(false); else { stm.Write(true); //CHANGED_BY_IVO (NOTE: order of angles is flipped!!!!) //float angles[3]; m_qspin.get_Euler_angles_xyz(angles[0],angles[1],angles[2]); //EULER_IVO //Vec3 TempAng; m_qspin.GetEulerAngles_XYZ( TempAng ); vectorf angles = Ang3::GetAnglesXYZ(matrix3x3f(m_qspin)); for(int i=0;i<3;i++) stm.Write((unsigned short)float2int((angles[i]+pi)*(65535.0/2/pi))); } if (m_wspin.len2()==0) stm.Write(false); else { stm.Write(true); stm.Write(m_wspin); }*/ return 1; } int CParticleEntity::SetStateFromSnapshot(CStream &stm, int flags) { bool bnz; int ver=0; stm.ReadNumberInBits(ver,4); if (ver!=SNAPSHOT_VERSION) return 0; if (!(flags & ssf_no_update)) { if (m_pWorld->m_vars.bMultiplayer) { stm.Read(bnz); if (bnz) { stm.Read(m_pos); stm.Read(bnz); if (bnz) { stm.Read(m_vel); stm.Read(bnz); if (bnz) { m_bSliding = 1; float yaw,pitch; stm.Read(pitch); stm.Read(yaw); m_slide_normal(cos_tpl(yaw)*cos_tpl(pitch),sin_tpl(yaw)*cos_tpl(pitch),sin_tpl(pitch)); } else m_bSliding = 0; } } } else { stm.Read(m_pos); stm.Read(m_vel); stm.Read(bnz); if (bnz) { m_bSliding = 1; float yaw,pitch; stm.Read(pitch); stm.Read(yaw); m_slide_normal(cos_tpl(yaw)*cos_tpl(pitch),sin_tpl(yaw)*cos_tpl(pitch),sin_tpl(pitch)); } else m_bSliding = 0; } if (m_bForceAwake!=0) m_bForceAwake = -1; } else { if (m_pWorld->m_vars.bMultiplayer) { stm.Read(bnz); if (bnz) { stm.Seek(stm.GetReadPos()+sizeof(vectorf)*8); stm.Read(bnz); if (bnz) { stm.Seek(stm.GetReadPos()+sizeof(vectorf)*8); stm.Read(bnz); if (bnz) stm.Seek(stm.GetReadPos()+2*sizeof(float)*8); } } } else { stm.Seek(stm.GetReadPos()+2*sizeof(vectorf)*8); stm.Read(bnz); if (bnz) stm.Seek(stm.GetReadPos()+2*sizeof(float)*8); } } /*stm.Read(bnz); if (bnz) { unsigned short tmp; int i; vectorf axis(zero); for(i=0,m_qspin.SetIdentity(); i<3; i++) { axis[i] = 1; //stm.Read(tmp); m_qspin*=quaternionf(tmp*(2*pi/65535.0)-pi, axis); stm.Read(tmp); m_qspin = GetRotationAA((float)(tmp*(2*pi/65535.0)-pi), axis)*m_qspin; axis[i] = 0; } } stm.Read(bnz); if (bnz) stm.Read(m_wspin); else m_wspin.zero();*/ return 1; } int CParticleEntity::GetStatus(pe_status* _status) { if (CPhysicalEntity::GetStatus(_status)) return 1; if (_status->type==pe_status_collisions::type_id) { pe_status_collisions *status = (pe_status_collisions*)_status; int i,n,nmax = min(status->len, sizeof(m_CollHistory)/sizeof(m_CollHistory[0])); for(i=m_iCollHistory,n=0; nage; i=i-1&sizeof(m_CollHistory)/sizeof(m_CollHistory[0])-1,n++) status->pHistory[n] = m_CollHistory[i]; if (status->bClearHistory) for(i=0;ilen = n; } if (_status->type==pe_status_dynamics::type_id) { pe_status_dynamics *status = (pe_status_dynamics*)_status; vectorf gravity; float kAirResistance; if (m_waterDensity>0 && (m_pos-m_waterPlane.origin)*m_waterPlane.n<0) { gravity = m_waterGravity; kAirResistance = m_kWaterResistance; } else { gravity = m_gravity; kAirResistance = m_kAirResistance; } status->v = m_vel; status->a = gravity+m_heading*m_accThrust-m_vel*kAirResistance+(m_heading^(m_heading^m_dirdown)).normalize()*m_kAccLift*m_vel.len(); status->w = m_wspin; status->centerOfMass = m_pos; return 1; } return 0; } int CParticleEntity::Action(pe_action* _action) { int res; if (res = CPhysicalEntity::Action(_action)) return res; if (_action->type==pe_action_impulse::type_id) { pe_action_impulse *action = (pe_action_impulse*)_action; vectorf P=action->impulse, L(zero); if (!is_unused(action->momentum)) L = action->momentum; else if (!is_unused(action->point)) L = action->point-m_pos^action->impulse; m_vel += P/m_mass; if (!(m_flags & particle_constant_orientation)) m_wspin += L/(0.4f*m_mass*m_dim); return 1; } if (_action->type==pe_action_reset::type_id) { m_vel.zero(); m_wspin.zero(); return 1; } return 0; } int CParticleEntity::Awake(int bAwake,int iSource) { if (bAwake) m_bForceAwake = 1; else { m_bForceAwake = 0; m_vel.zero(); } return m_iSimClass; } int CParticleEntity::IsAwake(int ipart) { vectorf gravity = m_waterDensity>0 && (m_pos-m_waterPlane.origin)*m_waterPlane.n<0 ? m_waterGravity : m_gravity; return m_vel.len2()-1000.0f && m_bForceAwake!=0 && (m_bForceAwake==1 || (m_vel.len2()>sqr(m_pWorld->m_vars.minSeparationSpeed) || m_CollHistory[m_iCollHistory].age>1E9 && !m_bSliding && gravity.len2()>0)); } void CParticleEntity::StartStep(float time_interval) { m_timeStepPerformed = 0; m_timeStepFull = time_interval; } float CParticleEntity::GetMaxTimeStep(float time_interval) { if (m_timeStepPerformed > m_timeStepFull-0.001f) return time_interval; return min_safe(m_timeStepFull-m_timeStepPerformed,time_interval); } int CParticleEntity::Step(float time_interval) { ray_hit hits[8]; pe_action_impulse ai; pe_action_register_coll_event arce; pe_status_dynamics sd; vectorf pos0,vel0,heading0,vtang,vel_next; float vn,vtang_len,rvtang_len,e,k,friction,vn0; int i,nhits,bHit,flags; vectorf gravity; float kAirResistance; if (m_waterDensity>0 && (m_pos-m_waterPlane.origin)*m_waterPlane.n<0) { gravity = m_waterGravity; kAirResistance = m_kWaterResistance; } else { gravity = m_gravity; kAirResistance = m_kAirResistance; } m_timeStepPerformed += time_interval; if (m_pColliderToIgnore && m_pColliderToIgnore->m_iSimClass==7) m_pColliderToIgnore = 0; for(i=0;iRayWorldIntersection(m_pos,m_slide_normal*(m_dim*-1.1f), ent_all, m_iPierceability|(geom_colltype0|geom_colltype_ray)<m_FrictionTable[m_surface_idx&NSURFACETYPES-1]+ m_pWorld->m_FrictionTable[hits[0].surface_idx&NSURFACETYPES-1])*0.5f); if (m_slide_normal.z<0.5f) friction = min(1.0f,friction); // limit sliding friction on slopes vn = hits[0].n*vel0; vtang = vel0-hits[0].n*vn; vtang_len = vtang.len(); rvtang_len = vtang_len>1e-4 ? 1.0f/vtang_len:0; if (((CPhysicalEntity*)hits[0].pCollider)->m_iSimClass>1) { hits[0].pCollider->GetStatus(&sd); vn0 = sd.v*hits[0].n; } else vn0 = 0; m_vel = vel0 = hits[0].n*max(vn0,vn) + vtang*(max(0.0f,vtang_len-max(0.0f,-(vn+(m_gravity*hits[0].n)*time_interval))*friction)*rvtang_len); m_wspin.zero(); m_qspin.SetIdentity(); if (!(m_flags & particle_constant_orientation)) (m_qrot = GetRotationV0V1(m_qrot*m_normal,hits[0].n)*m_qrot).Normalize(); //m_qrot.SetRotationV0V1(m_normal,hits[0].n); //m_qrot = quaternionf(m_normal,hits[0].n); flags |= particle_constant_orientation; } else { friction = m_pWorld->m_FrictionTable[m_surface_idx&NSURFACETYPES-1]; vel0 = m_vel = (m_vel-m_slide_normal*(m_vel*m_slide_normal))*max(0.0f,1.0f-time_interval*friction); m_wspin = m_slide_normal^m_vel*m_rdim; } if (m_flags & particle_single_contact) gravity.zero(); else gravity -= m_slide_normal*(m_slide_normal*gravity); m_bForceAwake = ((CPhysicalEntity*)hits[0].pCollider)->m_iSimClass<=2 || m_timeForceAwake>40.0f ? -1:1; } else { m_bSliding = 0; if (!(m_flags & particle_constant_orientation)) m_wspin = (m_heading^m_gravity).normalized()*((m_gravity*m_dirdown)*0.5f*m_rdim); } } m_vel += (gravity + m_heading*m_accThrust - m_vel*kAirResistance + (m_heading^(m_heading^m_dirdown)).normalize()*(m_kAccLift*m_vel.len()))*time_interval; (m_heading=m_vel).normalize(); if (!(flags & particle_constant_orientation)) { if (!(m_flags & particle_no_spin)) { if (m_wspin.len2()*sqr(time_interval)<0.1f*0.1f) { m_qspin += quaternionf(0,m_wspin*0.5f)*m_qspin*time_interval; m_qspin.Normalize(); } else { float wlen = m_wspin.len(); //m_qspin = quaternionf(wlen*time_interval,m_wspin/wlen)*m_qspin; m_qspin = GetRotationAA(wlen*time_interval,m_wspin/wlen)*m_qspin; } } else m_wspin.zero(); if (!(m_flags & particle_no_path_alignment)) { vectorf dirbuf[3]; dirbuf[0] = m_dirdown^m_heading; dirbuf[1] = m_heading; if (dirbuf[0].len2()<0.01f) dirbuf[0].SetOrthogonal(m_heading); dirbuf[0].normalize(); dirbuf[2] = dirbuf[0]^dirbuf[1]; m_qrot = m_qspin*quaternionf((matrix3x3CMf&)dirbuf[0]); } else m_qrot = m_qspin; } bHit = 0; if (m_pWorld->m_bWorldStep==2) { CPhysicalEntity **pentlist; pe_status_pos sp; sp.timeBack = 1;//time_interval; vectorf posFixed; int nents = m_pWorld->GetEntitiesAround(pos0-vectorf(m_dim,m_dim,m_dim),pos0+vectorf(m_dim,m_dim,m_dim),pentlist,ent_rigid); hits[0].dist = hits[1].dist = 1E10f; for(i=0;iGetStatus(&sp); posFixed = (pentlist[i]->m_qrot*!sp.q)*(pos0-sp.pos)+pentlist[i]->m_pos; if (bHit = (m_pWorld->RayTraceEntity(pentlist[i],posFixed,pos0-posFixed+(pos0-posFixed).normalized()*(m_dim*0.97f),hits+1) && pentlist[i]->m_parts[hits[1].ipart].flags&geom_colltype0 && hits[1].distRayWorldIntersection(pos0,hits[0].pt-pos0+heading0*m_dim,ent_terrain|ent_static, m_iPierceability|(geom_colltype0|geom_colltype_ray)<RayWorldIntersection(pos0,m_pos-pos0+heading0*m_dim, ent_all, m_iPierceability|(geom_colltype0|geom_colltype_ray)<=(bHit^1);i--) { // register all hits in history m_iCollHistory = m_iCollHistory+1 & sizeof(m_CollHistory)/sizeof(m_CollHistory[0])-1; RigidBody *pbody = ((CPhysicalEntity*)hits[i].pCollider)->GetRigidBody(hits[i].ipart); m_CollHistory[m_iCollHistory].pt = hits[i].pt-heading0*m_dim; // store not contact, but position of particle center at the time of contact m_CollHistory[m_iCollHistory].n = hits[i].n; // it's better for explosions to be created at some distance from the wall m_CollHistory[m_iCollHistory].v[0] = vel0; m_CollHistory[m_iCollHistory].v[1] = pbody->v+(pbody->w^m_CollHistory[m_iCollHistory].pt-pbody->pos); m_CollHistory[m_iCollHistory].mass[0] = m_mass; m_CollHistory[m_iCollHistory].mass[1] = pbody->M; m_CollHistory[m_iCollHistory].idCollider = m_pWorld->GetPhysicalEntityId(hits[i].pCollider); m_CollHistory[m_iCollHistory].partid[0] = 0; m_CollHistory[m_iCollHistory].partid[1] = hits[i].partid; m_CollHistory[m_iCollHistory].idmat[0] = m_surface_idx; m_CollHistory[m_iCollHistory].idmat[1] = hits[i].surface_idx; m_CollHistory[m_iCollHistory].age = 0; } } if (bHit) { e = max(min((m_pWorld->m_BouncinessTable[m_surface_idx] + m_pWorld->m_BouncinessTable[hits[0].surface_idx])*0.5f, 1.0f), 0.0f); float minv = ((CPhysicalEntity*)hits[0].pCollider)->GetMassInv(); k = m_mass*minv/(1+m_mass*minv); if (((CPhysicalEntity*)hits[0].pCollider)->m_iSimClass>0) { RigidBody *pbody = ((CPhysicalEntity*)hits[0].pCollider)->GetRigidBody(hits[0].ipart); vectorf vrel = vel0-pbody->v-(pbody->w^hits[0].pt-pbody->pos); if (vel0.len2()>sqr(m_minBounceVel)) { ai.point = hits[0].pt; ai.impulse = vrel*(m_mass*pbody->M/(m_mass+pbody->M)*(1+e)); ai.ipart = hits[0].ipart; hits[0].pCollider->Action(&ai); arce.pt = hits[0].pt; arce.n = hits[0].n; arce.v = m_vel; arce.collMass = m_mass; arce.pCollider = this; arce.partid[0] = hits[0].partid; arce.partid[1] = 0; arce.idmat[0] = hits[0].surface_idx; arce.idmat[1] = m_surface_idx; hits[0].pCollider->Action(&arce); } } vn = hits[0].n*vel0; vtang = vel0-hits[0].n*vn; if (vn>-m_minBounceVel || m_dimLyingm_vars.minSeparationSpeed) || m_flags & particle_single_contact) { m_vel.zero(); m_wspin.zero(); m_qspin.SetIdentity(); if (m_dim!=m_dimLying) m_pos = hits[0].pt+hits[0].n*m_dimLying; m_bSliding = 1; m_slide_normal = hits[0].n; if (!(flags & particle_constant_orientation)) m_qrot = GetRotationV0V1(m_qrot*m_normal,hits[0].n)*m_qrot; //m_qrot = quaternionf(m_normal,hits[0].n); } else { vel_next = m_vel + (gravity + m_heading*m_accThrust - m_vel*kAirResistance + (m_heading^(m_heading^m_dirdown)).normalize()*(m_kAccLift*m_vel.len()))*time_interval; if (vel_next*hits[0].nm_vars.minSeparationSpeed) { if (m_dim!=m_dimLying) m_pos = hits[0].pt+hits[0].n*m_dimLying; m_bSliding = 1; m_slide_normal = hits[0].n; if (m_flags & particle_no_roll && !(m_flags & particle_constant_orientation)) m_qrot = GetRotationV0V1(m_qrot*m_normal,hits[0].n)*m_qrot; //m_qrot = quaternionf(m_normal,hits[0].n); } if (m_flags & particle_no_roll) m_wspin.zero(); else m_wspin = (hits[0].n^vtang)*m_rdim; } if (((CPhysicalEntity*)hits[0].pCollider)->m_iSimClass>1) { hits[0].pCollider->GetStatus(&sd); m_vel += hits[0].n*(sd.v*hits[0].n); } m_bForceAwake = ((CPhysicalEntity*)hits[0].pCollider)->m_iSimClass<=2 || m_timeForceAwake>40.0f ? -1:1; } i = (m_bForceAwake ^ m_bForceAwake>>31) & 1; m_timeForceAwake = m_timeForceAwake*i + time_interval*i; m_sleepTime = 0; m_BBox[0] = m_pos-vectorf(m_dim,m_dim,m_dim); m_BBox[1] = m_pos+vectorf(m_dim,m_dim,m_dim); if (m_flags & particle_traceable) m_pWorld->RepositionEntity(this, 1); } else m_sleepTime += time_interval; return 1; } int CParticleEntity::RayTrace(CRayGeom *pRay,geom_contact *&pcontacts) { static geom_contact g_ParticleContact; prim_inters inters; box abox; abox.Basis.SetRow(2,m_qrot*m_normal); abox.Basis.SetRow(0,m_qrot*GetOrthogonal(m_normal).normalized()); abox.Basis.SetRow(1,abox.Basis.GetRow(2)^abox.Basis.GetRow(0)); abox.size(m_dim,m_dim,m_dimLying); abox.center = m_pos; if (box_ray_intersection(&abox,&pRay->m_ray,&inters)) { pcontacts = &g_ParticleContact; pcontacts->pt = inters.pt[0]; pcontacts->t = (inters.pt[0]-pRay->m_ray.origin)*pRay->m_dirn; pcontacts->id[0] = m_surface_idx; pcontacts->iNode[0] = 0; pcontacts->n = inters.n; return 1; } return 0; } void CParticleEntity::DrawHelperInformation(void (*DrawLineFunc)(float*,float*), int flags) { CPhysicalEntity::DrawHelperInformation(DrawLineFunc,flags); if (flags & pe_helper_geometry) { box abox; abox.Basis.SetRow(2,m_qrot*m_normal); abox.Basis.SetRow(0,m_qrot*GetOrthogonal(m_normal)); abox.Basis.SetRow(1,abox.Basis.GetRow(2)^abox.Basis.GetRow(0)); abox.center = m_pos; int i,j; vectorf pt[8]; for(i=0;i<8;i++) pt[i] = vectorf(m_dim*((i<<1&2)-1),m_dim*((i&2)-1),m_dimLying*((i>>1&2)-1))*abox.Basis+abox.center; for(i=0;i<8;i++) for(j=0;j<3;j++) if (i&1<AddObject(this, sizeof(CParticleEntity)); }