void LightManager::registerGlobalLights( const Frustum *frustum, bool staticLighting ) { PROFILE_SCOPE( LightManager_RegisterGlobalLights ); // TODO: We need to work this out... // // 1. Why do we register and unregister lights on every // render when they don't often change... shouldn't we // just register once and keep them? // // 2. If we do culling of lights should this happen as part // of registration or somewhere else? // // Grab the lights to process. Vector<SceneObject*> activeLights; const U32 lightMask = LightObjectType; if ( staticLighting || !frustum ) { // We're processing static lighting or want all the lights // in the container registerd... so no culling. getSceneManager()->getContainer()->findObjectList( lightMask, &activeLights ); } else { // Cull the lights using the frustum. getSceneManager()->getContainer()->findObjectList( *frustum, lightMask, &activeLights ); // Store the culling position for sun placement // later... see setSpecialLight. mCullPos = frustum->getPosition(); // HACK: Make sure the control object always gets // processed as lights mounted to it don't change // the shape bounds and can often get culled. GameConnection *conn = GameConnection::getConnectionToServer(); if ( conn->getControlObject() ) { GameBase *conObject = conn->getControlObject(); activeLights.push_back_unique( conObject ); } } // Let the lights register themselves. for ( U32 i = 0; i < activeLights.size(); i++ ) { ISceneLight *lightInterface = dynamic_cast<ISceneLight*>( activeLights[i] ); if ( lightInterface ) lightInterface->submitLights( this, staticLighting ); } }
void StdServerProcessList::onPreTickObject( ProcessObject *pobj ) { if ( pobj->mIsGameObject ) { SimObjectPtr<GameObject> obj = getGameObject( pobj ); // Each object is either advanced a single tick, or if it's // being controlled by a client, ticked once for each pending move. GameConnection *con = obj->getControllingClient(); if ( con && con->getControlObject() == obj ) { Move* movePtr; U32 numMoves; con->mMoveList->getMoves( &movePtr, &numMoves ); if ( numMoves == 0 ) { #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("no moves on object %i, skip tick",obj->getId()); #endif return; } } } Parent::onPreTickObject (pobj ); }
void HifiClientProcessList::advanceObjects() { #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("Advance client time..."); #endif // client re-computes this each time objects are advanced gMaxHiFiVelSq = 0; Parent::advanceObjects(); // We need to consume a move on the connections whether // there is a control object to consume the move or not, // otherwise client and server can get out of sync move-wise // during startup. If there is a control object, we cleared // a move above. Handle case where no control object here. // Note that we might consume an extra move here and there when // we had a control object in above loop but lost it during tick. // That is no big deal so we don't bother trying to carefully // track it. GameConnection * client = GameConnection::getConnectionToServer(); if (client && client->getControlObject() == NULL) client->mMoveList->clearMoves(1); #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("---------"); #endif }
static GameBase * getControlObj() { GameConnection * connection = GameConnection::getLocalClientConnection(); ShapeBase* control = 0; if(connection) control = dynamic_cast<ShapeBase*>(connection->getControlObject()); return(control); }
void WaterObject::updateUnderwaterEffect( SceneRenderState *state ) { AssertFatal( isClientObject(), "uWaterObject::updateUnderwaterEffect() called on the server" ); PostEffect *effect = getUnderwaterEffect(); if ( !effect ) return; // Never use underwater postFx with Basic Lighting, we don't have depth. if ( mBasicLighting ) { effect->disable(); return; } GameConnection *conn = GameConnection::getConnectionToServer(); if ( !conn ) return; GameBase *control = conn->getControlObject(); if ( !control ) return; WaterObject *water = control->getCurrentWaterObject(); if ( water == NULL ) effect->disable(); else if ( water == this ) { MatrixF mat; conn->getControlCameraTransform( 0, &mat ); if ( mUnderwater ) { effect->enable(); effect->setOnThisFrame( true ); mWaterFogData.depthGradMax = mDepthGradientMax; state->getSceneManager()->setWaterFogData( mWaterFogData ); // Register our depthGradient texture with a name so it can // be fetched by the effect when it renders. if ( !mNamedDepthGradTex.isRegistered() ) mNamedDepthGradTex.registerWithName( "waterDepthGradMap" ); mNamedDepthGradTex.setTexture( mDepthGradientTex ); } else effect->disable(); } }
void ExtendedClientProcessList::onTickObject( ProcessObject *obj ) { PROFILE_SCOPE( ExtendedClientProcessList_OnTickObject ); // In case the object deletes itself during its processTick. SimObjectPtr<SceneObject> safePtr = static_cast<SceneObject*>( obj ); // Each object is either advanced a single tick, or if it's // being controlled by a client, ticked once for each pending move. ExtendedMove* extMovePtr; U32 numMoves; GameConnection* con = obj->getControllingClient(); if ( con && con->getControlObject() == obj ) { ExtendedMoveList* extMoveList = static_cast<ExtendedMoveList*>(con->mMoveList); extMoveList->getExtMoves( &extMovePtr, &numMoves ); if ( numMoves ) { // Note: should only have a single move at this point AssertFatal(numMoves==1,"ClientProccessList::onTickObject: more than one move in queue"); #ifdef TORQUE_DEBUG_NET_MOVES U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum(obj->getControllingClient()); #endif if ( obj->isTicking() ) obj->processTick( extMovePtr ); if ( bool(safePtr) && obj->getControllingClient() ) { U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() ); // set checksum if not set or check against stored value if set extMovePtr->checksum = newsum; #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("move checksum: %i, (start %i), (move %f %f %f)", movePtr->checksum,sum,movePtr->yaw,movePtr->y,movePtr->z); #endif } con->mMoveList->clearMoves( 1 ); } } else if ( obj->isTicking() ) obj->processTick( 0 ); }
void HifiServerProcessList::onTickObject(ProcessObject * pobj) { // Each object is advanced a single tick // If it's controlled by a client, tick using a move. Move *movePtr; U32 numMoves; GameConnection *con = pobj->getControllingClient(); SimObjectPtr<GameBase> obj = getGameBase( pobj ); if ( obj && con && con->getControlObject() == obj && con->mMoveList->getMoves( &movePtr, &numMoves ) ) { #ifdef TORQUE_DEBUG_NET_MOVES U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() ); #endif obj->processTick(movePtr); if ( bool(obj) && obj->getControllingClient() ) { U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() ); // check move checksum if ( movePtr->checksum != newsum ) { #ifdef TORQUE_DEBUG_NET_MOVES if ( !obj->mIsAiControlled ) Con::printf( "move %i checksum disagree: %i != %i, (start %i), (move %f %f %f)", movePtr->id, movePtr->checksum, newsum, sum, movePtr->yaw, movePtr->y, movePtr->z ); #endif movePtr->checksum = Move::ChecksumMismatch; } else { #ifdef TORQUE_DEBUG_NET_MOVES Con::printf( "move %i checksum agree: %i == %i, (start %i), (move %f %f %f)", movePtr->id, movePtr->checksum, newsum, sum, movePtr->yaw, movePtr->y, movePtr->z ); #endif } // Adding this seems to fix constant corrections, but is it // really a sound fix? con->mMoveList->clearMoves( 1 ); } } else if ( pobj->isTicking() ) pobj->processTick( 0 ); }
void HifiClientProcessList::onTickObject(ProcessObject * pobj) { // Each object is advanced a single tick // If it's controlled by a client, tick using a move. Move *movePtr; U32 numMoves; GameConnection *con = pobj->getControllingClient(); SimObjectPtr<GameBase> obj = getGameBase( pobj ); if ( obj && con && con->getControlObject() == obj && con->mMoveList->getMoves( &movePtr, &numMoves) ) { #ifdef TORQUE_DEBUG_NET_MOVES U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() ); #endif obj->processTick( movePtr ); if ( bool(obj) && obj->getControllingClient() ) { U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() ); // set checksum if not set or check against stored value if set movePtr->checksum = newsum; #ifdef TORQUE_DEBUG_NET_MOVES Con::printf( "move checksum: %i, (start %i), (move %f %f %f)", movePtr->checksum,sum,movePtr->yaw,movePtr->y,movePtr->z ); #endif } con->mMoveList->clearMoves( 1 ); } else if ( pobj->isTicking() ) pobj->processTick( 0 ); if ( obj && ( obj->getTypeMask() & GameBaseHiFiObjectType ) ) { GameConnection * serverConnection = GameConnection::getConnectionToServer(); TickCacheEntry * tce = obj->getTickCache().addCacheEntry(); BitStream bs( tce->packetData, TickCacheEntry::MaxPacketSize ); obj->writePacketData( serverConnection, &bs ); Point3F vel = obj->getVelocity(); F32 velSq = mDot( vel, vel ); gMaxHiFiVelSq = getMax( gMaxHiFiVelSq, velSq ); } }
// Checks collisions with a SpeedZone bool SpeedZone::collide(BfObject *hitObject) { if(ignoreThisCollision) return false; // This is run on both server and client side to reduce lag if(isShipType(hitObject->getObjectTypeNumber())) // Only ships & robots collide { #ifndef ZAP_DEDICATED if(isGhost()) // On client, don't process speedZone on all moveObjects except the controlling one { ClientGame *client = static_cast<ClientGame *>(getGame()); GameConnection *gc = client->getConnectionToServer(); if(gc && gc->getControlObject() != hitObject) return false; } #endif return true; } return false; }
void Etherform::interpolateTick(F32 dt) { Parent::interpolateTick(dt); if(dt != 0.0f) { Point3F pos = delta.pos + delta.posVec * dt; Point3F rot = delta.rot + delta.rotVec * dt; this->setRenderPosition(pos, rot, dt); // update laser trails... for( S32 i=0; i < NUM_ETHERFORM_LASERTRAILS; i++ ) { if(mLaserTrailList[i]) mLaserTrailList[i]->setLastNodePos(pos); } // apply camera effects - is this the best place? - bramage GameConnection* connection = GameConnection::getConnectionToServer(); if(connection->isFirstPerson()) { GameBase* obj = connection->getControlObject(); if( obj == this ) { MatrixF curTrans = this->getRenderTransform(); curTrans.mul( gCamFXMgr.getTrans() ); Parent::setRenderTransform( curTrans ); } } } else { this->setRenderPosition(delta.pos, delta.rot, 0); } // Save last interpolation delta value. delta.dt = dt; }
//---------------------------------------------------------------------------- /// Core rendering method for this control. /// /// This method scans through all the current client ShapeBase objects. /// If one is named, it displays the name and damage information for it. /// /// Information is offset from the center of the object's bounding box, /// unless the object is a PlayerObjectType, in which case the eye point /// is used. /// /// @param updateRect Extents of control. void afxGuiTextHud::onRender( Point2I, const RectI &updateRect) { // Background fill first if (mShowFill) GFX->getDrawUtil()->drawRectFill(updateRect, mFillColor.toColorI()); // Must be in a TS Control GuiTSCtrl *parent = dynamic_cast<GuiTSCtrl*>(getParent()); if (!parent) return; // Must have a connection and control object GameConnection* conn = GameConnection::getConnectionToServer(); if (!conn) return; GameBase * control = dynamic_cast<GameBase*>(conn->getControlObject()); if (!control) return; // Get control camera info MatrixF cam; Point3F camPos; VectorF camDir; conn->getControlCameraTransform(0,&cam); cam.getColumn(3, &camPos); cam.getColumn(1, &camDir); F32 camFovCos; conn->getControlCameraFov(&camFovCos); camFovCos = mCos(mDegToRad(camFovCos) / 2); // Visible distance info & name fading F32 visDistance = gClientSceneGraph->getVisibleDistance(); F32 visDistanceSqr = visDistance * visDistance; F32 fadeDistance = visDistance * mDistanceFade; // Collision info. We're going to be running LOS tests and we // don't want to collide with the control object. static U32 losMask = TerrainObjectType | TerrainLikeObjectType | ShapeBaseObjectType; if (!mEnableControlObjectOcclusion) control->disableCollision(); if (mLabelAllShapes) { // This section works just like GuiShapeNameHud and renders labels for // all the shapes. // All ghosted objects are added to the server connection group, // so we can find all the shape base objects by iterating through // our current connection. for (SimSetIterator itr(conn); *itr; ++itr) { ///if ((*itr)->getTypeMask() & ShapeBaseObjectType) ///{ ShapeBase* shape = dynamic_cast<ShapeBase*>(*itr); if ( shape ) { if (shape != control && shape->getShapeName()) { // Target pos to test, if it's a player run the LOS to his eye // point, otherwise we'll grab the generic box center. Point3F shapePos; if (shape->getTypeMask() & PlayerObjectType) { MatrixF eye; // Use the render eye transform, otherwise we'll see jittering shape->getRenderEyeTransform(&eye); eye.getColumn(3, &shapePos); } else { // Use the render transform instead of the box center // otherwise it'll jitter. MatrixF srtMat = shape->getRenderTransform(); srtMat.getColumn(3, &shapePos); } VectorF shapeDir = shapePos - camPos; // Test to see if it's in range F32 shapeDist = shapeDir.lenSquared(); if (shapeDist == 0 || shapeDist > visDistanceSqr) continue; shapeDist = mSqrt(shapeDist); // Test to see if it's within our viewcone, this test doesn't // actually match the viewport very well, should consider // projection and box test. shapeDir.normalize(); F32 dot = mDot(shapeDir, camDir); if (dot < camFovCos) continue; // Test to see if it's behind something, and we want to // ignore anything it's mounted on when we run the LOS. RayInfo info; shape->disableCollision(); SceneObject *mount = shape->getObjectMount(); if (mount) mount->disableCollision(); bool los = !gClientContainer.castRay(camPos, shapePos,losMask, &info); shape->enableCollision(); if (mount) mount->enableCollision(); if (!los) continue; // Project the shape pos into screen space and calculate // the distance opacity used to fade the labels into the // distance. Point3F projPnt; shapePos.z += mVerticalOffset; if (!parent->project(shapePos, &projPnt)) continue; F32 opacity = (shapeDist < fadeDistance)? 1.0: 1.0 - (shapeDist - fadeDistance) / (visDistance - fadeDistance); // Render the shape's name drawName(Point2I((S32)projPnt.x, (S32)projPnt.y),shape->getShapeName(),opacity); } } } } // This section renders all text added by afxGuiText effects. for (S32 i = 0; i < text_items.size(); i++) { HudTextSpec* spec = &text_items[i]; if (spec->text && spec->text[0] != '\0') { VectorF shapeDir = spec->pos - camPos; // do range test F32 shapeDist = shapeDir.lenSquared(); if (shapeDist == 0 || shapeDist > visDistanceSqr) continue; shapeDist = mSqrt(shapeDist); // Test to see if it's within our viewcone, this test doesn't // actually match the viewport very well, should consider // projection and box test. shapeDir.normalize(); F32 dot = mDot(shapeDir, camDir); if (dot < camFovCos) continue; // Test to see if it's behind something, and we want to // ignore anything it's mounted on when we run the LOS. RayInfo info; if (spec->obj) spec->obj->disableCollision(); bool los = !gClientContainer.castRay(camPos, spec->pos, losMask, &info); if (spec->obj) spec->obj->enableCollision(); if (!los) continue; // Project the shape pos into screen space. Point3F projPnt; if (!parent->project(spec->pos, &projPnt)) continue; // Calculate the distance opacity used to fade text into the distance. F32 opacity = (shapeDist < fadeDistance)? 1.0 : 1.0 - (shapeDist - fadeDistance) / (25.0f); if (opacity > 0.01f) drawName(Point2I((S32)projPnt.x, (S32)projPnt.y), spec->text, opacity, &spec->text_clr); } } // Restore control object collision if (!mEnableControlObjectOcclusion) control->enableCollision(); // Border last if (mShowFrame) GFX->getDrawUtil()->drawRect(updateRect, mFrameColor.toColorI()); reset(); }
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; }
void SceneManager::_renderScene( SceneRenderState* state, U32 objectMask, SceneZoneSpace* baseObject, U32 baseZone ) { AssertFatal( this == gClientSceneGraph, "SceneManager::_buildSceneGraph - Only the client scenegraph can support this call!" ); PROFILE_SCOPE( SceneGraph_batchRenderImages ); // In the editor, override the type mask for diffuse passes. if( gEditingMission && state->isDiffusePass() ) objectMask = EDITOR_RENDER_TYPEMASK; // Update the zoning state and traverse zones. if( getZoneManager() ) { // Update. getZoneManager()->updateZoningState(); // If zone culling isn't disabled, traverse the // zones now. if( !state->getCullingState().disableZoneCulling() ) { // Find the start zone if we haven't already. if( !baseObject ) { getZoneManager()->findZone( state->getCameraPosition(), baseObject, baseZone ); AssertFatal( baseObject != NULL, "SceneManager::_renderScene - findZone() did not return an object" ); } // Traverse zones starting in base object. SceneTraversalState traversalState( &state->getCullingState() ); PROFILE_START( Scene_traverseZones ); baseObject->traverseZones( &traversalState, baseZone ); PROFILE_END(); // Set the scene render box to the area we have traversed. state->setRenderArea( traversalState.getTraversedArea() ); } } // Set the query box for the container query. Never // make it larger than the frustum's AABB. In the editor, // always query the full frustum as that gives objects // the opportunity to render editor visualizations even if // they are otherwise not in view. if( !state->getFrustum().getBounds().isOverlapped( state->getRenderArea() ) ) { // This handles fringe cases like flying backwards into a zone where you // end up pretty much standing on a zone border and looking directly into // its "walls". In that case the traversal area will be behind the frustum // (remember that the camera isn't where visibility starts, it's the near // distance). return; } Box3F queryBox = state->getFrustum().getBounds(); if( !gEditingMission ) { queryBox.minExtents.setMax( state->getRenderArea().minExtents ); queryBox.maxExtents.setMin( state->getRenderArea().maxExtents ); } PROFILE_START( Scene_cullObjects ); //TODO: We should split the codepaths here based on whether the outdoor zone has visible space. // If it has, we should use the container query-based path. // If it hasn't, we should fill the object list directly from the zone lists which will usually // include way fewer objects. // Gather all objects that intersect the scene render box. mBatchQueryList.clear(); getContainer()->findObjectList( queryBox, objectMask, &mBatchQueryList ); // Cull the list. U32 numRenderObjects = state->getCullingState().cullObjects( mBatchQueryList.address(), mBatchQueryList.size(), !state->isDiffusePass() ? SceneCullingState::CullEditorOverrides : 0 // Keep forced editor stuff out of non-diffuse passes. ); //HACK: If the control object is a Player and it is not in the render list, force // it into it. This really should be solved by collision bounds being separate from // object bounds; only because the Player class is using bounds not encompassing // the actual player object is it that we have this problem in the first place. // Note that we are forcing the player object into ALL passes here but such // is the power of proliferation of things done wrong. GameConnection* connection = GameConnection::getConnectionToServer(); if( connection ) { Player* player = dynamic_cast< Player* >( connection->getControlObject() ); if( player ) { mBatchQueryList.setSize( numRenderObjects ); if( !mBatchQueryList.contains( player ) ) { mBatchQueryList.push_back( player ); numRenderObjects ++; } } } PROFILE_END(); // Render the remaining objects. PROFILE_START( Scene_renderObjects ); state->renderObjects( mBatchQueryList.address(), numRenderObjects ); PROFILE_END(); // Render bounding boxes, if enabled. if( smRenderBoundingBoxes && state->isDiffusePass() ) { GFXDEBUGEVENT_SCOPE( Scene_renderBoundingBoxes, ColorI::WHITE ); GameBase* cameraObject = 0; if( connection ) cameraObject = connection->getCameraObject(); GFXStateBlockDesc desc; desc.setFillModeWireframe(); desc.setZReadWrite( true, false ); for( U32 i = 0; i < numRenderObjects; ++ i ) { SceneObject* object = mBatchQueryList[ i ]; // Skip global bounds object. if( object->isGlobalBounds() ) continue; // Skip camera object as we're viewing the scene from it. if( object == cameraObject ) continue; const Box3F& worldBox = object->getWorldBox(); GFX->getDrawUtil()->drawObjectBox( desc, Point3F( worldBox.len_x(), worldBox.len_y(), worldBox.len_z() ), worldBox.getCenter(), MatrixF::Identity, ColorI::WHITE ); } } }
void StdServerProcessList::onTickObject( ProcessObject *pobj ) { PROFILE_SCOPE( StdServerProcessList_OnTickObject ); // Each object is either advanced a single tick, or if it's // being controlled by a client, ticked once for each pending move. GameConnection *con = pobj->getControllingClient(); if ( pobj->mIsGameObject && con && con->getControlObject() == pobj ) { // In case the object is deleted during its own tick. SimObjectPtr<GameObject> obj = getGameObject( pobj ); Move* movePtr; U32 m, numMoves; con->mMoveList->getMoves( &movePtr, &numMoves ); // For debugging it can be useful to know when this happens. //if ( numMoves > 1 ) // Con::printf( "numMoves: %i", numMoves ); // Do we really need to test the control object each iteration? Does it change? for ( m = 0; m < numMoves && con && con->getControlObject() == obj; m++, movePtr++ ) { #ifdef TORQUE_DEBUG_NET_MOVES U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum(obj->getControllingClient()); #endif if ( obj->isTicking() ) obj->processMove( movePtr ); if ( con && con->getControlObject() == obj ) { U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() ); // check move checksum if ( movePtr->checksum != newsum ) { #ifdef TORQUE_DEBUG_NET_MOVES if( !obj->isAIControlled() ) Con::printf("move %i checksum disagree: %i != %i, (start %i), (move %f %f %f)", movePtr->id, movePtr->checksum,newsum,sum,movePtr->yaw,movePtr->y,movePtr->z); #endif movePtr->checksum = Move::ChecksumMismatch; } else { #ifdef TORQUE_DEBUG_NET_MOVES Con::printf("move %i checksum agree: %i == %i, (start %i), (move %f %f %f)", movePtr->id, movePtr->checksum,newsum,sum,movePtr->yaw,movePtr->y,movePtr->z); #endif } } } con->mMoveList->clearMoves( m ); } else if ( pobj->isTicking() ) pobj->processMove( 0 ); }
void LightFlareData::prepRender( SceneState *state, LightFlareState *flareState ) { PROFILE_SCOPE( LightFlareData_prepRender ); // No elements then nothing to render. if ( mElementCount == 0 ) return; // We need these all over the place later. const Point3F &camPos = state->getCameraPosition(); const RectI &viewport = GFX->getViewport(); const Point3F &lightPos = flareState->lightMat.getPosition(); LightInfo *lightInfo = flareState->lightInfo; bool isVectorLight = lightInfo->getType() == LightInfo::Vector; // Perform visibility testing on the light... // Project the light position from world to screen space, we need this // position later, and it tells us if it is actually onscreen. Point3F lightPosSS; bool onscreen = MathUtils::mProjectWorldToScreen( lightPos, &lightPosSS, viewport, GFX->getWorldMatrix(), gClientSceneGraph->getNonClipProjection() ); U32 visDelta = U32_MAX; U32 fadeOutTime = 20; U32 fadeInTime = 125; // Fade factor based on amount of occlusion. F32 occlusionFade = 1.0f; bool lightVisible = true; if ( !state->isReflectPass() ) { // It is onscreen, so raycast as a simple occlusion test. U32 losMask = STATIC_COLLISION_MASK | ShapeBaseObjectType | StaticTSObjectType | ItemObjectType | PlayerObjectType; GameConnection *conn = GameConnection::getConnectionToServer(); if ( !conn ) return; 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 ( flareState->occlusionQuery == NULL ) flareState->occlusionQuery = GFX->createOcclusionQuery(); if ( flareState->fullPixelQuery == NULL ) flareState->fullPixelQuery = GFX->createOcclusionQuery(); if ( flareState->occlusionQuery && ( ( isVectorLight && flareState->worldRadius > 0.0f ) || ( !isVectorLight && mOcclusionRadius > 0.0f ) ) ) { // Always treat light as onscreen if using HOQ // it will be faded out if offscreen anyway. onscreen = true; U32 pixels = -1; GFXOcclusionQuery::OcclusionQueryStatus status = flareState->occlusionQuery->getStatus( true, &pixels ); String str = flareState->occlusionQuery->statusToString( status ); Con::setVariable( "$Flare::OcclusionStatus", str.c_str() ); Con::setIntVariable( "$Flare::OcclusionVal", pixels ); if ( status == GFXOcclusionQuery::Occluded ) occlusionFade = 0.0f; if ( status != GFXOcclusionQuery::Unset ) needsRaycast = false; RenderPassManager *pass = state->getRenderPass(); OccluderRenderInst *ri = pass->allocInst<OccluderRenderInst>(); Point3F scale( Point3F::One ); if ( isVectorLight && flareState->worldRadius > 0.0f ) scale *= flareState->worldRadius; else scale *= mOcclusionRadius; ri->type = RenderPassManager::RIT_Occluder; ri->query = flareState->occlusionQuery; ri->query2 = flareState->fullPixelQuery; ri->position = lightPos; ri->scale = scale; ri->orientation = pass->allocUniqueXform( lightInfo->getTransform() ); ri->isSphere = true; state->getRenderPass()->addInst( ri ); if ( status == GFXOcclusionQuery::NotOccluded ) { U32 fullPixels; flareState->fullPixelQuery->getStatus( true, &fullPixels ); occlusionFade = (F32)pixels / (F32)fullPixels; // Approximation of the full pixel count rather than doing // two queries, but it is not very accurate. /* F32 dist = ( camPos - lightPos ).len(); F32 radius = scale.x; radius = ( radius / dist ) * state->getWorldToScreenScale().y; occlusionFade = (F32)pixels / (4.0f * radius * radius); occlusionFade = mClampF( occlusionFade, 0.0f, 1.0f ); */ } } Con::setFloatVariable( "$Flare::OcclusionFade", occlusionFade ); if ( needsRaycast ) { // Use a raycast to determine occlusion. bool fps = conn->isFirstPerson(); GameBase *control = conn->getControlObject(); if ( control && fps ) control->disableCollision(); RayInfo rayInfo; if ( gClientContainer.castRayRendered( camPos, lightPos, losMask, &rayInfo ) ) occlusionFade = 0.0f; if ( control && fps ) control->enableCollision(); } lightVisible = onscreen && occlusionFade > 0.0f; // To perform a fade in/out when we gain or lose visibility // we must update/store the visibility state and time. U32 currentTime = Sim::getCurrentTime(); if ( lightVisible != flareState->visible ) { flareState->visible = lightVisible; flareState->visChangedTime = currentTime; } // Save this in the state so that we have it during the reflect pass. flareState->occlusion = occlusionFade; visDelta = currentTime - flareState->visChangedTime; } else // state->isReflectPass() { occlusionFade = flareState->occlusion; lightVisible = flareState->visible; visDelta = Sim::getCurrentTime() - flareState->visChangedTime; } // We can only skip rendering if the light is not visible, and it // has elapsed the fadeOutTime. if ( !lightVisible && visDelta > fadeOutTime ) return; // In a reflection we only render the elements with zero distance. U32 elementCount = mElementCount; if ( state->isReflectPass() ) { elementCount = 0; for ( ; elementCount < mElementCount; elementCount++ ) { if ( mElementDist[elementCount] > 0.0f ) break; } } if ( elementCount == 0 ) return; // A bunch of preparatory math before generating verts... const Point2I &vpExtent = viewport.extent; Point3F viewportExtent( vpExtent.x, vpExtent.y, 1.0f ); Point2I halfViewportExtentI( viewport.extent / 2 ); Point3F halfViewportExtentF( (F32)halfViewportExtentI.x * 0.5f, (F32)halfViewportExtentI.y, 0.0f ); Point3F screenCenter( 0,0,0 ); Point3F oneOverViewportExtent( 1.0f / viewportExtent.x, 1.0f / viewportExtent.y, 1.0f ); lightPosSS.y -= viewport.point.y; lightPosSS *= oneOverViewportExtent; lightPosSS = ( lightPosSS * 2.0f ) - Point3F::One; lightPosSS.y = -lightPosSS.y; lightPosSS.z = 0.0f; Point3F flareVec( screenCenter - lightPosSS ); F32 flareLength = flareVec.len(); flareVec.normalizeSafe(); Point3F basePoints[4]; basePoints[0] = Point3F( -0.5, 0.5, 0.0 ); basePoints[1] = Point3F( -0.5, -0.5, 0.0 ); basePoints[2] = Point3F( 0.5, -0.5, 0.0 ); basePoints[3] = Point3F( 0.5, 0.5, 0.0 ); Point3F rotatedBasePoints[4]; rotatedBasePoints[0] = basePoints[0]; rotatedBasePoints[1] = basePoints[1]; rotatedBasePoints[2] = basePoints[2]; rotatedBasePoints[3] = basePoints[3]; Point3F fvec( -1, 0, 0 ); F32 rot = mAcos( mDot( fvec, flareVec ) ); Point3F rvec( 0, -1, 0 ); rot *= mDot( rvec, flareVec ) > 0.0f ? 1.0f : -1.0f; vectorRotateZAxis( rotatedBasePoints[0], rot ); vectorRotateZAxis( rotatedBasePoints[1], rot ); vectorRotateZAxis( rotatedBasePoints[2], rot ); vectorRotateZAxis( rotatedBasePoints[3], rot ); // 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; // Scale based on world space distance from camera to light source. F32 lightSourceWSDistanceScale = ( isVectorLight ) ? 1.0f : getMin( 10.0f / ( lightPos - camPos ).len(), 1.5f ); // Scale based on screen space distance from screen position of light source to the screen center. F32 lightSourceSSDistanceScale = ( 1.5f - ( lightPosSS - screenCenter ).len() ) / 1.5f; // Scale based on recent visibility changes, fading in or out. F32 fadeInOutScale = 1.0f; if ( lightVisible && visDelta < fadeInTime && flareState->occlusion ) 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; // The baseColor which modulates the color of all elements. ColorF baseColor; if ( flareState->fullBrightness == 0.0f ) baseColor = ColorF::BLACK; else // These are the factors which affect the "alpha" of the flare effect. // Modulate more in as appropriate. baseColor = ColorF::WHITE * lightSourceBrightnessScale * occlusionFade; // Fill in the vertex buffer... const U32 vertCount = 4 * elementCount; if ( flareState->vertBuffer.isNull() || flareState->vertBuffer->mNumVerts != vertCount ) flareState->vertBuffer.set( GFX, vertCount, GFXBufferTypeDynamic ); GFXVertexPCT *pVert = flareState->vertBuffer.lock(); const Point2I &widthHeightI = mFlareTexture.getWidthHeight(); Point2F oneOverTexSize( 1.0f / (F32)widthHeightI.x, 1.0f / (F32)widthHeightI.y ); for ( U32 i = 0; i < elementCount; i++ ) { Point3F *basePos = mElementRotate[i] ? rotatedBasePoints : basePoints; ColorF elementColor( baseColor * mElementTint[i] ); if ( mElementUseLightColor[i] ) elementColor *= lightInfo->getColor(); Point3F elementPos; elementPos = lightPosSS + flareVec * mElementDist[i] * flareLength; elementPos.z = 0.0f; F32 maxDist = 1.5f; F32 elementDist = mSqrt( ( elementPos.x * elementPos.x ) + ( elementPos.y * elementPos.y ) ); F32 distanceScale = ( maxDist - elementDist ) / maxDist; distanceScale = 1.0f; const RectF &elementRect = mElementRect[i]; Point3F elementSize( elementRect.extent.x, elementRect.extent.y, 1.0f ); elementSize *= mElementScale[i] * distanceScale * mScale * lightSourceIntensityScale; if ( elementSize.x < 100.0f ) { F32 alphaScale = mPow( elementSize.x / 100.0f, 2 ); elementColor *= alphaScale; } elementColor.clamp(); Point2F texCoordMin, texCoordMax; texCoordMin = elementRect.point * oneOverTexSize; texCoordMax = ( elementRect.point + elementRect.extent ) * oneOverTexSize; pVert->color = elementColor; pVert->point = ( basePos[0] * elementSize * oneOverViewportExtent ) + elementPos; pVert->texCoord.set( texCoordMin.x, texCoordMax.y ); pVert++; pVert->color = elementColor; pVert->point = ( basePos[1] * elementSize * oneOverViewportExtent ) + elementPos; pVert->texCoord.set( texCoordMax.x, texCoordMax.y ); pVert++; pVert->color = elementColor; pVert->point = ( basePos[2] * elementSize * oneOverViewportExtent ) + elementPos; pVert->texCoord.set( texCoordMax.x, texCoordMin.y ); pVert++; pVert->color = elementColor; pVert->point = ( basePos[3] * elementSize * oneOverViewportExtent ) + elementPos; pVert->texCoord.set( texCoordMin.x, texCoordMin.y ); pVert++; } flareState->vertBuffer.unlock(); // Create and submit the render instance... RenderPassManager *renderManager = state->getRenderPass(); ParticleRenderInst *ri = renderManager->allocInst<ParticleRenderInst>(); ri->vertBuff = &flareState->vertBuffer; ri->primBuff = &mFlarePrimBuffer; ri->translucentSort = true; ri->type = RenderPassManager::RIT_Particle; ri->sortDistSq = ( lightPos - camPos ).lenSquared(); ri->modelViewProj = &MatrixF::Identity; ri->bbModelViewProj = ri->modelViewProj; ri->count = elementCount; // Only draw the light flare in high-res mode, never off-screen mode ri->systemState = ParticleRenderInst::AwaitingHighResDraw; ri->blendStyle = ParticleRenderInst::BlendGreyscale; ri->diffuseTex = &*(mFlareTexture); ri->softnessDistance = 1.0f; // Sort by texture too. ri->defaultKey = ri->diffuseTex ? (U32)ri->diffuseTex : (U32)ri->vertBuff; renderManager->addInst( ri ); }
void Etherform::processTick(const Move* move) { Parent::processTick(move); // Warp to catch up to server? if(delta.warpTicks > 0) { delta.warpTicks--; // Set new pos. getTransform().getColumn(3,&delta.pos); delta.pos += delta.warpOffset; delta.rot += delta.rotOffset; this->setPosition(delta.pos,delta.rot); this->setRenderPosition(delta.pos,delta.rot); // Backstepping delta.posVec.x = -delta.warpOffset.x; delta.posVec.y = -delta.warpOffset.y; delta.posVec.z = -delta.warpOffset.z; delta.rotVec.x = -delta.rotOffset.x; delta.rotVec.y = -delta.rotOffset.y; delta.rotVec.z = -delta.rotOffset.z; } else { // If there is no move, the we're either an // unattached etherform on the server, or another etherform's // client ghost. if(!move) { if(isGhost()) { // If we haven't run out of prediction time, // predict using the last known move. if (mPredictionCount-- <= 0) return; move = &delta.move; } else move = &NullMove; } if(isServerObject() || (didRenderLastRender() || getControllingClient())) { delta.move = *move; if(isMounted()) { // If we're mounted then do not perform any collision checks // and clear our previous working list. mConvex.clearWorkingList(); } else { this->updateWorkingCollisionSet(); } this->updateVelocity(move); VectorF contactNormal(0,0,0); this->findContact(&contactNormal ); this->updatePos(); } } // Add node to lasertrails. if(this->isClientObject()) { GameConnection* conn = GameConnection::getConnectionToServer(); if(conn && this == conn->getControlObject()) { if(move->sendCount == 0) this->addLaserTrailNode(this->getPosition()); } else this->addLaserTrailNode(this->getPosition()); } }
Move HoverPodController::getMove(ShapeBase* obj) { Move retMove = NullMove; GameConnection* client = NULL; if(!Sim::findObject(mClientId, client)) return retMove; ShapeBase* control = client->getControlObject(); if(control) { Move cMove = client->lastReceivedMove(); Point3F v1, v2; control->getTransform().getColumn(0, &v1); control->getTransform().getColumn(1, &v2); Point3F pv = v1*cMove.x + v2*cMove.y; //pv = mDirection * mDot(pv, mDirection); obj->getTransform().getColumn(0, &v1); obj->getTransform().getColumn(1, &v2); retMove.x = mDot(v1, pv); retMove.y = mDot(v2, pv); } return retMove; #if 0 if(this->isServerObject() && this->isMounted()) { Move mMove = NullMove; ShapeBase* mount = this->getObjectMount(); if(mount->getType() & VehicleObjectType) { Vehicle* vehicle = (Vehicle*)mount; if(move && move->x != 0) { mMove.yaw = move->x; } else { Point3F zv; vehicle->getTransform().getColumn(2, &zv); zv.normalize(); Point3F m = vehicle->getRigid().angMomentum; //Point3F v = vehicle->getRigid().angVelocity; F32 dot = mDot(zv, m); //Con::printf("%f / %f %f %f / %f %f %f", dot, // m.x, m.y, m.z, v.x, v.y, v.z); mMove.yaw = dot / 50; } if(move) mMove.y = move->y; } else if(move) { Point3F v1, v2; this->getTransform().getColumn(0, &v1); this->getTransform().getColumn(1, &v2); Point3F pv = v1*move->x + v2*move->y; mount->getTransform().getColumn(0, &v1); mount->getTransform().getColumn(1, &v2); mMove.x = mDot(v1, pv); mMove.y = mDot(v2, pv); } mount->processTick(&mMove); } #endif