//----------------------------------------------------------------------------- // Copyright (c) 2012 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #include "platform/platform.h" #include "T3D/gameFunctions.h" #include "T3D/gameBase/gameConnection.h" #include "T3D/camera.h" #include "T3D/sfx/sfx3DWorld.h" #include "console/consoleTypes.h" #include "gui/3d/guiTSControl.h" #include "core/util/journal/process.h" #include "materials/materialManager.h" #include "math/mEase.h" #include "core/module.h" #include "console/engineAPI.h" #include "platform/output/IDisplayDevice.h" static void RegisterGameFunctions(); static void Process3D(); MODULE_BEGIN( 3D ) MODULE_INIT_AFTER( Process ) MODULE_INIT_AFTER( Scene ) MODULE_SHUTDOWN_BEFORE( Process ) MODULE_SHUTDOWN_BEFORE( Sim ) MODULE_SHUTDOWN_AFTER( Scene ) MODULE_INIT { Process::notify(Process3D, PROCESS_TIME_ORDER); GameConnection::smFovUpdate.notify(GameSetCameraFov); RegisterGameFunctions(); } MODULE_SHUTDOWN { GameConnection::smFovUpdate.remove(GameSetCameraFov); Process::remove(Process3D); } MODULE_END; static S32 gEaseInOut = Ease::InOut; static S32 gEaseIn = Ease::In; static S32 gEaseOut = Ease::Out; static S32 gEaseLinear = Ease::Linear; static S32 gEaseQuadratic= Ease::Quadratic; static S32 gEaseCubic= Ease::Cubic; static S32 gEaseQuartic = Ease::Quartic; static S32 gEaseQuintic = Ease::Quintic; static S32 gEaseSinusoidal= Ease::Sinusoidal; static S32 gEaseExponential = Ease::Exponential; static S32 gEaseCircular = Ease::Circular; static S32 gEaseElastic = Ease::Elastic; static S32 gEaseBack = Ease::Back; static S32 gEaseBounce = Ease::Bounce; extern bool gEditingMission; extern void ShowInit(); //------------------------------------------------------------------------------ /// Camera and FOV info namespace CameraAndFOV{ const U32 MaxZoomSpeed = 2000; ///< max number of ms to reach target FOV static F32 sConsoleCameraFov = 90.f; ///< updated to camera FOV each frame static F32 sDefaultFov = 90.f; ///< normal FOV static F32 sCameraFov = 90.f; ///< current camera FOV static F32 sTargetFov = 90.f; ///< the desired FOV static F32 sLastCameraUpdateTime = 0; ///< last time camera was updated static S32 sZoomSpeed = 500; ///< ms per 90deg fov change /// A scale to apply to the normal visible distance /// typically used for tuning performance. static F32 sVisDistanceScale = 1.0f; } // namespace {} // query static SimpleQueryList sgServerQueryList; static U32 sgServerQueryIndex = 0; //SERVER FUNCTIONS ONLY ConsoleFunctionGroupBegin( Containers, "Spatial query functions. Server side only!"); DefineConsoleFunction( containerFindFirst, const char*, (U32 typeMask, Point3F origin, Point3F size), , "(int mask, Point3F point, float x, float y, float z)" "@brief Find objects matching the bitmask type within a box centered at point, with extents x, y, z.\n\n" "@returns The first object found, or an empty string if nothing was found. Thereafter, you can get more " "results using containerFindNext()." "@see containerFindNext\n" "@ingroup Game") { //find out what we're looking for //build the container volume Box3F queryBox; queryBox.minExtents = origin; queryBox.maxExtents = origin; queryBox.minExtents -= size; queryBox.maxExtents += size; //initialize the list, and do the query sgServerQueryList.mList.clear(); gServerContainer.findObjects(queryBox, typeMask, SimpleQueryList::insertionCallback, &sgServerQueryList); //return the first element sgServerQueryIndex = 0; static const U32 bufSize = 100; char *buff = Con::getReturnBuffer(bufSize); if (sgServerQueryList.mList.size()) dSprintf(buff, bufSize, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId()); else buff[0] = '\0'; return buff; } DefineConsoleFunction( containerFindNext, const char*, (), , "()" "@brief Get more results from a previous call to containerFindFirst().\n\n" "@note You must call containerFindFirst() to begin the search.\n" "@returns The next object found, or an empty string if nothing else was found.\n" "@see containerFindFirst()\n" "@ingroup Game") { //return the next element static const U32 bufSize = 100; char *buff = Con::getReturnBuffer(bufSize); if (sgServerQueryIndex < sgServerQueryList.mList.size()) dSprintf(buff, bufSize, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId()); else buff[0] = '\0'; return buff; } ConsoleFunctionGroupEnd( Containers ); //------------------------------------------------------------------------------ bool GameGetCameraTransform(MatrixF *mat, Point3F *velocity) { // Return the position and velocity of the control object GameConnection* connection = GameConnection::getConnectionToServer(); return connection && connection->getControlCameraTransform(0, mat) && connection->getControlCameraVelocity(velocity); } //------------------------------------------------------------------------------ DefineEngineFunction( setDefaultFov, void, ( F32 defaultFOV ),, "@brief Set the default FOV for a camera.\n" "@param defaultFOV The default field of view in degrees\n" "@ingroup CameraSystem") { CameraAndFOV::sDefaultFov = mClampF(defaultFOV, MinCameraFov, MaxCameraFov); if(CameraAndFOV::sCameraFov == CameraAndFOV::sTargetFov) CameraAndFOV::sTargetFov = CameraAndFOV::sDefaultFov; } DefineEngineFunction( setZoomSpeed, void, ( S32 speed ),, "@brief Set the zoom speed of the camera.\n" "This affects how quickly the camera changes from one field of view " "to another.\n" "@param speed The camera's zoom speed in ms per 90deg FOV change\n" "@ingroup CameraSystem") { CameraAndFOV::sZoomSpeed = mClamp(speed, 0, CameraAndFOV::MaxZoomSpeed); } DefineEngineFunction( setFov, void, ( F32 FOV ),, "@brief Set the FOV of the camera.\n" "@param FOV The camera's new FOV in degrees\n" "@ingroup CameraSystem") { CameraAndFOV::sTargetFov = mClampF(FOV, MinCameraFov, MaxCameraFov); } F32 GameGetCameraFov() { return(CameraAndFOV::sCameraFov); } void GameSetCameraFov(F32 fov) { CameraAndFOV::sTargetFov = CameraAndFOV::sCameraFov = fov; } void GameSetCameraTargetFov(F32 fov) { CameraAndFOV::sTargetFov = fov; } void GameUpdateCameraFov() { F32 time = F32(Platform::getVirtualMilliseconds()); // need to update fov? if(CameraAndFOV::sTargetFov != CameraAndFOV::sCameraFov) { F32 delta = time - CameraAndFOV::sLastCameraUpdateTime; // snap zoom? if((CameraAndFOV::sZoomSpeed == 0) || (delta <= 0.0f)) CameraAndFOV::sCameraFov = CameraAndFOV::sTargetFov; else { // gZoomSpeed is time in ms to zoom 90deg F32 step = 90.f * (delta / F32(CameraAndFOV::sZoomSpeed)); if(CameraAndFOV::sCameraFov > CameraAndFOV::sTargetFov) { CameraAndFOV::sCameraFov -= step; if(CameraAndFOV::sCameraFov < CameraAndFOV::sTargetFov) CameraAndFOV::sCameraFov = CameraAndFOV::sTargetFov; } else { CameraAndFOV::sCameraFov += step; if(CameraAndFOV::sCameraFov > CameraAndFOV::sTargetFov) CameraAndFOV::sCameraFov = CameraAndFOV::sTargetFov; } } } // the game connection controls the vertical and the horizontal GameConnection * connection = GameConnection::getConnectionToServer(); if(connection) { // check if fov is valid on control object if(connection->isValidControlCameraFov(CameraAndFOV::sCameraFov)) connection->setControlCameraFov(CameraAndFOV::sCameraFov); else { // will set to the closest fov (fails only on invalid control object) if(connection->setControlCameraFov(CameraAndFOV::sCameraFov)) { F32 setFov = CameraAndFOV::sCameraFov; connection->getControlCameraFov(&setFov); CameraAndFOV::sTargetFov =CameraAndFOV::sCameraFov = setFov; } } } // update the console variable CameraAndFOV::sConsoleCameraFov = CameraAndFOV::sCameraFov; CameraAndFOV::sLastCameraUpdateTime = time; } //-------------------------------------------------------------------------- #ifdef TORQUE_DEBUG // ConsoleFunction(dumpTSShapes, void, 1, 1, "dumpTSShapes();") // { // argc, argv; // FindMatch match("*.dts", 4096); // gResourceManager->findMatches(&match); // for (U32 i = 0; i < match.numMatches(); i++) // { // U32 j; // Resource shape = ResourceManager::get().load(match.matchList[i]); // if (bool(shape) == false) // Con::errorf(" aaa Couldn't load: %s", match.matchList[i]); // U32 numMeshes = 0, numSkins = 0; // for (j = 0; j < shape->meshes.size(); j++) // if (shape->meshes[j]) // numMeshes++; // for (j = 0; j < shape->skins.size(); j++) // if (shape->skins[j]) // numSkins++; // Con::printf(" aaa Shape: %s (%d meshes, %d skins)", match.matchList[i], numMeshes, numSkins); // Con::printf(" aaa Meshes"); // for (j = 0; j < shape->meshes.size(); j++) // { // if (shape->meshes[j]) // Con::printf(" aaa %d -> nf: %d, nmf: %d, nvpf: %d (%d, %d, %d, %d, %d)", // shape->meshes[j]->meshType & TSMesh::TypeMask, // shape->meshes[j]->numFrames, // shape->meshes[j]->numMatFrames, // shape->meshes[j]->vertsPerFrame, // shape->meshes[j]->verts.size(), // shape->meshes[j]->norms.size(), // shape->meshes[j]->tverts.size(), // shape->meshes[j]->primitives.size(), // shape->meshes[j]->indices.size()); // } // Con::printf(" aaa Skins"); // for (j = 0; j < shape->skins.size(); j++) // { // if (shape->skins[j]) // Con::printf(" aaa %d -> nf: %d, nmf: %d, nvpf: %d (%d, %d, %d, %d, %d)", // shape->skins[j]->meshType & TSMesh::TypeMask, // shape->skins[j]->numFrames, // shape->skins[j]->numMatFrames, // shape->skins[j]->vertsPerFrame, // shape->skins[j]->verts.size(), // shape->skins[j]->norms.size(), // shape->skins[j]->tverts.size(), // shape->skins[j]->primitives.size(), // shape->skins[j]->indices.size()); // } // } // } #endif bool GameProcessCameraQuery(CameraQuery *query) { GameConnection* connection = GameConnection::getConnectionToServer(); if (connection && connection->getControlCameraTransform(0.032f, &query->cameraMatrix)) { query->object = dynamic_cast(connection->getControlObject()); query->nearPlane = gClientSceneGraph->getNearClip(); // Scale the normal visible distance by the performance // tuning scale which we never let over 1. CameraAndFOV::sVisDistanceScale = mClampF( CameraAndFOV::sVisDistanceScale, 0.01f, 1.0f ); query->farPlane = gClientSceneGraph->getVisibleDistance() * CameraAndFOV::sVisDistanceScale; // Provide some default values query->projectionOffset = Point2F::Zero; query->eyeOffset = Point3F::Zero; F32 cameraFov = 0.0f; bool fovSet = false; // Try to use the connection's display deivce, if any, but only if the editor // is not open if(!gEditingMission && connection->hasDisplayDevice()) { const IDisplayDevice* display = connection->getDisplayDevice(); // The connection's display device may want to set the FOV if(display->providesYFOV()) { cameraFov = mRadToDeg(display->getYFOV()); fovSet = true; } // The connection's display device may want to set the projection offset if(display->providesProjectionOffset()) { query->projectionOffset = display->getProjectionOffset(); } // The connection's display device may want to set the eye offset if(display->providesEyeOffset()) { query->eyeOffset = display->getEyeOffset(); } } // Use the connection's FOV settings if requried if(!fovSet && !connection->getControlCameraFov(&cameraFov)) { return false; } query->fov = mDegToRad(cameraFov); return true; } return false; } void GameRenderWorld() { PROFILE_START(GameRenderWorld); FrameAllocator::setWaterMark(0); gClientSceneGraph->renderScene( SPT_Diffuse ); // renderScene leaves some states dirty, which causes problems if GameTSCtrl is the last Gui object rendered GFX->updateStates(); AssertFatal(FrameAllocator::getWaterMark() == 0, "Error, someone didn't reset the water mark on the frame allocator!"); FrameAllocator::setWaterMark(0); PROFILE_END(); } static void Process3D() { MATMGR->updateTime(); // Update the SFX world, if there is one. if( gSFX3DWorld ) gSFX3DWorld->update(); } static void RegisterGameFunctions() { Con::addVariable( "$pref::Camera::distanceScale", TypeF32, &CameraAndFOV::sVisDistanceScale, "A scale to apply to the normal visible distance, typically used for tuning performance.\n" "@ingroup Game"); Con::addVariable( "$cameraFov", TypeF32, &CameraAndFOV::sConsoleCameraFov, "The camera's Field of View.\n\n" "@ingroup Game" ); // Stuff game types into the console Con::setIntVariable("$TypeMasks::StaticObjectType", StaticObjectType); Con::setIntVariable("$TypeMasks::EnvironmentObjectType", EnvironmentObjectType); Con::setIntVariable("$TypeMasks::TerrainObjectType", TerrainObjectType); Con::setIntVariable("$TypeMasks::WaterObjectType", WaterObjectType); Con::setIntVariable("$TypeMasks::TriggerObjectType", TriggerObjectType); Con::setIntVariable("$TypeMasks::MarkerObjectType", MarkerObjectType); Con::setIntVariable("$TypeMasks::GameBaseObjectType", GameBaseObjectType); Con::setIntVariable("$TypeMasks::ShapeBaseObjectType", ShapeBaseObjectType); Con::setIntVariable("$TypeMasks::CameraObjectType", CameraObjectType); Con::setIntVariable("$TypeMasks::StaticShapeObjectType", StaticShapeObjectType); Con::setIntVariable("$TypeMasks::DynamicShapeObjectType", DynamicShapeObjectType); Con::setIntVariable("$TypeMasks::PlayerObjectType", PlayerObjectType); Con::setIntVariable("$TypeMasks::ItemObjectType", ItemObjectType); Con::setIntVariable("$TypeMasks::VehicleObjectType", VehicleObjectType); Con::setIntVariable("$TypeMasks::VehicleBlockerObjectType", VehicleBlockerObjectType); Con::setIntVariable("$TypeMasks::ProjectileObjectType", ProjectileObjectType); Con::setIntVariable("$TypeMasks::ExplosionObjectType", ExplosionObjectType); Con::setIntVariable("$TypeMasks::CorpseObjectType", CorpseObjectType); Con::setIntVariable("$TypeMasks::DebrisObjectType", DebrisObjectType); Con::setIntVariable("$TypeMasks::PhysicalZoneObjectType", PhysicalZoneObjectType); Con::setIntVariable("$TypeMasks::LightObjectType", LightObjectType); Con::addVariable("Ease::InOut", TypeS32, &gEaseInOut, "InOut ease for curve movement.\n" "@ingroup Game"); Con::addVariable("Ease::In", TypeS32, &gEaseIn, "In ease for curve movement.\n" "@ingroup Game"); Con::addVariable("Ease::Out", TypeS32, &gEaseOut, "Out ease for curve movement.\n" "@ingroup Game"); Con::addVariable("Ease::Linear", TypeS32, &gEaseLinear, "Linear ease for curve movement.\n" "@ingroup Game"); Con::addVariable("Ease::Quadratic", TypeS32, &gEaseQuadratic, "Quadratic ease for curve movement.\n" "@ingroup Game"); Con::addVariable("Ease::Cubic", TypeS32, &gEaseCubic, "Cubic ease for curve movement.\n" "@ingroup Game"); Con::addVariable("Ease::Quartic", TypeS32, &gEaseQuartic, "Quartic ease for curve movement.\n" "@ingroup Game"); Con::addVariable("Ease::Quintic", TypeS32, &gEaseQuintic, "Quintic ease for curve movement.\n" "@ingroup Game"); Con::addVariable("Ease::Sinusoidal", TypeS32, &gEaseSinusoidal, "Sinusoidal ease for curve movement.\n" "@ingroup Game"); Con::addVariable("Ease::Exponential", TypeS32, &gEaseExponential, "Exponential ease for curve movement.\n" "@ingroup Game"); Con::addVariable("Ease::Circular", TypeS32, &gEaseCircular, "Circular ease for curve movement.\n" "@ingroup Game"); Con::addVariable("Ease::Elastic", TypeS32, &gEaseElastic, "Elastic ease for curve movement.\n" "@ingroup Game"); Con::addVariable("Ease::Back", TypeS32, &gEaseBack, "Backwards ease for curve movement.\n" "@ingroup Game"); Con::addVariable("Ease::Bounce", TypeS32, &gEaseBounce, "Bounce ease for curve movement.\n" "@ingroup Game"); }