Point2F BiQuadToSqr::transform( const Point2F &p ) const { Point2F kA = m_kP00 - p; F32 fAB = mDotPerp( kA, m_kB ); F32 fAC = mDotPerp( kA, m_kC); // 0 = ac*bc+(bc^2+ac*bd-ab*cd)*s+bc*bd*s^2 = k0 + k1*s + k2*s^2 F32 fK0 = fAC*m_fBC; F32 fK1 = m_fBC*m_fBC + fAC*m_fBD - fAB*m_fCD; F32 fK2 = m_fBC*m_fBD; if (mFabs(fK2) > POINT_EPSILON) { // s-equation is quadratic F32 fInv = 0.5f/fK2; F32 fDiscr = fK1*fK1 - 4.0f*fK0*fK2; F32 fRoot = mSqrt( mFabs(fDiscr) ); Point2F kResult0( 0, 0 ); kResult0.x = (-fK1 - fRoot)*fInv; kResult0.y = fAB/(m_fBC + m_fBD*kResult0.x); F32 fDeviation0 = deviation(kResult0); if ( fDeviation0 == 0.0f ) return kResult0; Point2F kResult1( 0, 0 ); kResult1.x = (-fK1 + fRoot)*fInv; kResult1.y = fAB/(m_fBC + m_fBD*kResult1.x); F32 fDeviation1 = deviation(kResult1); if ( fDeviation1 == 0.0f ) return kResult1; if (fDeviation0 <= fDeviation1) { if ( fDeviation0 < POINT_EPSILON ) return kResult0; } else { if ( fDeviation1 < POINT_EPSILON ) return kResult1; } } else { // s-equation is linear Point2F kResult( 0, 0 ); kResult.x = -fK0/fK1; kResult.y = fAB/(m_fBC + m_fBD*kResult.x); F32 fDeviation = deviation(kResult); if ( fDeviation < POINT_EPSILON ) return kResult; } // point is outside the quadrilateral, return invalid return Point2F(F32_MAX,F32_MAX); }
void VideoCapture::capture() { // If this is the first frame, capture and encode it right away if (mNextFramePosition == 0.0f) { mVideoCaptureStartTime = Platform::getVirtualMilliseconds(); mCapturedFramePos = -1.0f; } // Calculate the frame position for this captured frame U32 frameTimeMs = Platform::getVirtualMilliseconds() - mVideoCaptureStartTime; F32 framePosition = (F32)frameTimeMs / mMsPerFrame; // Repeat until the current frame is captured while (framePosition > mCapturedFramePos) { // If the frame position is closer to the next frame position // than the previous one capture it if ( mFabs(framePosition - mNextFramePosition) < mFabs(mCapturedFramePos - mNextFramePosition) ) { mFrameGrabber->captureBackBuffer(); mCapturedFramePos = framePosition; } // If the new frame position is greater or equal than the next frame time // tell the framegrabber to make bitmaps out from the last captured backbuffer until the video catches up while ( framePosition >= mNextFramePosition ) { mFrameGrabber->makeBitmap(); mNextFramePosition++; } } // Fetch bitmaps from the framegrabber and encode them GBitmap *bitmap = NULL; while ( (bitmap = mFrameGrabber->fetchBitmap()) != NULL ) { //mEncoder->pushProcessedBitmap(bitmap); if (!mEncoder->pushFrame(bitmap)) { Con::errorf("VideoCapture: an error occurred while encoding a frame. Recording aborted."); end(); break; } } // Garbage collect the processed bitmaps deleteProcessedBitmaps(); }
void TimeOfDay::_updatePosition() { //// Full azimuth/elevation calculation. //// calculate sun decline and meridian angle (in radians) //F32 sunDecline = mSin( M_2PI * mTimeOfYear ) * mDegToRad( mAxisTilt ); //F32 meridianAngle = mTimeOfDay * M_2PI - mDegToRad( mLongitude ); //// calculate the elevation and azimuth (in radians) //mElevation = _calcElevation( mDegToRad( mLatitude ), sunDecline, meridianAngle ); //mAzimuth = _calcAzimuth( mDegToRad( mLatitude ), sunDecline, meridianAngle ); // Simplified azimuth/elevation calculation. // calculate sun decline and meridian angle (in radians) F32 sunDecline = mDegToRad( mAxisTilt ); F32 meridianAngle = mTimeOfDay * M_2PI; mPrevElevation = mNextElevation; // calculate the elevation and azimuth (in radians) mElevation = _calcElevation( 0.0f, sunDecline, meridianAngle ); mAzimuth = _calcAzimuth( 0.0f, sunDecline, meridianAngle ); if ( mFabs( mAzimuthOverride ) ) { mElevation = mDegToRad( mTimeOfDay * 360.0f ); mAzimuth = mAzimuthOverride; } mNextElevation = mElevation; // Only the client updates the sun position! if ( isClientObject() ) smTimeOfDayUpdateSignal.trigger( this, mTimeOfDay ); }
U32 OptimizedPolyList::insertPlane(const PlaneF& plane) { S32 retIdx = -1; // Apply the transform PlaneF transPlane; mPlaneTransformer.transform(plane, transPlane); for (U32 i = 0; i < mPlaneList.size(); i++) { if (mPlaneList[i].equal(transPlane) && mFabs( mPlaneList[i].d - transPlane.d ) < POINT_EPSILON) { retIdx = i; break; } } if (retIdx == -1) { retIdx = mPlaneList.size(); mPlaneList.push_back(transPlane); } return (U32)retIdx; }
//----------------------------------------------------------------------------- // This function based on code originally written for the book: // 3D Game Engine Design, by David H. Eberly // F32 sqrDistanceEdges(const Point3F& start0, const Point3F& end0, const Point3F& start1, const Point3F& end1, Point3F* is, Point3F* it) { Point3F direction0 = end0 - start0; F32 fA00 = direction0.lenSquared(); Point3F direction1 = end1 - start1; F32 fA11 = direction1.lenSquared(); F32 fA01 = -mDot(direction0, direction1); Point3F kDiff = start0 - start1; F32 fC = kDiff.lenSquared(); F32 fB0 = mDot(kDiff, direction0); F32 fDet = mFabs(fA00*fA11 - fA01*fA01); // Since the endpoints are tested as vertices, we're not interested // in parallel lines, and intersections that don't involve end-points. if (fDet >= 0.00001) { // Calculate time of intersection for each line F32 fB1 = -mDot(kDiff, direction1); F32 fS = fA01*fB1-fA11*fB0; F32 fT = fA01*fB0-fA00*fB1; // Only interested in collisions that don't involve the end points if (fS >= 0.0 && fS <= fDet && fT >= 0.0 && fT <= fDet) { F32 fInvDet = 1.0 / fDet; fS *= fInvDet; fT *= fInvDet; F32 fSqrDist = (fS*(fA00*fS + fA01*fT + 2.0*fB0) + fT*(fA01*fS + fA11*fT + 2.0*fB1) + fC); // Intersection points. *is = start0 + direction0 * fS; *it = start1 + direction1 * fT; return mFabs(fSqrDist); } } // Return a large number in the cases where endpoints are involved. return 1e10f; }
BiQuadToSqr::BiQuadToSqr( const Point2F &p00, const Point2F &p10, const Point2F &p11, const Point2F &p01 ) : m_kP00( p00 ) { m_kB = p10 - p00 ; // width m_kC = p01 - p00; // height m_kD = p11 + p00 - p10 - p01; // diagonal dist if(mFabs(m_kD.x) < POINT_EPSILON) m_kD.x = 0.f; if(mFabs(m_kD.y) < POINT_EPSILON) m_kD.y = 0.f; m_fBC = mDotPerp( m_kB, m_kC ); m_fBD = mDotPerp( m_kB, m_kD ); m_fCD = mDotPerp( m_kC, m_kD ); }
bool OrientedBox3F::isContained( const Point3F& point ) const { Point3F distToCenter = point - getCenter(); for( U32 i = 0; i < 3; ++ i ) { F32 coeff = mDot( distToCenter, getAxis( i ) ); if( mFabs( coeff ) > getHalfExtents()[ i ] ) return false; } return true; }
// Perform inverse on full 4x4 matrix. Used in special cases only, so not at all optimized. bool MatrixF::fullInverse() { Point4F a,b,c,d; getRow(0,&a); getRow(1,&b); getRow(2,&c); getRow(3,&d); // det = a0*b1*c2*d3 - a0*b1*c3*d2 - a0*c1*b2*d3 + a0*c1*b3*d2 + a0*d1*b2*c3 - a0*d1*b3*c2 - // b0*a1*c2*d3 + b0*a1*c3*d2 + b0*c1*a2*d3 - b0*c1*a3*d2 - b0*d1*a2*c3 + b0*d1*a3*c2 + // c0*a1*b2*d3 - c0*a1*b3*d2 - c0*b1*a2*d3 + c0*b1*a3*d2 + c0*d1*a2*b3 - c0*d1*a3*b2 - // d0*a1*b2*c3 + d0*a1*b3*c2 + d0*b1*a2*c3 - d0*b1*a3*c2 - d0*c1*a2*b3 + d0*c1*a3*b2 F32 det = a.x*b.y*c.z*d.w - a.x*b.y*c.w*d.z - a.x*c.y*b.z*d.w + a.x*c.y*b.w*d.z + a.x*d.y*b.z*c.w - a.x*d.y*b.w*c.z - b.x*a.y*c.z*d.w + b.x*a.y*c.w*d.z + b.x*c.y*a.z*d.w - b.x*c.y*a.w*d.z - b.x*d.y*a.z*c.w + b.x*d.y*a.w*c.z + c.x*a.y*b.z*d.w - c.x*a.y*b.w*d.z - c.x*b.y*a.z*d.w + c.x*b.y*a.w*d.z + c.x*d.y*a.z*b.w - c.x*d.y*a.w*b.z - d.x*a.y*b.z*c.w + d.x*a.y*b.w*c.z + d.x*b.y*a.z*c.w - d.x*b.y*a.w*c.z - d.x*c.y*a.z*b.w + d.x*c.y*a.w*b.z; if (mFabs(det)<0.00001f) return false; Point4F aa,bb,cc,dd; aa.x = b.y*c.z*d.w - b.y*c.w*d.z - c.y*b.z*d.w + c.y*b.w*d.z + d.y*b.z*c.w - d.y*b.w*c.z; aa.y = -a.y*c.z*d.w + a.y*c.w*d.z + c.y*a.z*d.w - c.y*a.w*d.z - d.y*a.z*c.w + d.y*a.w*c.z; aa.z = a.y*b.z*d.w - a.y*b.w*d.z - b.y*a.z*d.w + b.y*a.w*d.z + d.y*a.z*b.w - d.y*a.w*b.z; aa.w = -a.y*b.z*c.w + a.y*b.w*c.z + b.y*a.z*c.w - b.y*a.w*c.z - c.y*a.z*b.w + c.y*a.w*b.z; bb.x = -b.x*c.z*d.w + b.x*c.w*d.z + c.x*b.z*d.w - c.x*b.w*d.z - d.x*b.z*c.w + d.x*b.w*c.z; bb.y = a.x*c.z*d.w - a.x*c.w*d.z - c.x*a.z*d.w + c.x*a.w*d.z + d.x*a.z*c.w - d.x*a.w*c.z; bb.z = -a.x*b.z*d.w + a.x*b.w*d.z + b.x*a.z*d.w - b.x*a.w*d.z - d.x*a.z*b.w + d.x*a.w*b.z; bb.w = a.x*b.z*c.w - a.x*b.w*c.z - b.x*a.z*c.w + b.x*a.w*c.z + c.x*a.z*b.w - c.x*a.w*b.z; cc.x = b.x*c.y*d.w - b.x*c.w*d.y - c.x*b.y*d.w + c.x*b.w*d.y + d.x*b.y*c.w - d.x*b.w*c.y; cc.y = -a.x*c.y*d.w + a.x*c.w*d.y + c.x*a.y*d.w - c.x*a.w*d.y - d.x*a.y*c.w + d.x*a.w*c.y; cc.z = a.x*b.y*d.w - a.x*b.w*d.y - b.x*a.y*d.w + b.x*a.w*d.y + d.x*a.y*b.w - d.x*a.w*b.y; cc.w = -a.x*b.y*c.w + a.x*b.w*c.y + b.x*a.y*c.w - b.x*a.w*c.y - c.x*a.y*b.w + c.x*a.w*b.y; dd.x = -b.x*c.y*d.z + b.x*c.z*d.y + c.x*b.y*d.z - c.x*b.z*d.y - d.x*b.y*c.z + d.x*b.z*c.y; dd.y = a.x*c.y*d.z - a.x*c.z*d.y - c.x*a.y*d.z + c.x*a.z*d.y + d.x*a.y*c.z - d.x*a.z*c.y; dd.z = -a.x*b.y*d.z + a.x*b.z*d.y + b.x*a.y*d.z - b.x*a.z*d.y - d.x*a.y*b.z + d.x*a.z*b.y; dd.w = a.x*b.y*c.z - a.x*b.z*c.y - b.x*a.y*c.z + b.x*a.z*c.y + c.x*a.y*b.z - c.x*a.z*b.y; setRow(0,aa); setRow(1,bb); setRow(2,cc); setRow(3,dd); mul(1.0f/det); return true; }
//---------------------------------------------------------------------------- // Create ring //---------------------------------------------------------------------------- SplashRing Splash::createRing() { SplashRing ring; U32 numPoints = mDataBlock->numSegments + 1; Point3F ejectionAxis( 0.0, 0.0, 1.0 ); Point3F axisx; if (mFabs(ejectionAxis.z) < 0.999f) mCross(ejectionAxis, Point3F(0, 0, 1), &axisx); else mCross(ejectionAxis, Point3F(0, 1, 0), &axisx); axisx.normalize(); for( U32 i=0; i<numPoints; i++ ) { F32 t = F32(i) / F32(numPoints); AngAxisF thetaRot( axisx, mDataBlock->ejectionAngle * (M_PI / 180.0)); AngAxisF phiRot( ejectionAxis, t * (M_PI * 2.0)); Point3F pointAxis = ejectionAxis; MatrixF temp; thetaRot.setMatrix(&temp); temp.mulP(pointAxis); phiRot.setMatrix(&temp); temp.mulP(pointAxis); Point3F startOffset = axisx; temp.mulV( startOffset ); startOffset *= mDataBlock->startRadius; SplashRingPoint point; point.position = getPosition() + startOffset; point.velocity = pointAxis * mDataBlock->velocity; ring.points.push_back( point ); } ring.color = mDataBlock->colors[0]; ring.lifetime = mDataBlock->ringLifetime; ring.elapsedTime = 0.0; ring.v = mDataBlock->texFactor * mFmod( mElapsedTime, 1.0 ); return ring; }
bool MatrixF::isAffine() const { // An affine transform is defined by the following structure // // [ X X X P ] // [ X X X P ] // [ X X X P ] // [ 0 0 0 1 ] // // Where X is an orthonormal 3x3 submatrix and P is an arbitrary translation // We'll check in the following order: // 1: [3][3] must be 1 // 2: Shear portion must be zero // 3: Dot products of rows and columns must be zero // 4: Length of rows and columns must be 1 // if (m[idx(3,3)] != 1.0f) return false; if (m[idx(0,3)] != 0.0f || m[idx(1,3)] != 0.0f || m[idx(2,3)] != 0.0f) return false; Point3F one, two, three; getColumn(0, &one); getColumn(1, &two); getColumn(2, &three); if (mDot(one, two) > 0.0001f || mDot(one, three) > 0.0001f || mDot(two, three) > 0.0001f) return false; if (mFabs(1.0f - one.lenSquared()) > 0.0001f || mFabs(1.0f - two.lenSquared()) > 0.0001f || mFabs(1.0f - three.lenSquared()) > 0.0001f) return false; getRow(0, &one); getRow(1, &two); getRow(2, &three); if (mDot(one, two) > 0.0001f || mDot(one, three) > 0.0001f || mDot(two, three) > 0.0001f) return false; if (mFabs(1.0f - one.lenSquared()) > 0.0001f || mFabs(1.0f - two.lenSquared()) > 0.0001f || mFabs(1.0f - three.lenSquared()) > 0.0001f) return false; // We're ok. return true; }
void ConvexShape::resizePlanes( const Point3F &size ) { //Point3F nSize; //mWorldToObj.mulV( nSize ); for ( S32 i = 0; i < mSurfaces.size(); i++ ) { MatrixF objToPlane( mSurfaces[i] ); objToPlane.inverse(); Point3F lim; objToPlane.mulV( size, &lim ); F32 sign = ( mPlanes[i].d > 0.0f ) ? 1.0f : -1.0f; mPlanes[i].d = mFabs(lim.z) * 0.5f * sign; //mPlanes[i].d = -lim.z * 0.5f; mSurfaces[i].setPosition( mPlanes[i].getPosition() ); } }
virtual btScalar addSingleResult( btCollisionWorld::LocalConvexResult &convexResult, bool normalInWorldSpace ) { // NOTE: I shouldn't have to do any of this, but Bullet // has some weird bugs. // // For one the plane type will return hits on a Z up surface // for sweeps that have no Z sweep component. // // Second the normal returned here is sometimes backwards // to the sweep direction... no clue why. // F32 dotN = mMoveVec.dot( convexResult.m_hitNormalLocal ); if ( mFabs( dotN ) < 0.1f ) return 1.0f; //.logicking (commented - sometimes we need to collide with NO_CONTACT_RESPONSE, i.e. invisible walls) //if ( convexResult.m_hitCollisionObject->getCollisionFlags() & btCollisionObject::CF_NO_CONTACT_RESPONSE ) // return 1.0f; return Parent::addSingleResult( convexResult, normalInWorldSpace ); }
F32 GjkCollisionState::distance(const MatrixF& a2w, const MatrixF& b2w, const F32 dontCareDist, const MatrixF* _w2a, const MatrixF* _w2b) { num_iterations = 0; MatrixF w2a,w2b; if (_w2a == NULL || _w2b == NULL) { w2a = a2w; w2b = b2w; w2a.inverse(); w2b.inverse(); } else { w2a = *_w2a; w2b = *_w2b; } reset(a2w,b2w); bits = 0; all_bits = 0; F32 mu = 0; do { nextBit(); VectorF va,sa; w2a.mulV(-v,&va); p[last] = a->support(va); a2w.mulP(p[last],&sa); VectorF vb,sb; w2b.mulV(v,&vb); q[last] = b->support(vb); b2w.mulP(q[last],&sb); VectorF w = sa - sb; F32 nm = mDot(v, w) / dist; if (nm > mu) mu = nm; if (mu > dontCareDist) return mu; if (mFabs(dist - mu) <= dist * rel_error) return dist; ++num_iterations; if (degenerate(w) || num_iterations > sIteration) { ++num_irregularities; return dist; } y[last] = w; all_bits = bits | last_bit; if (!closest(v)) { ++num_irregularities; return dist; } dist = v.len(); } while (bits < 15 && dist > sTolerance) ; if (bits == 15 && mu <= 0) dist = 0; return dist; }
void FlyingVehicle::updateJet(F32 dt) { // Thrust Animation threads // Back if (mJetSeq[BackActivate] >=0 ) { if(!mBackMaintainOn || mThrustDirection != ThrustForward) { if(mBackMaintainOn) { mShapeInstance->setPos(mJetThread[BackActivate], 1); mShapeInstance->destroyThread(mJetThread[BackMaintain]); mBackMaintainOn = false; } mShapeInstance->setTimeScale(mJetThread[BackActivate], (mThrustDirection == ThrustForward)? 1.0f : -1.0f); mShapeInstance->advanceTime(dt,mJetThread[BackActivate]); } if(mJetSeq[BackMaintain] >= 0 && !mBackMaintainOn && mShapeInstance->getPos(mJetThread[BackActivate]) >= 1.0) { mShapeInstance->setPos(mJetThread[BackActivate], 0); mShapeInstance->setTimeScale(mJetThread[BackActivate], 0); mJetThread[BackMaintain] = mShapeInstance->addThread(); mShapeInstance->setSequence(mJetThread[BackMaintain],mJetSeq[BackMaintain],0); mShapeInstance->setTimeScale(mJetThread[BackMaintain],1); mBackMaintainOn = true; } if(mBackMaintainOn) mShapeInstance->advanceTime(dt,mJetThread[BackMaintain]); } // Thrust Animation threads // Bottom if (mJetSeq[BottomActivate] >=0 ) { if(!mBottomMaintainOn || mThrustDirection != ThrustDown || !mJetting) { if(mBottomMaintainOn) { mShapeInstance->setPos(mJetThread[BottomActivate], 1); mShapeInstance->destroyThread(mJetThread[BottomMaintain]); mBottomMaintainOn = false; } mShapeInstance->setTimeScale(mJetThread[BottomActivate], (mThrustDirection == ThrustDown && mJetting)? 1.0f : -1.0f); mShapeInstance->advanceTime(dt,mJetThread[BottomActivate]); } if(mJetSeq[BottomMaintain] >= 0 && !mBottomMaintainOn && mShapeInstance->getPos(mJetThread[BottomActivate]) >= 1.0) { mShapeInstance->setPos(mJetThread[BottomActivate], 0); mShapeInstance->setTimeScale(mJetThread[BottomActivate], 0); mJetThread[BottomMaintain] = mShapeInstance->addThread(); mShapeInstance->setSequence(mJetThread[BottomMaintain],mJetSeq[BottomMaintain],0); mShapeInstance->setTimeScale(mJetThread[BottomMaintain],1); mBottomMaintainOn = true; } if(mBottomMaintainOn) mShapeInstance->advanceTime(dt,mJetThread[BottomMaintain]); } // Jet particles for (S32 j = 0; j < NumThrustDirections; j++) { JetActivation& jet = sJetActivation[j]; updateEmitter(mJetting && j == mThrustDirection,dt,mDataBlock->jetEmitter[jet.emitter], jet.node,FlyingVehicleData::MaxDirectionJets); } // Trail jets Point3F yv; mObjToWorld.getColumn(1,&yv); F32 speed = mFabs(mDot(yv,mRigid.linVelocity)); F32 trail = 0; if (speed > mDataBlock->minTrailSpeed) { trail = dt; if (speed < mDataBlock->maxSpeed) trail *= (speed - mDataBlock->minTrailSpeed) / mDataBlock->maxSpeed; } updateEmitter(trail,trail,mDataBlock->jetEmitter[FlyingVehicleData::TrailEmitter], FlyingVehicleData::TrailNode,FlyingVehicleData::MaxTrails); // Allocate/Deallocate voice on demand. if ( !mJetSound ) return; if ( !mJetting ) mJetSound->stop(); else { if ( !mJetSound->isPlaying() ) mJetSound->play(); mJetSound->setTransform( getTransform() ); mJetSound->setVelocity( getVelocity() ); } }
/** * This method gets the move list for an object, in the case * of the AI, it actually calculates the moves, and then * sends them down the pipe. * * @param movePtr Pointer to move the move list into * @param numMoves Number of moves in the move list */ U32 AIClient::getMoveList( Move **movePtr,U32 *numMoves ) { //initialize the move structure and return pointers mMove = NullMove; *movePtr = &mMove; *numMoves = 1; // Check if we got a player mPlayer = NULL; mPlayer = dynamic_cast<Player *>( getControlObject() ); // We got a something controling us? if( !mPlayer ) return 1; // What is The Matrix? MatrixF moveMatrix; moveMatrix.set( EulerF( 0, 0, 0 ) ); moveMatrix.setColumn( 3, Point3F( 0, 0, 0 ) ); moveMatrix.transpose(); // Position / rotation variables F32 curYaw, curPitch; F32 newYaw, newPitch; F32 xDiff, yDiff; F32 moveSpeed = mMoveSpeed; switch( mMoveMode ) { case ModeStop: return 1; // Stop means no action break; case ModeStuck: // Fall through, so we still try to move case ModeMove: // Get my location MatrixF const& myTransform = mPlayer->getTransform(); myTransform.getColumn( 3, &mLocation ); // Set rotation variables Point3F rotation = mPlayer->getRotation(); Point3F headRotation = mPlayer->getHeadRotation(); curYaw = rotation.z; curPitch = headRotation.x; xDiff = mAimLocation.x - mLocation.x; yDiff = mAimLocation.y - mLocation.y; // first do Yaw if( !mIsZero( xDiff ) || !mIsZero( yDiff ) ) { // use the cur yaw between -Pi and Pi while( curYaw > M_2PI_F ) curYaw -= M_2PI_F; while( curYaw < -M_2PI_F ) curYaw += M_2PI_F; // find the new yaw newYaw = mAtan2( xDiff, yDiff ); // find the yaw diff F32 yawDiff = newYaw - curYaw; // make it between 0 and 2PI if( yawDiff < 0.0f ) yawDiff += M_2PI_F; else if( yawDiff >= M_2PI_F ) yawDiff -= M_2PI_F; // now make sure we take the short way around the circle if( yawDiff > M_2PI_F ) yawDiff -= M_2PI_F; else if( yawDiff < -M_2PI_F ) yawDiff += M_2PI_F; mMove.yaw = yawDiff; // set up the movement matrix moveMatrix.set( EulerF( 0.0f, 0.0f, newYaw ) ); } else moveMatrix.set( EulerF( 0.0f, 0.0f, curYaw ) ); // next do pitch F32 horzDist = Point2F( mAimLocation.x, mAimLocation.y ).len(); if( !mIsZero( horzDist ) ) { //we shoot from the gun, not the eye... F32 vertDist = mAimLocation.z; newPitch = mAtan2( horzDist, vertDist ) - ( M_2PI_F / 2.0f ); F32 pitchDiff = newPitch - curPitch; mMove.pitch = pitchDiff; } // finally, mMove towards mMoveDestination xDiff = mMoveDestination.x - mLocation.x; yDiff = mMoveDestination.y - mLocation.y; // Check if we should mMove, or if we are 'close enough' if( ( ( mFabs( xDiff ) > mMoveTolerance ) || ( mFabs( yDiff ) > mMoveTolerance ) ) && ( !mIsZero( mMoveSpeed ) ) ) { if( mIsZero( xDiff ) ) mMove.y = ( mLocation.y > mMoveDestination.y ? -moveSpeed : moveSpeed ); else if( mIsZero( yDiff ) ) mMove.x = ( mLocation.x > mMoveDestination.x ? -moveSpeed : moveSpeed ); else if( mFabs( xDiff ) > mFabs( yDiff ) ) { F32 value = mFabs( yDiff / xDiff ) * mMoveSpeed; mMove.y = ( mLocation.y > mMoveDestination.y ? -value : value ); mMove.x = ( mLocation.x > mMoveDestination.x ? -moveSpeed : moveSpeed ); } else { F32 value = mFabs( xDiff / yDiff ) * mMoveSpeed; mMove.x = ( mLocation.x > mMoveDestination.x ? -value : value ); mMove.y = ( mLocation.y > mMoveDestination.y ? -moveSpeed : moveSpeed ); } //now multiply the mMove vector by the transpose of the object rotation matrix moveMatrix.transpose(); Point3F newMove; moveMatrix.mulP( Point3F( mMove.x, mMove.y, 0.0f ), &newMove ); //and sub the result back in the mMove structure mMove.x = newMove.x; mMove.y = newMove.y; // We should check to see if we are stuck... if( mLocation.x == mLastLocation.x && mLocation.y == mLastLocation.y && mLocation.z == mLastLocation.z ) { // We're stuck...probably setMoveMode( ModeStuck ); } else setMoveMode( ModeMove ); } else { // Ok, we are close enough, lets stop // setMoveMode( ModeStop ); // DON'T use this, it'll throw the wrong callback mMoveMode = ModeStop; throwCallback( "onReachDestination" ); // Callback } break; } // Test for target location in sight RayInfo dummy; Point3F targetLoc = mMoveDestination; // Change this if( mPlayer ) { if( !mPlayer->getContainer()->castRay( mLocation, targetLoc, StaticShapeObjectType | StaticObjectType | TerrainObjectType, &dummy ) ) { if( !mTargetInLOS ) throwCallback( "onTargetEnterLOS" ); } else { if( mTargetInLOS ) throwCallback( "onTargetExitLOS" ); } } // Copy over the trigger status for( int i = 0; i < MaxTriggerKeys; i++ ) { mMove.trigger[i] = mTriggers[i]; mTriggers[i] = false; } return 1; }
void AITurretShape::_trackTarget(F32 dt) { // Only on server if (isClientObject()) return; // We can only track a target if we have one if (!mTarget.isValid()) return; Point3F targetPos = mTarget.target->getBoxCenter(); // Can we see the target? MatrixF aimMat; getAimTransform(aimMat); Point3F start; aimMat.getColumn(3, &start); RayInfo ri; Point3F sightPoint; disableCollision(); bool los = _testTargetLineOfSight(start, mTarget.target, sightPoint); enableCollision(); if (!los) { // Target is blocked. Should we try to track from its last // known position and velocity? SimTime curTime = Sim::getCurrentTime(); if ( (curTime - mTarget.lastSightTime) > (mDataBlock->trackLostTargetTime * 1000.0f) ) { // Time's up. Stop tracking. _cleanupTargetAndTurret(); return; } // Use last known information to attempt to // continue to track target for a while. targetPos = mTarget.lastPos + mTarget.lastVel * F32(curTime - mTarget.lastSightTime) / 1000.0f; } else { // Target is visible // We only track targets that are alive if (mTarget.target->getDamageState() != Enabled) { // We can't track any more _cleanupTargetAndTurret(); return; } targetPos = sightPoint; // Store latest target info mTarget.lastPos = targetPos; mTarget.lastVel = mTarget.target->getVelocity(); mTarget.lastSightTime = Sim::getCurrentTime(); } // Calculate angles to face the target, specifically the part that we can see VectorF toTarget; MatrixF mat; S32 node = mDataBlock->aimNode; if (node != -1) { // Get the current position of our node MatrixF* nodeTrans = &mShapeInstance->mNodeTransforms[node]; Point3F currentPos; nodeTrans->getColumn(3, ¤tPos); // Turn this into a matrix we can use to put the target // position into our space. MatrixF nodeMat(true); nodeMat.setColumn(3, currentPos); mat.mul(mObjToWorld, nodeMat); mat.affineInverse(); } else { mat = mWorldToObj; } mat.mulP(targetPos, &toTarget); // lead the target F32 timeToTargetSquared = (mWeaponLeadVelocitySquared > 0) ? toTarget.lenSquared() / mWeaponLeadVelocitySquared : 0; if (timeToTargetSquared > 1.0) { targetPos = targetPos + (mTarget.lastVel * mSqrt(timeToTargetSquared)); mat.mulP(targetPos, &toTarget); } F32 yaw, pitch; MathUtils::getAnglesFromVector(toTarget, yaw, pitch); if (yaw > M_PI_F) yaw = yaw - M_2PI_F; //if (pitch > M_PI_F) // pitch = -(pitch - M_2PI_F); Point3F rot(-pitch, 0.0f, yaw); // If we have a rotation rate make sure we follow it if (mHeadingRate > 0) { F32 rate = mHeadingRate * dt; F32 rateCheck = mFabs(rot.z - mRot.z); if (rateCheck > rate) { // This will clamp the new value to the rate regardless if it // is increasing or decreasing. rot.z = mClampF(rot.z, mRot.z-rate, mRot.z+rate); } } if (mPitchRate > 0) { F32 rate = mPitchRate * dt; F32 rateCheck = mFabs(rot.x - mRot.x); if (rateCheck > rate) { // This will clamp the new value to the rate regardless if it // is increasing or decreasing. rot.x = mClampF(rot.x, mRot.x-rate, mRot.x+rate); } } // Test if the rotation to the target is outside of our limits if (_outsideLimits(rot)) { // We can't track any more _cleanupTargetAndTurret(); return; } // Test if the target is out of weapons range if (toTarget.lenSquared() > mWeaponRangeSquared) { // We can't track any more _cleanupTargetAndTurret(); return; } mRot = rot; _setRotation( mRot ); setMaskBits(TurretUpdateMask); }
void FlyingVehicle::updateForces(F32 /*dt*/) { PROFILE_SCOPE( FlyingVehicle_UpdateForces ); MatrixF currPosMat; mRigid.getTransform(&currPosMat); mRigid.atRest = false; Point3F massCenter; currPosMat.mulP(mDataBlock->massCenter,&massCenter); Point3F xv,yv,zv; currPosMat.getColumn(0,&xv); currPosMat.getColumn(1,&yv); currPosMat.getColumn(2,&zv); F32 speed = mRigid.linVelocity.len(); Point3F force = Point3F(0, 0, sFlyingVehicleGravity * mRigid.mass * mGravityMod); Point3F torque = Point3F(0, 0, 0); // Drag at any speed force -= mRigid.linVelocity * mDataBlock->minDrag; torque -= mRigid.angMomentum * mDataBlock->rotationalDrag; // Auto-stop at low speeds if (speed < mDataBlock->maxAutoSpeed) { F32 autoScale = 1 - speed / mDataBlock->maxAutoSpeed; // Gyroscope F32 gf = mDataBlock->autoAngularForce * autoScale; torque -= xv * gf * mDot(yv,Point3F(0,0,1)); // Manuevering jets F32 sf = mDataBlock->autoLinearForce * autoScale; force -= yv * sf * mDot(yv, mRigid.linVelocity); force -= xv * sf * mDot(xv, mRigid.linVelocity); } // Hovering Jet F32 vf = -sFlyingVehicleGravity * mRigid.mass * mGravityMod; F32 h = getHeight(); if (h <= 1) { if (h > 0) { vf -= vf * h * 0.1; } else { vf += mDataBlock->jetForce * -h; } } force += zv * vf; // Damping "surfaces" force -= xv * mDot(xv,mRigid.linVelocity) * mDataBlock->horizontalSurfaceForce; force -= zv * mDot(zv,mRigid.linVelocity) * mDataBlock->verticalSurfaceForce; // Turbo Jet if (mJetting) { if (mThrustDirection == ThrustForward) force += yv * mDataBlock->jetForce * mCeilingFactor; else if (mThrustDirection == ThrustBackward) force -= yv * mDataBlock->jetForce * mCeilingFactor; else force += zv * mDataBlock->jetForce * mDataBlock->vertThrustMultiple * mCeilingFactor; } // Maneuvering jets force += yv * (mThrust.y * mDataBlock->maneuveringForce * mCeilingFactor); force += xv * (mThrust.x * mDataBlock->maneuveringForce * mCeilingFactor); // Steering Point2F steering; steering.x = mSteering.x / mDataBlock->maxSteeringAngle; steering.x *= mFabs(steering.x); steering.y = mSteering.y / mDataBlock->maxSteeringAngle; steering.y *= mFabs(steering.y); torque -= xv * steering.y * mDataBlock->steeringForce; torque -= zv * steering.x * mDataBlock->steeringForce; // Roll torque += yv * steering.x * mDataBlock->steeringRollForce; F32 ar = mDataBlock->autoAngularForce * mDot(xv,Point3F(0,0,1)); ar -= mDataBlock->rollForce * mDot(xv, mRigid.linVelocity); torque += yv * ar; // Add in force from physical zones... force += mAppliedForce; // Container buoyancy & drag force -= Point3F(0, 0, 1) * (mBuoyancy * sFlyingVehicleGravity * mRigid.mass * mGravityMod); force -= mRigid.linVelocity * mDrag; // mRigid.force = force; mRigid.torque = torque; }
void TimeOfDay::_getSunColor( ColorF *outColor ) const { const COLOR_TARGET *ct = NULL; F32 ele = mClampF( M_2PI_F - mElevation, 0.0f, M_PI_F ); F32 phase = -1.0f; F32 div; if (!mColorTargets.size()) { outColor->set(1.0f,1.0f,1.0f); return; } if (mColorTargets.size() == 1) { ct = &mColorTargets[0]; outColor->set(ct->color.red, ct->color.green, ct->color.blue); return; } //simple check if ( mColorTargets[0].elevation != 0.0f ) { AssertFatal(0, "TimeOfDay::GetColor() - First elevation must be 0.0 radians") outColor->set(1.0f, 1.0f, 1.0f); //mBandMod = 1.0f; //mCurrentBandColor = color; return; } if ( mColorTargets[mColorTargets.size()-1].elevation != M_PI_F ) { AssertFatal(0, "Celestails::GetColor() - Last elevation must be PI") outColor->set(1.0f, 1.0f, 1.0f); //mBandMod = 1.0f; //mCurrentBandColor = color; return; } //we need to find the phase and interp... also loop back around U32 count=0; for (;count < mColorTargets.size() - 1; count++) { const COLOR_TARGET *one = &mColorTargets[count]; const COLOR_TARGET *two = &mColorTargets[count+1]; if (ele >= one->elevation && ele <= two->elevation) { div = two->elevation - one->elevation; //catch bad input divide by zero if ( mFabs( div ) < 0.01f ) div = 0.01f; phase = (ele - one->elevation) / div; outColor->interpolate( one->color, two->color, phase ); //mCurrentBandColor.interpolate(one->bandColor, two->bandColor, phase); //mBandMod = one->bandMod * (1.0f - phase) + two->bandMod * phase; return; } } AssertFatal(0,"This isn't supposed to happen"); }
F32 ScatterSky::_getMiePhase( F32 fCos, F32 fCos2, F32 g, F32 g2) { return 1.5f * ((1.0f - g2) / (2.0f + g2)) * (1.0f + fCos2) / mPow(mFabs(1.0f + g2 - 2.0f*g*fCos), 1.5f); }
/** * This method calculates the moves for the AI player * * @param movePtr Pointer to move the move list into */ bool AIPlayer::getAIMove(Move *movePtr) { *movePtr = NullMove; // Use the eye as the current position. MatrixF eye; getEyeTransform(&eye); Point3F location = eye.getPosition(); Point3F rotation = getRotation(); // Orient towards the aim point, aim object, or towards // our destination. if (mAimObject || mAimLocationSet || mMoveState != ModeStop) { // Update the aim position if we're aiming for an object if (mAimObject) mAimLocation = mAimObject->getPosition() + mAimOffset; else if (!mAimLocationSet) mAimLocation = mMoveDestination; F32 xDiff = mAimLocation.x - location.x; F32 yDiff = mAimLocation.y - location.y; if (!mIsZero(xDiff) || !mIsZero(yDiff)) { // First do Yaw // use the cur yaw between -Pi and Pi F32 curYaw = rotation.z; while (curYaw > M_2PI_F) curYaw -= M_2PI_F; while (curYaw < -M_2PI_F) curYaw += M_2PI_F; // find the yaw offset F32 newYaw = mAtan2( xDiff, yDiff ); F32 yawDiff = newYaw - curYaw; // make it between 0 and 2PI if( yawDiff < 0.0f ) yawDiff += M_2PI_F; else if( yawDiff >= M_2PI_F ) yawDiff -= M_2PI_F; // now make sure we take the short way around the circle if( yawDiff > M_PI_F ) yawDiff -= M_2PI_F; else if( yawDiff < -M_PI_F ) yawDiff += M_2PI_F; movePtr->yaw = yawDiff; // Next do pitch. if (!mAimObject && !mAimLocationSet) { // Level out if were just looking at our next way point. Point3F headRotation = getHeadRotation(); movePtr->pitch = -headRotation.x; } else { // This should be adjusted to run from the // eye point to the object's center position. Though this // works well enough for now. F32 vertDist = mAimLocation.z - location.z; F32 horzDist = mSqrt(xDiff * xDiff + yDiff * yDiff); F32 newPitch = mAtan2( horzDist, vertDist ) - ( M_PI_F / 2.0f ); if (mFabs(newPitch) > 0.01f) { Point3F headRotation = getHeadRotation(); movePtr->pitch = newPitch - headRotation.x; } } } } else { // Level out if we're not doing anything else Point3F headRotation = getHeadRotation(); movePtr->pitch = -headRotation.x; } // Move towards the destination if (mMoveState != ModeStop) { F32 xDiff = mMoveDestination.x - location.x; F32 yDiff = mMoveDestination.y - location.y; // Check if we should mMove, or if we are 'close enough' if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) { mMoveState = ModeStop; throwCallback("onReachDestination"); } else { // Build move direction in world space if (mIsZero(xDiff)) movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f; else if (mIsZero(yDiff)) movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f; else if (mFabs(xDiff) > mFabs(yDiff)) { F32 value = mFabs(yDiff / xDiff); movePtr->y = (location.y > mMoveDestination.y) ? -value : value; movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f; } else { F32 value = mFabs(xDiff / yDiff); movePtr->x = (location.x > mMoveDestination.x) ? -value : value; movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f; } // Rotate the move into object space (this really only needs // a 2D matrix) Point3F newMove; MatrixF moveMatrix; moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw))); moveMatrix.mulV( Point3F( movePtr->x, movePtr->y, 0.0f ), &newMove ); movePtr->x = newMove.x; movePtr->y = newMove.y; // Set movement speed. We'll slow down once we get close // to try and stop on the spot... if (mMoveSlowdown) { F32 speed = mMoveSpeed; F32 dist = mSqrt(xDiff*xDiff + yDiff*yDiff); F32 maxDist = 5.0f; if (dist < maxDist) speed *= dist / maxDist; movePtr->x *= speed; movePtr->y *= speed; mMoveState = ModeSlowing; } else { movePtr->x *= mMoveSpeed; movePtr->y *= mMoveSpeed; mMoveState = ModeMove; } if (mMoveStuckTestCountdown > 0) --mMoveStuckTestCountdown; else { // We should check to see if we are stuck... F32 locationDelta = (location - mLastLocation).len(); if (locationDelta < mMoveStuckTolerance && mDamageState == Enabled) { // If we are slowing down, then it's likely that our location delta will be less than // our move stuck tolerance. Because we can be both slowing and stuck // we should TRY to check if we've moved. This could use better detection. if ( mMoveState != ModeSlowing || locationDelta == 0 ) { mMoveState = ModeStuck; throwCallback("onMoveStuck"); } } } } } // Test for target location in sight if it's an object. The LOS is // run from the eye position to the center of the object's bounding, // which is not very accurate. if (mAimObject) { MatrixF eyeMat; getEyeTransform(&eyeMat); eyeMat.getColumn(3,&location); Point3F targetLoc = mAimObject->getBoxCenter(); // This ray ignores non-static shapes. Cast Ray returns true // if it hit something. RayInfo dummy; if (getContainer()->castRay( location, targetLoc, StaticShapeObjectType | StaticObjectType | TerrainObjectType, &dummy)) { if (mTargetInLOS) { throwCallback( "onTargetExitLOS" ); mTargetInLOS = false; } } else if (!mTargetInLOS) { throwCallback( "onTargetEnterLOS" ); mTargetInLOS = true; } } // Replicate the trigger state into the move so that // triggers can be controlled from scripts. for( int i = 0; i < MaxTriggerKeys; i++ ) movePtr->trigger[i] = getImageTriggerState(i); mLastLocation = location; return true; }
Point2I GuiColorPickerCtrl::findColor(const ColorF & color, const Point2I& offset, const Point2I& resolution, GBitmap& bmp) { RectI rect; Point2I ext = getExtent(); if (mDisplayMode != pDropperBackground) { ext.x -= 3; ext.y -= 2; rect = RectI(Point2I(1, 1), ext); } else { rect = RectI(Point2I(0, 0), ext); } Point2I closestPos(-1, -1); /* Debugging char filename[256]; dSprintf( filename, 256, "%s.%s", "colorPickerTest", "png" ); // Open up the file on disk. FileStream fs; if ( !fs.open( filename, Torque::FS::File::Write ) ) Con::errorf( "GuiObjectView::saveAsImage() - Failed to open output file '%s'!", filename ); else { // Write it and close. bmp.writeBitmap( "png", fs ); fs.close(); } */ ColorI tmp; U32 buf_x; U32 buf_y; ColorF curColor; F32 val(10000.0f); F32 closestVal(10000.0f); bool closestSet = false; for (S32 x = rect.point.x; x <= rect.extent.x; x++) { for (S32 y = rect.point.y; y <= rect.extent.y; y++) { buf_x = offset.x + x + 1; buf_y = (resolution.y - (offset.y + y + 1)); buf_y = resolution.y - buf_y; //Get the color at that position bmp.getColor(buf_x, buf_y, tmp); curColor = (ColorF)tmp; //Evaluate how close the color is to our desired color val = mFabs(color.red - curColor.red) + mFabs(color.green - curColor.green) + mFabs(color.blue - curColor.blue); if (!closestSet) { closestVal = val; closestPos.set(x, y); closestSet = true; } else if (val < closestVal) { closestVal = val; closestPos.set(x, y); } } } return closestPos; }
AtlasDiscreteMesh *AtlasDiscreteMesh::decimate(U32 target) { #ifndef ENABLE_DECIMATION Con::errorf("AtlasDiscreteMesh::decimate - decimation not enabled, define ENABLE_DECIMATION!"); return NULL; #else // collapse rules: // // side to top, to what vert (if any) // // center edge corner never // center any edge corner never // edge - line corner - // corner - - - - // never - - - - // First, copy ourselves into a MxStdModel MxStdModel sm(mVertexCount, mIndexCount/3); sm.texcoord_binding(MX_PERVERTEX); sm.normal_binding(MX_PERVERTEX); for(U32 i=0; i<mVertexCount; i++) { sm.add_texcoord(mTex[i].x, mTex[i].y); sm.add_vertex(mPos[i].x, mPos[i].y, mPos[i].z); sm.add_normal(mNormal[i].x, mNormal[i].y, mNormal[i].z); } for(U32 i=0; i<mIndexCount; i+=3) sm.add_face(mIndex[i+0],mIndex[i+1],mIndex[i+2]); Con::printf("AtlasDiscreteMesh::decimate - starting with %d verts, %d indices, %d faces allocated.", sm.vert_count(), mIndexCount, sm.face_count()); // Run it through the decimator. MxEdgeQSlim qslim(sm); qslim.placement_policy = MX_PLACE_ENDORMID; //MX_PLACE_OPTIMAL; <-- optimal causes f'ed up results! qslim.weighting_policy = MX_WEIGHT_AREA_AVG; qslim.compactness_ratio = 0.0; qslim.meshing_penalty = 1.0; qslim.will_join_only = false; // Now, calculate our constraints - basically take everything that's within // 0.1 meters of a bounding plane and mark it with that plane's IDs. Our // bounding planes are the 4 vertical faces of the bounding box. PlaneF planes[4]; Box3F bounds = calcBounds(); // Generate planes from three points... Up is z+, and the normal for the // plane should face towards the center (but it doesn't really matter, we // only ever take absolute distance). { Point3F a,b,c; a.x = bounds.minExtents.x; a.y = bounds.minExtents.y; a.z = bounds.minExtents.z; b.x = bounds.maxExtents.x; b.y = bounds.minExtents.y; b.z = bounds.minExtents.z; c = a + Point3F(0, 0, 10); planes[0].set(a,b,c); } { Point3F a,b,c; a.x = bounds.maxExtents.x; a.y = bounds.minExtents.y; a.z = bounds.minExtents.z; b.x = bounds.maxExtents.x; b.y = bounds.maxExtents.y; b.z = bounds.minExtents.z; c = a + Point3F(0, 0, 10); planes[1].set(a,b,c); } { Point3F a,b,c; a.x = bounds.maxExtents.x; a.y = bounds.maxExtents.y; a.z = bounds.minExtents.z; b.x = bounds.minExtents.x; b.y = bounds.maxExtents.y; b.z = bounds.minExtents.z; c = a + Point3F(0, 0, 10); planes[2].set(a,b,c); } { Point3F a,b,c; a.x = bounds.minExtents.x; a.y = bounds.maxExtents.y; a.z = bounds.minExtents.z; b.x = bounds.minExtents.x; b.y = bounds.minExtents.y; b.z = bounds.minExtents.z; c = a + Point3F(0, 0, 10); planes[3].set(a,b,c); } // Now categorize everything w/ constraints. for(U32 i=0; i<mVertexCount; i++) { U8 bits=0; for(S32 j=0; j<4; j++) { F32 d = mFabs(planes[j].distToPlane(mPos[i])); if(d < .1) bits |= BIT(j); } qslim.planarConstraints(i) = bits; } // Now we initialize and constrain... qslim.initialize(); qslim.decimate(target); // Copy everything out to a new ADM AtlasDiscreteMesh *admOut = new AtlasDiscreteMesh(); // First, mark stray vertices for removal for(U32 i=0; i<sm.vert_count(); i++) if(sm.vertex_is_valid(i) && sm.neighbors(i).length() == 0 ) sm.vertex_mark_invalid(i); // Compact vertex array so only valid vertices remain sm.compact_vertices(); // Copy verts out. Get a count, first. U32 vertCount = 0; for(U32 i=0; i<sm.vert_count(); i++) if(sm.vertex_is_valid(i)) vertCount++; admOut->mVertexCount = vertCount; admOut->mPos = new Point3F[vertCount]; admOut->mTex = new Point2F[vertCount]; admOut->mNormal = new Point3F[vertCount]; for(S32 i=0; i<vertCount; i++) { if(!sm.vertex_is_valid(i)) continue; const MxVertex &v = qslim.model().vertex(i); const MxTexCoord &t = qslim.model().texcoord(i); const MxNormal &n = qslim.model().normal(i); admOut->mPos[i].x = v[0]; admOut->mPos[i].y = v[1]; admOut->mPos[i].z = v[2]; admOut->mTex[i].x = t[0]; admOut->mTex[i].y = t[1]; admOut->mNormal[i].x = n[0]; admOut->mNormal[i].y = n[1]; admOut->mNormal[i].z = n[2]; } // We have to ignore non-valid faces, so let's recount our idxCount. U32 idxCount = 0; for(U32 i=0; i < sm.face_count(); i++) if(sm.face_is_valid(i)) idxCount += 3; Con::printf("AtlasDiscreteMesh::decimate - %d out of %d verts, %d out of %d indices.", vertCount, sm.vert_count(), idxCount, sm.face_count() * 3); admOut->mIndexCount = idxCount; admOut->mIndex = new U16[idxCount]; U16 *idx = admOut->mIndex; for(S32 i=0; i < sm.face_count(); i++) { // Skip invalid. if(!sm.face_is_valid(i)) continue; const MxFace &f = sm.face(i); idx[0] = f[0]; idx[1] = f[1]; idx[2] = f[2]; //Con::printf(" face #%d (%d, %d, %d)", i, f[0], f[1], f[2]); idx += 3; } // Make sure our vert and index counts are accurate. admOut->mIndexCount = idx - admOut->mIndex; return admOut; #endif }
void TSShapeInstance::renderDebugNormals( F32 normalScalar, S32 dl ) { if ( dl < 0 ) return; AssertFatal( dl >= 0 && dl < mShape->details.size(), "TSShapeInstance::renderDebugNormals() - Bad detail level!" ); static GFXStateBlockRef sb; if ( sb.isNull() ) { GFXStateBlockDesc desc; desc.setCullMode( GFXCullNone ); desc.setZReadWrite( true ); desc.zWriteEnable = false; desc.vertexColorEnable = true; sb = GFX->createStateBlock( desc ); } GFX->setStateBlock( sb ); const TSDetail *detail = &mShape->details[dl]; const S32 ss = detail->subShapeNum; if ( ss < 0 ) return; const S32 start = mShape->subShapeFirstObject[ss]; const S32 end = start + mShape->subShapeNumObjects[ss]; for ( S32 i = start; i < end; i++ ) { MeshObjectInstance *meshObj = &mMeshObjects[i]; if ( !meshObj ) continue; const MatrixF &meshMat = meshObj->getTransform(); // Then go through each TSMesh... U32 m = 0; for( TSMesh *mesh = meshObj->getMesh(m); mesh != NULL; mesh = meshObj->getMesh(m++) ) { // and pull out the list of normals. const U32 numNrms = mesh->mNumVerts; PrimBuild::begin( GFXLineList, 2 * numNrms ); for ( U32 n = 0; n < numNrms; n++ ) { Point3F norm = mesh->mVertexData[n].normal(); Point3F vert = mesh->mVertexData[n].vert(); meshMat.mulP( vert ); meshMat.mulV( norm ); // Then render them. PrimBuild::color4f( mFabs( norm.x ), mFabs( norm.y ), mFabs( norm.z ), 1.0f ); PrimBuild::vertex3fv( vert ); PrimBuild::vertex3fv( vert + (norm * normalScalar) ); } PrimBuild::end(); } } }
"@brief Find objects matching the bitmask type within a box centered at point, with extents x, y, z.\n\n" "@returns The first object found, or an empty string if nothing was found. Thereafter, you can get more " "results using containerFindNext()." "@see containerFindNext\n" "@ingroup Game") { //find out what we're looking for U32 typeMask = U32(dAtoi(argv[1])); //find the center of the container volume Point3F origin(0.0f, 0.0f, 0.0f); dSscanf(argv[2], "%g %g %g", &origin.x, &origin.y, &origin.z); //find the box dimensions Point3F size(0.0f, 0.0f, 0.0f); size.x = mFabs(dAtof(argv[3])); size.y = mFabs(dAtof(argv[4])); size.z = mFabs(dAtof(argv[5])); //build the container volume Box3F queryBox; queryBox.minExtents = origin; queryBox.maxExtents = origin; queryBox.minExtents -= size; queryBox.maxExtents += size; //initialize the list, and do the query sgServerQueryList.mList.clear(); gServerContainer.findObjects(queryBox, typeMask, SimpleQueryList::insertionCallback, &sgServerQueryList); //return the first element
bool Box3F::collideOrientedBox(const Point3F & bRadii, const MatrixF & toA) const { Point3F p; toA.getColumn(3,&p); Point3F aCenter = minExtents + maxExtents; aCenter *= 0.5f; p -= aCenter; // this essentially puts origin of toA target space on the center of the current box Point3F aRadii = maxExtents - minExtents; aRadii *= 0.5f; F32 absXX,absXY,absXZ; F32 absYX,absYY,absYZ; F32 absZX,absZY,absZZ; const F32 * f = toA; absXX = mFabs(f[0]); absYX = mFabs(f[1]); absZX = mFabs(f[2]); if (aRadii.x + bRadii.x * absXX + bRadii.y * absYX + bRadii.z * absZX - mFabs(p.x)<0.0f) return false; absXY = mFabs(f[4]); absYY = mFabs(f[5]); absZY = mFabs(f[6]); if (aRadii.y + bRadii.x * absXY + bRadii.y * absYY + bRadii.z * absZY - mFabs(p.y)<0.0f) return false; absXZ = mFabs(f[8]); absYZ = mFabs(f[9]); absZZ = mFabs(f[10]); if (aRadii.z + bRadii.x * absXZ + bRadii.y * absYZ + bRadii.z * absZZ - mFabs(p.z)<0.0f) return false; if (aRadii.x*absXX + aRadii.y*absXY + aRadii.z*absXZ + bRadii.x - mFabs(p.x*f[0] + p.y*f[4] + p.z*f[8])<0.0f) return false; if (aRadii.x*absYX + aRadii.y*absYY + aRadii.z*absYZ + bRadii.y - mFabs(p.x*f[1] + p.y*f[5] + p.z*f[9])<0.0f) return false; if (aRadii.x*absZX + aRadii.y*absZY + aRadii.z*absZZ + bRadii.z - mFabs(p.x*f[2] + p.y*f[6] + p.z*f[10])<0.0f) return false; if (mFabs(p.z*f[4] - p.y*f[8]) > aRadii.y * absXZ + aRadii.z * absXY + bRadii.y * absZX + bRadii.z * absYX) return false; if (mFabs(p.z*f[5] - p.y*f[9]) > aRadii.y * absYZ + aRadii.z * absYY + bRadii.x * absZX + bRadii.z * absXX) return false; if (mFabs(p.z*f[6] - p.y*f[10]) > aRadii.y * absZZ + aRadii.z * absZY + bRadii.x * absYX + bRadii.y * absXX) return false; if (mFabs(p.x*f[8] - p.z*f[0]) > aRadii.x * absXZ + aRadii.z * absXX + bRadii.y * absZY + bRadii.z * absYY) return false; if (mFabs(p.x*f[9] - p.z*f[1]) > aRadii.x * absYZ + aRadii.z * absYX + bRadii.x * absZY + bRadii.z * absXY) return false; if (mFabs(p.x*f[10] - p.z*f[2]) > aRadii.x * absZZ + aRadii.z * absZX + bRadii.x * absYY + bRadii.y * absXY) return false; if (mFabs(p.y*f[0] - p.x*f[4]) > aRadii.x * absXY + aRadii.y * absXX + bRadii.y * absZZ + bRadii.z * absYZ) return false; if (mFabs(p.y*f[1] - p.x*f[5]) > aRadii.x * absYY + aRadii.y * absYX + bRadii.x * absZZ + bRadii.z * absXZ) return false; if (mFabs(p.y*f[2] - p.x*f[6]) > aRadii.x * absZY + aRadii.y * absZX + bRadii.x * absYZ + bRadii.y * absXZ) return false; return true; }
//----------------------------------------------------------------------------- // Mouse Events //----------------------------------------------------------------------------- void WindowInputGenerator::handleMouseMove( WindowId did, U32 modifier, S32 x, S32 y, bool isRelative ) { if( !mInputController || !mFocused ) return; // jddTODO : Clean this up // CodeReview currently the Torque GuiCanvas deals with mouse input // as relative movement, even when the cursor is visible. Because // of this there is an asinine bit of code in there that manages // updating the cursor position on the class based on relative movement. // Because of this we always have to generate and send off for processing // relative events, even if the mouse is not locked. // I'm considering removing this in the Canvas refactor, thoughts? [7/6/2007 justind] // Generate a base Movement along and Axis event InputEventInfo event; event.deviceType = MouseDeviceType; event.deviceInst = 0; event.objType = SI_AXIS; event.modifier = convertModifierBits(modifier); event.ascii = 0; // Generate delta movement along each axis Point2F cursDelta; if(isRelative) { cursDelta.x = F32(x) * mPixelsPerMickey; cursDelta.y = F32(y) * mPixelsPerMickey; } else { cursDelta.x = F32(x - mLastCursorPos.x); cursDelta.y = F32(y - mLastCursorPos.y); } // If X axis changed, generate a relative event if(mFabs(cursDelta.x) > 0.1) { event.objInst = SI_XAXIS; event.action = SI_MOVE; event.fValue = cursDelta.x; generateInputEvent(event); } // If Y axis changed, generate a relative event if(mFabs(cursDelta.y) > 0.1) { event.objInst = SI_YAXIS; event.action = SI_MOVE; event.fValue = cursDelta.y; generateInputEvent(event); } // CodeReview : If we're not relative, pass along a positional update // so that the canvas can update it's internal cursor tracking // point. [7/6/2007 justind] if( !isRelative ) { if( mClampToWindow ) { Point2I winExtent = mWindow->getClientExtent(); x = mClampF(x, 0.0f, F32(winExtent.x - 1)); y = mClampF(y, 0.0f, F32(winExtent.y - 1)); } // When the window gains focus, we send a cursor position event if( mNotifyPosition ) { mNotifyPosition = false; // We use SI_MAKE to signify that the position is being set, not relatively moved. event.action = SI_MAKE; // X Axis event.objInst = SI_XAXIS; event.fValue = (F32)x; generateInputEvent(event); // Y Axis event.objInst = SI_YAXIS; event.fValue = (F32)y; generateInputEvent(event); } mLastCursorPos = Point2I(x,y); } else { mLastCursorPos += Point2I(x,y); mNotifyPosition = true; } }
bool DecalManager::clipDecal( DecalInstance *decal, Vector<Point3F> *edgeVerts, const Point2F *clipDepth ) { PROFILE_SCOPE( DecalManager_clipDecal ); // Free old verts and indices. _freeBuffers( decal ); ClippedPolyList clipper; clipper.mNormal.set( Point3F( 0, 0, 0 ) ); clipper.mPlaneList.setSize(6); F32 halfSize = decal->mSize * 0.5f; // Ugly hack for ProjectedShadow! F32 halfSizeZ = clipDepth ? clipDepth->x : halfSize; F32 negHalfSize = clipDepth ? clipDepth->y : halfSize; Point3F decalHalfSize( halfSize, halfSize, halfSize ); Point3F decalHalfSizeZ( halfSizeZ, halfSizeZ, halfSizeZ ); MatrixF projMat( true ); decal->getWorldMatrix( &projMat ); const VectorF &crossVec = decal->mNormal; const Point3F &decalPos = decal->mPosition; VectorF newFwd, newRight; projMat.getColumn( 0, &newRight ); projMat.getColumn( 1, &newFwd ); VectorF objRight( 1.0f, 0, 0 ); VectorF objFwd( 0, 1.0f, 0 ); VectorF objUp( 0, 0, 1.0f ); // See above re: decalHalfSizeZ hack. clipper.mPlaneList[0].set( ( decalPos + ( -newRight * halfSize ) ), -newRight ); clipper.mPlaneList[1].set( ( decalPos + ( -newFwd * halfSize ) ), -newFwd ); clipper.mPlaneList[2].set( ( decalPos + ( -crossVec * decalHalfSizeZ ) ), -crossVec ); clipper.mPlaneList[3].set( ( decalPos + ( newRight * halfSize ) ), newRight ); clipper.mPlaneList[4].set( ( decalPos + ( newFwd * halfSize ) ), newFwd ); clipper.mPlaneList[5].set( ( decalPos + ( crossVec * negHalfSize ) ), crossVec ); clipper.mNormal = -decal->mNormal; Box3F box( -decalHalfSizeZ, decalHalfSizeZ ); projMat.mul( box ); DecalData *decalData = decal->mDataBlock; PROFILE_START( DecalManager_clipDecal_buildPolyList ); getContainer()->buildPolyList( box, decalData->clippingMasks, &clipper ); PROFILE_END(); clipper.cullUnusedVerts(); clipper.triangulate(); clipper.generateNormals(); if ( clipper.mVertexList.empty() ) return false; #ifdef DECALMANAGER_DEBUG mDebugPlanes.clear(); mDebugPlanes.merge( clipper.mPlaneList ); #endif decal->mVertCount = clipper.mVertexList.size(); decal->mIndxCount = clipper.mIndexList.size(); Vector<Point3F> tmpPoints; tmpPoints.push_back(( objFwd * decalHalfSize ) + ( objRight * decalHalfSize )); tmpPoints.push_back(( objFwd * decalHalfSize ) + ( -objRight * decalHalfSize )); tmpPoints.push_back(( -objFwd * decalHalfSize ) + ( -objRight * decalHalfSize )); Point3F lowerLeft(( -objFwd * decalHalfSize ) + ( objRight * decalHalfSize )); projMat.inverse(); _generateWindingOrder( lowerLeft, &tmpPoints ); BiQuadToSqr quadToSquare( Point2F( lowerLeft.x, lowerLeft.y ), Point2F( tmpPoints[0].x, tmpPoints[0].y ), Point2F( tmpPoints[1].x, tmpPoints[1].y ), Point2F( tmpPoints[2].x, tmpPoints[2].y ) ); Point2F uv( 0, 0 ); Point3F vecX(0.0f, 0.0f, 0.0f); // Allocate memory for vert and index arrays _allocBuffers( decal ); Point3F vertPoint( 0, 0, 0 ); for ( U32 i = 0; i < clipper.mVertexList.size(); i++ ) { const ClippedPolyList::Vertex &vert = clipper.mVertexList[i]; vertPoint = vert.point; // Transform this point to // object space to look up the // UV coordinate for this vertex. projMat.mulP( vertPoint ); // Clamp the point to be within the quad. vertPoint.x = mClampF( vertPoint.x, -decalHalfSize.x, decalHalfSize.x ); vertPoint.y = mClampF( vertPoint.y, -decalHalfSize.y, decalHalfSize.y ); // Get our UV. uv = quadToSquare.transform( Point2F( vertPoint.x, vertPoint.y ) ); const RectF &rect = decal->mDataBlock->texRect[decal->mTextureRectIdx]; uv *= rect.extent; uv += rect.point; // Set the world space vertex position. decal->mVerts[i].point = vert.point; decal->mVerts[i].texCoord.set( uv.x, uv.y ); decal->mVerts[i].normal = clipper.mNormalList[i]; decal->mVerts[i].normal.normalize(); if( mFabs( decal->mVerts[i].normal.z ) > 0.8f ) mCross( decal->mVerts[i].normal, Point3F( 1.0f, 0.0f, 0.0f ), &vecX ); else if ( mFabs( decal->mVerts[i].normal.x ) > 0.8f ) mCross( decal->mVerts[i].normal, Point3F( 0.0f, 1.0f, 0.0f ), &vecX ); else if ( mFabs( decal->mVerts[i].normal.y ) > 0.8f ) mCross( decal->mVerts[i].normal, Point3F( 0.0f, 0.0f, 1.0f ), &vecX ); decal->mVerts[i].tangent = mCross( decal->mVerts[i].normal, vecX ); } U32 curIdx = 0; for ( U32 j = 0; j < clipper.mPolyList.size(); j++ ) { // Write indices for each Poly ClippedPolyList::Poly *poly = &clipper.mPolyList[j]; AssertFatal( poly->vertexCount == 3, "Got non-triangle poly!" ); decal->mIndices[curIdx] = clipper.mIndexList[poly->vertexStart]; curIdx++; decal->mIndices[curIdx] = clipper.mIndexList[poly->vertexStart + 1]; curIdx++; decal->mIndices[curIdx] = clipper.mIndexList[poly->vertexStart + 2]; curIdx++; } if ( !edgeVerts ) return true; Point3F tmpHullPt( 0, 0, 0 ); Vector<Point3F> tmpHullPts; for ( U32 i = 0; i < clipper.mVertexList.size(); i++ ) { const ClippedPolyList::Vertex &vert = clipper.mVertexList[i]; tmpHullPt = vert.point; projMat.mulP( tmpHullPt ); tmpHullPts.push_back( tmpHullPt ); } edgeVerts->clear(); U32 verts = _generateConvexHull( tmpHullPts, edgeVerts ); edgeVerts->setSize( verts ); projMat.inverse(); for ( U32 i = 0; i < edgeVerts->size(); i++ ) projMat.mulP( (*edgeVerts)[i] ); return true; }
/** * This method calculates the moves for the AI player * * @param movePtr Pointer to move the move list into */ bool AIPlayer::getAIMove(Move *movePtr) { *movePtr = NullMove; // Use the eye as the current position. MatrixF eye; getEyeTransform(&eye); Point3F location = eye.getPosition(); Point3F rotation = getRotation(); #ifdef TORQUE_NAVIGATION_ENABLED if(mDamageState == Enabled) { if(mMoveState != ModeStop) updateNavMesh(); if(!mFollowData.object.isNull()) { if(mPathData.path.isNull()) { if((getPosition() - mFollowData.object->getPosition()).len() > mFollowData.radius) followObject(mFollowData.object, mFollowData.radius); } else { if((mPathData.path->mTo - mFollowData.object->getPosition()).len() > mFollowData.radius) repath(); else if((getPosition() - mFollowData.object->getPosition()).len() < mFollowData.radius) { clearPath(); mMoveState = ModeStop; throwCallback("onTargetInRange"); } else if((getPosition() - mFollowData.object->getPosition()).len() < mAttackRadius) { throwCallback("onTargetInFiringRange"); } } } } #endif // TORQUE_NAVIGATION_ENABLED // Orient towards the aim point, aim object, or towards // our destination. if (mAimObject || mAimLocationSet || mMoveState != ModeStop) { // Update the aim position if we're aiming for an object if (mAimObject) mAimLocation = mAimObject->getPosition() + mAimOffset; else if (!mAimLocationSet) mAimLocation = mMoveDestination; F32 xDiff = mAimLocation.x - location.x; F32 yDiff = mAimLocation.y - location.y; if (!mIsZero(xDiff) || !mIsZero(yDiff)) { // First do Yaw // use the cur yaw between -Pi and Pi F32 curYaw = rotation.z; while (curYaw > M_2PI_F) curYaw -= M_2PI_F; while (curYaw < -M_2PI_F) curYaw += M_2PI_F; // find the yaw offset F32 newYaw = mAtan2( xDiff, yDiff ); F32 yawDiff = newYaw - curYaw; // make it between 0 and 2PI if( yawDiff < 0.0f ) yawDiff += M_2PI_F; else if( yawDiff >= M_2PI_F ) yawDiff -= M_2PI_F; // now make sure we take the short way around the circle if( yawDiff > M_PI_F ) yawDiff -= M_2PI_F; else if( yawDiff < -M_PI_F ) yawDiff += M_2PI_F; movePtr->yaw = yawDiff; // Next do pitch. if (!mAimObject && !mAimLocationSet) { // Level out if were just looking at our next way point. Point3F headRotation = getHeadRotation(); movePtr->pitch = -headRotation.x; } else { // This should be adjusted to run from the // eye point to the object's center position. Though this // works well enough for now. F32 vertDist = mAimLocation.z - location.z; F32 horzDist = mSqrt(xDiff * xDiff + yDiff * yDiff); F32 newPitch = mAtan2( horzDist, vertDist ) - ( M_PI_F / 2.0f ); if (mFabs(newPitch) > 0.01f) { Point3F headRotation = getHeadRotation(); movePtr->pitch = newPitch - headRotation.x; } } } } else { // Level out if we're not doing anything else Point3F headRotation = getHeadRotation(); movePtr->pitch = -headRotation.x; } // Move towards the destination if (mMoveState != ModeStop) { F32 xDiff = mMoveDestination.x - location.x; F32 yDiff = mMoveDestination.y - location.y; // Check if we should mMove, or if we are 'close enough' if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) { mMoveState = ModeStop; onReachDestination(); } else { // Build move direction in world space if (mIsZero(xDiff)) movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f; else if (mIsZero(yDiff)) movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f; else if (mFabs(xDiff) > mFabs(yDiff)) { F32 value = mFabs(yDiff / xDiff); movePtr->y = (location.y > mMoveDestination.y) ? -value : value; movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f; } else { F32 value = mFabs(xDiff / yDiff); movePtr->x = (location.x > mMoveDestination.x) ? -value : value; movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f; } // Rotate the move into object space (this really only needs // a 2D matrix) Point3F newMove; MatrixF moveMatrix; moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw))); moveMatrix.mulV( Point3F( movePtr->x, movePtr->y, 0.0f ), &newMove ); movePtr->x = newMove.x; movePtr->y = newMove.y; // Set movement speed. We'll slow down once we get close // to try and stop on the spot... if (mMoveSlowdown) { F32 speed = mMoveSpeed; F32 dist = mSqrt(xDiff*xDiff + yDiff*yDiff); F32 maxDist = mMoveTolerance*2; if (dist < maxDist) speed *= dist / maxDist; movePtr->x *= speed; movePtr->y *= speed; mMoveState = ModeSlowing; } else { movePtr->x *= mMoveSpeed; movePtr->y *= mMoveSpeed; mMoveState = ModeMove; } if (mMoveStuckTestCountdown > 0) --mMoveStuckTestCountdown; else { // We should check to see if we are stuck... F32 locationDelta = (location - mLastLocation).len(); if (locationDelta < mMoveStuckTolerance && mDamageState == Enabled) { // If we are slowing down, then it's likely that our location delta will be less than // our move stuck tolerance. Because we can be both slowing and stuck // we should TRY to check if we've moved. This could use better detection. if ( mMoveState != ModeSlowing || locationDelta == 0 ) { mMoveState = ModeStuck; onStuck(); } } } } } // Test for target location in sight if it's an object. The LOS is // run from the eye position to the center of the object's bounding, // which is not very accurate. if (mAimObject) { if (checkInLos(mAimObject.getPointer())) { if (!mTargetInLOS) { throwCallback( "onTargetEnterLOS" ); mTargetInLOS = true; } } else if (mTargetInLOS) { throwCallback( "onTargetExitLOS" ); mTargetInLOS = false; } } Pose desiredPose = mPose; if ( mSwimming ) desiredPose = SwimPose; else if ( mAiPose == 1 && canCrouch() ) desiredPose = CrouchPose; else if ( mAiPose == 2 && canProne() ) desiredPose = PronePose; else if ( mAiPose == 3 && canSprint() ) desiredPose = SprintPose; else if ( canStand() ) desiredPose = StandPose; setPose( desiredPose ); // Replicate the trigger state into the move so that // triggers can be controlled from scripts. for( U32 i = 0; i < MaxTriggerKeys; i++ ) movePtr->trigger[ i ] = getImageTriggerState( i ); #ifdef TORQUE_NAVIGATION_ENABLED if(mJump == Now) { movePtr->trigger[2] = true; mJump = None; } else if(mJump == Ledge) { // If we're not touching the ground, jump! RayInfo info; if(!getContainer()->castRay(getPosition(), getPosition() - Point3F(0, 0, 0.4f), StaticShapeObjectType, &info)) { movePtr->trigger[2] = true; mJump = None; } } #endif // TORQUE_NAVIGATION_ENABLED mLastLocation = location; return true; }
//-------------------------------------------------------- //-------------------------------------------------------- // JK: faster ray->convexHull test - taken from TSMesh... // // Used by lighting system... // bool castRayBrush(const Point3F &start, const Point3F &end, PlaneF *planes, U32 planeCount) { // F32 startTime = -0.01f; F32 startNum = -0.01f; F32 startDen = 1.00f; // F32 endTime = 1.01f; F32 endNum = 1.01f; F32 endDen = 1.00f; S32 curPlane = 0; U32 curMaterial = 0; bool found = false; bool tmpFound; S32 tmpPlane; F32 sgn = -1.0f; F32 * pnum = &startNum; F32 * pden = &startDen; S32 * pplane = &curPlane; bool * pfound = &found; for (S32 i=0; i<planeCount; i++) { // if start & end outside, no collision // if start & end inside, continue // if start outside, end inside, or visa versa, find intersection of line with plane // then update intersection of line with hull (using startTime and endTime) F32 dot1 = mDot(planes[i],start) + planes[i].d; F32 dot2 = mDot(planes[i],end) + planes[i].d; if (dot1*dot2>0.0f) { // same side of the plane...which side -- dot==0 considered inside if (dot1>0.0f) // start and end outside of this plane, no collision return false; // start and end inside plane, continue continue; } AssertFatal(dot1/(dot1-dot2)>=0.0f && dot1/(dot1-dot2)<=1.0f,"TSMesh::castRay (1)"); // find intersection (time) with this plane... // F32 time = dot1 / (dot1-dot2); F32 num = mFabs(dot1); F32 den = mFabs(dot1-dot2); if (sgn*dot1>=0) { sgn *= -1.0f; pnum = (F32*) ((dsize_t)pnum ^ (dsize_t)&endNum ^ (dsize_t)&startNum); pden = (F32*) ((dsize_t)pden ^ (dsize_t)&endDen ^ (dsize_t)&startDen); pplane = (S32*) ((dsize_t)pplane ^ (dsize_t)&tmpPlane ^ (dsize_t)&curPlane); pfound = (bool*) ((dsize_t)pfound ^ (dsize_t)&tmpFound ^ (dsize_t)&found); } bool noCollision = num*endDen*sgn<endNum*den*sgn && num*startDen*sgn<startNum*den*sgn; if (num * *pden * sgn < *pnum * den * sgn && !noCollision) { *pnum = num; *pden = den; *pplane = i; *pfound = true; } else if (noCollision) return false; } return found; }
bool SceneCullingState::createCullingVolume( const Point3F* vertices, U32 numVertices, SceneCullingVolume::Type type, SceneCullingVolume& outVolume ) { const Point3F& viewPos = getCameraState().getViewPosition(); const Point3F& viewDir = getCameraState().getViewDirection(); const bool isOrtho = getCullingFrustum().isOrtho(); //TODO: check if we need to handle penetration of the near plane for occluders specially // Allocate space for the clipping planes we generate. Assume the worst case // of every edge generating a plane and, for includers, all edges meeting at // steep angles so we need to insert extra planes (the latter is not possible, // of course, but it makes things less complicated here). For occluders, add // an extra plane for the near cap. const U32 maxPlanes = ( type == SceneCullingVolume::Occluder ? numVertices + 1 : numVertices * 2 ); PlaneF* planes = allocateData< PlaneF >( maxPlanes ); // Keep track of the world-space bounds of the polygon. We use this later // to derive some metrics. Box3F wsPolyBounds; wsPolyBounds.minExtents = Point3F( TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX ); wsPolyBounds.maxExtents = Point3F( TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN ); // For occluders, also keep track of the nearest, and two farthest silhouette points. We use // this later to construct a near capping plane. F32 minVertexDistanceSquared = TypeTraits< F32 >::MAX; U32 leastDistantVert = 0; F32 maxVertexDistancesSquared[ 2 ] = { TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN }; U32 mostDistantVertices[ 2 ] = { 0, 0 }; // Generate the extrusion volume. For orthographic projections, extrude // parallel to the view direction whereas for parallel projections, extrude // from the viewpoint. U32 numPlanes = 0; U32 lastVertex = numVertices - 1; bool invert = false; for( U32 i = 0; i < numVertices; lastVertex = i, ++ i ) { AssertFatal( numPlanes < maxPlanes, "SceneCullingState::createCullingVolume - Did not allocate enough planes!" ); const Point3F& v1 = vertices[ i ]; const Point3F& v2 = vertices[ lastVertex ]; // Keep track of bounds. wsPolyBounds.minExtents.setMin( v1 ); wsPolyBounds.maxExtents.setMax( v1 ); // Skip the edge if it's length is really short. const Point3F edgeVector = v2 - v1; const F32 edgeVectorLenSquared = edgeVector.lenSquared(); if( edgeVectorLenSquared < 0.025f ) continue; //TODO: might need to do additional checks here for non-planar polygons used by occluders //TODO: test for colinearity of edge vector with view vector (occluders only) // Create a plane for the edge. if( isOrtho ) { // Compute a plane through the two edge vertices and one // of the vertices extended along the view direction. if( !invert ) planes[ numPlanes ] = PlaneF( v1, v1 + viewDir, v2 ); else planes[ numPlanes ] = PlaneF( v2, v1 + viewDir, v1 ); } else { // Compute a plane going through the viewpoint and the two // edge vertices. if( !invert ) planes[ numPlanes ] = PlaneF( v1, viewPos, v2 ); else planes[ numPlanes ] = PlaneF( v2, viewPos, v1 ); } numPlanes ++; // If this is the first plane that we have created, find out whether // the vertex ordering is giving us the plane orientations that we want // (facing inside). If not, invert vertex order from now on. if( numPlanes == 1 ) { Point3F center( 0, 0, 0 ); for( U32 n = 0; n < numVertices; ++ n ) center += vertices[n]; center /= numVertices; if( planes[numPlanes - 1].whichSide( center ) == PlaneF::Back ) { invert = true; planes[ numPlanes - 1 ].invert(); } } // For occluders, keep tabs of the nearest, and two farthest vertices. if( type == SceneCullingVolume::Occluder ) { const F32 distSquared = ( v1 - viewPos ).lenSquared(); if( distSquared < minVertexDistanceSquared ) { minVertexDistanceSquared = distSquared; leastDistantVert = i; } if( distSquared > maxVertexDistancesSquared[ 0 ] ) { // Move 0 to 1. maxVertexDistancesSquared[ 1 ] = maxVertexDistancesSquared[ 0 ]; mostDistantVertices[ 1 ] = mostDistantVertices[ 0 ]; // Replace 0. maxVertexDistancesSquared[ 0 ] = distSquared; mostDistantVertices[ 0 ] = i; } else if( distSquared > maxVertexDistancesSquared[ 1 ] ) { // Replace 1. maxVertexDistancesSquared[ 1 ] = distSquared; mostDistantVertices[ 1 ] = i; } } } // If the extrusion produced no useful result, abort. if( numPlanes < 3 ) return false; // For includers, test the angle of the edges at the current vertex. // If too steep, add an extra plane to improve culling efficiency. if( false )//type == SceneCullingVolume::Includer ) { const U32 numOriginalPlanes = numPlanes; U32 lastPlaneIndex = numPlanes - 1; for( U32 i = 0; i < numOriginalPlanes; lastPlaneIndex = i, ++ i ) { const PlaneF& currentPlane = planes[ i ]; const PlaneF& lastPlane = planes[ lastPlaneIndex ]; // Compute the cosine of the angle between the two plane normals. const F32 cosAngle = mFabs( mDot( currentPlane, lastPlane ) ); // The planes meet at increasingly steep angles the more they point // in opposite directions, i.e the closer the angle of their normals // is to 180 degrees. Skip any two planes that don't get near that. if( cosAngle > 0.1f ) continue; //TODO const Point3F addNormals = currentPlane + lastPlane; const Point3F crossNormals = mCross( currentPlane, lastPlane ); Point3F newNormal = currentPlane + lastPlane;//addNormals - mDot( addNormals, crossNormals ) * crossNormals; // planes[ numPlanes ] = PlaneF( currentPlane.getPosition(), newNormal ); numPlanes ++; } } // Compute the metrics of the culling volume in relation to the view frustum. // // For this, we are short-circuiting things slightly. The correct way (other than doing // full screen projections) would be to transform all the polygon points into camera // space, lay an AABB around those points, and then find the X and Z extents on the near plane. // // However, while not as accurate, a faster way is to just project the axial vectors // of the bounding box onto both the camera right and up vector. This gives us a rough // estimate of the camera-space size of the polygon we're looking at. const MatrixF& cameraTransform = getCameraState().getViewWorldMatrix(); const Point3F cameraRight = cameraTransform.getRightVector(); const Point3F cameraUp = cameraTransform.getUpVector(); const Point3F wsPolyBoundsExtents = wsPolyBounds.getExtents(); F32 widthEstimate = getMax( mFabs( wsPolyBoundsExtents.x * cameraRight.x ), getMax( mFabs( wsPolyBoundsExtents.y * cameraRight.y ), mFabs( wsPolyBoundsExtents.z * cameraRight.z ) ) ); F32 heightEstimate = getMax( mFabs( wsPolyBoundsExtents.x * cameraUp.x ), getMax( mFabs( wsPolyBoundsExtents.y * cameraUp.y ), mFabs( wsPolyBoundsExtents.z * cameraUp.z ) ) ); // If the current camera is a perspective one, divide the two estimates // by the distance of the nearest bounding box vertex to the camera // to account for perspective distortion. if( !isOrtho ) { const Point3F nearestVertex = wsPolyBounds.computeVertex( Box3F::getPointIndexFromOctant( - viewDir ) ); const F32 distance = ( nearestVertex - viewPos ).len(); widthEstimate /= distance; heightEstimate /= distance; } // If we are creating an occluder, check to see if the estimates fit // our minimum requirements. if( type == SceneCullingVolume::Occluder ) { const F32 widthEstimatePercentage = widthEstimate / getCullingFrustum().getWidth(); const F32 heightEstimatePercentage = heightEstimate / getCullingFrustum().getHeight(); if( widthEstimatePercentage < smOccluderMinWidthPercentage || heightEstimatePercentage < smOccluderMinHeightPercentage ) return false; // Reject. } // Use the area estimate as the volume's sort point. const F32 sortPoint = widthEstimate * heightEstimate; // Finally, if it's an occluder, compute a near cap. The near cap prevents objects // in front of the occluder from testing positive. The same could be achieved by // manually comparing distances before testing objects but since that would amount // to the same checks the plane/AABB tests do, it's easier to just add another plane. // Additionally, it gives the benefit of being able to create more precise culling // results by angling the plane. //NOTE: Could consider adding a near cap for includers too when generating a volume // for the outdoor zone as that may prevent quite a bit of space from being included. // However, given that this space will most likely just be filled with interior // stuff anyway, it's probably not worth it. if( type == SceneCullingVolume::Occluder ) { const U32 nearCapIndex = numPlanes; planes[ nearCapIndex ] = PlaneF( vertices[ mostDistantVertices[ 0 ] ], vertices[ mostDistantVertices[ 1 ] ], vertices[ leastDistantVert ] ); // Invert the plane, if necessary. if( planes[ nearCapIndex ].whichSide( viewPos ) == PlaneF::Front ) planes[ nearCapIndex ].invert(); numPlanes ++; } // Create the volume from the planes. outVolume = SceneCullingVolume( type, PlaneSetF( planes, numPlanes ) ); outVolume.setSortPoint( sortPoint ); // Done. return true; }