801 lines
27 KiB
C++
801 lines
27 KiB
C++
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Rope Entity
|
|
//
|
|
// File: ropeentity.cpp
|
|
// Description : CRopeEntity class implementation
|
|
//
|
|
// History:
|
|
// -:Created by Anton Knyazev
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "bvtree.h"
|
|
#include "geometry.h"
|
|
#include "overlapchecks.h"
|
|
#include "raybv.h"
|
|
#include "raygeom.h"
|
|
#include "rigidbody.h"
|
|
#include "physicalplaceholder.h"
|
|
#include "physicalentity.h"
|
|
#include "geoman.h"
|
|
#include "physicalworld.h"
|
|
#include "ropeentity.h"
|
|
|
|
|
|
CRopeEntity::CRopeEntity(CPhysicalWorld *pWorld) : CPhysicalEntity(pWorld)
|
|
{
|
|
m_nSegs = -1;
|
|
m_segs = 0;
|
|
m_length = 0;
|
|
m_pTiedTo[0] = m_pTiedTo[1] = 0;
|
|
m_iTiedPart[0] = m_iTiedPart[1] = 0;
|
|
m_idConstraint = 0;
|
|
m_iConstraintClient = 0;
|
|
m_gravity = pWorld->m_vars.gravity;
|
|
m_bAwake = m_nSlowFrames = 0;
|
|
m_damping = 0.5f;
|
|
m_iSimClass = 4;
|
|
m_Emin = sqr(0.003f);
|
|
m_maxAllowedStep = 0.01f;
|
|
m_mass = 1.0f;
|
|
m_collDist = 0.01f;
|
|
m_flags = 0;
|
|
m_surface_idx = 0;
|
|
m_ig[0].x=m_ig[1].x=m_ig[0].y=m_ig[1].y = -3;
|
|
m_defpart.id = 0;
|
|
m_friction = 0.2f;
|
|
}
|
|
|
|
CRopeEntity::~CRopeEntity()
|
|
{
|
|
if (m_segs)
|
|
delete[] m_segs;
|
|
}
|
|
|
|
void CRopeEntity::AlertNeighbourhoodND()
|
|
{
|
|
pe_params_rope pr;
|
|
pr.pEntTiedTo[0] = pr.pEntTiedTo[1] = 0;
|
|
SetParams(&pr);
|
|
if (m_segs)
|
|
delete[] m_segs;
|
|
m_segs = 0; m_nSegs = 0;
|
|
}
|
|
|
|
int CRopeEntity::Awake(int bAwake,int iSource)
|
|
{
|
|
m_bAwake=bAwake; m_nSlowFrames=0;
|
|
int i;
|
|
if (m_nSegs>0) {
|
|
for(i=0;i<=m_nSegs;i++) if (m_segs[i].pContactEnt && m_segs[i].pContactEnt->m_iSimClass==7)
|
|
m_segs[i].pContactEnt = 0;
|
|
for(i=0;i<2;i++) if (m_pTiedTo[i] && m_pTiedTo[i]->m_iSimClass==7)
|
|
m_pTiedTo[i] = 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
int CRopeEntity::SetParams(pe_params *_params)
|
|
{
|
|
int res;
|
|
vectorf prevpos = m_pos;
|
|
if (res = CPhysicalEntity::SetParams(_params)) {
|
|
if (_params->type==pe_params_pos::type_id) {
|
|
//matrix3x3f R; m_qrot.getmatrix(R); //Q2M_IVO
|
|
matrix3x3f R = matrix3x3f(m_qrot);
|
|
for(int i=0;i<=m_nSegs;i++) {
|
|
m_segs[i].pt = R*(m_segs[i].pt-prevpos)+m_pos;
|
|
m_segs[i].vel = R*m_segs[i].vel;
|
|
m_segs[i].dir = R*m_segs[i].dir;
|
|
}
|
|
m_qrot.SetIdentity();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
if (_params->type==pe_simulation_params::type_id) {
|
|
pe_simulation_params *params = (pe_simulation_params*)_params;
|
|
if (!is_unused(params->gravity)) m_gravity = params->gravity;
|
|
if (!is_unused(params->damping)) m_damping = params->damping;
|
|
if (!is_unused(params->maxTimeStep)) m_maxAllowedStep = params->maxTimeStep;
|
|
if (!is_unused(params->minEnergy)) m_Emin = params->minEnergy;
|
|
return 1;
|
|
}
|
|
|
|
if (_params->type==pe_params_rope::type_id) {
|
|
pe_params_rope *params = (pe_params_rope*)_params;
|
|
int i;
|
|
if (!is_unused(params->length)) m_length = params->length;
|
|
if (!is_unused(params->mass)) m_mass = params->mass;
|
|
if (!is_unused(params->collDist)) m_collDist = params->collDist;
|
|
if (!is_unused(params->surface_idx)) m_surface_idx = params->surface_idx;
|
|
if (!is_unused(params->friction)) m_friction = params->friction;
|
|
if (!is_unused(params->nSegments)) {
|
|
if (params->nSegments>m_nSegs) {
|
|
if (m_segs) delete[] m_segs;
|
|
m_segs = new rope_segment[params->nSegments+1];
|
|
memset(m_segs, 0, (params->nSegments+1)*sizeof(rope_segment));
|
|
float seglen = m_length/params->nSegments;
|
|
for(i=0;i<=params->nSegments;i++)
|
|
(m_segs[i].pt = m_pos).z -= seglen*i;
|
|
}
|
|
m_nSegs = params->nSegments;
|
|
}
|
|
if (!is_unused(params->pPoints) && params->pPoints)
|
|
for(i=0;i<=m_nSegs;i++) m_segs[i].pt = params->pPoints[i];
|
|
if (!is_unused(params->pVelocities) && params->pVelocities)
|
|
for(i=0;i<=m_nSegs;i++) m_segs[i].vel = params->pVelocities[i];
|
|
|
|
if (m_idConstraint && m_pTiedTo[m_iConstraintClient]) {
|
|
pe_action_remove_constraint arc;
|
|
arc.idConstraint = m_idConstraint;
|
|
m_pTiedTo[m_iConstraintClient]->Action(&arc);
|
|
m_idConstraint = 0;
|
|
}
|
|
for(i=0;i<2;i++) if (m_pTiedTo[i]) {
|
|
if (m_pTiedTo[i]->m_iSimClass==7)
|
|
m_pTiedTo[i] = 0;
|
|
else
|
|
m_pTiedTo[i]->Awake();
|
|
}
|
|
|
|
for(i=0;i<2;i++) if (!is_unused(params->pEntTiedTo[i])) {
|
|
if (m_pTiedTo[i]) m_pTiedTo[i]->Release();
|
|
m_pTiedTo[i] = params->pEntTiedTo[i]==WORLD_ENTITY ? &g_StaticPhysicalEntity :
|
|
(params->pEntTiedTo[i] ? ((CPhysicalPlaceholder*)params->pEntTiedTo[i])->GetEntity() : 0);
|
|
if (!is_unused(params->idPartTiedTo[i])) {
|
|
for(m_iTiedPart[i]=0; m_iTiedPart[i]<m_pTiedTo[i]->m_nParts &&
|
|
params->idPartTiedTo[i]!=m_pTiedTo[i]->m_parts[m_iTiedPart[i]].id; m_iTiedPart[i]++);
|
|
if (m_iTiedPart[i]>=m_pTiedTo[i]->m_nParts)
|
|
m_iTiedPart[i] = 0;
|
|
}
|
|
if (m_pTiedTo[i]) {
|
|
RigidBody *pbody = m_pTiedTo[i]->GetRigidBody(m_iTiedPart[i]);
|
|
if (!is_unused(params->ptTiedTo[i]))
|
|
m_ptTiedLoc[i] = (params->ptTiedTo[i]-pbody->pos)*pbody->q;
|
|
else if (!is_unused(params->pEntTiedTo[i]) || !is_unused(params->idPartTiedTo[i]))
|
|
m_ptTiedLoc[i] = (m_segs[m_nSegs*i].pt-pbody->pos)*pbody->q;
|
|
m_pTiedTo[i]->AddRef();
|
|
}
|
|
}
|
|
m_bAwake = 1;
|
|
m_nSlowFrames = 0;
|
|
|
|
if (m_pTiedTo[0] && (m_pTiedTo[0]==this || m_pTiedTo[0]->m_iSimClass==3 || m_pTiedTo[0]->GetType()==PE_ROPE || m_pTiedTo[0]->GetType()==PE_SOFT) ||
|
|
m_pTiedTo[1] && (m_pTiedTo[1]==this || m_pTiedTo[1]->m_iSimClass==3 || m_pTiedTo[1]->GetType()==PE_ROPE || m_pTiedTo[1]->GetType()==PE_SOFT))
|
|
{ // rope cannot be attached to such objects
|
|
if (m_pTiedTo[0]) m_pTiedTo[0]->Release();
|
|
if (m_pTiedTo[1]) m_pTiedTo[1]->Release();
|
|
m_pTiedTo[0] = 0; m_pTiedTo[1] = 0;
|
|
return 0;
|
|
}
|
|
|
|
if (m_pTiedTo[0] && m_pTiedTo[1]) {
|
|
pe_action_add_constraint aac;
|
|
m_iConstraintClient = isneg(m_pTiedTo[0]->GetMassInv()-m_pTiedTo[1]->GetMassInv());
|
|
for(i=0;i<2;i++) {
|
|
RigidBody *pbody = m_pTiedTo[m_iConstraintClient^i]->GetRigidBody(m_iTiedPart[m_iConstraintClient^i]);
|
|
aac.pt[i] = pbody->pos + pbody->q*m_ptTiedLoc[m_iConstraintClient^i];
|
|
aac.partid[i] = m_pTiedTo[m_iConstraintClient^i]->m_parts[m_iTiedPart[m_iConstraintClient^i]].id;
|
|
}
|
|
aac.pBuddy = m_pTiedTo[m_iConstraintClient^1];
|
|
aac.pConstraintEntity = this;
|
|
m_idConstraint = m_pTiedTo[m_iConstraintClient]->Action(&aac);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int CRopeEntity::GetParams(pe_params *_params)
|
|
{
|
|
int res;
|
|
if (res = CPhysicalEntity::GetParams(_params))
|
|
return res;
|
|
|
|
if (_params->type==pe_simulation_params::type_id) {
|
|
pe_simulation_params *params = (pe_simulation_params*)_params;
|
|
params->gravity = m_gravity;
|
|
params->damping = m_damping;
|
|
params->maxTimeStep = m_maxAllowedStep;
|
|
params->minEnergy = m_Emin;
|
|
return 1;
|
|
}
|
|
|
|
if (_params->type==pe_params_rope::type_id) {
|
|
pe_params_rope *params = (pe_params_rope*)_params;
|
|
params->length = m_length;
|
|
params->mass = m_mass;
|
|
params->nSegments = m_nSegs;
|
|
params->collDist = m_collDist;
|
|
params->surface_idx = m_surface_idx;
|
|
params->friction = m_friction;
|
|
params->pPoints = &m_segs[0].pt;
|
|
params->pVelocities = &m_segs[0].vel;
|
|
params->iStride = sizeof(rope_segment);
|
|
for(int i=0;i<2;i++) if (params->pEntTiedTo[i] = m_pTiedTo[i]) {
|
|
if (m_pTiedTo[i]==&g_StaticPhysicalEntity)
|
|
params->pEntTiedTo[i] = WORLD_ENTITY;
|
|
params->idPartTiedTo[i] = m_pTiedTo[i]->m_parts[m_iTiedPart[i]].id;
|
|
RigidBody *pbody = m_pTiedTo[i]->GetRigidBody(m_iTiedPart[i]);
|
|
params->ptTiedTo[i] = pbody->pos + pbody->q*m_ptTiedLoc[i];
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int CRopeEntity::GetStatus(pe_status *_status)
|
|
{
|
|
int res;
|
|
if (res = CPhysicalEntity::GetStatus(_status)) {
|
|
if (_status->type==pe_status_caps::type_id) {
|
|
pe_status_caps *status = (pe_status_caps*)_status;
|
|
status->bCanAlterOrientation = 1;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
if (_status->type==pe_status_rope::type_id) {
|
|
pe_status_rope *status = (pe_status_rope*)_status;
|
|
status->nSegments = m_nSegs;
|
|
int i;
|
|
if (!is_unused(status->pPoints) && status->pPoints)
|
|
for(i=0;i<=m_nSegs;i++) status->pPoints[i] = m_segs[i].pt;
|
|
if (!is_unused(status->pVelocities) && status->pVelocities)
|
|
for(i=0;i<=m_nSegs;i++) status->pVelocities[i] = m_segs[i].vel;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int CRopeEntity::Action(pe_action *_action)
|
|
{
|
|
int res,i,j;
|
|
if (res = CPhysicalEntity::Action(_action))
|
|
return res;
|
|
|
|
if (_action->type==pe_action_impulse::type_id) {
|
|
pe_action_impulse *action = (pe_action_impulse*)_action;
|
|
if (!is_unused(action->ipart)) i = action->ipart;
|
|
else if (!is_unused(action->partid)) i = action->partid;
|
|
else if (!is_unused(action->point)) {
|
|
for(j=1,i=0;j<=m_nSegs;j++) if ((m_segs[j].pt-action->point).len2()<(m_segs[i].pt-action->point).len2())
|
|
i=j;
|
|
if ((m_segs[i].pt-action->point).len2()>sqr(m_collDist*3) &&
|
|
(i==0 || (m_segs[i].pt-m_segs[i-1].pt^action->point-m_segs[i-1].pt).len2() > sqr(m_collDist)*(m_segs[i].pt-m_segs[i-1].pt).len2()))
|
|
return 0;
|
|
} else
|
|
return 0;
|
|
if ((unsigned int)i>(unsigned int)m_nSegs)
|
|
return 0;
|
|
m_segs[i].vel_ext += action->impulse*((m_nSegs+1)/m_mass);
|
|
m_bAwake = 1; m_nSlowFrames = 0;
|
|
return 1;
|
|
}
|
|
|
|
if (_action->type==pe_action_reset::type_id) {
|
|
for(i=0;i<=m_nSegs;i++) {
|
|
m_segs[i].vel.zero();
|
|
if (m_segs[i].pContactEnt) {
|
|
m_segs[i].pContactEnt->Release();
|
|
m_segs[i].pContactEnt = 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void CRopeEntity::StartStep(float time_interval)
|
|
{
|
|
m_timeStepPerformed = 0;
|
|
m_timeStepFull = time_interval;
|
|
}
|
|
|
|
|
|
float CRopeEntity::GetMaxTimeStep(float time_interval)
|
|
{
|
|
if (m_timeStepPerformed > m_timeStepFull-0.001f)
|
|
return time_interval;
|
|
return min(min(m_timeStepFull-m_timeStepPerformed,m_maxAllowedStep),time_interval);
|
|
}
|
|
|
|
int __ropeframe = 0;
|
|
|
|
|
|
int CRopeEntity::Step(float time_interval)
|
|
{
|
|
if (m_nSegs<=0 || !m_bAwake)
|
|
return 1;
|
|
|
|
int i;
|
|
for(i=0;i<=m_nSegs;i++) if (m_segs[i].pContactEnt && m_segs[i].pContactEnt->m_iSimClass==7)
|
|
m_segs[i].pContactEnt = 0;
|
|
for(i=0;i<2;i++) if (m_pTiedTo[i] && m_pTiedTo[i]->m_iSimClass==7)
|
|
m_pTiedTo[i] = 0;
|
|
|
|
if (m_timeStepPerformed > m_timeStepFull-0.001f)
|
|
return 1;
|
|
m_timeStepPerformed += time_interval;
|
|
|
|
FUNCTION_PROFILER( GetISystem(),PROFILE_PHYSICS );
|
|
PHYS_ENTITY_PROFILER
|
|
|
|
__ropeframe++;
|
|
|
|
float seglen=m_length/m_nSegs,seglen2=sqr(seglen), rseglen=m_nSegs/m_length,rseglen2=sqr(rseglen),Ebefore;
|
|
int iDir,iStart,iEnd,iter,bStretched,bHasContacts=0;
|
|
vectorf dir,ptend[2],sz;
|
|
float len2,diff,a,b,r2,r2new,pAp,vmax,k,E,damping=max(0.0f,1.0f-m_damping*time_interval);
|
|
|
|
for(i=0;i<=m_nSegs;i++) {
|
|
m_segs[i].pt += m_segs[i].vel*time_interval;
|
|
m_segs[i].vel += m_gravity*time_interval;
|
|
}
|
|
for(i=0;i<2;i++) if (m_pTiedTo[i]) {
|
|
RigidBody *pbody = m_pTiedTo[i]->GetRigidBody(m_iTiedPart[i]);
|
|
m_segs[m_nSegs*i].pt = ptend[i] = pbody->pos + pbody->q*m_ptTiedLoc[i];
|
|
m_segs[m_nSegs*i].vel = pbody->v + (pbody->w^ptend[i]-pbody->pos);
|
|
}
|
|
|
|
if (m_pTiedTo[0] && m_pTiedTo[1] && (ptend[1]-ptend[0]).len2()>sqr(m_length*0.99f)) {
|
|
float newlen=(ptend[1]-ptend[0]).len(), newseglen=newlen/m_nSegs;
|
|
dir = (ptend[1]-ptend[0]).normalized();
|
|
for(i=1;i<m_nSegs;i++) {
|
|
m_segs[i].dir = dir;
|
|
m_segs[i].pt = ptend[0] + m_segs[i].dir*(newseglen*i);
|
|
}
|
|
m_bAwake = m_pTiedTo[0]->GetStatus(&pe_status_awake()) | m_pTiedTo[1]->GetStatus(&pe_status_awake());
|
|
m_BBox[0].x = min(m_segs[0].pt.x,m_segs[m_nSegs].pt.x); m_BBox[1].x = max(m_segs[0].pt.x,m_segs[m_nSegs].pt.x);
|
|
m_BBox[0].y = min(m_segs[0].pt.y,m_segs[m_nSegs].pt.y); m_BBox[1].y = max(m_segs[0].pt.y,m_segs[m_nSegs].pt.y);
|
|
m_BBox[0].z = min(m_segs[0].pt.z,m_segs[m_nSegs].pt.z); m_BBox[1].z = max(m_segs[0].pt.z,m_segs[m_nSegs].pt.z);
|
|
a = max(max(m_BBox[1].x-m_BBox[0].x,m_BBox[1].y-m_BBox[0].y),m_BBox[1].z-m_BBox[0].z)*0.01f+m_collDist*2;
|
|
m_BBox[0] -= vectorf(a,a,a); m_BBox[1] += vectorf(a,a,a);
|
|
if (m_flags & pef_traceable)
|
|
m_pWorld->RepositionEntity(this,1);
|
|
return 1;
|
|
}
|
|
|
|
// first, ensure that all vertices have distance between them equal to seglen
|
|
iter=300;
|
|
iDir = m_pTiedTo[1] && !m_pTiedTo[0] ? 1:-1;
|
|
do {
|
|
iDir = -iDir;
|
|
iStart = m_nSegs & iDir>>31;
|
|
iEnd = m_nSegs & -iDir>>31;
|
|
|
|
for(i=iStart;i!=iEnd;i+=iDir) {
|
|
dir = m_segs[i+iDir].pt-m_segs[i].pt; len2 = dir.len2();
|
|
diff = fabs_tpl(len2-seglen2);
|
|
if (bStretched = (diff>seglen2*0.01f)) {
|
|
if (diff<seglen2*0.2f) { // use 3 terms of 1/sqrt(x) Taylor series expansion
|
|
k = len2*rseglen2; k = 1.875f-(1.25f-0.375f*k)*k;
|
|
} else
|
|
k = seglen/sqrt_tpl(len2);
|
|
m_segs[i+iDir].pt = m_segs[i].pt + dir*k;
|
|
iter--;
|
|
} else k = 1.0f;
|
|
m_segs[i+(iDir>>31)].dir = dir*(k*rseglen);
|
|
}
|
|
|
|
if (m_pTiedTo[0]) m_segs[0].pt = ptend[0];
|
|
if (m_pTiedTo[1]) m_segs[m_nSegs].pt = ptend[1];
|
|
} while(m_pTiedTo[iDir+1>>1] && bStretched && iter>0);
|
|
|
|
m_BBox[0] = m_BBox[1] = m_segs[0].pt;
|
|
for(i=0;i<=m_nSegs;i++) {
|
|
(m_segs[i].vel += m_segs[i].vel_ext) *= damping;
|
|
m_segs[i].vel_ext.zero();
|
|
m_segs[i].bRecalcDir = 0;
|
|
m_BBox[0].x = min(m_BBox[0].x, m_segs[i].pt.x); m_BBox[1].x = max(m_BBox[1].x, m_segs[i].pt.x);
|
|
m_BBox[0].y = min(m_BBox[0].y, m_segs[i].pt.y); m_BBox[1].y = max(m_BBox[1].y, m_segs[i].pt.y);
|
|
m_BBox[0].z = min(m_BBox[0].z, m_segs[i].pt.z); m_BBox[1].z = max(m_BBox[1].z, m_segs[i].pt.z);
|
|
}
|
|
a = max(max(m_BBox[1].x-m_BBox[0].x,m_BBox[1].y-m_BBox[0].y),m_BBox[1].z-m_BBox[0].z)*0.01f+m_collDist*2;
|
|
m_BBox[0] -= vectorf(a,a,a); m_BBox[1] += vectorf(a,a,a);
|
|
|
|
if (m_flags & rope_collides) {
|
|
CPhysicalEntity **pentlist;
|
|
int j,iseg,nEnts,nCheckParts,nLocChecks,iend;
|
|
box boxrope,boxpart;
|
|
vectorf center,ptres[2],rotax,n;
|
|
float angle,dist2,t,cost,sint;
|
|
struct check_part {
|
|
vectorf offset;
|
|
matrix3x3f R;
|
|
float scale;
|
|
box bbox;
|
|
CPhysicalEntity *pent;
|
|
int ipart;
|
|
};
|
|
check_part checkParts[20];
|
|
CRayGeom aray; aray.m_iCollPriority = 10;
|
|
intersection_params ip;
|
|
geom_contact *pcontact;
|
|
CPhysicalEntity *pent;
|
|
geom_world_data gwd;
|
|
|
|
g_Overlapper.Init();
|
|
boxrope.size = (m_BBox[1]-m_BBox[0])*0.5f;
|
|
center = (m_BBox[0]+m_BBox[1])*0.5f;
|
|
ip.bStopAtFirstTri = true;
|
|
ip.iUnprojectionMode = 1;
|
|
|
|
nEnts = m_pWorld->GetEntitiesAround(m_BBox[0],m_BBox[1], pentlist,
|
|
(m_flags & rope_collides_with_terrain ? ent_terrain:0) |
|
|
ent_static|ent_sleeping_rigid|ent_rigid|ent_living|ent_sort_by_mass|ent_ignore_noncolliding|ent_triggers, this);
|
|
|
|
for(i=j=nCheckParts=0;i<nEnts;i++) if (pentlist[i]!=this)
|
|
for(j=0;j<pentlist[i]->m_nParts;j++) {
|
|
checkParts[nCheckParts].offset = pentlist[i]->m_pos+pentlist[i]->m_qrot*pentlist[i]->m_parts[j].pos;
|
|
//(pentlist[i]->m_qrot*pentlist[i]->m_parts[j].q).getmatrix(boxrope.Basis); //Q2M_IVO
|
|
boxrope.Basis = matrix3x3f(pentlist[i]->m_qrot*pentlist[i]->m_parts[j].q);
|
|
boxrope.center = (center-checkParts[nCheckParts].offset)*boxrope.Basis;
|
|
if (pentlist[i]->m_parts[j].pPhysGeomProxy->pGeom->GetType()!=GEOM_HEIGHTFIELD) {
|
|
pentlist[i]->m_parts[j].pPhysGeomProxy->pGeom->GetBBox(&checkParts[nCheckParts].bbox);
|
|
checkParts[nCheckParts].bbox.center *= pentlist[i]->m_parts[j].scale;
|
|
checkParts[nCheckParts].bbox.size *= pentlist[i]->m_parts[j].scale;
|
|
} else
|
|
checkParts[nCheckParts].bbox = boxrope;
|
|
boxrope.bOriented++;
|
|
if (box_box_overlap_check(&boxrope,&checkParts[nCheckParts].bbox)) {
|
|
checkParts[nCheckParts].pent = pentlist[i];
|
|
checkParts[nCheckParts].ipart = j;
|
|
checkParts[nCheckParts].R = boxrope.Basis;
|
|
checkParts[nCheckParts].scale = pentlist[i]->m_parts[j].scale;
|
|
pentlist[i]->m_parts[j].pPhysGeomProxy->pGeom->PrepareForRayTest(
|
|
pentlist[i]->m_parts[j].scale==1.0f ? seglen:seglen/pentlist[i]->m_parts[j].scale);
|
|
if (++nCheckParts==sizeof(checkParts)/sizeof(checkParts[0]))
|
|
goto enoughgeoms;
|
|
}
|
|
} enoughgeoms:
|
|
|
|
if (m_pTiedTo[0] && m_pTiedTo[1])
|
|
iDir = isneg(m_pTiedTo[1]->GetRigidBody(m_iTiedPart[1])->Minv-m_pTiedTo[0]->GetRigidBody(m_iTiedPart[0])->Minv);
|
|
else
|
|
iDir = iszero((intptr_t)m_pTiedTo[0]);
|
|
iDir = 1-iDir*2;
|
|
iStart = m_nSegs & iDir>>31;
|
|
iEnd = m_nSegs & -iDir>>31;
|
|
for(i=iStart;i!=iEnd;i+=iDir) {
|
|
iseg = i+(iDir>>31);
|
|
if (pent=m_segs[iseg].pContactEnt) {
|
|
//(pent->m_qrot*pent->m_parts[m_segs[iseg].iContactPart].q).getmatrix(gwd.R); //Q2M_IVO
|
|
gwd.R = matrix3x3f(pent->m_qrot*pent->m_parts[m_segs[iseg].iContactPart].q);
|
|
gwd.offset = pent->m_pos + pent->m_qrot*pent->m_parts[m_segs[iseg].iContactPart].pos;
|
|
gwd.scale = pent->m_parts[m_segs[iseg].iContactPart].scale;
|
|
if (!m_segs[iseg].bRecheckContact &&
|
|
pent->m_parts[m_segs[iseg].iContactPart].pPhysGeomProxy->pGeom->FindClosestPoint(&gwd, m_segs[iseg].iPrim,m_segs[iseg].iFeature,
|
|
m_segs[i+iDir].pt,m_segs[i].pt, ptres)>=0 &&
|
|
(dist2=(n=ptres[1]-ptres[0]).len2())<sqr(m_collDist*2) && // drop contact when gap becomes too big
|
|
sqr_signed(m_segs[iseg].vreq=n*m_segs[iseg].ncontact)>n.len2()*sqr(0.75f)) // drop contact if normal changes abruptly
|
|
{
|
|
n.normalize();
|
|
m_segs[i].tcontact = (ptres[1]-m_segs[iseg].pt).len()*rseglen;
|
|
if (dist2<sqr(m_collDist*0.85f)) { // reinforce the distance
|
|
t = (iDir>>31&1) + m_segs[i].tcontact*iDir; // 1.0-t for iDir==-1
|
|
j = isneg(t-0.1f); t += j; j = -j & iDir;
|
|
if ((unsigned int)i-j<=(unsigned int)m_nSegs) {
|
|
rotax = (m_segs[i+iDir-j].pt-m_segs[i-j].pt^n).normalized();
|
|
angle = (m_collDist-sqrt_tpl(dist2))/(t*seglen);
|
|
m_segs[i+iDir-j].pt = m_segs[i+iDir-j].pt.rotated(m_segs[i-j].pt, rotax, cos_tpl(angle),sin_tpl(angle));
|
|
m_segs[i+iDir-j].bRecalcDir = m_segs[max(0,i+iDir-j-1)].bRecalcDir = 1;
|
|
}
|
|
m_segs[iseg].vreq = 0;
|
|
} else
|
|
m_segs[iseg].vreq = (m_collDist-m_segs[iseg].vreq)*10.0f;
|
|
m_segs[i].ncontact = n;
|
|
RigidBody *pbody = m_segs[iseg].pContactEnt->GetRigidBody(m_segs[iseg].iContactPart);
|
|
m_segs[iseg].vcontact = pbody->v+(pbody->w^ptres[0]-pbody->pos);
|
|
bHasContacts = 1;
|
|
} else {
|
|
m_segs[iseg].pContactEnt->Release();
|
|
m_segs[iseg].pContactEnt = 0;
|
|
}
|
|
}
|
|
|
|
if (!m_segs[iseg].pContactEnt) for(j=nLocChecks=0;j<nCheckParts && nLocChecks<6;j++) {
|
|
aray.m_ray.origin = (m_segs[i].pt-checkParts[j].offset)*checkParts[j].R;
|
|
aray.m_ray.dir = (m_segs[i+iDir].pt-checkParts[j].offset)*checkParts[j].R-aray.m_ray.origin;
|
|
if (box_ray_overlap_check(&checkParts[j].bbox,&aray.m_ray)) {
|
|
ip.centerOfRotation = aray.m_ray.origin;
|
|
gwd.offset.zero(); gwd.R.SetIdentity(); gwd.scale=checkParts[j].scale;
|
|
if (checkParts[j].pent->m_parts[checkParts[j].ipart].pPhysGeomProxy->pGeom->Intersect(&aray,&gwd,0,&ip,pcontact) &&
|
|
pcontact->iUnprojMode==1)
|
|
{
|
|
if (pcontact->t>(real)0.5) {
|
|
pcontact->t=(real)0.5; m_segs[iseg].bRecheckContact=1;
|
|
} else
|
|
m_segs[iseg].bRecheckContact=0;
|
|
if (m_segs[iseg].pContactEnt)
|
|
m_segs[iseg].pContactEnt->Release();
|
|
m_segs[iseg].pContactEnt = 0;
|
|
diff = m_segs[iseg].tcontact = (pcontact->pt-aray.m_ray.origin).len();
|
|
m_segs[iseg].tcontact = m_segs[iseg].tcontact*iDir-seglen*(iDir>>31); // flip tcontact when iDir is -1
|
|
iend = -iseg>>31 & 1;
|
|
if ((unsigned int)(iseg-1)>=(unsigned int)(m_nSegs-2) && m_pTiedTo[iend] && isneg(m_segs[iseg].tcontact*rseglen-0.5f)^iend)
|
|
continue; // ignore collisions too close to tied ends
|
|
(m_segs[iseg].pContactEnt = checkParts[j].pent)->AddRef();
|
|
m_segs[iseg].iContactPart = checkParts[j].ipart;
|
|
rotax = checkParts[j].R*-pcontact->dir;
|
|
if (m_collDist > diff*0.25f) {
|
|
if ((unsigned int)(i-iDir)>(unsigned int)m_nSegs)
|
|
continue;
|
|
// the contact is too close to the segment start (requires >15 deg. gap unproj), rotate start point to get the safe gap
|
|
float tgap = m_collDist/seglen;
|
|
cost = cos_tpl(tgap); sint = sin_tpl(tgap);
|
|
m_segs[i].pt = m_segs[i].pt.rotated(m_segs[i-iDir].pt, rotax, cost,sint);
|
|
m_segs[i+iDir].pt = m_segs[i+iDir].pt.rotated(m_segs[i-iDir].pt, rotax, cost,sint);
|
|
m_segs[i].bRecalcDir = m_segs[max(0,i-1)].bRecalcDir = 1;
|
|
} else
|
|
pcontact->t += m_collDist/diff;
|
|
cost = cos_tpl(pcontact->t); sint = sin_tpl(pcontact->t);
|
|
m_segs[iseg].ncontact = checkParts[j].R*(pcontact->n.normalized().rotated(pcontact->dir,cost,-sint));
|
|
m_segs[iseg].tcontact *= rseglen;
|
|
m_segs[i+iDir].pt = m_segs[i+iDir].pt.rotated(m_segs[i].pt, rotax, cost,sint);
|
|
aray.m_ray.dir = (m_segs[i+iDir].pt-checkParts[j].offset)*checkParts[j].R-aray.m_ray.origin;
|
|
m_segs[iseg].iPrim = pcontact->iPrim[0];
|
|
m_segs[iseg].iFeature = pcontact->iFeature[0];
|
|
/*m_segs[iseg].friction[0] = 0.5f*max(0.0f, m_pWorld->m_FrictionTable[m_surface_idx&NSURFACETYPES-1] +
|
|
m_pWorld->m_FrictionTable[pcontact->id[0]&NSURFACETYPES-1]);
|
|
m_segs[iseg].friction[1] = 0.5f*max(0.0f, m_pWorld->m_DynFrictionTable[m_surface_idx&NSURFACETYPES-1] +
|
|
m_pWorld->m_DynFrictionTable[pcontact->id[0]&NSURFACETYPES-1]);*/
|
|
m_segs[iseg].friction[0] = m_segs[iseg].friction[1] = m_friction;
|
|
RigidBody *pbody = m_segs[iseg].pContactEnt->GetRigidBody(m_segs[iseg].iContactPart);
|
|
m_segs[iseg].vcontact = pbody->v+(pbody->w^pcontact->pt-pbody->pos);
|
|
m_segs[i+iDir].bRecalcDir = m_segs[max(0,i+iDir-1)].bRecalcDir = 1;
|
|
m_segs[iseg].vreq = 0;
|
|
bHasContacts = 1;
|
|
//break;
|
|
}
|
|
//nLocChecks++;
|
|
}
|
|
}
|
|
}
|
|
|
|
for(i=0;i<m_nSegs;i++) if (m_segs[i].bRecalcDir)
|
|
m_segs[i].dir = (m_segs[i+1].pt-m_segs[i].pt).normalized();
|
|
}
|
|
|
|
for(i=0,Ebefore=0;i<=m_nSegs;i++)
|
|
Ebefore += m_segs[i].vel.len2();
|
|
|
|
if (bHasContacts) {
|
|
int iter,bBounced;
|
|
float vrel,dPtang;
|
|
vectorf dp;
|
|
|
|
for(i=0;i<m_nSegs;i++)
|
|
m_segs[i].kdP = 0.5f;
|
|
if (m_pTiedTo[0]) m_segs[0].kdP=0.0f;
|
|
if (m_pTiedTo[1]) m_segs[m_nSegs-1].kdP=1.0f;
|
|
|
|
// solve for velocities using relaxation solver with friction
|
|
iter = 200;
|
|
do {
|
|
for(i=bBounced=0;i<m_nSegs;i++) {
|
|
if (fabsf(vrel=(m_segs[i+1].vel-m_segs[i].vel)*m_segs[i].dir) > seglen*0.005f) {
|
|
m_segs[i].vel += m_segs[i].dir*(vrel*m_segs[i].kdP);
|
|
m_segs[i+1].vel -= m_segs[i].dir*(vrel*(1.0f-m_segs[i].kdP));
|
|
bBounced++;
|
|
}
|
|
if (m_segs[i].pContactEnt) {
|
|
dp = m_segs[i].vel*(1.0f-m_segs[i].tcontact)+m_segs[i+1].vel*m_segs[i].tcontact-m_segs[i].vcontact;
|
|
if ((vrel=dp*m_segs[i].ncontact-m_segs[i].vreq) < -seglen*0.005f) {
|
|
if (m_segs[i].friction[0]>0.01f) {
|
|
m_segs[i].dP -= dPtang=sqrt_tpl(max(0.0f,dp.len2()-sqr(vrel)));
|
|
if (m_segs[i].dP-vrel*m_segs[i].friction[0] < 0) { // friction cannot stop sliding
|
|
dp += (dp-m_segs[i].ncontact*vrel)*((m_segs[i].dP-vrel*m_segs[i].friction[1])/dPtang); // remove part of dp that friction cannot stop
|
|
dp *= vrel/(m_segs[i].ncontact*dp); // apply impulse along dp so that it stops normal component
|
|
m_segs[i].dP = 0;
|
|
} else
|
|
m_segs[i].dP -= vrel*m_segs[i].friction[0];
|
|
} else
|
|
dp = m_segs[i].ncontact*vrel;
|
|
m_segs[i].vel -= dp*((1.0f-m_segs[i].tcontact)*m_segs[i].kdP);
|
|
m_segs[i+1].vel -= dp*(m_segs[i].tcontact*(1.0f-m_segs[i].kdP));
|
|
bBounced++;
|
|
}
|
|
}
|
|
}
|
|
} while (bBounced && --iter);
|
|
} else {
|
|
// solve for velocities using conjugate gradient
|
|
for(i=0,r2=0;i<m_nSegs;i++) {
|
|
r2 += sqr(m_segs[i].dP = m_segs[i].r = (m_segs[i+1].vel-m_segs[i].vel)*m_segs[i].dir);
|
|
m_segs[i].cosnext = m_segs[i].dir*m_segs[i+1].dir;
|
|
m_segs[i].kdP = 2.0f;
|
|
m_segs[i].P = 0;
|
|
}
|
|
if (m_pTiedTo[0]) m_segs[0].kdP=1.0f;
|
|
if (m_pTiedTo[1]) m_segs[m_nSegs-1].kdP=1.0f;
|
|
iter = m_nSegs*3;
|
|
|
|
do {
|
|
m_segs[0].dv = m_segs[0].dP*m_segs[0].kdP;
|
|
m_segs[1].dv = -m_segs[0].cosnext*m_segs[0].dP;
|
|
for(i=1;i<m_nSegs;i++) {
|
|
m_segs[i-1].dv -= m_segs[i-1].cosnext*m_segs[i].dP;
|
|
m_segs[i].dv += m_segs[i].dP*m_segs[i].kdP;
|
|
m_segs[i+1].dv = -m_segs[i].cosnext*m_segs[i].dP;
|
|
}
|
|
|
|
for(i=0,pAp=0;i<m_nSegs;i++)
|
|
pAp += m_segs[i].dv*m_segs[i].dP;
|
|
if (fabs_tpl(pAp)<1E-10) break;
|
|
a = r2/pAp;
|
|
for(i=0,r2new=0;i<m_nSegs;i++) {
|
|
r2new += sqr(m_segs[i].r -= m_segs[i].dv*a);
|
|
m_segs[i].P += m_segs[i].dP*a;
|
|
}
|
|
if (r2new>r2*500)
|
|
break;
|
|
b = r2new/r2; r2 = r2new;
|
|
for(i=0,vmax=0;i<m_nSegs;i++) {
|
|
(m_segs[i].dP*=b) += m_segs[i].r;
|
|
vmax = max(vmax,sqr(m_segs[i].r));
|
|
}
|
|
} while(--iter && vmax>sqr(0.003f));
|
|
|
|
if (!m_pTiedTo[0]) m_segs[0].vel += m_segs[0].dir*m_segs[0].P;
|
|
if (!m_pTiedTo[1]) m_segs[m_nSegs].vel -= m_segs[m_nSegs-1].dir*m_segs[m_nSegs-1].P;
|
|
m_segs[1].vel -= m_segs[0].dir*m_segs[0].P;
|
|
m_segs[m_nSegs-1].vel += m_segs[m_nSegs-1].dir*m_segs[m_nSegs-1].P;
|
|
for(i=1;i<m_nSegs-1;i++) {
|
|
m_segs[i].vel += m_segs[i].dir*m_segs[i].P;
|
|
m_segs[i+1].vel -= m_segs[i].dir*m_segs[i].P;
|
|
}
|
|
}
|
|
|
|
for(i=0,E=0;i<=m_nSegs;i++) E += m_segs[i].vel.len2();
|
|
if (E>Ebefore && E>m_Emin) {
|
|
k = sqrt_tpl(Ebefore/E);
|
|
for(i=0;i<=m_nSegs;i++) m_segs[i].vel*=k;
|
|
E = Ebefore;
|
|
}
|
|
i = -isneg(E-m_Emin*(m_nSegs+1));
|
|
m_nSlowFrames = (m_nSlowFrames&i)-i;
|
|
if (!(m_bAwake = isneg(m_nSlowFrames-4)))
|
|
for(i=iszero((intptr_t)m_pTiedTo[0])^1; i<m_nSegs+iszero((intptr_t)m_pTiedTo[1]); i++)
|
|
m_segs[i].vel.zero();
|
|
|
|
m_pos = m_segs[0].pt;
|
|
if (m_flags & pef_traceable)
|
|
m_pWorld->RepositionEntity(this,1);
|
|
|
|
return isneg(m_timeStepFull-m_timeStepPerformed-0.001f);
|
|
}
|
|
|
|
|
|
int CRopeEntity::RayTrace(CRayGeom *pRay,geom_contact *&pcontacts)
|
|
{
|
|
static geom_contact g_RopeContact;
|
|
vectorf dp,dir,l,pt;
|
|
float t,t1,llen2,raylen=pRay->m_ray.dir*pRay->m_dirn;
|
|
int i;
|
|
|
|
for(i=0;i<m_nSegs;i++) {
|
|
dp = pRay->m_ray.origin-m_segs[i].pt;
|
|
if ((dp^pRay->m_dirn).len2()<sqr(m_collDist) && inrange((m_segs[i].pt-pRay->m_ray.origin)*pRay->m_dirn, 0.0f,raylen)) {
|
|
pt = m_segs[i].pt; break;
|
|
}
|
|
dir = m_segs[i+1].pt-m_segs[i].pt;
|
|
l = dir^pRay->m_dirn; llen2 = l.len2();
|
|
t = (dp^pRay->m_dirn)*l; t1 = (dp^dir)*l;
|
|
if (sqr(dp*l)<sqr(m_collDist)*llen2 && inrange(t, 0.0f,llen2) && inrange(t1, 0.0f,llen2*raylen)) {
|
|
pt = m_segs[i].pt+dir*(t/llen2); break;
|
|
}
|
|
}
|
|
|
|
if (i<m_nSegs) {
|
|
pcontacts = &g_RopeContact;
|
|
pcontacts->pt = pt;
|
|
pcontacts->t = (pt-pRay->m_ray.origin)*pRay->m_dirn;
|
|
pcontacts->id[0] = m_surface_idx;
|
|
pcontacts->iNode[0] = 0;
|
|
pcontacts->n = -pRay->m_dirn;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int CRopeEntity::GetStateSnapshot(CStream &stm,float time_back,int flags)
|
|
{
|
|
stm.WriteNumberInBits(SNAPSHOT_VERSION,4);
|
|
WritePacked(stm,m_nSegs);
|
|
|
|
if (m_nSegs>0) for(int i=0; i<=m_nSegs; i++) {
|
|
stm.Write(m_segs[i].pt);
|
|
if (m_segs[i].vel.len2()>0) {
|
|
stm.Write(true);
|
|
stm.Write(m_segs[i].vel);
|
|
} else stm.Write(false);
|
|
}
|
|
stm.Write(m_bAwake!=0);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
int CRopeEntity::SetStateFromSnapshot(CStream &stm, int flags)
|
|
{
|
|
int i,ver=0;
|
|
bool bnz;
|
|
|
|
stm.ReadNumberInBits(ver,4);
|
|
if (ver!=SNAPSHOT_VERSION)
|
|
return 0;
|
|
ReadPacked(stm,i);
|
|
if (i!=m_nSegs)
|
|
return 0;
|
|
|
|
if (!(flags & ssf_no_update)) {
|
|
if (m_nSegs>0) for(i=0; i<=m_nSegs; i++) {
|
|
stm.Read(m_segs[i].pt);
|
|
stm.Read(bnz); if (bnz)
|
|
stm.Read(m_segs[i].vel);
|
|
else
|
|
m_segs[i].vel.zero();
|
|
if (m_segs[i].pContactEnt) {
|
|
m_segs[i].pContactEnt->Release();
|
|
m_segs[i].pContactEnt = 0;
|
|
}
|
|
}
|
|
stm.Read(bnz);
|
|
m_bAwake = bnz ? 1:0;
|
|
|
|
for(i=0;i<m_nSegs;i++)
|
|
m_segs[i].dir = (m_segs[i+1].pt-m_segs[i].pt).normalized();
|
|
m_pos = m_segs[0].pt;
|
|
if (m_flags & pef_traceable)
|
|
m_pWorld->RepositionEntity(this,1);
|
|
} else {
|
|
for(i=0;i<=m_nSegs;i++) {
|
|
stm.Seek(stm.GetReadPos()+sizeof(vectorf)*8);
|
|
stm.Read(bnz); if (bnz)
|
|
stm.Seek(stm.GetReadPos()+sizeof(vectorf)*8);
|
|
}
|
|
stm.Read(bnz);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
void CRopeEntity::DrawHelperInformation(void (*DrawLineFunc)(float*,float*), int flags)
|
|
{
|
|
CPhysicalEntity::DrawHelperInformation(DrawLineFunc,flags);
|
|
|
|
int i;
|
|
if (flags & pe_helper_geometry) for(i=0;i<m_nSegs;i++)
|
|
DrawLineFunc(m_segs[i].pt,m_segs[i+1].pt);
|
|
if (flags & pe_helper_collisions) for(i=0;i<m_nSegs;i++) if (m_segs[i].pContactEnt) {
|
|
vectorf pt = m_segs[i].pt+(m_segs[i+1].pt-m_segs[i].pt)*m_segs[i].tcontact;
|
|
DrawLineFunc(pt,pt+m_segs[i].ncontact*m_pWorld->m_vars.maxContactGap*30);
|
|
}
|
|
}
|
|
|
|
|
|
void CRopeEntity::GetMemoryStatistics(ICrySizer *pSizer)
|
|
{
|
|
CPhysicalEntity::GetMemoryStatistics(pSizer);
|
|
if (GetType()==PE_ROPE)
|
|
pSizer->AddObject(this, sizeof(CRopeEntity));
|
|
pSizer->AddObject(m_segs, m_nSegs*sizeof(m_segs[0]));
|
|
} |