From a7e8ae591a6069e861a4e02b814dac00206c4810 Mon Sep 17 00:00:00 2001 From: Ben Boudaoud Date: Mon, 20 Apr 2020 18:07:48 -0400 Subject: [PATCH 1/8] First-pass changes for playerCamera --- docs/scene.md | 48 ++++++++++++++ source/App.cpp | 153 ++++++++++++++++++++++++++------------------- source/App.h | 4 ++ source/Session.cpp | 1 + source/Weapon.cpp | 20 +++--- source/Weapon.h | 18 +++--- 6 files changed, 161 insertions(+), 83 deletions(-) create mode 100644 docs/scene.md diff --git a/docs/scene.md b/docs/scene.md new file mode 100644 index 00000000..ec7d351f --- /dev/null +++ b/docs/scene.md @@ -0,0 +1,48 @@ +# Scene Files Requirements +First Person Science uses a modified version of the G3D `scene.any` file specification modified to include a few required fields. + +## Additional Fields +In addition to the base `.scene.any` file, the following fields must/can be provided for a scene file to be considered valid for FPSci: + +### Physics +Two "physics-based" parameters are also provided by the scene file in their own `Physics` header, these are: + +* `minHeight` - A height at which the player is respawned (at their spawn location) if the fall below +* `gravity` - Currently unused... + +An example of this configuration (from within the top-level of a `scene.Any` file) is provided below: + +``` +Physics = { + minHeight = -10.0; +}, +``` + +### Player Entity +The `PlayerEntity` is specified by the following sub-fields: + +* `model` - A model to display for the player (currently not drawn) +* `frame` - An initial frame for the player (in the scene geometry coordinate units) +* `heading` - An (independent) initial heading for the player (in radians) +* `collisionSphere` - A `Sphere` to be used as a collision proxy for the player (normally used to specify the radius of the proxy in scene units) + +The parameters above allow the scene file to completely contain all scene-dependent information related to player motion. An example from the top-level of a `scene.any` file is provided below: + +``` +player = PlayerEntity { + model = "playerModel"; // Use the model (specifed below as "playerModel") for the player + frame = Point3(0, 1.5, 0); // Initialize the player 1.5m above the origin + heading = 0; // Player initial rotation of 0 radians + collisionSphere = Sphere(1.0); // Use a 1m sphere as the collision proxy +}; +``` + +### Player Camera +The player camera may be specified as with any other camera in a `scene.any` file, but is required to have the name `playerCamera` as it's name to specify it as the camera used for the player view. If no player camera is provided in the scene file, but a `PlayerEntity` is provided, the application creates the player camera from the player entity. + +The player camera is a way to modify camera properties for the player view, excluding the following properties which are overriden in the application: + +* Anti-aliasing +* Bloom strength +* Position/Rotation +* Field of View \ No newline at end of file diff --git a/source/App.cpp b/source/App.cpp index 0b55ccac..35301b84 100644 --- a/source/App.cpp +++ b/source/App.cpp @@ -64,9 +64,11 @@ void App::onInit() { // Setup the scene setScene(PhysicsScene::create(m_ambientOcclusion)); scene()->registerEntitySubclass("PlayerEntity", &PlayerEntity::create); // Register the player entity for creation - scene()->registerEntitySubclass("FlyingEntity", &FlyingEntity::create); // Create a target - m_weapon = Weapon::create(std::make_shared(experimentConfig.weapon), scene(), activeCamera(), &m_projectileArray); + // Re-create the developer window + developerWindow->close(); + developerWindow.reset(); + createDeveloperHUD(); // Setup the GUI showRenderingStats = false; @@ -162,10 +164,6 @@ void App::loadDecals() { } void App::loadModels() { - if ((experimentConfig.weapon.renderModel || startupConfig.developerMode) && !experimentConfig.weapon.modelSpec.filename.empty()) { - // Load the model if we (might) need it - m_weapon->loadModels(); - } // Add all the unqiue targets to this list Table targetsToBuild; @@ -470,6 +468,48 @@ void App::updateParameters(int frameDelay, float frameRate) { setFrameDuration(dt, GApp::REAL_TIME); } +shared_ptr App::updatePlayer() { + // Pick between experiment and session settings + Vector3 grav = experimentConfig.player.gravity; + float FoV = experimentConfig.render.hFoV; + if (sessConfig != nullptr) { + grav = sessConfig->player.gravity; + FoV = sessConfig->render.hFoV; + } + + // Get the reset height + shared_ptr pscene = typedScene(); + pscene->setGravity(grav); + float resetHeight = pscene->resetHeight(); + if (isnan(resetHeight)) { + resetHeight = -1e6; + } + + // For now make the player invisible (prevent issues w/ seeing model from inside) + shared_ptr player = scene()->typedEntity("player"); + player->setVisible(false); + player->setRespawnHeight(resetHeight); + player->setRespawnPosition(player->frame().translation); + + UserConfig* user = userTable.getCurrentUser(); + // Copied from old FPM code + double mouseSens = 2.0 * pi() * 2.54 * 1920.0 / (user->cmp360 * user->mouseDPI); + mouseSens *= 1.0675 / 2.0; // 10.5 / 10.0 * 30.5 / 30.0 + player->mouseSensitivity = (float)mouseSens; + player->turnScale = currentTurnScale(); // Compound the session turn scale w/ the user turn scale... + player->moveRate = &sessConfig->player.moveRate; + player->moveScale = &sessConfig->player.moveScale; + player->axisLock = &sessConfig->player.axisLock; + player->jumpVelocity = &sessConfig->player.jumpVelocity; + player->jumpInterval = &sessConfig->player.jumpInterval; + player->jumpTouch = &sessConfig->player.jumpTouch; + player->height = &sessConfig->player.height; + player->crouchHeight = &sessConfig->player.crouchHeight; + + playerCamera()->setFieldOfView(FoV * units::degrees(), FOVDirection::HORIZONTAL); + return player; +} + void App::updateSession(const String& id) { // Check for a valid ID (non-emtpy and Array ids; @@ -511,10 +551,8 @@ void App::updateSession(const String& id) { m_loadedScene = sessConfig->sceneName; } - // Check for play mode specific parameters - m_weapon->setConfig(sessConfig->weapon); - m_weapon->setScene(scene()); - m_weapon->setCamera(activeCamera()); + // Update weapon + m_weapon = Weapon::create(sessConfig->weapon, scene(), playerCamera(), &m_projectileArray); // Update weapon model (if drawn) and sounds loadDecals(); @@ -534,22 +572,8 @@ void App::updateSession(const String& id) { } // Player parameters - shared_ptr player = scene()->typedEntity("player"); + shared_ptr player = updatePlayer(); sess->initialHeadingRadians = player->heading(); - UserConfig *user = userTable.getCurrentUser(); - // Copied from old FPM code - double mouseSens = 2.0 * pi() * 2.54 * 1920.0 / (user->cmp360 * user->mouseDPI); - mouseSens *= 1.0675 / 2.0; // 10.5 / 10.0 * 30.5 / 30.0 - player->mouseSensitivity = (float)mouseSens; - player->turnScale = currentTurnScale(); // Compound the session turn scale w/ the user turn scale... - player->moveRate = &sessConfig->player.moveRate; - player->moveScale = &sessConfig->player.moveScale; - player->axisLock = &sessConfig->player.axisLock; - player->jumpVelocity = &sessConfig->player.jumpVelocity; - player->jumpInterval = &sessConfig->player.jumpInterval; - player->jumpTouch = &sessConfig->player.jumpTouch; - player->height = &sessConfig->player.height; - player->crouchHeight = &sessConfig->player.crouchHeight; // Check for need to start latency logging and if so run the logger now SystemConfig sysConfig = SystemConfig::load(); @@ -593,35 +617,36 @@ void App::quitRequest() { } void App::onAfterLoadScene(const Any& any, const String& sceneName) { - // Pick between experiment and session settings - Vector3 grav = experimentConfig.player.gravity; - float FoV = experimentConfig.render.hFoV; - if (sessConfig != nullptr) { - grav = sessConfig->player.gravity; - FoV = sessConfig->render.hFoV; + + // make sure the scene has a "player" entity + shared_ptr player = scene()->typedEntity("player"); + alwaysAssertM(player, "All FPSci scene files must provide a \"PlayerEntity\"!"); + if (isNull(player)) { + // Decide if we want to insert a player here... if so make sure to add a "playerCamera" as well... + //shared_ptr newPlayer = PlayerEntity::create("player", scene().get(), CFrame(), nullptr); + //scene()->insert(newPlayer); } + // Set the active camera to the player - setActiveCamera(scene()->typedEntity("camera")); - // make sure the scene has a "player" entity - if (isNull(scene()->typedEntity("player"))) { - shared_ptr newPlayer = PlayerEntity::create("player", scene().get(), CFrame(), nullptr); - scene()->insert(newPlayer); - } + shared_ptr playerCam = playerCamera(); + // Check for no player cam, but a player, if so make the camera from the player + if (isNull(playerCam) && notNull(player)) { + playerCam = Camera::create("playerCamera"); + scene()->insert((shared_ptr)playerCam); + } + setActiveCamera(playerCam); - // Get the reset height - shared_ptr pscene = typedScene(); - pscene->setGravity(grav); - float resetHeight = pscene->resetHeight(); - if (isnan(resetHeight)) { - resetHeight = -1e6; + + // Update the player entity + updatePlayer(); + + if (m_weapon) { + m_weapon->setScene(scene()); + m_weapon->setCamera(playerCamera()); } - // For now make the player invisible (prevent issues w/ seeing model from inside) - shared_ptr player = scene()->typedEntity("player"); - player->setVisible(false); - player->setRespawnHeight(resetHeight); - player->setRespawnPosition(player->frame().translation); - activeCamera()->setFieldOfView(FoV * units::degrees(), FOVDirection::HORIZONTAL); + // Clear decals (if any remain) + m_currentMissDecals.clear(); } void App::onAI() { @@ -655,8 +680,8 @@ void App::onGraphics3D(RenderDevice* rd, Array >& surface) { } scene()->lightingEnvironment().ambientOcclusionSettings.enabled = !emergencyTurbo; - activeCamera()->filmSettings().setAntialiasingEnabled(!emergencyTurbo); - activeCamera()->filmSettings().setBloomStrength(emergencyTurbo ? 0.0f : 0.5f); + playerCamera()->filmSettings().setAntialiasingEnabled(!emergencyTurbo); + playerCamera()->filmSettings().setBloomStrength(emergencyTurbo ? 0.0f : 0.5f); GApp::onGraphics3D(rd, surface); @@ -732,7 +757,7 @@ void App::simulateProjectiles(RealTime dt) { if (closest < hitThreshold) { hitTarget(closestTarget); // Offset position slightly along normal to avoid Z-fighting the target - const Vector3& camDir = -activeCamera()->frame().lookVector(); + const Vector3& camDir = -playerCamera()->frame().lookVector(); drawDecal(info.point + 0.01 * camDir, camDir, true); projectile.clearRemainingTime(); } @@ -805,7 +830,7 @@ void App::onSimulation(RealTime rdt, SimTime sdt, SimTime idt) { // Move the player const shared_ptr& p = scene()->typedEntity("player"); - activeCamera()->setFrame(p->getCameraFrame()); + playerCamera()->setFrame(p->getCameraFrame()); // Handle developer mode features here if (startupConfig.developerMode) { @@ -982,7 +1007,7 @@ void App::onPostProcessHDR3DEffects(RenderDevice *rd) { // Draw target health bars if (sessConfig->targetView.showHealthBars) { for (auto const& target : sess->targetArray()) { - target->drawHealthBar(rd, *activeCamera(), *m_framebuffer, + target->drawHealthBar(rd, *playerCamera(), *m_framebuffer, sessConfig->targetView.healthBarSize, sessConfig->targetView.healthBarOffset, sessConfig->targetView.healthBarBorderSize, @@ -995,7 +1020,7 @@ void App::onPostProcessHDR3DEffects(RenderDevice *rd) { if (sessConfig->targetView.showCombatText) { Array toRemove; for (int i = 0; i < m_combatTextList.size(); i++) { - bool remove = !m_combatTextList[i]->draw(rd, *activeCamera(), *m_framebuffer); + bool remove = !m_combatTextList[i]->draw(rd, *playerCamera(), *m_framebuffer); if (remove) m_combatTextList[i] = nullptr; // Null pointers to remove } // Remove the expired elements here @@ -1171,7 +1196,7 @@ void App::drawHUD(RenderDevice *rd) { Vector2 App::currentTurnScale() { Vector2 baseTurnScale = sessConfig->player.turnScale * userTable.getCurrentUser()->turnScale;; // If we're not scoped just return the normal user turn scale - if (!m_weapon->scoped()) return baseTurnScale; + if (!m_weapon || !m_weapon->scoped()) return baseTurnScale; // Otherwise create scaled turn scale for the scoped state if (userTable.getCurrentUser()->scopeTurnScale.length() > 0) { // User scoped turn scale specified, don't perform default scaling @@ -1179,7 +1204,7 @@ Vector2 App::currentTurnScale() { } else { // Otherwise scale the scope turn scalue using the ratio of FoV - return activeCamera()->fieldOfViewAngleDegrees() / sessConfig->render.hFoV * baseTurnScale; + return playerCamera()->fieldOfViewAngleDegrees() / sessConfig->render.hFoV * baseTurnScale; } } @@ -1189,7 +1214,7 @@ void App::setScopeView(bool scoped) { const float scopeFoV = sessConfig->weapon.scopeFoV > 0 ? sessConfig->weapon.scopeFoV : sessConfig->render.hFoV; m_weapon->setScoped(scoped); // Update the weapon state const float FoV = (scoped ? scopeFoV : sessConfig->render.hFoV); // Get new FoV in degrees (depending on scope state) - activeCamera()->setFieldOfView(FoV * pif() / 180.f, FOVDirection::HORIZONTAL); // Set the camera FoV + playerCamera()->setFieldOfView(FoV * pif() / 180.f, FOVDirection::HORIZONTAL); // Set the camera FoV player->turnScale = currentTurnScale(); // Scale sensitivity based on the field of view change here } @@ -1233,7 +1258,7 @@ void App::hitTarget(shared_ptr target) { else if (target->health() <= 0) { // Position explosion CFrame explosionFrame = target->frame(); - explosionFrame.rotation = activeCamera()->frame().rotation; + explosionFrame.rotation = playerCamera()->frame().rotation; // Create the explosion const shared_ptr newExplosion = VisibleEntity::create( format("explosion%d", m_explosionIdx), @@ -1355,7 +1380,7 @@ void App::onUserInput(UserInput* ui) { WeaponConfig& wConfig = sessConfig->weapon; if (notNull(target)) { // Check if we hit anything hitTarget(target); // If we did, we are in hitscan mode, apply the damage and manage the target here - const Vector3& camDir = -activeCamera()->frame().lookVector(); + const Vector3& camDir = -playerCamera()->frame().lookVector(); // Offset position slightly along normal to avoid Z-fighting the target drawDecal(info.point + 0.01f*camDir, camDir, true); } @@ -1411,7 +1436,7 @@ void App::onUserInput(UserInput* ui) { // Draw a decal here if we are in hitscan mode if (sessConfig->weapon.hitScan && hitDist < finf()) { // Draw decal at the lookRay/world intersection - CFrame frame = activeCamera()->frame(); + CFrame frame = playerCamera()->frame(); Point3 position = frame.translation + frame.lookRay().direction() * (hitDist - 0.01f); drawDecal(position, info.normal); } @@ -1424,7 +1449,7 @@ void App::onUserInput(UserInput* ui) { setReticle(userTable.getCurrentUser()->reticleIndex); } - activeCamera()->filmSettings().setSensitivity(sceneBrightness); + playerCamera()->filmSettings().setSensitivity(sceneBrightness); END_PROFILER_EVENT(); } @@ -1433,7 +1458,7 @@ void App::onPose(Array >& surface, Array()->poseExceptExcluded(surface, "player"); - m_weapon->onPose(surface); + if (m_weapon) { m_weapon->onPose(surface); } } void App::onGraphics2D(RenderDevice* rd, Array>& posed2D) { @@ -1641,7 +1666,7 @@ void App::oneFrame() { // The debug camera is not in the scene, so we have // to explicitly pose it. This actually does nothing, but // it allows us to trigger the TAA code. - activeCamera()->onPose(m_posed3D); + playerCamera()->onPose(m_posed3D); } m_poseWatch.tock(); END_PROFILER_EVENT(); diff --git a/source/App.h b/source/App.h index 171be80d..c501c872 100644 --- a/source/App.h +++ b/source/App.h @@ -99,6 +99,10 @@ class App : public GApp { void loadDecals(); void updateUser(void); void updateUserGUI(); + shared_ptr updatePlayer(); + + /** Get the player camera */ + shared_ptr playerCamera() { return scene()->typedEntity("playerCamera"); } /** Get the current turn scale (per user and scope setting) */ Vector2 currentTurnScale(); diff --git a/source/Session.cpp b/source/Session.cpp index 93ecd968..1947b371 100644 --- a/source/Session.cpp +++ b/source/Session.cpp @@ -701,6 +701,7 @@ void Session::insertTarget(shared_ptr target) { void Session::destroyTarget(int index) { // Not a reference because we're about to manipulate the array const shared_ptr target = m_targetArray[index]; + if (!target) return; // Remove the target from the target array m_targetArray.fastRemove(index); // Remove the target from the scene diff --git a/source/Weapon.cpp b/source/Weapon.cpp index 200a19e1..580c12c7 100644 --- a/source/Weapon.cpp +++ b/source/Weapon.cpp @@ -2,14 +2,14 @@ void Weapon::onPose(Array >& surface) { - if (m_config->renderModel || m_config->renderBullets) { // || m_config->renderMuzzleFlash) { + if (m_camera && (m_config.renderModel || m_config.renderBullets)) { // || m_config->renderMuzzleFlash) { // Update the weapon frame for all of these cases const float yScale = -0.12f; const float zScale = -yScale * 0.5f; const float lookY = m_camera->frame().lookVector().y; m_frame = m_camera->frame() * CFrame::fromXYZYPRDegrees(0.3f, -0.4f + lookY * yScale, -1.1f + lookY * zScale, 10, 5); // Pose the view model (weapon) for render here - if (m_config->renderModel) { + if (m_config.renderModel) { const float prevLookY = m_camera->previousFrame().lookVector().y; const CFrame prevWeaponPos = CFrame::fromXYZYPRDegrees(0.3f, -0.4f + prevLookY * yScale, -1.1f + prevLookY * zScale, 10, 5); m_viewModel->pose(surface, m_frame, m_camera->previousFrame() * prevWeaponPos, nullptr, nullptr, nullptr, Surface::ExpressiveLightScatteringProperties()); @@ -35,12 +35,12 @@ shared_ptr Weapon::fire( if (closest < finf()) { hitDist = closest; } // Create the bullet (if we need to draw it or are using non-hitscan behavior) - if (m_config->renderBullets || !m_config->hitScan) { + if (m_config.renderBullets || !m_config.hitScan) { // Create the bullet start frame from the weapon frame plus muzzle offset CFrame bulletStartFrame = m_camera->frame(); // Apply bullet offset w/ camera rotation here - bulletStartFrame.translation += m_camera->frame().rotation * m_config->bulletOffset; + bulletStartFrame.translation += m_camera->frame().rotation * m_config.bulletOffset; // Angle the bullet start frame towards the aim point Point3 aimPoint = m_camera->frame().translation + m_camera->frame().lookVector() * 1000.0f; @@ -51,12 +51,12 @@ shared_ptr Weapon::fire( bulletStartFrame.lookAt(aimPoint); // Non-laser weapon, draw a projectile - if (!m_config->isLaser()) { + if (!m_config.isLaser()) { const shared_ptr& bullet = VisibleEntity::create(format("bullet%03d", ++m_lastBulletId), m_scene.get(), m_bulletModel, bulletStartFrame); bullet->setShouldBeSaved(false); bullet->setCanCauseCollisions(false); bullet->setCastsShadows(false); - bullet->setVisible(m_config->renderBullets); + bullet->setVisible(m_config.renderBullets); /* const shared_ptr& track = Entity::Track::create(bullet.get(), scene().get(), @@ -64,7 +64,7 @@ shared_ptr Weapon::fire( bullet->setTrack(track); */ - m_projectiles->push(Projectile(bullet, m_config->bulletSpeed, !m_config->hitScan, m_config->bulletGravity, fmin((closest+1.0f)/ m_config->bulletSpeed, 10.0f))); + m_projectiles->push(Projectile(bullet, m_config.bulletSpeed, !m_config.hitScan, m_config.bulletGravity, fmin((closest+1.0f)/ m_config.bulletSpeed, 10.0f))); m_scene->insert(bullet); } // Laser weapon (very hacky for now...) @@ -76,7 +76,7 @@ shared_ptr Weapon::fire( } // Hit scan specific logic here - if(m_config->hitScan){ + if(m_config.hitScan){ // Check whether we hit any targets int closestIndex = -1; for (int t = 0; t < targets.size(); ++t) { @@ -96,8 +96,8 @@ shared_ptr Weapon::fire( } // If we're not in laser mode play the sounce (once) here - if (!m_config->isLaser()) { - m_fireSound->play(m_config->fireSoundVol); + if (!m_config.isLaser()) { + m_fireSound->play(m_config.fireSoundVol); //m_fireSound->play(activeCamera()->frame().translation, activeCamera()->frame().lookVector() * 2.0f, 0.5f); } diff --git a/source/Weapon.h b/source/Weapon.h index 1ba760b5..e2c4dffd 100644 --- a/source/Weapon.h +++ b/source/Weapon.h @@ -63,7 +63,7 @@ class Projectile : public Entity { class Weapon : Entity { public: - static shared_ptr create(shared_ptr config, shared_ptr scene, shared_ptr cam, Array* projectiles) { + static shared_ptr create(WeaponConfig& config, shared_ptr scene, shared_ptr cam, Array* projectiles) { return createShared(config, scene, cam, projectiles); }; @@ -77,8 +77,8 @@ class Weapon : Entity { void loadModels() { // Create the view model - if (m_config->modelSpec.filename != "") { - m_viewModel = ArticulatedModel::create(m_config->modelSpec, "viewModel"); + if (m_config.modelSpec.filename != "") { + m_viewModel = ArticulatedModel::create(m_config.modelSpec, "viewModel"); } else { const static Any modelSpec = PARSE_ANY(ArticulatedModel::Specification{ @@ -93,8 +93,8 @@ class Weapon : Entity { } // Create the bullet model - const Vector3& scale = m_config->bulletScale; - const Color3& color = m_config->bulletColor; + const Vector3& scale = m_config.bulletScale; + const Color3& color = m_config.bulletColor; const static Any bulletSpec = Any::parse(format( "ArticulatedModel::Specification{\ filename = \"ifs/d10.ifs\";\ @@ -114,9 +114,9 @@ class Weapon : Entity { void loadSounds() { // Check for play mode specific parameters if (notNull(m_fireAudio)) { m_fireAudio->stop(); } - m_fireSound = Sound::create(System::findDataFile(m_config->fireSound), m_config->isLaser()); + m_fireSound = Sound::create(System::findDataFile(m_config.fireSound), m_config.isLaser()); } - void setConfig(const WeaponConfig& config) { m_config = std::make_shared(config); } + void setConfig(const WeaponConfig& config) { m_config = config; } void setCamera(const shared_ptr& cam) { m_camera = cam; } void setScene(const shared_ptr& scene) { m_scene = scene; } void setScoped(bool state = true) { m_scoped = state; } @@ -136,7 +136,7 @@ class Weapon : Entity { bool firing() { return m_firing; } protected: - Weapon(shared_ptr config, shared_ptr& scene, shared_ptr& cam, Array* projectiles) : + Weapon(WeaponConfig& config, shared_ptr& scene, shared_ptr& cam, Array* projectiles) : m_config(config), m_scene(scene), m_camera(cam), m_projectiles(projectiles) {}; shared_ptr m_viewModel; ///< Model for the weapon @@ -144,7 +144,7 @@ class Weapon : Entity { shared_ptr m_fireSound; ///< Sound for weapon firing shared_ptr m_fireAudio; ///< Audio channel for fire sound - shared_ptr m_config; ///< Weapon configuration + WeaponConfig& m_config; ///< Weapon configuration int m_lastBulletId = 0; ///< Bullet ID (auto incremented) bool m_scoped = false; ///< Flag used for scope management bool m_firing = false; ///< Flag used for auto fire management From fa1808ee3f7f9302e52ecf5e37e8278ddd1c9817 Mon Sep 17 00:00:00 2001 From: Ben Boudaoud Date: Mon, 20 Apr 2020 21:32:41 -0400 Subject: [PATCH 2/8] Get scene editor working/in the right place --- source/App.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/App.cpp b/source/App.cpp index 35301b84..923b3e84 100644 --- a/source/App.cpp +++ b/source/App.cpp @@ -65,11 +65,6 @@ void App::onInit() { setScene(PhysicsScene::create(m_ambientOcclusion)); scene()->registerEntitySubclass("PlayerEntity", &PlayerEntity::create); // Register the player entity for creation - // Re-create the developer window - developerWindow->close(); - developerWindow.reset(); - createDeveloperHUD(); - // Setup the GUI showRenderingStats = false; makeGUI(); @@ -270,6 +265,11 @@ void App::makeGUI() { theme = GuiTheme::fromFile(System::findDataFile("osx-10.7.gtm")); + // Update the scene editor (for new PhysicsScene pointer, initially loaded in GApp) + removeWidget(developerWindow->sceneEditorWindow); + developerWindow->sceneEditorWindow = SceneEditorWindow::create(this, scene(), theme); + developerWindow->sceneEditorWindow->moveTo(developerWindow->cameraControlWindow->rect().x0y1() + Vector2(0, 15)); + // Add the control panes here updateControls(); From 9faa890b616e6097754dba22a1eef98d84922996 Mon Sep 17 00:00:00 2001 From: Ben Boudaoud Date: Mon, 20 Apr 2020 21:57:01 -0400 Subject: [PATCH 3/8] Only show the HUD in the player camera --- source/App.cpp | 143 ++++++++++++++++++++++++++----------------------- 1 file changed, 75 insertions(+), 68 deletions(-) diff --git a/source/App.cpp b/source/App.cpp index 923b3e84..e945a099 100644 --- a/source/App.cpp +++ b/source/App.cpp @@ -959,35 +959,38 @@ bool App::onEvent(const GEvent& event) { updateMouseSensitivity(); foundKey = true; } - - // Override 'q', 'z', 'c', and 'e' keys (unused) - const Array unused = { (GKey)'e', (GKey)'z', (GKey)'c', (GKey)'q' }; - if (unused.contains(ksym)) { - foundKey = true; - } - else if (keyMap.map["crouch"].contains(ksym)) { - scene()->typedEntity("player")->setCrouched(true); - foundKey = true; - } - else if (keyMap.map["jump"].contains(ksym)) { - scene()->typedEntity("player")->setJumpPressed(true); - foundKey = true; - } - else if (keyMap.map["quit"].contains(ksym)) { - quitRequest(); - return true; + else if (activeCamera() == playerCamera()) { + // Override 'q', 'z', 'c', and 'e' keys (unused) + // THIS IS A PROBLEM IF THESE ARE KEY MAPPED!!! + const Array unused = { (GKey)'e', (GKey)'z', (GKey)'c', (GKey)'q' }; + if (unused.contains(ksym)) { + foundKey = true; + } + else if (keyMap.map["crouch"].contains(ksym)) { + scene()->typedEntity("player")->setCrouched(true); + foundKey = true; + } + else if (keyMap.map["jump"].contains(ksym)) { + scene()->typedEntity("player")->setJumpPressed(true); + foundKey = true; + } + else if (keyMap.map["quit"].contains(ksym)) { + quitRequest(); + return true; + } } - } - else if ((event.type == GEventType::KEY_UP)){ - if (keyMap.map["crouch"].contains(ksym)) { - scene()->typedEntity("player")->setCrouched(false); - foundKey = true; + else if ((event.type == GEventType::KEY_UP)) { + if (keyMap.map["crouch"].contains(ksym)) { + scene()->typedEntity("player")->setCrouched(false); + foundKey = true; + } } } if (foundKey) { return true; } + // Handle window-based close ("X" button) if (event.type == GEventType::QUIT) { quitRequest(); @@ -999,44 +1002,45 @@ bool App::onEvent(const GEvent& event) { } void App::onPostProcessHDR3DEffects(RenderDevice *rd) { - // Put elements that should be delayed along w/ 3D here - rd->push2D(); { - const float scale = rd->viewport().width() / 1920.0f; - rd->setBlendFunc(RenderDevice::BLEND_SRC_ALPHA, RenderDevice::BLEND_ONE_MINUS_SRC_ALPHA); - - // Draw target health bars - if (sessConfig->targetView.showHealthBars) { - for (auto const& target : sess->targetArray()) { - target->drawHealthBar(rd, *playerCamera(), *m_framebuffer, - sessConfig->targetView.healthBarSize, - sessConfig->targetView.healthBarOffset, - sessConfig->targetView.healthBarBorderSize, - sessConfig->targetView.healthBarColors, - sessConfig->targetView.healthBarBorderColor); + if (activeCamera() == playerCamera()) { + // Put elements that should be delayed along w/ 3D here + rd->push2D(); { + const float scale = rd->viewport().width() / 1920.0f; + rd->setBlendFunc(RenderDevice::BLEND_SRC_ALPHA, RenderDevice::BLEND_ONE_MINUS_SRC_ALPHA); + + // Draw target health bars + if (sessConfig->targetView.showHealthBars) { + for (auto const& target : sess->targetArray()) { + target->drawHealthBar(rd, *playerCamera(), *m_framebuffer, + sessConfig->targetView.healthBarSize, + sessConfig->targetView.healthBarOffset, + sessConfig->targetView.healthBarBorderSize, + sessConfig->targetView.healthBarColors, + sessConfig->targetView.healthBarBorderColor); + } } - } - // Draw the combat text - if (sessConfig->targetView.showCombatText) { - Array toRemove; - for (int i = 0; i < m_combatTextList.size(); i++) { - bool remove = !m_combatTextList[i]->draw(rd, *playerCamera(), *m_framebuffer); - if (remove) m_combatTextList[i] = nullptr; // Null pointers to remove + // Draw the combat text + if (sessConfig->targetView.showCombatText) { + Array toRemove; + for (int i = 0; i < m_combatTextList.size(); i++) { + bool remove = !m_combatTextList[i]->draw(rd, *playerCamera(), *m_framebuffer); + if (remove) m_combatTextList[i] = nullptr; // Null pointers to remove + } + // Remove the expired elements here + m_combatTextList.removeNulls(); } - // Remove the expired elements here - m_combatTextList.removeNulls(); - } - - if (sessConfig->clickToPhoton.enabled && sessConfig->clickToPhoton.mode == "total") { - drawClickIndicator(rd, "total"); - } - // Draw the HUD here - if (sessConfig->hud.enable) { - drawHUD(rd); - } - }rd->pop2D(); + if (sessConfig->clickToPhoton.enabled && sessConfig->clickToPhoton.mode == "total") { + drawClickIndicator(rd, "total"); + } + // Draw the HUD here + if (sessConfig->hud.enable) { + drawHUD(rd); + } + }rd->pop2D(); + } if (sessConfig->render.shader != "") { // Copy the post-VFX HDR (input) framebuffer static shared_ptr input = Framebuffer::create(Texture::createEmpty("FPSci::3DShaderPass::iChannel0", m_framebuffer->width(), m_framebuffer->height(), m_framebuffer->texture(0)->format())); @@ -1320,7 +1324,7 @@ void App::onUserInput(UserInput* ui) { (void)ui; const shared_ptr& player = scene()->typedEntity("player"); - if (!m_userSettingsMode && notNull(player)) { + if (!m_userSettingsMode && activeCamera() == playerCamera() && notNull(player)) { player->updateFromInput(ui); } else { // Zero the player velocity and rotation when in the setting menu @@ -1512,18 +1516,21 @@ void App::onGraphics2D(RenderDevice* rd, Array>& posed2D) drawClickIndicator(rd, sessConfig->clickToPhoton.mode); } - // Reticle - UserConfig* user = userTable.getCurrentUser(); - float tscale = max(min(((float)(System::time() - sess->lastFireTime()) / user->reticleShrinkTimeS), 1.0f), 0.0f); - float rScale = tscale * user->reticleScale[0] + (1.0f - tscale)*user->reticleScale[1]; - Color4 rColor = user->reticleColor[1] * (1.0f - tscale) + user->reticleColor[0] * tscale; - Draw::rect2D(((reticleTexture->rect2DBounds() - reticleTexture->vector2Bounds() / 2.0f))*rScale / 2.0f + rd->viewport().wh() / 2.0f, rd, rColor, reticleTexture); - - // Handle the feedback message - String message = sess->getFeedbackMessage(); - if (!message.empty()) { - outputFont->draw2D(rd, message.c_str(), - (Point2(rd->viewport().width()*0.5f, rd->viewport().height()*0.4f)).floor(), floor(20.0f * scale), Color3::yellow(), Color4::clear(), GFont::XALIGN_CENTER, GFont::YALIGN_CENTER); + // Player camera only indicators + if (activeCamera() == playerCamera()) { + // Reticle + UserConfig* user = userTable.getCurrentUser(); + float tscale = max(min(((float)(System::time() - sess->lastFireTime()) / user->reticleShrinkTimeS), 1.0f), 0.0f); + float rScale = tscale * user->reticleScale[0] + (1.0f - tscale) * user->reticleScale[1]; + Color4 rColor = user->reticleColor[1] * (1.0f - tscale) + user->reticleColor[0] * tscale; + Draw::rect2D(((reticleTexture->rect2DBounds() - reticleTexture->vector2Bounds() / 2.0f)) * rScale / 2.0f + rd->viewport().wh() / 2.0f, rd, rColor, reticleTexture); + + // Handle the feedback message + String message = sess->getFeedbackMessage(); + if (!message.empty()) { + outputFont->draw2D(rd, message.c_str(), + (Point2(rd->viewport().width() * 0.5f, rd->viewport().height() * 0.4f)).floor(), floor(20.0f * scale), Color3::yellow(), Color4::clear(), GFont::XALIGN_CENTER, GFont::YALIGN_CENTER); + } } } rd->pop2D(); From 020351b51f31b2e51c5340181a7213deb06d16b4 Mon Sep 17 00:00:00 2001 From: Josef Spjut Date: Tue, 21 Apr 2020 12:51:35 -0400 Subject: [PATCH 4/8] Set motion change time in TargetEntity --- source/TargetEntity.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/source/TargetEntity.cpp b/source/TargetEntity.cpp index 1806ec08..9543a51b 100644 --- a/source/TargetEntity.cpp +++ b/source/TargetEntity.cpp @@ -126,6 +126,11 @@ void TargetEntity::onSimulation(SimTime absoluteTime, SimTime deltaTime) { Point3 delta = currDest.position - nextDest.position; // Get the delta vector to move along setFrame((prog*delta) + currDest.position + m_offset); // Set the new positions + // Set changed time if it moved + if (delta != Point3(0.f, 0.f, 0.f) && m_offset != Vector3(0.f, 0.f, 0.f)) { + m_lastChangeTime = System::time(); + } + #ifdef DRAW_BOUNDING_SPHERES // Draw a 1m sphere at this position debugDraw(Sphere(m_frame, BOUNDING_SPHERE_RADIUS), 0.0f, Color4::clear(), Color3::black()); @@ -291,7 +296,6 @@ void FlyingEntity::onSimulation(SimTime absoluteTime, SimTime deltaTime) { throw "Cannot lock all axes for non-static target!"; } m_velocity = vel * (destination - m_frame.translation).direction(); - } // Check for whether the target has "left" the bounds, if so "reflect" it about the wall else if (!m_bounds.contains(pos)) { @@ -318,6 +322,11 @@ void FlyingEntity::onSimulation(SimTime absoluteTime, SimTime deltaTime) { // Update the position and set the frame pos += m_velocity*deltaTime; setFrame(pos); + + // Set changed time if it moved + if (m_velocity != Vector3(0.f, 0.f, 0.f)) { + m_lastChangeTime = System::time(); + } } else { // Handle non-world space (player projection here) @@ -386,6 +395,11 @@ void FlyingEntity::onSimulation(SimTime absoluteTime, SimTime deltaTime) { const Vector3& V = (destinationVector - currentVector * projection).direction(); setFrame(m_orbitCenter + (cos(angleChange) * U + sin(angleChange) * V) * radius); + + // Set changed time if it moved + if (angleChange != 0.f) { + m_lastChangeTime = System::time(); + } } if (m_upperHemisphereOnly) { @@ -612,6 +626,11 @@ void JumpingEntity::onSimulation(SimTime absoluteTime, SimTime deltaTime) { // Update the position setFrame(pos); + + // Set changed time if it moved + if (m_velocity != Vector3(0.f, 0.f, 0.f) || m_inJump) { + m_lastChangeTime = System::time(); + } } else { while (deltaTime > 0.000001f) { @@ -667,6 +686,9 @@ void JumpingEntity::onSimulation(SimTime absoluteTime, SimTime deltaTime) { Point3 relativePos = m_simulatedPos - m_orbitCenter; m_frame.translation = relativePos.direction() * m_orbitRadius + m_orbitCenter; + // Set changed time since we don't track whether we moved + m_lastChangeTime = System::time(); + /// Update velocity if (m_inJump) { m_speed = m_speed + m_acc * t; From 2dcd7ebf6d6f14807a051ffaa1d93ff69a1b2d6f Mon Sep 17 00:00:00 2001 From: Ben Boudaoud Date: Mon, 22 Jun 2020 10:49:08 -0400 Subject: [PATCH 5/8] Minimal change to get debug camera moving with FPM --- source/FPSciApp.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/FPSciApp.cpp b/source/FPSciApp.cpp index bb9adc9e..73fe7ed3 100644 --- a/source/FPSciApp.cpp +++ b/source/FPSciApp.cpp @@ -595,7 +595,6 @@ void FPSciApp::onSimulation(RealTime rdt, SimTime sdt, SimTime idt) { m_widgetManager->onSimulation(rdt, sdt, idt); if (scene()) { scene()->onSimulation(sdt); } - // make sure mouse sensitivity is set right if (m_userSettingsWindow->visible()) { updateMouseSensitivity(); @@ -626,6 +625,11 @@ void FPSciApp::onSimulation(RealTime rdt, SimTime sdt, SimTime idt) { // Handle developer mode features here if (startupConfig.developerMode) { + // If the debug camera is selected, update it's position from the FPM + if (activeCamera() == m_debugCamera) { + m_debugCamera->setFrame(m_cameraManipulator->frame()); + } + // Handle frame rate/delay updates here if (sessConfig->render.frameRate != lastSetFrameRate || displayLagFrames != sessConfig->render.frameDelay) { updateParameters(sessConfig->render.frameDelay, sessConfig->render.frameRate); From 705d6dc2ae8558ad28d27f54855ea3b2211d9a18 Mon Sep 17 00:00:00 2001 From: Ben Boudaoud Date: Mon, 22 Jun 2020 11:37:30 -0400 Subject: [PATCH 6/8] Add a player if one is not in the scene --- source/FPSciApp.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/source/FPSciApp.cpp b/source/FPSciApp.cpp index 260aeba6..4ce0bde6 100644 --- a/source/FPSciApp.cpp +++ b/source/FPSciApp.cpp @@ -519,20 +519,26 @@ void FPSciApp::toggleUserSettingsMenu() { void FPSciApp::onAfterLoadScene(const Any& any, const String& sceneName) { - // make sure the scene has a "player" entity + // Make sure the scene has a "player" entity shared_ptr player = scene()->typedEntity("player"); - alwaysAssertM(player, "All FPSci scene files must provide a \"PlayerEntity\"!"); + //alwaysAssertM(player, "All FPSci scene files must provide a \"PlayerEntity\"!"); + + // Add a player if one isn't present in the scene + if (isNull(player)) { + logPrintf("WARNING: Didn't find a \"player\" specified in \"%s\"! Adding one at the origin.", sceneName); + shared_ptr newPlayer = PlayerEntity::create("player", scene().get(), CFrame(), nullptr); + scene()->insert(newPlayer); + } // Set the active camera to the player shared_ptr playerCam = playerCamera(); // Check for no player cam, but a player, if so make the camera from the player - if (isNull(playerCam) && notNull(player)) { + if (isNull(playerCam)) { playerCam = Camera::create("playerCamera"); scene()->insert((shared_ptr)playerCam); } setActiveCamera(playerCam); - // Update the player entity updatePlayer(); From 41fbdfc85e7740e856c5dff5b0ebd790b77d695e Mon Sep 17 00:00:00 2001 From: Josef Spjut Date: Mon, 22 Jun 2020 14:15:03 -0400 Subject: [PATCH 7/8] getPlayer() EXPECTs and ASSERTs --- tests/FPSciTests.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/FPSciTests.cpp b/tests/FPSciTests.cpp index 14c32752..645e60b1 100644 --- a/tests/FPSciTests.cpp +++ b/tests/FPSciTests.cpp @@ -163,6 +163,7 @@ inline const shared_ptr FPSciTests::getPlayer() void FPSciTests::zeroCameraRotation() { auto player = getPlayer(); + EXPECT_TRUE(notNull(player)); if (player) { player->respawn(); } @@ -229,7 +230,8 @@ TEST_F(FPSciTests, InitialTestConditions) { ASSERT_TRUE(notNull(s_app->sess)); // Make sure a player exists - (void)getPlayer(); + auto player = getPlayer(); + ASSERT_TRUE(notNull(player)); // Default config properties EXPECT_FALSE(s_app->sessConfig->weapon.autoFire); @@ -256,8 +258,7 @@ TEST_F(FPSciTests, TestTargetPositions) { } auto player = getPlayer(); - if (!player) - return; + ASSERT_TRUE(notNull(player)); auto camPos = s_app->activeCamera()->frame().translation; @@ -342,8 +343,7 @@ TEST_F(FPSciTests, KillTargetRightTranslate) { zeroCameraRotation(); auto player = getPlayer(); - if (!player) - return; + ASSERT_TRUE(notNull(player)); // Kill the right target by moving to line it up const float moveX = 2.5f; @@ -422,8 +422,7 @@ TEST_F(FPSciTests, MoveCamera) { Vector3 startPos = s_app->activeCamera()->frame().translation; auto player = getPlayer(); - if (!player) - return; + ASSERT_TRUE(notNull(player)); *player->moveRate = (float)(1.0 / fixedTestDeltaTime()); *player->moveScale = Vector2::one(); From b23af8648e6297b659ac1527e15b9ab6a6c12eaf Mon Sep 17 00:00:00 2001 From: Josef Spjut Date: Mon, 22 Jun 2020 14:16:31 -0400 Subject: [PATCH 8/8] Ignore test generated user config --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 28bfbc81..a5f483f7 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ data-files/result_data data-files/*.Any data-files/*.jpg *.mp4 +# Test generated files +data-files/test/userconfig.Any # Don't check in any results "scripts/event logger/software/Logs" scripts/configs/*.Any