void Ball::Collide(CollisionEvent *coll) { Ball *pball = coll->ball; const Vertex3Ds vnormal = coll->normal[0]; if (pball->fFrozen) return; // correct displacements, mostly from low velocity, alternative to true acceleration processing // target ball to object ball delta velocity const Vertex3Ds impulse = pball->collisionMass * pball->vel - collisionMass * vel; float dot = impulse.Dot(vnormal); if (dot >= -C_LOWNORMVEL ) // nearly receding ... make sure of conditions { // otherwise if clearly approaching .. process the collision if (dot > C_LOWNORMVEL) return; //is this velocity clearly receding (i.e must > a minimum) #ifdef C_EMBEDDED if (coll->distance < -C_EMBEDDED) dot = -C_EMBEDSHOT; // has ball become embedded???, give it a kick else return; #endif } #ifdef C_DISP_GAIN float edist = -C_DISP_GAIN * coll->distance; if (edist > 1.0e-4f) { if (edist > C_DISP_LIMIT) edist = C_DISP_LIMIT; // crossing ramps, delta noise if (!fFrozen) edist *= 0.5f; // if the hitten ball is not frozen pball->pos += edist * vnormal;// push along norm, back to free area // use the norm, but is not correct, but cheaply handled } edist = -C_DISP_GAIN * m_coll.distance; // noisy value .... needs investigation if (!fFrozen && edist > 1.0e-4f) { if (edist > C_DISP_LIMIT) edist = C_DISP_LIMIT; // crossing ramps, delta noise edist *= 0.5f; pos -= edist * vnormal; // pull along norm, back to free area } #endif const float averageMass = (collisionMass + pball->collisionMass)*0.5f; const float impulse1 = ((float)(-1.8 * 0.5) * dot) * pball->collisionMass / (averageMass * collisionMass); float impulse2 = ((float)(-1.8 * 0.5) * dot) * collisionMass / (averageMass * pball->collisionMass); if (!fFrozen) { vel -= impulse1 * vnormal; m_fDynamic = C_DYNAMIC; } else impulse2 += impulse1; pball->vel += impulse2 * vnormal; pball->m_fDynamic = C_DYNAMIC; }
//simple hacked together load function, assumes file exists and mesh is initialized to default void WarlockeryModelLoader::LoadWarlockeryModelFileToMesh(const std::string& fileName, Mesh* mesh, bool useCommonFilePath){ const std::string WarlockeryModelExt = ".c23"; std::string fullModelPath = fileName + WarlockeryModelExt; if (useCommonFilePath) fullModelPath = COMMON_MODEL_FILE_PATH + fileName + "/" + fileName + WarlockeryModelExt; BinaryFileParser WarlockeryModelParser = BinaryFileParser(fullModelPath); m_fileHeader.ReadFileHeader(WarlockeryModelParser); if (VerifyWarlockeryModelFile(&m_fileHeader)){ //read num vertices //mesh = new Mesh(); mesh->m_numVerticesToDraw = WarlockeryModelParser.ReadNextUInt(); //read to vertex 3Ds Vertex3Ds meshVerts; meshVerts.clear(); meshVerts.reserve(mesh->m_numVerticesToDraw * sizeof(Vertex3D)); for (unsigned int i = 0; i < mesh->m_numVerticesToDraw; i++){ meshVerts.push_back(WarlockeryModelParser.ReadNextVertex3D()); } mesh->CopyMeshVertexData(meshVerts); //bind indices mesh->m_numIndicesToDraw = WarlockeryModelParser.ReadNextUInt(); //DEBUG temporarily test lighting with vertex arrays mesh->m_numIndicesToDraw = 0; std::vector<unsigned int> meshIndices; meshIndices.clear(); meshIndices.reserve(mesh->m_numIndicesToDraw * sizeof(unsigned int)); if (mesh->m_numIndicesToDraw > 0){ for (unsigned int i = 0; i < mesh->m_numIndicesToDraw; i++){ meshIndices.push_back(WarlockeryModelParser.ReadNextUInt()); } mesh->CopyMeshIndexData(meshIndices); } else{ mesh->m_numIndicesToDraw = 0; } } }
void HitObject::FireHitEvent(Ball* pball) { if (m_pfe && m_fEnabled) { // is this the same place as last event? if same then ignore it const Vertex3Ds dist = pball->m_Event_Pos - pball->m_pos; pball->m_Event_Pos = pball->m_pos; //remember last collide position if (dist.LengthSquared() > 0.25f) // must be a new place if only by a little m_pfe->FireGroupEvent(DISPID_HitEvents_Hit); } }
void Ball::UpdateDisplacements(const float dtime) { if (!fFrozen) { const Vertex3Ds ds = dtime * vel; pos += ds; drsq = ds.LengthSquared(); // used to determine if static ball if (vel.z < 0.f && pos.z <= z_min) //rolling point below the table and velocity driving deeper { pos.z = z_min; // set rolling point to table surface vel.z *= -0.2f; // reflect velocity ... dull bounce vel.x *= c_hardFriction; vel.y *= c_hardFriction; //friction other axiz const Vertex3Ds vnormal(0.0f,0.0f,1.0f); AngularAcceleration(vnormal); } else if (vel.z > 0.f && pos.z >= z_max) //top glass ...contact and going higher { pos.z = z_max; // set diametric rolling point to top glass vel.z *= -0.2f; // reflect velocity ... dull bounce } // side walls are handled via actual collision objects set up in Player::CreateBoundingHitShapes CalcHitRect(); Matrix3 mat3; mat3.CreateSkewSymmetric(m_angularvelocity); Matrix3 addedorientation; addedorientation.MultiplyMatrix(&mat3, &m_orientation); addedorientation.MultiplyScalar(dtime); m_orientation.AddMatrix(&addedorientation, &m_orientation); m_orientation.OrthoNormalize(); Matrix3 matTransposeOrientation; m_orientation.Transpose(&matTransposeOrientation); m_inverseworldinertiatensor.MultiplyMatrix(&m_orientation,&m_inversebodyinertiatensor); m_inverseworldinertiatensor.MultiplyMatrix(&m_inverseworldinertiatensor,&matTransposeOrientation); m_angularvelocity = m_inverseworldinertiatensor.MultiplyVector(m_angularmomentum); } }
void LineSegSlingshot::Collide(CollisionEvent* coll) { Ball *pball = coll->ball; const Vertex3Ds& hitnormal = coll->hitnormal; const float dot = pball->m_vel.x * hitnormal.x + pball->m_vel.y * hitnormal.y; // normal velocity to slingshot const bool threshold = (dot <= -m_psurface->m_d.m_slingshot_threshold); // normal greater than threshold? if (!m_psurface->m_fDisabled && threshold) // enabled and if velocity greater than threshold level { const float len = (v2.x - v1.x)*hitnormal.y - (v2.y - v1.y)*hitnormal.x; // length of segment, Unit TAN points from V1 to V2 const Vertex2D vhitpoint(pball->m_pos.x - hitnormal.x * pball->m_radius, //project ball radius along norm pball->m_pos.y - hitnormal.y * pball->m_radius); // vhitpoint will now be the point where the ball hits the line // Calculate this distance from the center of the slingshot to get force const float btd = (vhitpoint.x - v1.x)*hitnormal.y - (vhitpoint.y - v1.y)*hitnormal.x; // distance to vhit from V1 float force = (len != 0.0f) ? ((btd+btd)/len - 1.0f) : -1.0f; // -1..+1 force = 0.5f *(1.0f-force*force); //!! maximum value 0.5 ...I think this should have been 1.0...oh well // will match the previous physics force *= m_force;//-80; pball->m_vel.x -= hitnormal.x * force; // boost velocity, drive into slingshot (counter normal) pball->m_vel.y -= hitnormal.y * force; // allow CollideWall to handle the remainder } pball->Collide2DWall(hitnormal, m_elasticity, m_elasticityFalloff, m_friction, m_scatter); if (m_pfe && !m_psurface->m_fDisabled && threshold) { // is this the same place as last event? if same then ignore it const Vertex3Ds dist = pball->m_Event_Pos - pball->m_pos; pball->m_Event_Pos = pball->m_pos; //remember last collide position if (dist.LengthSquared() > 0.25f) // must be a new place if only by a little { m_pfe->FireGroupEvent(DISPID_SurfaceEvents_Slingshot); m_slingshotanim.m_TimeReset = g_pplayer->m_time_msec + 100; } } }
HitLine3D::HitLine3D(const Vertex3Ds& v1, const Vertex3Ds& v2) { Vertex3Ds vLine = v2 - v1; vLine.Normalize(); // Axis of rotation to make 3D cylinder a cylinder along the z-axis Vertex3Ds transaxis; /*const Vertex3Ds vup(0,0,1.0f); CrossProduct(vLine, vup, &transaxis);*/ transaxis.x = vLine.y; transaxis.y = -vLine.x; transaxis.z = 0.0f; const float l = transaxis.LengthSquared(); if (l <= 1e-6f) // line already points in z axis? transaxis.Set(1.f, 0.f, 0.f); // choose arbitrary rotation vector else transaxis /= sqrtf(l); // Angle to rotate the line into the z-axis const float dot = vLine.z; //vLine.Dot(&vup); //const float transangle = acosf(dot); //matTrans.RotationAroundAxis(transaxis, -transangle); m_matrix.RotationAroundAxis(transaxis,-sqrtf(1.f-dot*dot),dot); const Vertex3Ds vtrans1 = m_matrix * v1; const float vtrans2z = (m_matrix * v2).z; // set up HitLineZ parameters m_xy.x = vtrans1.x; m_xy.y = vtrans1.y; m_zlow = min(vtrans1.z, vtrans2z); m_zhigh = max(vtrans1.z, vtrans2z); m_hitBBox.left = min(v1.x, v2.x); m_hitBBox.right = max(v1.x, v2.x); m_hitBBox.top = min(v1.y, v2.y); m_hitBBox.bottom = max(v1.y, v2.y); m_hitBBox.zlow = min(v1.z, v2.z); m_hitBBox.zhigh = max(v1.z, v2.z); }
void Ball::AngularAcceleration(const Vertex3Ds& hitnormal) { const Vertex3Ds bccpd = -radius * hitnormal; // vector ball center to contact point displacement const float bnv = vel.Dot(hitnormal); // ball normal velocity to hit face const Vertex3Ds bvn = bnv * hitnormal; // project the normal velocity along normal const Vertex3Ds bvt = vel - bvn; // calc the tangent velocity Vertex3Ds bvT = bvt; // ball tangent velocity Unit Tangent bvT.Normalize(); const Vertex3Ds bstv = // ball surface tangential velocity CrossProduct(m_angularvelocity, bccpd); // velocity of ball surface at contact point const float dot = bstv.Dot(bvT); // speed ball surface contact point tangential to contact surface point const Vertex3Ds cpvt = dot * bvT; // contact point velocity tangential to hit face const Vertex3Ds slideVel = bstv - cpvt; // contact point slide velocity with ball center velocity -- slide velocity // If the point and the ball are travelling in opposite directions, // and the point's velocity is at least the magnitude of the balls, // then we have a natural roll Vertex3Ds cpctrv = -slideVel; //contact point co-tangential reverse velocity if (vel.LengthSquared() > (float)(0.7*0.7)) { // Calculate the maximum amount the point velocity can change this // time segment due to friction Vertex3Ds FrictionForce = cpvt + bvt; // If the point can change fast enough to go directly to a natural roll, then do it. if (FrictionForce.LengthSquared() > (float)(ANGULARFORCE*ANGULARFORCE)) FrictionForce.Normalize(ANGULARFORCE); cpctrv -= FrictionForce; } // Divide by the inertial tensor for a sphere in order to change // linear force into angular momentum cpctrv *= (float)(2.0/5.0); // Inertial tensor for a sphere const Vertex3Ds vResult = CrossProduct(bccpd, cpctrv); // ball center contact point displacement X reverse contact point co-tan vel m_angularmomentum *= 0.99f; m_angularmomentum += vResult; // add delta m_angularvelocity = m_inverseworldinertiatensor.MultiplyVector(m_angularmomentum); }
size_t CalcVertexArraySize(const Vertex3Ds& m_vertexArray) { return sizeof(Vertex3D) * (m_vertexArray.size()); }
float Ball::HitTest(const Ball * pball_, float dtime, CollisionEvent& coll) { Ball * pball = const_cast<Ball*>(pball_); // HACK; needed below Vertex3Ds d = pos - pball->pos; // delta position Vertex3Ds dv = vel - pball->vel; // delta velocity float bcddsq = d.LengthSquared(); // square of ball center's delta distance float bcdd = sqrtf(bcddsq); // length of delta if (bcdd < 1.0e-8f) // two balls center-over-center embedded { //return -1; d.z = -1.0f; // patch up pball->pos.z -= d.z; // lift up bcdd = 1.0f; // patch up bcddsq = 1.0f; // patch up dv.z = 0.1f; // small speed difference pball->vel.z -= dv.z; } float b = dv.Dot(d); // inner product const float bnv = b/bcdd; // normal speed of balls toward each other if ( bnv > C_LOWNORMVEL) return -1.0f; // dot of delta velocity and delta displacement, postive if receding no collison const float totalradius = pball->radius + radius; const float bnd = bcdd - totalradius; // distance between ball surfaces float hittime; if (bnd < (float)PHYS_TOUCH) // in contact??? { if (bnd <= (float)(-PHYS_SKIN*2.0)) return -1.0f; // embedded too deep if ((fabsf(bnv) > C_CONTACTVEL) // >fast velocity, return zero time //zero time for rigid fast bodies || (bnd <= (float)(-PHYS_TOUCH))) hittime = 0; // slow moving but embedded else hittime = bnd/(float)(2.0*PHYS_TOUCH) + 0.5f; // don't compete for fast zero time events } else { // find collision time as solution of quadratic equation // at^2 + bt + c = 0 const float a = dv.LengthSquared(); // square of differential velocity if (a < 1.0e-8f) return -1.0f; // ball moving really slow, then wait for contact const float c = bcddsq - totalradius*totalradius; //first contact test: square delta position - square of radii b *= 2.0f; // two inner products float result = b*b - 4.0f*a*c; // squareroot term (discriminant) in quadratic equation if (result < 0.0f) return -1.0f; // no collision path exist result = sqrtf(result); // compute the two solutions to the quadratic equation float time1 = (-b + result)/(2.0f * a); const float time2 = (-b - result)/(2.0f * a); // choose smallest non-negative solution hittime = std::min(time1, time2); if (hittime < 0) hittime = std::max(time1, time2); if (infNaN(hittime) || hittime < 0 || hittime > dtime) return -1.0f; // .. was some time previous || beyond the next physics tick } const Vertex3Ds hitPos = pball->pos + hittime * dv; // new ball position //calc unit normal of collision coll.normal[0] = hitPos - pos; coll.normal[0].Normalize(); coll.distance = bnd; //actual contact distance coll.hitRigid = true; //rigid collision type return hittime; }
float HitTriangle::HitTest(const Ball * pball, float dtime, CollisionEvent& coll) { if (!m_fEnabled) return -1.0f; const float bnv = normal.Dot(pball->m_vel); // speed in Normal-vector direction if (bnv >= C_CONTACTVEL) // return if clearly ball is receding from object return -1.0f; // Point on the ball that will hit the polygon, if it hits at all const float bRadius = pball->m_radius; Vertex3Ds hitPos = pball->m_pos - bRadius * normal; // nearest point on ball ... projected radius along norm const float bnd = normal.Dot( hitPos - m_rgv[0] ); // distance from plane to ball float hittime; if (bnd < -pball->m_radius/**2.0f*/) //!! *2 necessary? return -1.0f; // (ball normal distance) excessive pentratration of object skin ... no collision HACK bool isContact = false; if (bnd <= (float)PHYS_TOUCH) { if (fabsf(bnv) <= C_CONTACTVEL) { hittime = 0; isContact = true; } else if (bnd <= 0) hittime = 0; // zero time for rigid fast bodies else hittime = bnd / -bnv; } else if (fabsf(bnv) > C_LOWNORMVEL ) // not velocity low? hittime = bnd / -bnv; // rate ok for safe divide else return -1.0f; // wait for touching if (infNaN(hittime) || hittime < 0 || hittime > dtime) return -1.0f; // time is outside this frame ... no collision hitPos += hittime * pball->m_vel; // advance hit point to contact // check if hitPos is within the triangle // Compute vectors const Vertex3Ds v0 = m_rgv[2] - m_rgv[0]; const Vertex3Ds v1 = m_rgv[1] - m_rgv[0]; const Vertex3Ds v2 = hitPos - m_rgv[0]; // Compute dot products const float dot00 = v0.Dot(v0); const float dot01 = v0.Dot(v1); const float dot02 = v0.Dot(v2); const float dot11 = v1.Dot(v1); const float dot12 = v1.Dot(v2); // Compute barycentric coordinates const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01); const float u = (dot11 * dot02 - dot01 * dot12) * invDenom; const float v = (dot00 * dot12 - dot01 * dot02) * invDenom; // Check if point is in triangle const bool pointInTri = (u >= 0.f) && (v >= 0.f) && (u + v <= 1.f); if (pointInTri) { coll.hitnormal = normal; coll.hitdistance = bnd; // 3dhit actual contact distance ... //coll.hitRigid = true; // collision type if (isContact) { coll.isContact = true; coll.hitvelocity.z = bnv; } return hittime; } else return -1.0f; }
void RenderDebugPathMeshOnMap2D(OpenGLRenderer* renderer, MeshRenderer& pathMeshRenderer, Path& pathToRender, Map* map){ UNUSED(renderer); //renderer->SetTextureViewTransparent(); ModelViewMatrix& mapToWorldTransform = map->m_mapToWorldTransformMatrix; //set mesh renderer Vertex3Ds inPathVerts; inPathVerts.clear(); Tile* pathingTile = NULL; //render start/goal/open tiles if (map && !pathToRender.IsOpenListEmpty()){ //render start tile pathingTile = map->GetTileAtMapPosition(pathToRender.m_startPosition); GenerateVertexArrayTextureQuad(inPathVerts, pathingTile->m_renderBounds, AABB2::ZERO_TO_ONE, Rgba::SILVER, false); //render goal tile pathingTile = map->GetTileAtMapPosition(pathToRender.m_goalPosition); GenerateVertexArrayTextureQuad(inPathVerts, pathingTile->m_renderBounds, AABB2::ZERO_TO_ONE, Rgba::GOLD, false); if (pathToRender.m_activeNode){ pathingTile = map->GetTileAtMapPosition(pathToRender.m_activeNode->m_position); GenerateVertexArrayTextureQuad(inPathVerts, pathingTile->m_renderBounds, AABB2::ZERO_TO_ONE, Rgba::MAGENTA, false); //OUTPUT_COLOR_STRING_TO_SCREEN(IntToString(pathToRender.m_activeNode->m_nodeCost.f), pathingTile->m_renderBounds.mins.x, pathingTile->m_renderBounds.maxs.y, Rgba::GOLD); } static Rgba openListColor = Rgba::BLUE; openListColor.a = 127; for (OpenListPathMapIterator it = pathToRender.m_openList.begin(); it != pathToRender.m_openList.end(); ++it){ PathNode& pathnode = *(it->second); pathingTile = map->GetTileAtMapPosition(pathnode.m_position); GenerateVertexArrayTextureQuad(inPathVerts, pathingTile->m_renderBounds, AABB2::ZERO_TO_ONE, openListColor , false); //OUTPUT_COLOR_STRING_TO_SCREEN(IntToString(pathnode.m_nodeCost.f), pathingTile->m_renderBounds.mins.x, pathingTile->m_renderBounds.maxs.y, Rgba::GOLD); } } //create path mesh for all of closed list if (map && pathToRender.m_closedList.size() > 1 ){ static Rgba closedListColor = Rgba::RED; closedListColor.a = 127; for (ClosedListIterator it = pathToRender.m_closedList.begin() + 1; it != pathToRender.m_closedList.end() - 1; ++it){ PathNode& pathnode = (*it); pathingTile = map->GetTileAtMapPosition(pathnode.m_position); GenerateVertexArrayTextureQuad(inPathVerts, pathingTile->m_renderBounds, AABB2::ZERO_TO_ONE, closedListColor, false); //OUTPUT_COLOR_STRING_TO_SCREEN(IntToString(pathnode.m_nodeCost.f), pathingTile->m_renderBounds.mins.x, pathingTile->m_renderBounds.maxs.y, Rgba::GOLD); }//end of for if (pathToRender.m_isImpossible){ return; } static Rgba pathColor = Rgba::GREEN; pathColor.a = 210; //Create path mesh for only path ClosedListIterator mypathIterator = pathToRender.m_closedList.end() - 1; PathNode& myPathGoalNode = (*mypathIterator); //start traversing from goal back to start PathNode* traversalPathNode = &myPathGoalNode; //traversalPathNode = traversalPathNode->m_parent; //create last closed node added pathingTile = map->GetTileAtMapPosition(traversalPathNode->m_position); GenerateVertexArrayTextureQuad(inPathVerts, pathingTile->m_renderBounds, AABB2::ZERO_TO_ONE, pathColor, false); if (traversalPathNode->m_parent != NULL) traversalPathNode = traversalPathNode->m_parent; //create path while (traversalPathNode->m_parent != NULL){ pathingTile = map->GetTileAtMapPosition(traversalPathNode->m_position); //has an infinite loop in certain cases GenerateVertexArrayTextureQuad(inPathVerts, pathingTile->m_renderBounds, AABB2::ZERO_TO_ONE, pathColor, false); //OUTPUT_COLOR_STRING_TO_SCREEN(IntToString(traversalPathNode->m_nodeCost.f), pathingTile->m_renderBounds.mins.x, pathingTile->m_renderBounds.maxs.y, Rgba::WHITE); traversalPathNode = traversalPathNode->m_parent; }//end of while }//end of outer if //copy all verts to mesh pathMeshRenderer.m_mesh->CopyMeshVertexData(inPathVerts); pathMeshRenderer.RenderMesh2D(&mapToWorldTransform); inPathVerts.clear(); }
float HitPoint::HitTest(const Ball * pball, float dtime, CollisionEvent& coll) { if (!m_fEnabled) return -1.0f; const Vertex3Ds dist = pball->m_pos - m_p; // relative ball position const float bcddsq = dist.LengthSquared(); // ball center to line distance squared const float bcdd = sqrtf(bcddsq); // distance ball to line if (bcdd <= 1.0e-6f) return -1.0f; // no hit on exact center const float b = dist.Dot(pball->m_vel); const float bnv = b/bcdd; // ball normal velocity if (bnv > C_CONTACTVEL) return -1.0f; // clearly receding from radius const float bnd = bcdd - pball->m_radius; // ball distance to line const float a = pball->m_vel.LengthSquared(); float hittime = 0; bool isContact = false; if (bnd < (float)PHYS_TOUCH) // already in collision distance? { if (fabsf(bnv) <= C_CONTACTVEL) { isContact = true; hittime = 0; } else hittime = std::max(0.0f, -bnd / bnv); // estimate based on distance and speed along distance } else { if (a < 1.0e-8f) return -1.0f; // no hit - ball not moving relative to object float time1, time2; if (!SolveQuadraticEq(a, 2.0f*b, bcddsq - pball->m_radius*pball->m_radius, time1, time2)) return -1.0f; hittime = (time1*time2 < 0) ? max(time1,time2) : min(time1,time2); // find smallest nonnegative solution } if (infNaN(hittime) || hittime < 0 || hittime > dtime) return -1.0f; // contact out of physics frame const Vertex3Ds hitPos = pball->m_pos + hittime * pball->m_vel; coll.hitnormal = hitPos - m_p; coll.hitnormal.Normalize(); coll.isContact = isContact; if (isContact) coll.hitvelocity.z = bnv; coll.hitdistance = bnd; // actual contact distance //coll.hitRigid = true; return hittime; }
float HitCircle::HitTestBasicRadius(const Ball * pball, float dtime, CollisionEvent& coll, bool direction, bool lateral, bool rigid) { if (!m_fEnabled || pball->m_frozen) return -1.0f; Vertex3Ds c(center.x, center.y, 0.0f); Vertex3Ds dist = pball->m_pos - c; // relative ball position Vertex3Ds dv = pball->m_vel; float targetRadius; bool capsule3D; if (!lateral && pball->m_pos.z > zhigh) { capsule3D = true; // handle ball over target? //const float hcap = radius*(float)(1.0/5.0); // cap height to hit-circle radius ratio //targetRadius = radius*radius/(hcap*2.0f) + hcap*0.5f; // c = (r^2+h^2)/(2*h) targetRadius = radius*(float)(13.0/5.0); // optimized version of above code //c.z = zhigh - (targetRadius - hcap); // b = c - h c.z = zhigh - radius*(float)(12.0/5.0); // optimized version of above code dist.z = pball->m_pos.z - c.z; // ball rolling point - capsule center height } else { capsule3D = false; targetRadius = radius; if (lateral) targetRadius += pball->m_radius; dist.z = dv.z = 0.0f; } const float bcddsq = dist.LengthSquared(); // ball center to circle center distance ... squared const float bcdd = sqrtf(bcddsq); // distance center to center if (bcdd <= 1.0e-6f) return -1.0f; // no hit on exact center const float b = dist.Dot(dv); const float bnv = b/bcdd; // ball normal velocity if (direction && bnv > C_LOWNORMVEL) return -1.0f; // clearly receding from radius const float bnd = bcdd - targetRadius; // ball normal distance to const float a = dv.LengthSquared(); float hittime = 0; bool fUnhit = false; bool isContact = false; // Kicker is special.. handle ball stalled on kicker, commonly hit while receding, knocking back into kicker pocket if (m_ObjType == eKicker && bnd <= 0 && bnd >= -radius && a < C_CONTACTVEL*C_CONTACTVEL ) { if (pball->m_vpVolObjs) pball->m_vpVolObjs->RemoveElement(m_pObj); // cause capture } if (rigid && bnd < (float)PHYS_TOUCH) // positive: contact possible in future ... Negative: objects in contact now { if (bnd < -pball->m_radius/**2.0f*/) //!! *2 necessary? return -1.0f; else if (fabsf(bnv) <= C_CONTACTVEL) { isContact = true; hittime = 0; } else hittime = std::max(0.0f, -bnd / bnv); // estimate based on distance and speed along distance } else if (m_ObjType >= eTrigger // triggers & kickers && pball->m_vpVolObjs && ((bnd < 0.f) == (pball->m_vpVolObjs->IndexOf(m_pObj) < 0))) { // here if ... ball inside and no hit set .... or ... ball outside and hit set if (fabsf(bnd-radius) < 0.05f) // if ball appears in center of trigger, then assumed it was gen'ed there { if (pball->m_vpVolObjs) pball->m_vpVolObjs->AddElement(m_pObj); //special case for trigger overlaying a kicker } // this will add the ball to the trigger space without a Hit else { hittime = 0; fUnhit = (bnd > 0.f); // ball on outside is UnHit, otherwise it's a Hit } } else { if((!rigid && bnd * bnv > 0.f) || // (outside and receding) or (inside and approaching) (a < 1.0e-8f)) return -1.0f; // no hit ... ball not moving relative to object float time1, time2; if (!SolveQuadraticEq(a, 2.0f*b, bcddsq - targetRadius*targetRadius, time1, time2)) return -1.0f; fUnhit = (time1*time2 < 0.f); hittime = fUnhit ? max(time1,time2) : min(time1,time2); // ball is inside the circle } if (infNaN(hittime) || hittime < 0.f || hittime > dtime) return -1.0f; // contact out of physics frame const float hitz = pball->m_pos.z - pball->m_radius + pball->m_vel.z * hittime; // rolling point if(((hitz + pball->m_radius *1.5f) < zlow) || (!capsule3D && (hitz + pball->m_radius*0.5f) > zhigh) || (capsule3D && (pball->m_pos.z + pball->m_vel.z * hittime) < zhigh)) return -1.0f; const float hitx = pball->m_pos.x + pball->m_vel.x*hittime; const float hity = pball->m_pos.y + pball->m_vel.y*hittime; const float sqrlen = (hitx - c.x)*(hitx - c.x) + (hity - c.y)*(hity - c.y); if (sqrlen > 1.0e-8f) // over center??? { // no const float inv_len = 1.0f/sqrtf(sqrlen); coll.hitnormal.x = (hitx - c.x)*inv_len; coll.hitnormal.y = (hity - c.y)*inv_len; coll.hitnormal.z = 0.0f; } else { // yes, over center coll.hitnormal.x = 0.0f; // make up a value, any direction is ok coll.hitnormal.y = 1.0f; coll.hitnormal.z = 0.0f; } if (!rigid) // non rigid body collision? return direction coll.hitvelocity.x = fUnhit ? 1.0f : 0.0f; // UnHit signal is receding from target coll.isContact = isContact; if (isContact) coll.hitvelocity.z = bnv; coll.hitdistance = bnd; //actual contact distance ... //coll.hitRigid = rigid; // collision type return hittime; }