This PR adds a WIP timeline UI for animating elements in the scene.
### Implemented Features
* The timeline can be panned with MMB and zoomed with Ctrl + Mouse Wheel
* The timeline can be "scrubbed" by dragging the red "play-head" with
LMB
* The timeline sidebar (displaying the list of tracks) can be resized by
dragging on the edge of the sidebar
* Keyframes can be added by pressing the "Add Keyframe" CTA in the UI
* Keyframes can be selected for editing by clicking the diamond-shaped
markers in the timeline
* Selected entities (including but not limited to keyframes) can be
deleted by pressing the "Delete" key
* Keyframes can be moved temporally by dragging the diamond-shaped
markers in the timeline
### Known issues
* Zooming the timeline in too far results in a soft-lock
* When zooming the timeline, the "transform origin" is currently
centered on the `00:00` timestamp. For ease of use, it should center on
the mouse cursor position instead.
* The timeline track-list is currently hard-coded to list the Camera and
Camera Target. Clicking on these entries currently serves no useful
purpose and may result in a crash.
* Keyframe markers in the timeline are not currently associated with
entries in the track-list and simply appear vertically centered in the
timeline view.
* Added keyframes currently record the position and orientation of the
scene camera when added, and are rendered in the world with debug
visualizations, but serve no useful purpose. Some design work is needed
(both user-experience and architectural) to decide on a workflow for
actually animating scene elements.
* Entity deletion cannot currently be undone/redone
This PR adds a `StudioMode` resource to toggle editing capabilities, and
configures the frontend application to read the desired mode from a URL
parameter.
For now, setting the app to `StudioMode::Viewer` with
`<app-url>?mode=viewer` hides the Transform Toolbar and disables most of
the interactive features of the app, but otherwise exhibits the same
behavior as `StudioMode::Editor` / `<app-url>?mode=studio`.
In the future, the "Viewer" and "Editor" modes will receive different
initialization inputs to better serve the needs of their use cases. In
particular:
* The "Viewer" mode will accept a BVH animation handle, which should be
played in a loop on a simple mannequin asset, allowing a user to preview
a given animation before using it within the full studio workflow.
* The "Editor" mode will accept a full Bevy scene handle which can be
edited and saved to the user's account. These scenes will serve as the
basis for the generative-AI workflows Storyteller Studio is ultimately
seeking to facilitate.
* It will likely be useful for "Editor" mode to alternatively accept
only a BVH animation handle, which would be used to initialize a new
scene for editing similar to "Viewer" mode, but with editing
capabilities enabled.
* Migrate `System::pipe(...)` calls to `System::map(...)`
* Migrate `EventReader::iter()` and `EventReader::into_iter()` calls to
`EventReader::read()`
* Migrate asset handling
* Generate `*.meta` files for assets
* Migrate a shader import
* Fix emissive materials double-applying their emissive strength
* Fix non-normalized bone weights resulting in incorrect vertex positions
* Set shadow filtering to `Hardware2x2` to mitigate increase in shader
compilation times
#8 introduced some additional debug information to the view, which was already
rendering some information and visualizations which were not necessary relevant
to the end-user experience. This PR adds an input action (bound to Ctrl + `) to
toggle the display of those visualizations, and disables them by default.
This PR implements basic object selection, with help from `bevy_mod_picking` and
`bevy_mod_outline`.
### Work Done
* Click to select objects in the world
* Selected objects become visually highlighted
* The selected entity is available globally on the `Res<Selection>` resource
* Selected entities can also be queried via the `Selected` marker component
* Pressing `F` will "focus" the camera on the selected entity
* Pressing `Esc` will clear the active selection
### Known Issues
* We're currently using `bevy_mod_picking`'s "Selection" feature, which isn't
aware of our own input events. As a consequence, it will select the object
under the cursor when releasing `LMB` after orbiting the camera, which is not
what we want. This should be pretty easily fixable by dropping
`bevy_mod_picking`'s selection feature and reading the object under the
cursor in our own click listener that only runs if the LMB event is
unhandled.
* The skinned mesh is not currently selectable. This is likely fixable by
adjusting the logic in `scene::mark_scene_objects`... somehow. The `Gltf`
data structure is kind of a nightmare to work with, so this will take some
investigation.
### Future Enhancements
* For parity with other DCC apps, the "Focus" action should also adjust the
camera zoom level to "frame" the selected object in the view. Punting on that
for now because I'm not super confident about the math involved.
* Group selection with Shift/Ctrl and/or marquee selection would be really nice
to have, but probably not critical for now.
Adds `Shift` as an alternate modifier for FPS-style camera rotation, and binds
wheel events to camera zoom/dolly.
An attempt was made to translate 2D scroll events on a touchpad to camera
panning (this is what Blender does, and it's smooth as butter), but Bevy and/or
`winit` and/or the browser is doing some weird preprocessing of those events
before we're able to read them, which makes that unfeasible for now. That issue
has been documented with a `FIXME` comment.
This PR adds FPS-style controls to the camera in addition to the existing DCC-style pan/orbit/dolly controls. Additionally, some architectural changes have been made to facilitate this and pave the way toward making all of the app controls configurable in the future. Namely, a new `input` module has been added, loosely inspired by Unreal Engine's "Enhanced Input" system (and as I understand it, conceptually similar to Unity's "new" input system as well).
The basic idea is that raw device inputs (coming from `bevy::input`) are mapped to named, app-specific "input actions" before being consumed by other parts of the application. For example, in the camera motion system, instead of checking for mouse motion while the middle mouse button is pressed, we now check for a `CameraMotion::Pan` input action event.
Input actions carry information about their lifecycle phase (`Start`, `Ongoing`, and `Stop` — a stand-in for Bevy's `just_pressed`, `pressed`, and `just_released` methods) and some type of magnitude (`f32`, `Vec2`, or `Vec3` depending on the action).
At present, we've simply moved the hard-coded device input checks from the `orbit_cam` module to the new `input` module, but theoretically, this could eventually be refactored to read the mappings between device inputs and input actions from some kind of user configuration.