bool VolumetricFog::onAdd() { if (!Parent::onAdd()) return false; if (!VFRTM->IsInitialized()) { Con::errorf("No VolumetricFogRTManager present!!"); return false; } resetWorldBox(); mShapeLoaded = LoadShape(); setRenderTransform(mObjToWorld); addToScene(); ColBox.set(getTransform(), (mObjBox.getExtents() * getScale() * COLBOX_SCALE)); mObjSize = mWorldBox.getGreatestDiagonalLength(); mObjScale = getScale(); mTexTiles = mAbs(mTexTiles); mSpeed.set(mSpeed1.x, mSpeed1.y, mSpeed2.x, mSpeed2.y); mInvScale = (1.0f / getMax(getMax(mObjScale.x, mObjScale.y), mObjScale.z)); if (isClientObject()) { InitTexture(); return setupRenderer(); } VFRTM->IncFogObjects(); return true; }
String getUniqueName( const char *inName ) { String outName( inName ); if ( outName.isEmpty() ) return String::EmptyString; SimObject *dummy; if ( !Sim::findObject( outName, dummy ) ) return outName; S32 suffixNumb = -1; String nameStr( String::GetTrailingNumber( outName, suffixNumb ) ); suffixNumb = mAbs( suffixNumb ) + 1; #define MAX_TRIES 100 for ( U32 i = 0; i < MAX_TRIES; i++ ) { outName = String::ToString( "%s%d", nameStr.c_str(), suffixNumb ); if ( !Sim::findObject( outName, dummy ) ) return outName; suffixNumb++; } Con::errorf( "Sim::getUniqueName( %s ) - failed after %d attempts", inName, MAX_TRIES ); return String::EmptyString; }
TEST(TimeManager, BasicAPI) { handle handler; // Initialize the time manager... TimeManager time; time.timeEvent.notify(&handler, &handle::timeEvent); // Event loop till at least one second has passed. const U32 start = Platform::getRealMilliseconds(); while(Process::processEvents()) { // If we go too long, kill it off... if(Platform::getRealMilliseconds() - start > 30*1000) { EXPECT_TRUE(false) << "Terminated process loop due to watchdog, not due to time manager event, after 30 seconds."; Process::requestShutdown(); } } const U32 end = Platform::getRealMilliseconds(); // Now, confirm we have approximately similar elapsed times. S32 elapsedRealTime = end - start; EXPECT_LT(mAbs(elapsedRealTime - handler.mElapsedTime), 50) << "Failed to elapse time to within the desired tolerance."; EXPECT_GT(handler.mNumberCalls, 0) << "Somehow got no event callbacks from TimeManager?"; };
bool VolumetricFog::onAdd() { if (!Parent::onAdd()) return false; if (!VFRTM->IsInitialized()) { Con::errorf("No VolumetricFogRTManager present!!"); return false; } resetWorldBox(); mShapeLoaded = LoadShape(); setRenderTransform(mObjToWorld); addToScene(); ColBox.set(getTransform(), (mObjBox.getExtents() * getScale() * COLBOX_SCALE)); mObjSize = mWorldBox.getGreatestDiagonalLength(); mObjScale = getScale(); mTexTiles = mAbs(mTexTiles); mSpeed.set(mSpeed1.x, mSpeed1.y, mSpeed2.x, mSpeed2.y); mInvScale = (1.0f / getMax(getMax(mObjScale.x, mObjScale.y), mObjScale.z)); if (isClientObject()) { conn = GameConnection::getConnectionToServer(); if (!conn) { Con::errorf("VolumetricFog::onAdd - No Serverconnection"); return false; } glowFX = static_cast<PostEffect*>(Sim::findObject("VolFogGlowPostFx")); mOldLightRayStrength = Con::getFloatVariable("$LightRayPostFX::brightScalar",1.0f); GuiCanvas* cv = dynamic_cast<GuiCanvas*>(Sim::findObject("Canvas")); if (cv == NULL) { Con::errorf("VolumetricFog::onAdd - Canvas not found!!"); return false; } mPlatformWindow = cv->getPlatformWindow(); VolumetricFogRTManager::getVolumetricFogRTMResizeSignal().notify(this, &VolumetricFog::handleResize); GuiCanvas::getCanvasSizeChangeSignal().notify(this, &VolumetricFog::handleCanvasResize); InitTexture(); return setupRenderer(); } VFRTM->IncFogObjects(); return true; }
void GuiButtonBaseCtrl::onMouseDragged( const GuiEvent& event ) { if( mUseMouseEvents ) { // If we haven't started a drag yet, find whether we have moved past // the tolerance value. if( !mMouseDragged ) { Point2I delta = mMouseDownPoint - event.mousePoint; if( mAbs( delta.x ) > 2 || mAbs( delta.y ) > 2 ) mMouseDragged = true; } if( mMouseDragged ) onMouseDragged_callback(); } Parent::onMouseDragged( event ); }
//----------------------------------------------------------------------------- // // VFadeEvent::onTrigger( pTime, pDelta ); // // Start the fade sequence if a valid fade control can be found. // //----------------------------------------------------------------------------- void VFadeEvent::onTrigger( const S32 &pTime, const S32 &pDelta ) { Parent::onTrigger( pTime, pDelta ); // Fetch GUI Control. VFadeControl *fadeControl; if ( !Sim::findObject( "VFadeControlGUI", fadeControl ) ) { // Invalid. return; } // Start Fade. fadeControl->start( getFadeType(), mDuration ); // Set Elapsed Time. fadeControl->mElapsedTime = mAbs( pTime - getStartTime() ); }
//----------------------------------------------------------------------------- // // VCameraShakeEvent::onTrigger( pTime, pDelta ); // // Start shaking the camera. Also account for any offet in playtime, and // timescale. // //----------------------------------------------------------------------------- void VCameraShakeEvent::onTrigger( const S32 &pTime, const S32 &pDelta ) { Parent::onTrigger( pTime, pDelta ); // Fetch Group. VCameraGroup *group; if ( !getGroup( group ) || !group->isActive() ) { // Inactive. return; } // Duration. //const F32 duration = ( mDuration - mAbs( pTime - getStartTime() ) ) / ( 1000.f * mFabs( getControllerTimeScale() ) ); const F32 duration = ( mDuration - mAbs( pTime - getStartTime() ) ) / 1000.f; // Shake Camera. VTorque::startCameraShake( duration, mFalloff, mAmplitude, mFrequency ); }
void ClipMap::recenter(Point2F center) { bool wantCompleteRefill = false; if(mNeedRefill || mForceClipmapPurge || Con::getBoolVariable("$forceFullClipmapPurgeEveryFrame", false)) wantCompleteRefill = true; PROFILE_START(ClipMap_recenter); // Reset our budget. mMaxTexelUploadPerRecenter = mClipMapSize * mClipMapSize * 2; AssertFatal(isPow2(mClipMapSize), "ClipMap::recenter - require pow2 clipmap size!"); // Clamp the center to the unit square. /* if(!mTile) { center.x = mClampF(center.x, 0.f, 1.f); center.y = mClampF(center.y, 0.f, 1.f); } */ // Ok, we're going to do toroidal updates on each entry of the clipstack // (except for the cap, which covers the whole texture), based on this // new center point. if( !wantCompleteRefill ) { // Calculate the new texel at most detailed level. Point2F texelCenterF = center * F32(mClipMapSize) * mLevels[0].mScale; Point2I texelCenter((S32)mFloor(texelCenterF.y), (S32)mFloor(texelCenterF.x)); // Update interest region. mImageCache->setInterestCenter(texelCenter); } // Note how many we were at so we can cut off at the right time. S32 lastTexelsUpdated = mTexelsUpdated; // For each texture... for(S32 i=mClipStackDepth-2; i>=0; i--) { ClipStackEntry &cse = mLevels[i]; // Calculate new center point for this texture. Point2F texelCenterF = center * F32(mClipMapSize) * cse.mScale; const S32 texelMin = mClipMapSize/2; //const S32 texelMax = S32(F32(mClipMapSize) * cse.mScale) - texelMin; Point2I texelTopLeft; //if(mTile) //{ texelTopLeft.x = S32(mFloor(texelCenterF.y)) - texelMin; texelTopLeft.y = S32(mFloor(texelCenterF.x)) - texelMin; //} //else //{ // texelTopLeft.x = mClamp(S32(mFloor(texelCenterF.y)), texelMin, texelMax) - texelMin; // texelTopLeft.y = mClamp(S32(mFloor(texelCenterF.x)), texelMin, texelMax) - texelMin; //} // Also, prevent very small updates - the RT changes are costly. Point2I d = cse.mToroidalOffset - texelTopLeft; if(mAbs(d.x) <= 2 && mAbs(d.y) <= 2) { // Update the center; otherwise we get some weird conditions around // edges of the clipmap space. cse.mClipCenter = center; continue; } // This + current toroid offset tells us what regions have to be blasted. RectI oldData(cse.mToroidalOffset, Point2I(mClipMapSize, mClipMapSize)); RectI newData(texelTopLeft, Point2I(mClipMapSize, mClipMapSize)); // Update clipstack level. cse.mClipCenter = center; cse.mToroidalOffset = texelTopLeft; // If we're refilling, that's all we want; continue with next level. if( wantCompleteRefill ) continue; // Make sure we have available data... if(!mImageCache->isDataAvailable(getMipLevel(cse.mScale), newData)) continue; // Alright, determine the set of data we actually need to upload. S32 rectCount = 0; RectI buffer[8]; calculateModuloDeltaBounds(oldData, newData, buffer, &rectCount); AssertFatal(rectCount < 8, "ClipMap::recenter - got too many rects back!"); /*if(rectCount) Con::printf(" issuing %d updates to clipmap level %d (offset=%dx%d)", rectCount, i, texelTopLeft.x, texelTopLeft.y); */ if(rectCount) { if (!mImageCache->beginRectUpdates(cse)) { mForceClipmapPurge = true; return; } //Con::errorf("layer %x, %d updates", &cse, rectCount); // And GO! for(S32 j=0; j<rectCount; j++) { PROFILE_START(ClipMap_recenter_upload); AssertFatal(buffer[j].isValidRect(),"ClipMap::recenter - got invalid rect!"); // Note the rect, so we can then wrap and let the image cache do its thing. RectI srcRegion = buffer[j]; buffer[j].point.x = srcRegion.point.x % mClipMapSize; buffer[j].point.y = srcRegion.point.y % mClipMapSize; AssertFatal(newData.contains(srcRegion), "ClipMap::recenter - got update buffer outside of expected new data bounds."); mTotalUpdates++; mTexelsUpdated += srcRegion.extent.x * srcRegion.extent.y; //Con::printf("updating (%d %d %d %d)", // buffer[j].point.x, buffer[j].point.y, buffer[j].extent.x, buffer[j].extent.y); mImageCache->doRectUpdate(getMipLevel(cse.mScale), cse, srcRegion, buffer[j]); PROFILE_END(); } mImageCache->finishRectUpdates(cse); } // Check if we've overrun our budget. if((mTexelsUpdated - lastTexelsUpdated) > mMaxTexelUploadPerRecenter) { //Con::warnf("ClipMap::recenter - exceeded budget for this frame, deferring till next frame."); break; } } if( wantCompleteRefill ) { fillWithTextureData(); mNeedRefill = false; } PROFILE_END(); }
void ClipMap::calculateModuloDeltaBounds(const RectI &oldData, const RectI &newData, RectI *outRects, S32 *outRectCount) { // Sanity checking. /*AssertFatal(oldData.point.x >= 0 && oldData.point.y >= 0 && oldData.isValidRect(), "ClipMap::calculateModuloDeltaBounds - negative oldData origin or bad rect!"); AssertFatal(newData.point.x >= 0 && newData.point.y >= 0 && newData.isValidRect(), "ClipMap::calculateModuloDeltaBounds - negative newData origin or bad rect!");*/ AssertFatal(newData.extent == oldData.extent, "ClipMap::calculateModuloDeltaBounts - mismatching extents, can only work with matching extents!"); // Easiest case - if they're the same then do nothing. if(oldData.point == newData.point) { *outRectCount = 0; return; } // Easy case - if there's no overlap then it's all new! if(!oldData.overlaps(newData)) { // Clip out to return buffer, and we're done. clipAgainstGrid(mClipMapSize, newData, outRectCount, outRects); return; } // Calculate some useful values for both X and Y. Delta is used a lot // in determining bounds, and the boundary values are important for // determining where to start copying new data in. const S32 xDelta = newData.point.x - oldData.point.x; const S32 yDelta = newData.point.y - oldData.point.y; const S32 xBoundary = (oldData.point.x + oldData.extent.x) % mClipMapSize; const S32 yBoundary = (oldData.point.y + oldData.extent.y) % mClipMapSize; AssertFatal(xBoundary % mClipMapSize == oldData.point.x % mClipMapSize, "ClipMap::calculateModuleDeltaBounds - we assume that left and " "right of the dataset are identical (ie, it's periodical on size of clipmap!) (x)"); AssertFatal(yBoundary % mClipMapSize == oldData.point.y % mClipMapSize, "ClipMap::calculateModuleDeltaBounds - we assume that left and " "right of the dataset are identical (ie, it's periodical on size of clipmap!) (y)"); // Now, let's build up our rects. We have one rect if we are moving // on the X or Y axis, two if both. We dealt with the no-move case // previously. if(xDelta == 0) { // Moving on Y! So generate and store clipped results. RectI yRect; if(yDelta < 0) { // We need to generate the box from right of old to right of new. yRect.point = newData.point; yRect.extent.x = mClipMapSize; yRect.extent.y = -yDelta; } else { // We need to generate the box from left of old to left of new. yRect.point.x = newData.point.x; // Doesn't matter which rect we get this from. yRect.point.y = (oldData.point.y + oldData.extent.y); yRect.extent.x = mClipMapSize; yRect.extent.y = yDelta; } // Clip out to return buffer, and we're done. clipAgainstGrid(mClipMapSize, yRect, outRectCount, outRects); return; } else if(yDelta == 0) { // Moving on X! So generate and store clipped results. RectI xRect; if(xDelta < 0) { // We need to generate the box from right of old to right of new. xRect.point = newData.point; xRect.extent.x = -xDelta; xRect.extent.y = mClipMapSize; } else { // We need to generate the box from left of old to left of new. xRect.point.x = (oldData.point.x + oldData.extent.x); xRect.point.y = newData.point.y; // Doesn't matter which rect we get this from. xRect.extent.x = xDelta; xRect.extent.y = mClipMapSize; } // Clip out to return buffer, and we're done. clipAgainstGrid(mClipMapSize, xRect, outRectCount, outRects); return; } else { // Both! We have an L shape. So let's do the bulk of it in one rect, // and the remainder in the other. We'll choose X as the dominant axis. // // a-----b---------c going from e to a. // | | | // | | | // d-----e---------f So the dominant rect is abgh and the passive // | | | rect is bcef. Obviously depending on delta we // | | | have to switch things around a bit. // | | | y+ ^ // | | | | // g-----h---------i x+-> | RectI xRect, yRect; if(xDelta < 0) { // Case in the diagram. xRect.point = newData.point; xRect.extent.x = -xDelta; xRect.extent.y = mClipMapSize; // Set up what of yRect we know, too. yRect.point.x = xRect.point.x + xRect.extent.x; yRect.extent.x = mClipMapSize - mAbs(xDelta); } else { // Opposite of case in diagram! xRect.point.x = oldData.point.x + oldData.extent.x; xRect.point.y = newData.point.y; xRect.extent.x = xDelta; xRect.extent.y = mClipMapSize; // Set up what of yRect we know, too. yRect.point.x = (xRect.point.x + xRect.extent.x )- mClipMapSize; yRect.extent.x = mClipMapSize - xRect.extent.x; } if(yDelta < 0) { // Case in the diagram. yRect.point.y = newData.point.y; yRect.extent.y = -yDelta; } else { // Opposite of case in diagram! yRect.point.y = oldData.point.y + oldData.extent.y; yRect.extent.y = yDelta; } // Make sure we don't overlap. AssertFatal(!yRect.overlaps(xRect), "ClipMap::calculateModuloDeltaBounds - have overlap in result rects!"); // Ok, now run them through the clipper. S32 firstCount; clipAgainstGrid(mClipMapSize, xRect, &firstCount, outRects); clipAgainstGrid(mClipMapSize, yRect, outRectCount, outRects + firstCount); *outRectCount += firstCount; // All done! return; } }
//----------------------------------------------------------------------------- // // VMotionTrack::getObjectSpeed(); // // Determine the Speed that an object must move at to travel over the segment // length of the Path. // //----------------------------------------------------------------------------- F32 VMotionEvent::getObjectSpeed( void ) { // Fetch Parent Track. VMotionTrack *track; if ( !getTrack( track ) ) { // Invalid Track. return 0.f; } // Fetch Path & Reference Object. VTorque::PathObjectType *path = track->getPath(); VTorque::SceneObjectType *object = getSceneObject(); if ( !path || !object ) { // Invalid Object(s). return 0.f; } // Fetch Node Index. const S32 &srcNodeIndex = getNodeIndex( ( isControllerPlayingForward() ) ? 0 : -1 ); // Fetch the Next Event. VEvent *nextEvent = getNextEvent(); // Valid Destination Node? if ( !isControllerLooping() && !nextEvent ) { // No Next Node. return 0.f; } // Valid Next Node? if ( nextEvent ) { // Fetch Segment Length & Duration. const F32 &length = VTorque::getPathNodeLength( path, srcNodeIndex ); const F32 &duration = mAbs( getTriggerTime() - nextEvent->getTriggerTime() ); // Speed = Distance / Duration. return ( length / ( duration / 1000.f ) ); } // Playing Forwards? if ( isControllerPlayingForward() ) { // Fetch the First Event. VEvent *firstEvent = dynamic_cast<VEvent*>( track->getChild() ); // Fetch Segment Length & Duration. const F32 &length = VTorque::getPathNodeLength( path, srcNodeIndex ); const F32 &duration = ( getControllerDuration() - getTriggerTime() ) + firstEvent->getTriggerTime(); // Speed = Distance / Duration. return ( length / ( duration / 1000.f ) ); } // Fetch the Last Event. VEvent *lastEvent = dynamic_cast<VEvent*>( track->getLastChild() ); // Fetch Segment Length & Duration. const F32 &length = VTorque::getPathNodeLength( path, srcNodeIndex ); const F32 &duration = ( getControllerDuration() - lastEvent->getTriggerTime() ) + getTriggerTime(); // Speed = Distance / Duration. return ( length / ( duration / 1000.f ) ); }