void TurretShape::_applyLimits(Point3F& rot) { rot.x = mClampF(rot.x, mPitchUp, mPitchDown); if (mHeadingMax < mDegToRad(180.0f)) { rot.z = mClampF(rot.z, -mHeadingMax, mHeadingMax); } }
void GFXCubemap::initNormalize( U32 size ) { Point3F axis[6] = {Point3F(1.0, 0.0, 0.0), Point3F(-1.0, 0.0, 0.0), Point3F(0.0, 1.0, 0.0), Point3F( 0.0, -1.0, 0.0), Point3F(0.0, 0.0, 1.0), Point3F( 0.0, 0.0, -1.0),}; Point3F s[6] = {Point3F(0.0, 0.0, -1.0), Point3F( 0.0, 0.0, 1.0), Point3F(1.0, 0.0, 0.0), Point3F( 1.0, 0.0, 0.0), Point3F(1.0, 0.0, 0.0), Point3F(-1.0, 0.0, 0.0),}; Point3F t[6] = {Point3F(0.0, -1.0, 0.0), Point3F(0.0, -1.0, 0.0), Point3F(0.0, 0.0, 1.0), Point3F(0.0, 0.0, -1.0), Point3F(0.0, -1.0, 0.0), Point3F(0.0, -1.0, 0.0),}; F32 span = 2.0; F32 start = -1.0; F32 stride = span / F32(size - 1); GFXTexHandle faces[6]; for(U32 i=0; i<6; i++) { GFXTexHandle &tex = faces[i]; GBitmap *bitmap = new GBitmap(size, size); // fill in... for(U32 v=0; v<size; v++) { for(U32 u=0; u<size; u++) { Point3F vector; vector = axis[i] + ((F32(u) * stride) + start) * s[i] + ((F32(v) * stride) + start) * t[i]; vector.normalizeSafe(); vector = ((vector * 0.5) + Point3F(0.5, 0.5, 0.5)) * 255.0; vector.x = mClampF(vector.x, 0.0f, 255.0f); vector.y = mClampF(vector.y, 0.0f, 255.0f); vector.z = mClampF(vector.z, 0.0f, 255.0f); // easy way to avoid knowledge of the format (RGB, RGBA, RGBX, ...)... U8 *bits = bitmap->getAddress(u, v); bits[0] = U8(vector.x); bits[1] = U8(vector.y); bits[2] = U8(vector.z); } } tex.set(bitmap, &GFXDefaultStaticDiffuseProfile, true, "Cubemap"); } initStatic(faces); }
void SFXSource::setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ) { mConeInsideAngle = mClampF( innerAngle, 0.0f, 360.0 ); mConeOutsideAngle = mClampF( outerAngle, mConeInsideAngle, 360.0 ); mConeOutsideVolume = mClampF( outerVolume, 0.0f, 1.0f ); if ( mVoice && mIs3D ) mVoice->setCone( mConeInsideAngle, mConeOutsideAngle, mConeOutsideVolume ); }
void SFXSource::setVolume( F32 volume ) { mVolume = mClampF( volume, 0, 1 ); if ( mVoice ) mVoice->setVolume( mVolume * mModulativeVolume ); }
void GuiTSCtrl::drawLine( Point3F p0, Point3F p1, const ColorI &color, F32 width ) { if ( !mSaveFrustum.clipSegment( p0, p1 ) ) return; MathUtils::mProjectWorldToScreen( p0, &p0, mSaveViewport, mSaveModelview, mSaveProjection ); MathUtils::mProjectWorldToScreen( p1, &p1, mSaveViewport, mSaveModelview, mSaveProjection ); p0.x = mClampF( p0.x, 0.0f, mSaveViewport.extent.x ); p0.y = mClampF( p0.y, 0.0f, mSaveViewport.extent.y ); p1.x = mClampF( p1.x, 0.0f, mSaveViewport.extent.x ); p1.y = mClampF( p1.y, 0.0f, mSaveViewport.extent.y ); p0.z = p1.z = 0.0f; _drawLine( p0, p1, color, width ); }
static inline F32 moveClamp(F32 v) { // Support function to convert/clamp the input into a move rotation // which only allows 0 -> M_2PI. F32 a = mClampF(v, -M_2PI_F, M_2PI_F); return (a < 0) ? a + M_2PI_F : a; }
void EditorCamera::mainLoop() { if (mRenderCamera == NULL) return; mVerticalAngle = mClampF(mVerticalAngle, -4.7f, -1.7f); VectorF rotation = mTransform.getRotationEuler(); rotation.y = mVerticalAngle; rotation.z = mHorizontalAngle; mTransform.setRotation(rotation); VectorF up(0.0f, 0.0f, 1.0f); Point3F look; Point3F cameraForward(1.0f, 0.0f, 0.0f); bx::vec3MulMtx(look, cameraForward, mTransform.matrix); if (mForwardVelocity.len() > 0.01f) { MatrixF lookMatrix; bx::mtxLookAt(lookMatrix, mWorldPosition, look, up); mWorldPosition += (lookMatrix.getForwardVector() * mForwardVelocity.x); mWorldPosition -= (lookMatrix.getRightVector() * mForwardVelocity.y); mTransform.setPosition(mWorldPosition); } bx::vec3MulMtx(look, cameraForward, mTransform.matrix); bx::mtxLookAt(mRenderCamera->viewMatrix, mWorldPosition, look, up); mRenderCamera->position = mWorldPosition; }
bool GuiSliderCtrl::onWake() { if( !Parent::onWake() ) return false; mHasTexture = mProfile->constructBitmapArray() >= NumBitmaps; if( mHasTexture ) { mBitmapBounds = mProfile->mBitmapArrayRects.address(); mThumbSize = Point2I( mBitmapBounds[ SliderButtonNormal ].extent.x, mBitmapBounds[ SliderButtonNormal ].extent.y ); } F32 value; if( mConsoleVariable[ 0 ] ) value = getFloatVariable(); else value = mValue; mValue = mClampF( value, mRange.x, mRange.y ); // mouse scroll increment percentage is 5% of the range mIncAmount = ( ( mRange.y - mRange.x ) * 0.05 ); if( ( mThumbSize.y + mProfile->mFont->getHeight() - 4 ) <= getExtent().y ) mDisplayValue = true; else mDisplayValue = false; _updateThumb( mValue, mSnap, true ); return true; }
void PathCamera::setPosition(F32 pos) { mPosition = mClampF(pos, (F32)mNodeBase, (F32)(mNodeBase + mNodeCount - 1)); MatrixF mat; interpolateMat(mPosition,&mat); Parent::setTransform(mat); setMaskBits(PositionMask); }
void TurretShape::updateAnimation(F32 dt) { if (mRecoilThread) mShapeInstance->advanceTime(dt,mRecoilThread); if (mImageStateThread) mShapeInstance->advanceTime(dt,mImageStateThread); // Update any pitch and heading threads if (mPitchThread) { F32 d = mPitchDown - mPitchUp; if (!mIsZero(d)) { F32 pos = (mRot.x - mPitchUp) / d; mShapeInstance->setPos(mPitchThread, mClampF(pos, 0.0f, 1.0f)); } } if (mHeadingThread) { F32 pos = 0.0f; if (mHeadingMax < mDegToRad(180.0f)) { F32 d = mHeadingMax * 2.0f; if (!mIsZero(d)) { pos = (mRot.z + mHeadingMax) / d; } } else { pos = mRot.z / M_2PI; if (pos < 0.0f) { // We don't want negative rotations to simply mirror the // positive rotations but to animate into them as if -0.0 // is equivalent to 1.0. pos = mFmod(pos, 1.0f) + 1.0f; } if (pos > 1.0f) { pos = mFmod(pos, 1.0f); } } mShapeInstance->setPos(mHeadingThread, mClampF(pos, 0.0f, 1.0f)); } }
void SFXDSVoice::setPitch( F32 pitch ) { F32 sampleRate = _getBuffer()->getFormat().getSamplesPerSecond(); F32 frequency = mFloor( mClampF( sampleRate * pitch, DSBFREQUENCY_MIN, DSBFREQUENCY_MAX ) ); DSAssert( mDSBuffer->SetFrequency( ( U32 )frequency ), "SFXDSVoice::setPitch - couldn't set playback frequency."); }
void ScatterSky::_conformLights() { _initCurves(); F32 val = mCurves[0].getVal( mTimeOfDay ); mNightInterpolant = 1.0f - val; VectorF lightDirection; F32 brightness; // Build the light direction from the azimuth and elevation. F32 yaw = mDegToRad(mClampF(mSunAzimuth,0,359)); F32 pitch = mDegToRad(mClampF(mSunElevation,-360,+360)); MathUtils::getVectorFromAngles(lightDirection, yaw, pitch); lightDirection.normalize(); mSunDir = -lightDirection; yaw = mDegToRad(mClampF(mMoonAzimuth,0,359)); pitch = mDegToRad(mClampF(mMoonElevation,-360,+360)); MathUtils::getVectorFromAngles( mMoonLightDir, yaw, pitch ); mMoonLightDir.normalize(); mMoonLightDir = -mMoonLightDir; brightness = mCurves[2].getVal( mTimeOfDay ); if ( mNightInterpolant >= 1.0f ) lightDirection = -mMoonLightDir; mLight->setDirection( -lightDirection ); mLight->setBrightness( brightness * mBrightness ); mLightDir = lightDirection; // Have to do interpolation // after the light direction is set // otherwise the sun color will be invalid. _interpolateColors(); mLight->setAmbient( mAmbientColor ); mLight->setColor( mSunColor ); mLight->setCastShadows( mCastShadows ); FogData fog = getSceneManager()->getFogData(); fog.color = mFogColor; getSceneManager()->setFogData( fog ); }
bool WaterObject::_checkDensity( void *object, const char *index, const char *data ) { //Water densities above 1000 shoot the player high and fast into the air. //value clamped to prevent errors. WaterObject *water = static_cast<WaterObject*>( object ); water->mDensity = mClampF(dAtof( data ), 0.0f, 1000.0f); return false; }
void GuiProgressBitmapCtrl::setScriptValue(const char *value) { //set the value if (! value) mProgress = 0.0f; else mProgress = dAtof(value); //validate the value mProgress = mClampF(mProgress, 0.f, 1.f); setUpdate(); }
void GuiProgressBitmapCtrl::onPreRender() { const char * var = getVariable(); if(var) { F32 value = mClampF(dAtof(var), 0.f, 1.f); if(value != mProgress) { mProgress = value; setUpdate(); } } }
void Sun::_conformLights() { // Build the light direction from the azimuth and elevation. F32 yaw = mDegToRad(mClampF(mSunAzimuth,0,359)); F32 pitch = mDegToRad(mClampF(mSunElevation,-360,+360)); VectorF lightDirection; MathUtils::getVectorFromAngles(lightDirection, yaw, pitch); lightDirection.normalize(); mLight->setDirection( -lightDirection ); mLight->setBrightness( mBrightness ); // Now make sure the colors are within range. mLightColor.clamp(); mLight->setColor( mLightColor ); mLightAmbient.clamp(); mLight->setAmbient( mLightAmbient ); // Optimization... disable shadows if the ambient and // directional color are the same. bool castShadows = mLightColor != mLightAmbient && mCastShadows; mLight->setCastShadows( castShadows ); }
bool ParticleData::protectedSetTimes( void *object, const char *index, const char *data) { ParticleData *pData = static_cast<ParticleData*>( object ); F32 val = dAtof(data); U32 i; if (!index) i = 0; else i = dAtoui(index); pData->times[i] = mClampF( val, 0.f, 1.f ); return false; }
bool EditorCamera::onMouseMove(int x, int y) { if (!mMouseDown) return false; Point2I pos = Point2I(x, y); Point2I delta = mMouseStart - pos; mMouseStart = pos; if (delta.isZero()) return false; mHorizontalAngle += delta.x * 0.01f; mVerticalAngle -= delta.y * 0.01f; mVerticalAngle = mClampF(mVerticalAngle, -4.7f, -1.7f); return false; }
void PSSMLightShadowMap::_calcSplitPos(const Frustum& currFrustum) { const F32 nearDist = 0.01f; // TODO: Should this be adjustable or different? const F32 farDist = currFrustum.getFarDist(); for ( U32 i = 1; i < mNumSplits; i++ ) { F32 step = (F32) i / (F32) mNumSplits; F32 logSplit = nearDist * mPow(farDist / nearDist, step); F32 linearSplit = nearDist + (farDist - nearDist) * step; mSplitDist[i] = mLerp( linearSplit, logSplit, mClampF( mLogWeight, 0.0f, 1.0f ) ); } mSplitDist[0] = nearDist; mSplitDist[mNumSplits] = farDist; }
void TSShapeLoader::generateObjectState(TSShape::Object& obj, F32 t, bool addFrame, bool addMatFrame) { shape->objectStates.increment(); TSShape::ObjectState& state = shape->objectStates.last(); state.frameIndex = 0; state.matFrameIndex = 0; state.vis = mClampF(appMeshes[obj.startMeshIndex]->getVisValue(t), 0.0f, 1.0f); if (addFrame || addMatFrame) { generateFrame(obj, t, addFrame, addMatFrame); // set the frame number for the object state state.frameIndex = appMeshes[obj.startMeshIndex]->numFrames - 1; state.matFrameIndex = appMeshes[obj.startMeshIndex]->numMatFrames - 1; } }
void ProjectedShadow::_calcScore( const SceneRenderState *state ) { if ( !mDecalInstance ) return; F32 pixRadius = mDecalInstance->calcPixelSize( state->getViewport().extent.y, state->getCameraPosition(), state->getWorldToScreenScale().y ); F32 pct = pixRadius / mDecalInstance->mDataBlock->fadeStartPixelSize; U32 msSinceLastRender = Platform::getVirtualMilliseconds() - getLastRenderTime(); ShapeBaseData *data = NULL; if ( mShapeBase ) data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() ); // For every 1s this shadow hasn't been // updated we'll add 10 to the score. F32 secs = mFloor( (F32)msSinceLastRender / 1000.0f ); mScore = pct + secs; mClampF( mScore, 0.0f, 2000.0f ); }
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; }
//----------------------------------------------------------------------------- // 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; } }
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 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"); }
bool LightFlareData::_testVisibility(const SceneRenderState *state, LightFlareState *flareState, U32 *outVisDelta, F32 *outOcclusionFade, Point3F *outLightPosSS) { // Reflections use the results from the last forward // render so we don't need multiple queries. if ( state->isReflectPass() ) { *outOcclusionFade = flareState->occlusion; *outVisDelta = Sim::getCurrentTime() - flareState->visChangedTime; return flareState->visible; } // Initialize it to something first. *outOcclusionFade = 0; // First check to see if the flare point // is on scren at all... if not then return // the last result. const Point3F &lightPos = flareState->lightMat.getPosition(); const RectI &viewport = GFX->getViewport(); MatrixF projMatrix; state->getCameraFrustum().getProjectionMatrix(&projMatrix); if( state->isReflectPass() ) projMatrix = state->getSceneManager()->getNonClipProjection(); bool onScreen = MathUtils::mProjectWorldToScreen( lightPos, outLightPosSS, viewport, GFX->getWorldMatrix(), projMatrix ); // It is onscreen, so raycast as a simple occlusion test. const LightInfo *lightInfo = flareState->lightInfo; const bool isVectorLight = lightInfo->getType() == LightInfo::Vector; const bool useOcclusionQuery = isVectorLight ? flareState->worldRadius > 0.0f : mOcclusionRadius > 0.0f; bool needsRaycast = true; // NOTE: if hardware does not support HOQ it will return NULL // and we will retry every time but there is not currently a good place // for one-shot initialization of LightFlareState if ( useOcclusionQuery ) { // Always treat light as onscreen if using HOQ // it will be faded out if offscreen anyway. onScreen = true; needsRaycast = false; // Test the hardware queries for rendered pixels. U32 pixels = 0, fullPixels = 0; GFXOcclusionQuery::OcclusionQueryStatus status; flareState->occlusionQuery.getLastStatus( false, &status, &pixels ); flareState->fullPixelQuery.getLastStatus( false, NULL, &fullPixels ); if ( status == GFXOcclusionQuery::NotOccluded && fullPixels != 0 ) *outOcclusionFade = mClampF( (F32)pixels / (F32)fullPixels, 0.0f, 1.0f ); if( !flareState->occlusionQuery.isWaiting() ) { // Setup the new queries. RenderPassManager *rpm = state->getRenderPass(); OccluderRenderInst *ri = rpm->allocInst<OccluderRenderInst>(); ri->type = RenderPassManager::RIT_Occluder; ri->query = flareState->occlusionQuery.getQuery(); ri->query2 = flareState->fullPixelQuery.getQuery(); ri->isSphere = true; ri->position = lightPos; if ( isVectorLight && flareState->worldRadius > 0.0f ) ri->scale.set( flareState->worldRadius ); else ri->scale.set( mOcclusionRadius ); ri->orientation = rpm->allocUniqueXform( lightInfo->getTransform() ); // Submit the queries. state->getRenderPass()->addInst( ri ); } } const Point3F &camPos = state->getCameraPosition(); if ( needsRaycast ) { // Use a raycast to determine occlusion. GameConnection *conn = GameConnection::getConnectionToServer(); if ( !conn ) return false; const bool fps = conn->isFirstPerson(); GameBase *control = conn->getControlObject(); if ( control && fps ) control->disableCollision(); RayInfo rayInfo; if ( !gClientContainer.castRay( camPos, lightPos, LosMask, &rayInfo ) ) *outOcclusionFade = 1.0f; if ( control && fps ) control->enableCollision(); } // The raycast and hardware occlusion query only calculate if // the flare is on screen... if does not account for being // partially offscreen. // // The code here clips a box against the viewport to // get an approximate percentage of onscreen area. // F32 worldRadius = flareState->worldRadius > 0 ? flareState->worldRadius : mOcclusionRadius; if ( worldRadius > 0.0f ) { F32 dist = ( camPos - lightPos ).len(); F32 pixelRadius = state->projectRadius(dist, worldRadius); RectI visRect( outLightPosSS->x - pixelRadius, outLightPosSS->y - pixelRadius, pixelRadius * 2.0f, pixelRadius * 2.0f ); F32 fullArea = visRect.area(); if ( visRect.intersect( viewport ) ) { F32 visArea = visRect.area(); *outOcclusionFade *= visArea / fullArea; onScreen = true; } else *outOcclusionFade = 0.0f; } const bool lightVisible = onScreen && *outOcclusionFade > 0.0f; // To perform a fade in/out when we gain or lose visibility // we must update/store the visibility state and time. const U32 currentTime = Sim::getCurrentTime(); if ( lightVisible != flareState->visible ) { flareState->visible = lightVisible; flareState->visChangedTime = currentTime; } // Return the visibility delta for time fading. *outVisDelta = currentTime - flareState->visChangedTime; // Store the final occlusion fade so that it can // be used in reflection rendering later. flareState->occlusion = *outOcclusionFade; return lightVisible; }
S32 TSShapeInstance::setDetailFromDistance( const SceneRenderState *state, F32 scaledDistance ) { PROFILE_SCOPE( TSShapeInstance_setDetailFromDistance ); // For debugging/metrics. smLastScaledDistance = scaledDistance; // Shortcut if the distance is really close or negative. if ( scaledDistance <= 0.0f ) { mShape->mDetailLevelLookup[0].get( mCurrentDetailLevel, mCurrentIntraDetailLevel ); return mCurrentDetailLevel; } // The pixel scale is used the linearly scale the lod // selection based on the viewport size. // // The original calculation from TGEA was... // // pixelScale = viewport.extent.x * 1.6f / 640.0f; // // Since we now work on the viewport height, assuming // 4:3 aspect ratio, we've changed the reference value // to 300 to be more compatible with legacy shapes. // const F32 pixelScale = state->getViewport().extent.y / 300.0f; // This is legacy DTS support for older "multires" based // meshes. The original crossbow weapon uses this. // // If we have more than one detail level and the maxError // is non-negative then we do some sort of screen error // metric for detail selection. // if ( mShape->mUseDetailFromScreenError ) { // The pixel size of 1 meter at the input distance. F32 pixelRadius = state->projectRadius( scaledDistance, 1.0f ) * pixelScale; static const F32 smScreenError = 5.0f; return setDetailFromScreenError( smScreenError / pixelRadius ); } // We're inlining SceneRenderState::projectRadius here to // skip the unnessasary divide by zero protection. F32 pixelRadius = ( mShape->radius / scaledDistance ) * state->getWorldToScreenScale().y * pixelScale; F32 pixelSize = pixelRadius * smDetailAdjust; if ( pixelSize < smSmallestVisiblePixelSize ) { mCurrentDetailLevel = -1; return mCurrentDetailLevel; } if ( pixelSize > smSmallestVisiblePixelSize && pixelSize <= mShape->mSmallestVisibleSize ) pixelSize = mShape->mSmallestVisibleSize + 0.01f; // For debugging/metrics. smLastPixelSize = pixelSize; // Clamp it to an acceptable range for the lookup table. U32 index = (U32)mClampF( pixelSize, 0, mShape->mDetailLevelLookup.size() - 1 ); // Check the lookup table for the detail and intra detail levels. mShape->mDetailLevelLookup[ index ].get( mCurrentDetailLevel, mCurrentIntraDetailLevel ); // Restrict the chosen detail level by cutoff value. if ( smNumSkipRenderDetails > 0 && mCurrentDetailLevel >= 0 ) { S32 cutoff = getMin( smNumSkipRenderDetails, mShape->mSmallestVisibleDL ); if ( mCurrentDetailLevel < cutoff ) { mCurrentDetailLevel = cutoff; mCurrentIntraDetailLevel = 1.0f; } } return mCurrentDetailLevel; }
void ForestWind::processTick() { PROFILE_SCOPE( ForestWind_ProcessTick ); const F32 deltaTime = 0.032f; const U32 simTime = Sim::getCurrentTime(); Point2F finalVec( 0, 0 ); Point2F windDir( mParent->mWindDirection.x, mParent->mWindDirection.y ); if ( mLastGustTime < simTime ) { Point2F turbVec( 0, 0 ); if ( mLastGustTime < simTime + (mParent->mWindTurbulenceFrequency * 1000.0f) ) turbVec = (mRandom.randF() * mParent->mWindTurbulenceStrength) * windDir; mLastGustTime = simTime + (mParent->mWindGustFrequency * 1000.0f); Point2F gustVec = (mRandom.randF() * mParent->mWindGustStrength + mRandom.randF() * mParent->mWindGustWobbleStrength) * windDir; finalVec += gustVec + turbVec; //finalVec.normalizeSafe(); } //bool rotationChange = false; if ( mLastYawTime < simTime ) { mLastYawTime = simTime + (mParent->mWindGustYawFrequency * 1000.0f); F32 rotateAmt = mRandom.randF() * mParent->mWindGustYawAngle + mRandom.randF() * mParent->mWindGustWobbleStrength; if ( mRandom.randF() <= 0.5f ) rotateAmt = -rotateAmt; rotateAmt = mDegToRad( rotateAmt ); if ( rotateAmt > M_2PI_F ) rotateAmt -= M_2PI_F; else if ( rotateAmt < -M_2PI_F ) rotateAmt += M_2PI_F; mTargetYawAngle = rotateAmt; //finalVec.rotate( rotateAmt ); mCurrentTarget.rotate( rotateAmt ); } //mCurrentTarget.normalizeSafe(); if ( mCurrentTarget.isZero() || mCurrentInterp >= 1.0f ) { mCurrentInterp = 0; mCurrentTarget.set( 0, 0 ); Point2F windDir( mDirection.x, mDirection.y ); windDir.normalizeSafe(); mCurrentTarget = finalVec + windDir; } else { mCurrentInterp += deltaTime; mCurrentInterp = mClampF( mCurrentInterp, 0.0f, 1.0f ); mDirection.interpolate( mDirection, Point3F( mCurrentTarget.x, mCurrentTarget.y, 0 ), mCurrentInterp ); //F32 rotateAmt = mLerp( 0, mTargetYawAngle, mCurrentInterp ); //mTargetYawAngle -= rotateAmt; //Point2F dir( mDirection.x, mDirection.y ); //if ( mTargetYawAngle > 0.0f ) // dir.rotate( rotateAmt ); //mDirection.set( dir.x, dir.y, 0 ); } }
void GuiObjectView::setOrbitDistance( F32 distance ) { // Make sure the orbit distance is within the acceptable range mOrbitDist = mClampF( distance, mMinOrbitDist, mMaxOrbitDist ); }
bool DecalManager::prepRenderImage(SceneState* state, const U32 stateKey, const U32 /*startZone*/, const bool /*modifyBaseState*/) { PROFILE_SCOPE( DecalManager_RenderDecals ); if ( !smDecalsOn || !mData ) return false; if (isLastState(state, stateKey)) return false; setLastState(state, stateKey); if ( !state->isDiffusePass() && !state->isReflectPass() ) return false; PROFILE_START( DecalManager_RenderDecals_SphereTreeCull ); // Grab this before anything here changes it. mCuller = state->getFrustum(); // Populate vector of decal instances to be rendered with all // decals from visible decal spheres. mDecalQueue.clear(); const Vector<DecalSphere*> &grid = mData->getGrid(); for ( U32 i = 0; i < grid.size(); i++ ) { const DecalSphere *decalSphere = grid[i]; const SphereF &worldSphere = decalSphere->mWorldSphere; if ( !mCuller.sphereInFrustum( worldSphere.center, worldSphere.radius ) ) continue; // TODO: If each sphere stored its largest decal instance we // could do an LOD step on it here and skip adding any of the // decals in the sphere. mDecalQueue.merge( decalSphere->mItems ); } PROFILE_END(); PROFILE_START( DecalManager_RenderDecals_Update ); const U32 &curSimTime = Sim::getCurrentTime(); const Point2I &viewportExtent = state->getViewportExtent(); Point3F cameraOffset; F32 decalSize, pixelRadius; U32 delta, diff; DecalInstance *dinst; // Loop through DecalQueue once for preRendering work. // 1. Update DecalInstance fade (over time) // 2. Clip geometry if flagged to do so. // 3. Calculate lod - if decal is far enough away it will not render. for ( U32 i = 0; i < mDecalQueue.size(); i++ ) { dinst = mDecalQueue[i]; // LOD calculation. // See TSShapeInstance::setDetailFromDistance for more // information on these calculations. decalSize = getMax( dinst->mSize, 0.001f ); pixelRadius = dinst->calcPixelRadius( state ); // Need to clamp here. if ( pixelRadius < dinst->calcEndPixRadius( viewportExtent ) ) { mDecalQueue.erase_fast( i ); i--; continue; } // We're gonna try to render this decal... so do any // final adjustments to it before rendering. // Update fade and delete expired. if ( !( dinst->mFlags & PermanentDecal || dinst->mFlags & CustomDecal ) ) { delta = ( curSimTime - dinst->mCreateTime ); if ( delta > dinst->mDataBlock->lifeSpan ) { diff = delta - dinst->mDataBlock->lifeSpan; dinst->mVisibility = 1.0f - (F32)diff / (F32)dinst->mDataBlock->fadeTime; if ( dinst->mVisibility <= 0.0f ) { mDecalQueue.erase_fast( i ); removeDecal( dinst ); i--; continue; } } } // Build clipped geometry for this decal if needed. if ( dinst->mFlags & ClipDecal/* && !( dinst->mFlags & CustomDecal ) */) { // Turn off the flag so we don't continually try to clip // if it fails. if(!clipDecal( dinst )) { dinst->mFlags = dinst->mFlags & ~ClipDecal; if ( !(dinst->mFlags & CustomDecal) ) { // Clipping failed to get any geometry... // Remove it from the render queue. mDecalQueue.erase_fast( i ); i--; // If the decal is one placed at run-time (not the editor) // then we should also permanently delete the decal instance. if ( !(dinst->mFlags & SaveDecal) ) { removeDecal( dinst ); } } // If this is a decal placed by the editor it will be // flagged to attempt clipping again the next time it is // modified. For now we just skip rendering it. continue; } } // If we get here and the decal still does not have any geometry // skip rendering it. It must be an editor placed decal that failed // to clip any geometry but has not yet been flagged to try again. if ( !dinst->mVerts || dinst->mVertCount == 0 || dinst->mIndxCount == 0 ) { mDecalQueue.erase_fast( i ); i--; continue; } // F32 alpha = pixelRadius / (dinst->mDataBlock->startPixRadius * decalSize) - 1.0f; if ( dinst->mFlags & CustomDecal ) { alpha = mClampF( alpha, 0.0f, 1.0f ); alpha *= dinst->mVisibility; } else alpha = mClampF( alpha * dinst->mVisibility, 0.0f, 1.0f ); // for ( U32 v = 0; v < dinst->mVertCount; v++ ) dinst->mVerts[v].color.set( 255, 255, 255, alpha * 255.0f ); } PROFILE_END(); if ( mDecalQueue.empty() ) return false; // Sort queued decals... // 1. Editor decals - in render priority order first, creation time second, and material third. // 2. Dynamic decals - in render priority order first and creation time second. // // With the constraint that decals with different render priority cannot // be rendered together in the same draw call. PROFILE_START( DecalManager_RenderDecals_Sort ); dQsort( mDecalQueue.address(), mDecalQueue.size(), sizeof(DecalInstance*), cmpDecalRenderOrder ); PROFILE_END(); PROFILE_SCOPE( DecalManager_RenderDecals_RenderBatch ); mPrimBuffs.clear(); mVBs.clear(); RenderPassManager *renderPass = state->getRenderPass(); // Base render instance for convenience we use for convenience. // Data shared by all instances we allocate below can be copied // from the base instance at the same time. MeshRenderInst baseRenderInst; baseRenderInst.clear(); MatrixF *tempMat = renderPass->allocUniqueXform( MatrixF( true ) ); MathUtils::getZBiasProjectionMatrix( gDecalBias, mCuller, tempMat ); baseRenderInst.projection = tempMat; baseRenderInst.objectToWorld = &MatrixF::Identity; baseRenderInst.worldToCamera = renderPass->allocSharedXform(RenderPassManager::View); baseRenderInst.type = RenderPassManager::RIT_Decal; // Make it the sort distance the max distance so that // it renders after all the other opaque geometry in // the prepass bin. baseRenderInst.sortDistSq = F32_MAX; // Get the best lights for the current camera position. LightManager *lm = state->getLightManager(); if ( lm ) { lm->setupLights( NULL, mCuller.getPosition(), mCuller.getTransform().getForwardVector(), mCuller.getFarDist() ); lm->getBestLights( baseRenderInst.lights, 4 ); lm->resetLights(); } Vector<DecalBatch> batches; DecalBatch *currentBatch = NULL; // Loop through DecalQueue collecting them into render batches. for ( U32 i = 0; i < mDecalQueue.size(); i++ ) { DecalInstance *decal = mDecalQueue[i]; DecalData *data = decal->mDataBlock; Material *mat = data->getMaterial(); if ( currentBatch == NULL ) { // Start a new batch, beginning with this decal. batches.increment(); currentBatch = &batches.last(); currentBatch->startDecal = i; currentBatch->decalCount = 1; currentBatch->iCount = decal->mIndxCount; currentBatch->vCount = decal->mVertCount; currentBatch->mat = mat; currentBatch->matInst = decal->mDataBlock->getMaterialInstance(); currentBatch->priority = decal->getRenderPriority(); currentBatch->dynamic = !(decal->mFlags & SaveDecal); continue; } if ( currentBatch->iCount + decal->mIndxCount >= smMaxIndices || currentBatch->vCount + decal->mVertCount >= smMaxVerts || currentBatch->mat != mat || currentBatch->priority != decal->getRenderPriority() || decal->mCustomTex ) { // End batch. currentBatch = NULL; i--; continue; } // Add on to current batch. currentBatch->decalCount++; currentBatch->iCount += decal->mIndxCount; currentBatch->vCount += decal->mVertCount; } // Loop through batches allocating buffers and submitting render instances. for ( U32 i = 0; i < batches.size(); i++ ) { DecalBatch ¤tBatch = batches[i]; // Allocate buffers... GFXVertexBufferHandle<DecalVertex> vb; vb.set( GFX, currentBatch.vCount, GFXBufferTypeDynamic ); DecalVertex *vpPtr = vb.lock(); GFXPrimitiveBufferHandle pb; pb.set( GFX, currentBatch.iCount, 0, GFXBufferTypeDynamic ); U16 *pbPtr; pb.lock( &pbPtr ); // Copy data into the buffers from all decals in this batch... U32 lastDecal = currentBatch.startDecal + currentBatch.decalCount; U32 voffset = 0; U32 ioffset = 0; // This is an ugly hack for ProjectedShadow! GFXTextureObject *customTex = NULL; for ( U32 j = currentBatch.startDecal; j < lastDecal; j++ ) { DecalInstance *dinst = mDecalQueue[j]; for ( U32 k = 0; k < dinst->mIndxCount; k++ ) { *( pbPtr + ioffset + k ) = dinst->mIndices[k] + voffset; } ioffset += dinst->mIndxCount; dMemcpy( vpPtr + voffset, dinst->mVerts, sizeof( DecalVertex ) * dinst->mVertCount ); voffset += dinst->mVertCount; // Ugly hack for ProjectedShadow! if ( (dinst->mFlags & CustomDecal) && dinst->mCustomTex != NULL ) customTex = *dinst->mCustomTex; } AssertFatal( ioffset == currentBatch.iCount, "bad" ); AssertFatal( voffset == currentBatch.vCount, "bad" ); pb.unlock(); vb.unlock(); // DecalManager must hold handles to these buffers so they remain valid, // we don't actually use them elsewhere. mPrimBuffs.push_back( pb ); mVBs.push_back( vb ); // Submit render inst... MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>(); *ri = baseRenderInst; ri->primBuff = &mPrimBuffs.last(); ri->vertBuff = &mVBs.last(); ri->matInst = currentBatch.matInst; ri->prim = renderPass->allocPrim(); ri->prim->type = GFXTriangleList; ri->prim->minIndex = 0; ri->prim->startIndex = 0; ri->prim->numPrimitives = currentBatch.iCount / 3; ri->prim->startVertex = 0; ri->prim->numVertices = currentBatch.vCount; // Ugly hack for ProjectedShadow! if ( customTex ) ri->miscTex = customTex; // The decal bin will contain render instances for both decals and decalRoad's. // Dynamic decals render last, then editor decals and roads in priority order. // DefaultKey is sorted in descending order. ri->defaultKey = currentBatch.dynamic ? 0xFFFFFFFF : (U32)currentBatch.priority; ri->defaultKey2 = 1;//(U32)lastDecal->mDataBlock; renderPass->addInst( ri ); } return false; }