Skip to content

Commit

Permalink
Merge pull request #334 from NVlabs/SceneHeading
Browse files Browse the repository at this point in the history
Scene heading
  • Loading branch information
jspjutNV authored Nov 11, 2021
2 parents 1a21cdd + 006ed4d commit 7818659
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 51 deletions.
13 changes: 10 additions & 3 deletions data-files/scene/Simple_Hallway.Scene.Any
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@

player = PlayerEntity {
model = "playerModel";
frame = Point3(47.522999, -2.3549, -0.35916 );
heading = -1.5707963267948966192313216916398;
// When the player entity is specified, its frame overrides the camera frame
// even if not specified and filled with the default.
// If you prefer to use a specified camera frame, you can specify which camera to use
// in the experiment config as long as there's no `PlayerEntity` in the scene config.
frame = CFrame::fromXYZYPRDegrees(46.0, -2.2, 0.0, -90, 0, 0 );
};

camera = Camera {
// As long as the PlayerEntity is specified, FPSci will ignore this camera's CFrame,
// even if you select this camera in the experiment config.
frame = CFrame::fromXYZYPRDegrees( -13.3, 1, -11.2, 0, 0, 0 );

depthOfFieldSettings = DepthOfFieldSettings {
Expand Down Expand Up @@ -82,7 +87,9 @@
vignetteTopStrength = 0.5;
};

frame = CFrame::fromXYZYPRDegrees(47.523, -2.3549, -0.35916, 87.025, 3.163, 0 );
// This camera is set to the same location as the player entity for use in other G3D applications.
// The player entity frame takes presidence in FPSci.
frame = CFrame::fromXYZYPRDegrees(46.0, -2.2, 0.0, -90, 0, 0 );
mass = 1;
motionBlurSettings = MotionBlurSettings {
enabled = false;
Expand Down
4 changes: 2 additions & 2 deletions data-files/scene/Test_Cornell_Box.Scene.Any
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
};

camera = Camera {
frame = CFrame::fromXYZYPRDegrees( 0.0f, 1.0f, 6.0f, 0.0f, 0, 0.0f);
frame = CFrame::fromXYZYPRDegrees( 0.0f, 0.5f, 0.0f, 0.0f, 0, 0.0f);

depthOfFieldSettings = DepthOfFieldSettings {
model = "NONE";
Expand All @@ -24,7 +24,7 @@
};

customCamera = Camera {
frame = CFrame::fromXYZYPRDegrees( 0.5f, 1.5f, 4.0f, 0.0f, 0, 0.0f);
frame = CFrame::fromXYZYPRDegrees( 0.0f, 0.5f, 0.1f, 0.0f, 0, 0.0f);

depthOfFieldSettings = DepthOfFieldSettings {
model = "NONE";
Expand Down
6 changes: 1 addition & 5 deletions data-files/test/experimentconfig.Any
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
settingsVersion = 1;

description = "TestExperiment";
scene = { name = "G3D Simple Cornell Box (Empty CO)"; };
scene = { name = "FPSci Test Cornell Box (Empty CO)"; };

frameDelay = 0;
frameRate = 60;
Expand Down Expand Up @@ -49,15 +49,11 @@
{
id = "defaultCamera";
trials = ( { ids = ( "small"); count = 1; } );
scene = {
name = "FPSci Test Cornell Box (Empty CO)";
};
},
{
id = "customCamera";
trials = ( { ids = ( "small"); count = 1; } );
scene = {
name = "FPSci Test Cornell Box (Empty CO)";
playerCamera = "customCamera";
};
},
Expand Down
11 changes: 11 additions & 0 deletions docs/general_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ scene = {
};
```

*Note:* The full priority chain for setting heading/position in the scene is as follows:

1. This scene configuration's `spawnPosition` and `spawnHeading` parameters
2. The [scene.Any file's `PlayerEntity`](scene.md#player-entity) `frame` value (if the `PlayerEntity` is specified)
3. The specified `playerCamera`'s `frame` (if specified)
4. The [scene.Any file's `defaultCamera`](scene.md#player-camera) `frame`

Experiment designers should be careful to avoid setting the player spawn position Y-value lower than the player reset height (specified using either `resetHeight` above, the `minHeight` value in the [scene's `Physics` field](scene.md#physics), or a default value of 1e-6). A runtime exception will occur if this requirement is violated.

One practical configuration would be to specify a set of cameras in the `scene.Any` file without specifying a `PlayerEntity` in that file, then in the experiment config use a line like `scene = { playerCamera = "cameraName"; };` to specify the use of a specific camera (named `cameraName` in this example). This would allow for different sessions to change the spawn location within the same scene.

### Scene Name
If unspecified, the scene `name` field comes from:

Expand Down
16 changes: 11 additions & 5 deletions docs/scene.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,25 @@ Physics = {
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)
* `frame` - An initial frame for the player (in the scene geometry coordinate units) can include rotation
* `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
frame = CFrame::fromXYZYPRDegrees(0, 1.5, 0, 0, 0, 0); // Initialize the player 1.5m above the origin
collisionSphere = Sphere(1.0); // Use a 1m sphere as the collision proxy
};
```

If no `spawnPosition` or `spawnHeading` is provided as part of the [scene configuration](general_config.md#scene-settings) within the experiment-level configuration, the `frame` from the `PlayerEntity` is used for the default spawn position/heading.

Note that if a `PlayerEntity` is specified in a scene file its `frame` parameter will override any camera-based fallback. This includes the case where a `PlayerEntity` is specified without a `frame` field (its `frame` will default to the scene origin with 0 heading values).

*Note:* A runtime exception will occur if the `frame` Y value for the `PlayerEntity` (or specified/default camera if no `PlayerEntity` exists) is less than the `Physics`' `minHeight` parameter (if no `Physics` are specified the default `minHeight` value is 1e-6).

### Player Camera
Any camera specified in the scene can be used as the camera attached to the player. This mapping is done by putting the name of the chosen camera in the [FPSci scene settings](./general_config.md#scene-settings). If no name is specified, the `defaultCamera` will be used.

Expand All @@ -45,4 +49,6 @@ The player camera is a way to modify camera properties for the player view, excl
* Anti-aliasing
* Bloom strength
* Position/Rotation
* Field of View
* Field of View

If no `spawnPosition` or `spawnHeading` is specified in the experiment-specific configuration *and* no `PlayerEntity` is present in the scene file then the player/default camera's `frame` is used to initialize player position/heading in the scene.
62 changes: 45 additions & 17 deletions source/FPSciApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ void FPSciApp::exportScene() {
CFrame frame = scene()->typedEntity<PlayerEntity>("player")->frame();
logPrintf("Player position is: [%f, %f, %f]\n", frame.translation.x, frame.translation.y, frame.translation.z);
String filename = Scene::sceneNameToFilename(sessConfig->scene.name);
scene()->toAny().save(filename, startupConfig.jsonAnyOutput);
scene()->toAny().save(filename); // Save this w/o JSON format (breaks scene.Any file)
}

void FPSciApp::showPlayerControls() {
Expand Down Expand Up @@ -486,9 +486,15 @@ void FPSciApp::updateParameters(int frameDelay, float frameRate) {
setFrameDuration(dt, simStepDuration());
}

void FPSciApp::initPlayer(bool firstSpawn) {
shared_ptr<PlayerEntity> player = scene()->typedEntity<PlayerEntity>("player");
void FPSciApp::initPlayer(bool setSpawnPosition) {
shared_ptr<PhysicsScene> pscene = typedScene<PhysicsScene>();
shared_ptr<PlayerEntity> player = scene()->typedEntity<PlayerEntity>("player"); // Get player from the scene

// Update the player camera
const String pcamName = sessConfig->scene.playerCamera;
playerCamera = pcamName.empty() ? scene()->defaultCamera() : scene()->typedEntity<Camera>(pcamName);
alwaysAssertM(notNull(playerCamera), format("Scene %s does not contain a camera named \"%s\"!", sessConfig->scene.name, pcamName));
setActiveCamera(playerCamera);

// Set gravity and camera field of view
Vector3 grav = experimentConfig.player.gravity;
Expand All @@ -498,24 +504,38 @@ void FPSciApp::initPlayer(bool firstSpawn) {
FoV = sessConfig->render.hFoV;
}
pscene->setGravity(grav);

String respawnHeightSource;
playerCamera->setFieldOfView(FoV * units::degrees(), FOVDirection::HORIZONTAL);
if (!m_sceneHasPlayerEntity) { // Scene doesn't have player entity, copy the player entity frame from the camera
respawnHeightSource = format("\"%s\" camera in scene.Any file", playerCamera->name());
player->setFrame(m_initialCameraFrames[playerCamera->name()]);
setSpawnPosition = true; // Set the player spawn position from the camera
}
else {
respawnHeightSource = "PlayerEntity in scene.Any file";
}
playerCamera->setFrame(player->getCameraFrame());

// For now make the player invisible (prevent issues w/ seeing model from inside)
player->setVisible(false);

// Set the reset height
String resetHeightSource = "scene configuration \"resetHeight\" parameter";
float resetHeight = sessConfig->scene.resetHeight;
if (isnan(resetHeight)) {
float resetHeight = pscene->resetHeight();
resetHeightSource = "scene.Any Physics \"minHeight\" field";
resetHeight = pscene->resetHeight();
if (isnan(resetHeight)) {
resetHeightSource = "default value";
resetHeight = -1e6;
}
}
player->setRespawnHeight(resetHeight);

// Update the respawn heading
if (isnan(sessConfig->scene.spawnHeadingDeg)) {
if (firstSpawn) { // This is the first spawn in the scene
if (setSpawnPosition) { // This is the first spawn in the scene
// No SceneConfig spawn heading specified, get heading from scene.Any player entity heading field
Point3 view_dir = playerCamera->frame().lookVector();
float spawnHeadingDeg = atan2(view_dir.x, -view_dir.z) * 180 / pif();
Expand All @@ -527,13 +547,21 @@ void FPSciApp::initPlayer(bool firstSpawn) {
}

// Set player respawn location
float respawnPosHeight = player->respawnPosHeight(); // Report the respawn position height
if (sessConfig->scene.spawnPosition.isNaN()) {
if (firstSpawn) { // This is the first spawn, copy the respawn position from the scene
if (setSpawnPosition) { // This is the first spawn, copy the respawn position from the scene
player->setRespawnPosition(player->frame().translation);
respawnPosHeight = player->frame().translation.y;
}
}
else { // Respawn position set by scene config
player->setRespawnPosition(sessConfig->scene.spawnPosition);
respawnPosHeight = sessConfig->scene.spawnPosition.y;
respawnHeightSource = "scene configuration \"spawnPosition\" parameter";
}

if (respawnPosHeight < resetHeight) {
throw format("Invalid respawn height (%f) from %s (< %f specified from %s)!", respawnPosHeight, respawnHeightSource.c_str(), resetHeight, resetHeightSource.c_str());
}

// Set player values from session config
Expand Down Expand Up @@ -621,6 +649,9 @@ void FPSciApp::updateSession(const String& id, bool forceReload) {
m_loadedScene = sessConfig->scene;
}

// Player parameters
initPlayer();

// Check for play mode specific parameters
if (notNull(weapon)) weapon->clearDecals();
weapon->setConfig(&sessConfig->weapon);
Expand Down Expand Up @@ -649,9 +680,6 @@ void FPSciApp::updateSession(const String& id, bool forceReload) {
materials.set(id, makeMaterials(tconfig));
}

// Player parameters
initPlayer();

const String resultsDirPath = startupConfig.experimentList[experimentIdx].resultsDirPath;

// Check for need to start latency logging and if so run the logger now
Expand Down Expand Up @@ -710,19 +738,19 @@ void FPSciApp::onAfterLoadScene(const Any& any, const String& sceneName) {

// Make sure the scene has a "player" entity
shared_ptr<PlayerEntity> player = scene()->typedEntity<PlayerEntity>("player");

// Add a player if one isn't present in the scene
if (isNull(player)) {
m_sceneHasPlayerEntity = notNull(player);
if (!m_sceneHasPlayerEntity) { // Add a player if one isn't present in the scene
logPrintf("WARNING: Didn't find a \"player\" specified in \"%s\"! Adding one at the origin.", sceneName);
shared_ptr<Entity> newPlayer = PlayerEntity::create("player", scene().get(), CFrame(), nullptr);
scene()->insert(newPlayer);
}

// Set the active camera to the player
const String pcamName = sessConfig->scene.playerCamera;
playerCamera = pcamName.empty() ? scene()->defaultCamera() : scene()->typedEntity<Camera>(sessConfig->scene.playerCamera);
alwaysAssertM(notNull(playerCamera), format("Scene %s does not contain a camera named \"%s\"!", sessConfig->scene.name, sessConfig->scene.playerCamera));
setActiveCamera(playerCamera);
// Build lookup of initial camera positions here
Array<shared_ptr<Camera>> camArray;
scene()->getTypedEntityArray<Camera>(camArray);
for (shared_ptr<Camera> cam : camArray) {
m_initialCameraFrames.set(cam->name(), cam->frame());
}

initPlayer(true); // Initialize the player (first time for this scene)

Expand Down
5 changes: 4 additions & 1 deletion source/FPSciApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,12 @@ class FPSciApp : public GApp {
MouseInputMode m_mouseInputMode = MouseInputMode::MOUSE_CURSOR; ///< Does the mouse currently have control over the view
bool m_showUserMenu = true; ///< Show the user menu after update?

bool m_firstSession = true;
bool m_firstSession = true; ///< Flag indicating that this is the first session run
UserConfig m_lastSavedUser; ///< Used to track if user has changed since last save

bool m_sceneHasPlayerEntity = false; ///< Flag indicating whether loaded scene has a PlayerEntity specified
Table<String, CFrame> m_initialCameraFrames; ///< Initial frames for all cameras in the scene

shared_ptr<PlayerControls> m_playerControls; ///< Player controls window (developer mode)
shared_ptr<RenderControls> m_renderControls; ///< Render controls window (developer mode)
shared_ptr<WeaponControls> m_weaponControls; ///< Weapon controls window (developer mode)
Expand Down
18 changes: 7 additions & 11 deletions source/PlayerEntity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,34 +45,31 @@ shared_ptr<Entity> PlayerEntity::create
// Initialize each base class, which parses its own fields
playerEntity->Entity::init(name, scene, position, shared_ptr<Entity::Track>(), true, true);
playerEntity->VisibleEntity::init(model, true, Surface::ExpressiveLightScatteringProperties(), ArticulatedModel::PoseSpline());
playerEntity->PlayerEntity::init(Vector3::zero(), Sphere(1.0f));
playerEntity->PlayerEntity::init(Sphere(1.0f));

return playerEntity;
}


void PlayerEntity::init(AnyTableReader& propertyTable) {
// Get values from Any
Vector3 v;
float heading = 0.0f;
propertyTable.getIfPresent("heading", heading);
Sphere s(1.5f);
propertyTable.getIfPresent("collisionSphere", s);
Sphere collisionSphere(1.5f);
propertyTable.getIfPresent("collisionSphere", collisionSphere);
// Create the player
init(v, s, heading);
init(collisionSphere);
}

float PlayerEntity::heightOffset(float height) const {
return height - m_collisionProxySphere.radius;
}

void PlayerEntity::init(const Vector3& velocity, const Sphere& collisionProxy, float heading) {
void PlayerEntity::init(const Sphere& collisionProxy) {
m_collisionProxySphere = collisionProxy;
m_desiredOSVelocity = Vector3::zero();
m_desiredYawVelocity = 0;
m_desiredPitchVelocity = 0;
m_spawnHeadingRadians = heading;
m_headingRadians = heading;
m_spawnHeadingRadians = frame().getHeading();
m_headingRadians = frame().getHeading();
m_headTilt = 0;
}

Expand All @@ -84,7 +81,6 @@ bool PlayerEntity::doDamage(float damage) {
Any PlayerEntity::toAny(const bool forceAll) const {
Any a = VisibleEntity::toAny(forceAll);
a.setName("PlayerEntity");
a["heading"] = m_headingRadians;
a["collisionSphere"] = m_collisionProxySphere;
return a;
}
Expand Down
12 changes: 7 additions & 5 deletions source/PlayerEntity.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ class PlayerEntity : public VisibleEntity {
#pragma clang diagnostic ignored "-Woverloaded-virtual"
#endif
void init(AnyTableReader& propertyTable);

void init(const Vector3& velocity, const Sphere& collisionSphere, float heading=0);
void init(const Sphere& collisionSphere);
#ifdef G3D_OSX
#pragma clang diagnostic pop
#endif
Expand Down Expand Up @@ -88,7 +87,7 @@ class PlayerEntity : public VisibleEntity {
bool slideMove(SimTime deltaTime);

float heightOffset(float height) const;

float respawnPosHeight() { return m_respawnPosition.y; }
bool doDamage(float damage);

/** In world space */
Expand All @@ -98,7 +97,9 @@ class PlayerEntity : public VisibleEntity {

const CFrame getCameraFrame() const {
CFrame f = frame();
f.translation += Point3(0.0f, heightOffset(m_crouched ? *crouchHeight : *height), 0.0f);
if (notNull(height)) {
f.translation += Point3(0.0f, heightOffset(m_crouched ? *crouchHeight : *height), 0.0f);
}
return f;
}

Expand All @@ -113,7 +114,8 @@ class PlayerEntity : public VisibleEntity {
void respawn() {
m_frame.translation = m_respawnPosition;
m_headingRadians = m_spawnHeadingRadians;
m_headTilt = 0.0f;
m_headTilt = 0.0f; // Reset heading tilt
m_inAir = true; // Set in air to let player "fall" if needed
setDesiredOSVelocity(Vector3::zero());
setDesiredAngularVelocity(0.0f, 0.0f);
}
Expand Down
4 changes: 2 additions & 2 deletions tests/FPSciTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,11 +552,11 @@ TEST_F(FPSciTests, TestCameraSelection) {

SelectSession("defaultCamera");
s_app->oneFrame();
EXPECT_TRUE(s_app->playerCamera->name() == "camera");
EXPECT_TRUE(s_app->playerCamera->name() == "camera") << "Camera name was " << s_app->playerCamera->name().c_str();

SelectSession("customCamera");
s_app->oneFrame();
EXPECT_TRUE(s_app->playerCamera->name() == "customCamera");
EXPECT_TRUE(s_app->playerCamera->name() == "customCamera") << "Camera name was " << s_app->playerCamera->name().c_str();

}

Expand Down

0 comments on commit 7818659

Please sign in to comment.