//----------------------------------------------------------------------------- // // VActorPhysicsController::isMoving(); // // Is the Actor currently Moving? // //----------------------------------------------------------------------------- const bool VActorPhysicsController::isMoving( void ) { return ( !mIsZero( getVelocity().lenSquared() ) ); }
void TerrainCellMaterial::_updateMaterialConsts( Pass *pass ) { PROFILE_SCOPE( TerrainCellMaterial_UpdateMaterialConsts ); for ( U32 j=0; j < pass->materials.size(); j++ ) { MaterialInfo *matInfo = pass->materials[j]; F32 detailSize = matInfo->mat->getDetailSize(); F32 detailScale = 1.0f; if ( !mIsZero( detailSize ) ) detailScale = mTerrain->getWorldBlockSize() / detailSize; // Scale the distance by the global scalar. const F32 distance = mTerrain->smDetailScale * matInfo->mat->getDetailDistance(); // NOTE: The negation of the y scale is to make up for // my mistake early in development and passing the wrong // y texture coord into the system. // // This negation fixes detail, normal, and parallax mapping // without harming the layer id blending code. // // Eventually we should rework this to correct this little // mistake, but there isn't really a hurry to. // Point4F detailScaleAndFade( detailScale, -detailScale, distance, 0 ); if ( !mIsZero( distance ) ) detailScaleAndFade.w = 1.0f / distance; Point3F detailIdStrengthParallax( matInfo->layerId, matInfo->mat->getDetailStrength(), matInfo->mat->getParallaxScale() ); pass->consts->setSafe( matInfo->detailInfoVConst, detailScaleAndFade ); pass->consts->setSafe( matInfo->detailInfoPConst, detailIdStrengthParallax ); // macro texture info F32 macroSize = matInfo->mat->getMacroSize(); F32 macroScale = 1.0f; if ( !mIsZero( macroSize ) ) macroScale = mTerrain->getWorldBlockSize() / macroSize; // Scale the distance by the global scalar. const F32 macroDistance = mTerrain->smDetailScale * matInfo->mat->getMacroDistance(); Point4F macroScaleAndFade( macroScale, -macroScale, macroDistance, 0 ); if ( !mIsZero( macroDistance ) ) macroScaleAndFade.w = 1.0f / macroDistance; Point3F macroIdStrengthParallax( matInfo->layerId, matInfo->mat->getMacroStrength(), 0 ); pass->consts->setSafe( matInfo->macroInfoVConst, macroScaleAndFade ); pass->consts->setSafe( matInfo->macroInfoPConst, macroIdStrengthParallax ); } }
//-------------------------------------------------------------------------- // from Graphics Gems I: pp 738-742 U32 mSolveCubic_c(F32 a, F32 b, F32 c, F32 d, F32 * x) { if(mIsZero(a)) return(mSolveQuadratic(b, c, d, x)); // normal form: x^3 + Ax^2 + BX + C = 0 F32 A = b / a; F32 B = c / a; F32 C = d / a; // substitute x = y - A/3 to eliminate quadric term and depress // the cubic equation to (x^3 + px + q = 0) F32 A2 = A * A; F32 A3 = A2 * A; F32 p = (1.f/3.f) * (((-1.f/3.f) * A2) + B); F32 q = (1.f/2.f) * (((2.f/27.f) * A3) - ((1.f/3.f) * A * B) + C); // use Cardano's fomula to solve the depressed cubic F32 p3 = p * p * p; F32 q2 = q * q; F32 D = q2 + p3; U32 num = 0; if(mIsZero(D)) // 1 or 2 solutions { if(mIsZero(q)) // 1 triple solution { x[0] = 0.f; num = 1; } else // 1 single and 1 double { F32 u = mCbrt(-q); x[0] = 2.f * u; x[1] = -u; num = 2; } } else if(D < 0.f) // 3 solutions: casus irreducibilis { F32 phi = (1.f/3.f) * mAcos(-q / mSqrt(-p3)); F32 t = 2.f * mSqrt(-p); x[0] = t * mCos(phi); x[1] = -t * mCos(phi + (M_PI / 3.f)); x[2] = -t * mCos(phi - (M_PI / 3.f)); num = 3; } else // 1 solution { F32 sqrtD = mSqrt(D); F32 u = mCbrt(sqrtD - q); F32 v = -mCbrt(sqrtD + q); x[0] = u + v; num = 1; } // resubstitute F32 sub = (1.f/3.f) * A; for(U32 i = 0; i < num; i++) x[i] -= sub; // sort the roots for(S32 j = 0; j < (num - 1); j++) for(S32 k = j + 1; k < num; k++) if(x[k] < x[j]) swap(x[k], x[j]); return(num); }
/** * 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, InteriorObjectType | 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; }
/** * 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; }
//-------------------------------------------------------------------------- // from Graphics Gems I: pp 738-742 U32 mSolveQuartic_c(F32 a, F32 b, F32 c, F32 d, F32 e, F32 * x) { if(mIsZero(a)) return(mSolveCubic(b, c, d, e, x)); // normal form: x^4 + ax^3 + bx^2 + cx + d = 0 F32 A = b / a; F32 B = c / a; F32 C = d / a; F32 D = e / a; // substitue x = y - A/4 to eliminate cubic term: // x^4 + px^2 + qx + r = 0 F32 A2 = A * A; F32 A3 = A2 * A; F32 A4 = A2 * A2; F32 p = ((-3.f/8.f) * A2) + B; F32 q = ((1.f/8.f) * A3) - ((1.f/2.f) * A * B) + C; F32 r = ((-3.f/256.f) * A4) + ((1.f/16.f) * A2 * B) - ((1.f/4.f) * A * C) + D; U32 num = 0; if(mIsZero(r)) // no absolute term: y(y^3 + py + q) = 0 { num = mSolveCubic(1.f, 0.f, p, q, x); x[num++] = 0.f; } else { // solve the resolvent cubic F32 q2 = q * q; a = 1.f; b = (-1.f/2.f) * p; c = -r; d = ((1.f/2.f) * r * p) - ((1.f/8.f) * q2); mSolveCubic(a, b, c, d, x); F32 z = x[0]; // build 2 quadratic equations from the one solution F32 u = (z * z) - r; F32 v = (2.f * z) - p; if(mIsZero(u)) u = 0.f; else if(u > 0.f) u = mSqrt(u); else return(0); if(mIsZero(v)) v = 0.f; else if(v > 0.f) v = mSqrt(v); else return(0); // solve the two quadratics a = 1.f; b = v; c = z - u; num = mSolveQuadratic(a, b, c, x); a = 1.f; b = -v; c = z + u; num += mSolveQuadratic(a, b, c, x + num); } // resubstitute F32 sub = (1.f/4.f) * A; for(U32 i = 0; i < num; i++) x[i] -= sub; // sort the roots for(S32 j = 0; j < (num - 1); j++) for(S32 k = j + 1; k < num; k++) if(x[k] < x[j]) swap(x[k], x[j]); return(num); }
bool TurretShape::onNewDataBlock(GameBaseData* dptr, bool reload) { mDataBlock = dynamic_cast<TurretShapeData*>(dptr); if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) return false; // Mark these nodes for control by code only (will not animate in a sequence) if (mDataBlock->headingNode != -1) mShapeInstance->setNodeAnimationState(mDataBlock->headingNode, TSShapeInstance::MaskNodeHandsOff); if (mDataBlock->pitchNode != -1) mShapeInstance->setNodeAnimationState(mDataBlock->pitchNode, TSShapeInstance::MaskNodeHandsOff); for (U32 i=0; i<TurretShapeData::NumMirrorDirectionNodes; ++i) { if (mDataBlock->pitchNodes[i] != -1) { mShapeInstance->setNodeAnimationState(mDataBlock->pitchNodes[i], TSShapeInstance::MaskNodeHandsOff); } if (mDataBlock->headingNodes[i] != -1) { mShapeInstance->setNodeAnimationState(mDataBlock->headingNodes[i], TSShapeInstance::MaskNodeHandsOff); } } if (mIsZero(mDataBlock->pitchRate)) { mPitchAllowed = false; } else { mPitchAllowed = true; if (mDataBlock->pitchRate > 0) { mPitchRate = mDegToRad(mDataBlock->pitchRate); } else { mPitchRate = -1; } } if (mIsZero(mDataBlock->headingRate)) { mHeadingAllowed = false; } else { mHeadingAllowed = true; if (mDataBlock->headingRate > 0) { mHeadingRate = mDegToRad(mDataBlock->headingRate); } else { mHeadingRate = -1; } } mPitchUp = -mDegToRad(mDataBlock->maxPitch); mPitchDown = mDegToRad(mDataBlock->minPitch); mHeadingMax = mDegToRad(mDataBlock->maxHeading); // Create Recoil thread if any recoil sequences are specified. // Note that the server player does not play this animation. mRecoilThread = 0; if (isGhost()) for (U32 s = 0; s < TurretShapeData::NumRecoilSequences; s++) if (mDataBlock->recoilSequence[s] != -1) { mRecoilThread = mShapeInstance->addThread(); mShapeInstance->setSequence(mRecoilThread, mDataBlock->recoilSequence[s], 0); mShapeInstance->setTimeScale(mRecoilThread, 0); break; } // Reset the image state driven animation thread. This will be properly built // in onImageStateAnimation() when needed. mImageStateThread = 0; // Optional rotation threads. These only play on the client. mPitchThread = 0; mHeadingThread = 0; if (isGhost()) { if (mDataBlock->pitchSequence != -1) { mPitchThread = mShapeInstance->addThread(); mShapeInstance->setSequence(mPitchThread, mDataBlock->pitchSequence, 0); mShapeInstance->setTimeScale(mPitchThread, 0); } if (mDataBlock->headingSequence != -1) { mHeadingThread = mShapeInstance->addThread(); mShapeInstance->setSequence(mHeadingThread, mDataBlock->headingSequence, 0); mShapeInstance->setTimeScale(mHeadingThread, 0); } } if (!mSubclassTurretShapeHandlesScene) { scriptOnNewDataBlock(); } return true; }
void Portal::_traverseConnectedZoneSpaces( SceneTraversalState* state ) { PROFILE_SCOPE( Portal_traverseConnectedZoneSpaces ); // Don't traverse out from the portal if it is invalid. if( mClassification == InvalidPortal ) return; AssertFatal( !mIsGeometryDirty, "Portal::_traverseConnectedZoneSpaces - Geometry not up-to-date!" ); // When starting traversal within a portal zone, we cannot really use the portal // plane itself to direct our visibility queries. For example, the camera might // actually be located in front of the portal plane and thus cannot actually look // through the portal, though it will still see what lies on front of where the // portal leads. // // So if we're the start of the traversal chain, i.e. the traversal has started // out in the portal zone, then just put the traversal through to SceneZoneSpace // so it can hand it over to all connected zone managers. // // Otherwise, just do a normal traversal by stepping through the portal. if( state->getTraversalDepth() == 1 ) { Parent::_traverseConnectedZoneSpaces( state ); return; } SceneCullingState* cullingState = state->getCullingState(); const SceneCameraState& cameraState = cullingState->getCameraState(); // Get the data of the zone we're coming from. Note that at this point // the portal zone itself is already on top of the traversal stack, so // we skip over the bottom-most entry. const U32 sourceZoneId = state->getZoneIdFromStack( 1 ); const SceneZoneSpace* sourceZoneSpace = state->getZoneFromStack( 1 ); // Find out which side of the portal we are on given the // source zone. const Portal::Side currentZoneSide = sourceZoneId == SceneZoneSpaceManager::RootZoneId ? ( getInteriorSideOfExteriorPortal() == FrontSide ? BackSide : FrontSide ) : getSideRelativeToPortalPlane( sourceZoneSpace->getPosition() ); // Don't step through portal if the side we're interested in isn't passable. if( !isSidePassable( currentZoneSide ) ) return; // If the viewpoint isn't on the same side of the portal as the source zone, // then stepping through the portal would mean we are stepping back towards // the viewpoint which doesn't make sense; so, skip the portal. const Point3F& viewPos = cameraState.getViewPosition(); const F32 viewPosDistToPortal = mFabs( getPortalPlane().distToPlane( viewPos ) ); if( !mIsZero( viewPosDistToPortal ) && getSideRelativeToPortalPlane( viewPos ) != currentZoneSide ) return; // Before we go ahead and do the real work, try to find out whether // the portal is at a perpendicular or near-perpendicular angle to the view // direction. If so, there's no point in going further since we can't really // see much through the portal anyway. It also prevents us from stepping // over front/back side ambiguities. Point3F viewDirection = cameraState.getViewDirection(); const F32 dotProduct = mDot( viewDirection, getPortalPlane() ); if( mIsZero( dotProduct ) ) return; // Finally, if we have come through a portal to the current zone, check if the target // portal we are trying to step through now completely lies on the "backside"--i.e. // the side of portal on which our current zone lies--of the source portal. If so, // we can be sure this portal leads us in the wrong direction. This prevents the // outdoor zone from having just arrived through a window on one side of a house just // to go round the house and re-enter it from the other side. Portal* sourcePortal = state->getTraversalDepth() > 2 ? dynamic_cast< Portal* >( state->getZoneFromStack( 2 ) ) : NULL; if( sourcePortal != NULL ) { const Side sourcePortalFrontSide = sourceZoneId == SceneZoneSpaceManager::RootZoneId ? sourcePortal->getInteriorSideOfExteriorPortal() : sourcePortal->getSideRelativeToPortalPlane( sourceZoneSpace->getPosition() ); const PlaneF::Side sourcePortalPlaneFrontSide = sourcePortalFrontSide == FrontSide ? PlaneF::Front : PlaneF::Back; bool allPortalVerticesOnBackside = true; const U32 numVertices = mPortalPolygonWS.size(); for( U32 i = 0; i < numVertices; ++ i ) { // Not using getSideRelativeToPortalPlane here since we want PlaneF::On to be // counted as backside here. if( sourcePortal->mPortalPlane.whichSide( mPortalPolygonWS[ i ] ) == sourcePortalPlaneFrontSide ) { allPortalVerticesOnBackside = false; break; } } if( allPortalVerticesOnBackside ) return; } // If we come from the outdoor zone, then we don't want to step through any portal // where the interior zones are actually on the same side as our camera since that // would mean we are stepping into an interior through the backside of a portal. if( sourceZoneId == SceneZoneSpaceManager::RootZoneId ) { const Portal::Side cameraSide = getSideRelativeToPortalPlane( viewPos ); if( cameraSide == getInteriorSideOfExteriorPortal() ) return; } // Clip the current culling volume against the portal's polygon. If the polygon // lies completely outside the volume or for some other reason there's no good resulting // volume, _generateCullingVolume() will return false and we terminate this portal sequence // here. // // However, don't attempt to clip the portal if we are standing really close to or // even below the near distance away from the portal plane. In that case, trying to // clip the portal will only result in trouble so we stick to the original culling volume // in that case. bool haveClipped = false; if( viewPosDistToPortal > ( cameraState.getFrustum().getNearDist() + 0.1f ) ) { SceneCullingVolume volume; if( !_generateCullingVolume( state, volume ) ) return; state->pushCullingVolume( volume ); haveClipped = true; } // Short-circuit things if we are stepping from an interior zone outside. In this // case we know that the only zone we care about is the outdoor zone so head straight // into it. if( isExteriorPortal() && sourceZoneId != SceneZoneSpaceManager::RootZoneId ) getSceneManager()->getZoneManager()->getRootZone()->traverseZones( state ); else { // Go through the zones that the portal connects to and // traverse into them. for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext ) { SceneZoneSpace* targetSpace = ref->mZoneSpace; if( targetSpace == sourceZoneSpace ) continue; // Skip space we originated from. // We have handled the case of stepping into the outdoor zone above and // by skipping the zone we originated from, we have implicitly handled the // case of stepping out of the outdoor zone. Thus, we should not see the // outdoor zone here. Important as getPosition() is meaningless for it. AssertFatal( targetSpace->getZoneRangeStart() != SceneZoneSpaceManager::RootZoneId, "Portal::_traverseConnectedZoneSpaces - Outdoor zone must have been handled already" ); // Skip zones that lie on the same side as the zone // we originated from. if( getSideRelativeToPortalPlane( targetSpace->getPosition() ) == currentZoneSide ) continue; // Traverse into the space. targetSpace->traverseZones( state ); } } // If we have pushed our own clipping volume, // remove that from the stack now. if( haveClipped ) state->popCullingVolume(); }
void ConvexShape::Geometry::generate( const Vector< PlaneF > &planes, const Vector< Point3F > &tangents ) { PROFILE_SCOPE( Geometry_generate ); points.clear(); faces.clear(); AssertFatal( planes.size() == tangents.size(), "ConvexShape - incorrect plane/tangent count." ); #ifdef TORQUE_ENABLE_ASSERTS for ( S32 i = 0; i < planes.size(); i++ ) { F32 dt = mDot( planes[i], tangents[i] ); AssertFatal( mIsZero( dt, 0.0001f ), "ConvexShape - non perpendicular input vectors." ); AssertFatal( planes[i].isUnitLength() && tangents[i].isUnitLength(), "ConvexShape - non unit length input vector." ); } #endif const U32 planeCount = planes.size(); Point3F linePt, lineDir; for ( S32 i = 0; i < planeCount; i++ ) { Vector< MathUtils::Line > collideLines; // Find the lines defined by the intersection of this plane with all others. for ( S32 j = 0; j < planeCount; j++ ) { if ( i == j ) continue; if ( planes[i].intersect( planes[j], linePt, lineDir ) ) { collideLines.increment(); MathUtils::Line &line = collideLines.last(); line.origin = linePt; line.direction = lineDir; } } if ( collideLines.empty() ) continue; // Find edges and points defined by the intersection of these lines. // As we find them we fill them into our working ConvexShape::Face // structure. Face newFace; for ( S32 j = 0; j < collideLines.size(); j++ ) { Vector< Point3F > collidePoints; for ( S32 k = 0; k < collideLines.size(); k++ ) { if ( j == k ) continue; MathUtils::LineSegment segment; MathUtils::mShortestSegmentBetweenLines( collideLines[j], collideLines[k], &segment ); F32 dist = ( segment.p0 - segment.p1 ).len(); if ( dist < 0.0005f ) { S32 l = 0; for ( ; l < planeCount; l++ ) { if ( planes[l].whichSide( segment.p0 ) == PlaneF::Front ) break; } if ( l == planeCount ) collidePoints.push_back( segment.p0 ); } } //AssertFatal( collidePoints.size() <= 2, "A line can't collide with more than 2 other lines in a convex shape..." ); if ( collidePoints.size() != 2 ) continue; // Push back collision points into our points vector // if they are not duplicates and determine the id // index for those points to be used by Edge(s). const Point3F &pnt0 = collidePoints[0]; const Point3F &pnt1 = collidePoints[1]; S32 idx0 = -1; S32 idx1 = -1; for ( S32 k = 0; k < points.size(); k++ ) { if ( pnt0.equal( points[k] ) ) { idx0 = k; break; } } for ( S32 k = 0; k < points.size(); k++ ) { if ( pnt1.equal( points[k] ) ) { idx1 = k; break; } } if ( idx0 == -1 ) { points.push_back( pnt0 ); idx0 = points.size() - 1; } if ( idx1 == -1 ) { points.push_back( pnt1 ); idx1 = points.size() - 1; } // Construct the Face::Edge defined by this collision. S32 localIdx0 = newFace.points.push_back_unique( idx0 ); S32 localIdx1 = newFace.points.push_back_unique( idx1 ); newFace.edges.increment(); ConvexShape::Edge &newEdge = newFace.edges.last(); newEdge.p0 = localIdx0; newEdge.p1 = localIdx1; } if ( newFace.points.size() < 3 ) continue; //AssertFatal( newFace.points.size() == newFace.edges.size(), "ConvexShape - face point count does not equal edge count." ); // Fill in some basic Face information. newFace.id = i; newFace.normal = planes[i]; newFace.tangent = tangents[i]; // Make a working array of Point3Fs on this face. U32 pntCount = newFace.points.size(); Point3F *workPoints = new Point3F[ pntCount ]; for ( S32 j = 0; j < pntCount; j++ ) workPoints[j] = points[ newFace.points[j] ]; // Calculate the average point for calculating winding order. Point3F averagePnt = Point3F::Zero; for ( S32 j = 0; j < pntCount; j++ ) averagePnt += workPoints[j]; averagePnt /= pntCount; // Sort points in correct winding order. U32 *vertMap = new U32[pntCount]; MatrixF quadMat( true ); quadMat.setPosition( averagePnt ); quadMat.setColumn( 0, newFace.tangent ); quadMat.setColumn( 1, mCross( newFace.normal, newFace.tangent ) ); quadMat.setColumn( 2, newFace.normal ); quadMat.inverse(); // Transform working points into quad space // so we can work with them as 2D points. for ( S32 j = 0; j < pntCount; j++ ) quadMat.mulP( workPoints[j] ); MathUtils::sortQuadWindingOrder( true, workPoints, vertMap, pntCount ); // Save points in winding order. for ( S32 j = 0; j < pntCount; j++ ) newFace.winding.push_back( vertMap[j] ); // Calculate the area and centroid of the face. newFace.area = 0.0f; for ( S32 j = 0; j < pntCount; j++ ) { S32 k = ( j + 1 ) % pntCount; const Point3F &p0 = workPoints[ vertMap[j] ]; const Point3F &p1 = workPoints[ vertMap[k] ]; // Note that this calculation returns positive area for clockwise winding // and negative area for counterclockwise winding. newFace.area += p0.y * p1.x; newFace.area -= p0.x * p1.y; } //AssertFatal( newFace.area > 0.0f, "ConvexShape - face area was not positive." ); if ( newFace.area > 0.0f ) newFace.area /= 2.0f; F32 factor; F32 cx = 0.0f, cy = 0.0f; for ( S32 j = 0; j < pntCount; j++ ) { S32 k = ( j + 1 ) % pntCount; const Point3F &p0 = workPoints[ vertMap[j] ]; const Point3F &p1 = workPoints[ vertMap[k] ]; factor = p0.x * p1.y - p1.x * p0.y; cx += ( p0.x + p1.x ) * factor; cy += ( p0.y + p1.y ) * factor; } factor = 1.0f / ( newFace.area * 6.0f ); newFace.centroid.set( cx * factor, cy * factor, 0.0f ); quadMat.inverse(); quadMat.mulP( newFace.centroid ); delete [] workPoints; workPoints = NULL; // Make polygons / triangles for this face. const U32 polyCount = pntCount - 2; newFace.triangles.setSize( polyCount ); for ( S32 j = 0; j < polyCount; j++ ) { ConvexShape::Triangle &poly = newFace.triangles[j]; poly.p0 = vertMap[0]; if ( j == 0 ) { poly.p1 = vertMap[ 1 ]; poly.p2 = vertMap[ 2 ]; } else { poly.p1 = vertMap[ 1 + j ]; poly.p2 = vertMap[ 2 + j ]; } } delete [] vertMap; // Calculate texture coordinates for each point in this face. const Point3F binormal = mCross( newFace.normal, newFace.tangent ); PlaneF planey( newFace.centroid - 0.5f * binormal, binormal ); PlaneF planex( newFace.centroid - 0.5f * newFace.tangent, newFace.tangent ); newFace.texcoords.setSize( newFace.points.size() ); for ( S32 j = 0; j < newFace.points.size(); j++ ) { F32 x = planex.distToPlane( points[ newFace.points[ j ] ] ); F32 y = planey.distToPlane( points[ newFace.points[ j ] ] ); newFace.texcoords[j].set( -x, -y ); } // Data verification tests. #ifdef TORQUE_ENABLE_ASSERTS //S32 triCount = newFace.triangles.size(); //S32 edgeCount = newFace.edges.size(); //AssertFatal( triCount == edgeCount - 2, "ConvexShape - triangle/edge count do not match." ); /* for ( S32 j = 0; j < triCount; j++ ) { F32 area = MathUtils::mTriangleArea( points[ newFace.points[ newFace.triangles[j][0] ] ], points[ newFace.points[ newFace.triangles[j][1] ] ], points[ newFace.points[ newFace.triangles[j][2] ] ] ); AssertFatal( area > 0.0f, "ConvexShape - triangle winding bad." ); }*/ #endif // Done with this Face. faces.push_back( newFace ); } }
/** * 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; }
void LightFlareData::prepRender( SceneRenderState *state, LightFlareState *flareState ) { PROFILE_SCOPE( LightFlareData_prepRender ); const LightInfo *lightInfo = flareState->lightInfo; if ( mIsZero( flareState->fullBrightness ) || mIsZero( lightInfo->getBrightness() ) ) return; // Figure out the element count to render. U32 elementCount = mElementCount; const bool isReflectPass = state->isReflectPass(); if ( isReflectPass ) { // Then we don't render anything this pass. if ( !mRenderReflectPass ) return; // Find the zero distance elements which make // up the corona of the light flare. elementCount = 0.0f; for ( U32 i=0; i < mElementCount; i++ ) if ( mIsZero( mElementDist[i] ) ) elementCount++; } // Better have something to render. if ( elementCount == 0 ) return; U32 visDelta = U32_MAX; F32 occlusionFade = 1.0f; Point3F lightPosSS; bool lightVisible = _testVisibility( state, flareState, &visDelta, &occlusionFade, &lightPosSS ); // We can only skip rendering if the light is not // visible, and it has elapsed the fade out time. if ( mIsZero( occlusionFade ) || !lightVisible && visDelta > FadeOutTime ) return; const RectI &viewport = GFX->getViewport(); Point3F oneOverViewportExtent( 1.0f / (F32)viewport.extent.x, 1.0f / (F32)viewport.extent.y, 0.0f ); // Really convert it to screen space. lightPosSS.x -= viewport.point.x; lightPosSS.y -= viewport.point.y; lightPosSS *= oneOverViewportExtent; lightPosSS = ( lightPosSS * 2.0f ) - Point3F::One; lightPosSS.y = -lightPosSS.y; lightPosSS.z = 0.0f; // Take any projection offset into account so that the point where the flare's // elements converge is at the 'eye' point rather than the center of the viewport. const Point2F& projOffset = state->getCameraFrustum().getProjectionOffset(); Point3F flareVec( -lightPosSS + Point3F(projOffset.x, projOffset.y, 0.0f) ); const F32 flareLength = flareVec.len(); if ( flareLength > 0.0f ) flareVec *= 1.0f / flareLength; // Setup the flare quad points. Point3F rotatedBasePoints[4]; dMemcpy(rotatedBasePoints, sBasePoints, sizeof( sBasePoints )); // Rotate the flare quad. F32 rot = mAcos( -1.0f * flareVec.x ); rot *= flareVec.y > 0.0f ? -1.0f : 1.0f; MathUtils::vectorRotateZAxis( rot, rotatedBasePoints, 4 ); // Here we calculate a the light source's influence on // the effect's size and brightness. // Scale based on the current light brightness compared to its normal output. F32 lightSourceBrightnessScale = lightInfo->getBrightness() / flareState->fullBrightness; const Point3F &camPos = state->getCameraPosition(); const Point3F &lightPos = flareState->lightMat.getPosition(); const bool isVectorLight = lightInfo->getType() == LightInfo::Vector; // Scale based on world space distance from camera to light source. F32 distToCamera = ( camPos - lightPos ).len(); F32 lightSourceWSDistanceScale = isVectorLight && distToCamera > 0.0f ? 1.0f : getMin( 10.0f / distToCamera, 10.0f ); // Scale based on screen space distance from screen position of light source to the screen center. F32 lightSourceSSDistanceScale = getMax( ( 1.5f - flareLength ) / 1.5f, 0.0f ); // Scale based on recent visibility changes, fading in or out. F32 fadeInOutScale = 1.0f; if ( lightVisible && visDelta < FadeInTime && flareState->occlusion > 0.0f ) fadeInOutScale = (F32)visDelta / (F32)FadeInTime; else if ( !lightVisible && visDelta < FadeOutTime ) fadeInOutScale = 1.0f - (F32)visDelta / (F32)FadeOutTime; // This combined scale influences the size of all elements this effect renders. // Note we also add in a scale that is user specified in the Light. F32 lightSourceIntensityScale = lightSourceBrightnessScale * lightSourceWSDistanceScale * lightSourceSSDistanceScale * fadeInOutScale * flareState->scale * occlusionFade; if ( mIsZero( lightSourceIntensityScale ) ) return; // The baseColor which modulates the color of all elements. // // These are the factors which affect the "alpha" of the flare effect. // Modulate more in as appropriate. ColorF baseColor = ColorF::WHITE * lightSourceBrightnessScale * occlusionFade; // Setup the vertex buffer for the maximum flare elements. const U32 vertCount = 4 * mElementCount; if ( flareState->vertBuffer.isNull() || flareState->vertBuffer->mNumVerts != vertCount ) flareState->vertBuffer.set( GFX, vertCount, GFXBufferTypeDynamic ); GFXVertexPCT *vert = flareState->vertBuffer.lock(); const Point2F oneOverTexSize( 1.0f / (F32)mFlareTexture.getWidth(), 1.0f / (F32)mFlareTexture.getHeight() ); for ( U32 i = 0; i < mElementCount; i++ ) { // Skip non-zero elements for reflections. if ( isReflectPass && mElementDist[i] > 0.0f ) continue; Point3F *basePos = mElementRotate[i] ? rotatedBasePoints : sBasePoints; ColorF color( baseColor * mElementTint[i] ); if ( mElementUseLightColor[i] ) color *= lightInfo->getColor(); color.clamp(); Point3F pos( lightPosSS + flareVec * mElementDist[i] * flareLength ); const RectF &rect = mElementRect[i]; Point3F size( rect.extent.x, rect.extent.y, 1.0f ); size *= mElementScale[i] * mScale * lightSourceIntensityScale; AssertFatal( size.x >= 0.0f, "LightFlareData::prepRender - Got a negative element size?" ); if ( size.x < 100.0f ) { F32 alphaScale = mPow( size.x / 100.0f, 2 ); color *= alphaScale; } Point2F texCoordMin, texCoordMax; texCoordMin = rect.point * oneOverTexSize; texCoordMax = ( rect.point + rect.extent ) * oneOverTexSize; size.x = getMax( size.x, 1.0f ); size.y = getMax( size.y, 1.0f ); size *= oneOverViewportExtent; vert->color = color; vert->point = ( basePos[0] * size ) + pos; vert->texCoord.set( texCoordMin.x, texCoordMax.y ); vert++; vert->color = color; vert->point = ( basePos[1] * size ) + pos; vert->texCoord.set( texCoordMax.x, texCoordMax.y ); vert++; vert->color = color; vert->point = ( basePos[2] * size ) + pos; vert->texCoord.set( texCoordMax.x, texCoordMin.y ); vert++; vert->color = color; vert->point = ( basePos[3] * size ) + pos; vert->texCoord.set( texCoordMin.x, texCoordMin.y ); vert++; } flareState->vertBuffer.unlock(); RenderPassManager *rpm = state->getRenderPass(); // Create and submit the render instance. ParticleRenderInst *ri = rpm->allocInst<ParticleRenderInst>(); ri->type = RenderPassManager::RIT_Particle; ri->vertBuff = &flareState->vertBuffer; ri->primBuff = &mFlarePrimBuffer; ri->translucentSort = true; ri->sortDistSq = ( lightPos - camPos ).lenSquared(); ri->modelViewProj = &MatrixF::Identity; ri->bbModelViewProj = &MatrixF::Identity; ri->count = elementCount; ri->blendStyle = ParticleRenderInst::BlendGreyscale; ri->diffuseTex = mFlareTexture; ri->softnessDistance = 1.0f; ri->defaultKey = ri->diffuseTex ? (uintptr_t)ri->diffuseTex : (uintptr_t)ri->vertBuff; // Sort by texture too. // NOTE: Offscreen partical code is currently disabled. ri->systemState = PSS_AwaitingHighResDraw; rpm->addInst( ri ); }