o3de / sig-content

8 stars 12 forks source link

Proposed RFC Feature View Bookmarks #38

Closed Nachomartgar closed 2 years ago

Nachomartgar commented 2 years ago

Overview

This feature proposes a View Bookmark System in which the user can store several camera properties (position, orientation, fov, etc.) and allow them to group these Bookmarks in order to improve workflows such as level or environment design.

What is the relevance of this feature?

Currently, the only feature that can accomplish a similar behavior is the "Viewport Camera Selector". Nevertheless, this feature was not designed for that use and has the following drawbacks:

  1. It cycles between entities with camera components, so every time we want to add a new camera we need to create an entity with a camera attached to it. These entities are game objects that, if they are not removed, would be shipped to the final product.
  2. The cameras cannot be grouped.
  3. You can only modify the camera properties by accessing the entity that holds the camera component and then changing the properties in the component itself.

With the new system we would be storing just Bookmarks which are structs that contain different properties for the camera and then, when the user accesses one of those Bookmarks, we would load these properties into the main viewport camera. These Bookmarks also hold other information such as tags that will helpful in organizing widgets to easily display these Bookmarks.

Feature design description

This feature is all based in the View Bookmark concept which is a struct in which we store camera properties. Depending on the use case we want, these Bookmarks could be kept in memory or stored persistently. If they are stored persistently we may want to have them stored locally or shared amongst the development team. For Example:

Item Stored/Not stored Local/Shared
Restore the camera transform to the last known location of the camera in the level when it is opened. Stored Local
Transient Camera Transform (used to remember the camera transform when going in and out of game mode.) Not Stored Local
Shared Level View Bookmarks Stored Shared
Local Level View Bookmarks Stored Local

Local Bookmarks vs Shared Bookmarks

The only difference between these two options is where the data would be stored. If we want to store a bookmark locally we would store it in the Settings Registry (project/user/Registry/ViewBookmarks), whereas if we want to make it shared we would store it in the prefab itself.

Bookmark Widget

Bookmark Widget

Technical design description

This is our MVP.

Bookmark System Diagram

struct ViewBookmark
{
    AZ::Name m_cameraName;
    AZ::Transform m_cameraTransform;
    // This can be extended to contain more data
    // e.g. isOrthographic, cameraFov, etc
}

//! Interface for ViewBookmarkSystemComponentNotifications, which is the EBus that dispatches changes to listeners.
class ViewBookmarkSystemComponentNotifications
    : public ComponentBus
{
    //! Called when a new ViewBookmark is added.
    void OnNewViewBookmarkAdded(const ViewBookmark& savedBookmark){};

    //! Called when a new ViewBookmark is removed.
    void OnViewBookmarkRemoved(const ViewBookmark& removedBookmark){};

    //! Called when a ViewBookmark is modified.
    void OnViewBookmarkUpdated(const ViewBookmark& modifiedBookmark){};
};

//! Interface for ViewBookmarkSystemComponentRequestBus, which is an EBus that receives requests
//! to save, remove and modify Bookmarks.
class ViewBookmarkSystemComponentRequestBus
    : public ComponentBus
{
    //! Saves a new ViewBookmark.
    bool AddViewBookmark(ViewBookmark newBookmark) = 0;

    //! Removes a ViewBookmark.
    bool RemoveViewBookmark(ViewBookmark bookmarkToRemove) = 0;

    //! Modifies an existing ViewBookmark.
    bool UpdateViewBookmark(ViewBookmark BookmarkToModify , AZ::Transform newTransform) = 0;
};

class ViewBookmarkSystemComponent
    : public AzToolsFramework::Components::EditorComponentBase
    , public ViewBookmarkSystemComponentRequestBus::Handler
    , public ViewBookmarkSystemComponentNotifications::Handler
{
public:
    ViewBookmarkSystemComponent();

    static void Reflect(AZ::ReflectContext* context);

    ////////////////////////////////////////////////////////////////////
    // ViewBookmarkSystemComponentRequestBus

    //! Adds a new ViewBookmark.
    bool AddViewBookmark(ViewBookmark newBookmark);

    //! Removes a ViewBookmark.
    bool RemoveViewBookmark(ViewBookmark bookmarkToRemove);

    //! Updates an existing CameraBookmark.
    bool UpdateViewBookmark(ViewBookmark BookmarkToModify, AZ::Transform newTransform);

    ////////////////////////////////////////////////////////////////////
    // ViewBookmarkSystemComponentNotifications

    //! Called when a new ViewBookmark is added.
    void OnNewViewBookmarkAdded(const ViewBookmark& savedBookmark);

    //! Called when a new ViewBookmark is removed.
    void OnViewBookmarkRemoved(const ViewBookmark& removedBookmark);

    //! Called when a ViewBookmark is updated.
    void OnViewBookmarkUpdated(const ViewBookmark& modifiedBookmark);

private:
    AZStd::vector<ViewBookmark> m_viewBookmarks;
};

Storing the data

This is an example of how the Bookmark data would look like stored in the prefab

{
    "ContainerEntity": {
        "Id": "Entity_[1146574390643]",
        "Name": "Level",
        "Components":
        {
            "Component_[5126822819577294041]": {
                "$type": "ViewBookmarkComponent",
                "Id": 5126822819577294041,
                "ViewBookmarks": {
                    "LocalBookmarkFileName": "Level1639763579377.setreg",
                    "ViewBookmarks": [
                        {
                            "name": "MobSpawn_1",
                            "tags": ["Spawns"],
                            "Position": [
                                10.12,
                                140.0,
                                -45.5
                            ],
                            "Rotation": [
                                3.13,
                                -17.54,
                                46.98
                            ]
                        },
                        {
                            "name": "MobSpawn_2",
                            "tags": ["Spawns", "Combat"],
                            "Position": [
                                10.0,
                                20.0,
                                30.0
                            ],
                            "Rotation": [
                                30.0,
                                20.0,
                                10.0
                            ]
                        }
                    ]
                }
            },

        }
    },
    "Entities": {...},
    "Instances": {...}
}

and in the Settings Registry for the TestViewBookmarkLevel

{
    "TestViewBookmarkLevel1889822833412.setreg": {
        "LocalBookmarks": [
            {
                "name": "MobSpawn_1",
                "tags": ["Spawns"],
                "Position": [
                    24.0,
                    556.97998046875,
                    14.899999618530272
                ],
                "Rotation": [
                    -87.0,
                    -45.97999954223633,
                    4.900000095367432
                ]
            },
            {
                "name": "MobSpawn_2",
                "tags": ["Spawns", "Combat"],
                "Position": [
                    -54.0,
                    12.97998046875,
                    14.899999618530272
                ],
                "Rotation": [
                    -76.0,
                    -45.97999954223633,
                    4.900000095367432
                ]
            }
        ],
        "LastKnownLocation": {
            "Position": [
                199.0,
                1224.8900146484375,
                4413.0
            ],
            "Rotation": [
                10.0,
                20.88999938964844,
                0.0
            ]
        }
    }
}

What are the advantages of the feature?

This feature will improve workflows such as level design and environmental design by making them much more agile, allowing the users to edit, store and organize several camera properties of a level. It will also address several issues that are currently present in the engine.

Issues that will be addressed with this feature:

What are the disadvantages of the feature?

How will this be implemented or integrated into the O3DE environment?

The main part would be implementing this ViewBookmarkSystemComponent.

Are there any alternatives to this feature?

As mentioned before, we currently have the "Viewport Camera Selector" but this is a feature wasn't meant to be utilized in this context and users are just using it due to not having a proper feature for these kind of workflows.

How will users learn this feature?

Are there any open questions?

Changelog

[2020/02/03] - Renamed Camera Bookmark RFC to View Bookmark RFC to clarify feature scope.

[2020/03/07] - Updated with new data from investigating the issue.

galibzon commented 2 years ago

Awesome feature, looking forward to use it.

tjmichaels commented 2 years ago

It would be nice to be able to "share" a singular bookmark in some way so that another user could just cut/paste a singular string/value and have it add to their local bookmarks or maybe just update the camera to the new position/orientation as though it was a bookmark they clicked?

Nachomartgar commented 2 years ago

It would be nice to be able to "share" a singular bookmark in some way so that another user could just cut/paste a singular string/value and have it add to their local bookmarks or maybe just update the camera to the new position/orientation as though it was a bookmark they clicked?

Yes I agree, this could be a nice stretch goal! We could generate an URL (something like this o3de://set_camera/e2xldmVsX25hbW) with base 64 encoding that could be shared between developers. For example, if a designer finds a missing texture in the level this URL could be generated and shared with the artist responsible, then when clicked, it will set the camera to that specific position in the level.

nick-l-o3de commented 2 years ago

When I watched someone use this, they were using the existing level cameras to 'look through' so that they could get a WYSIWYG view of the viewpoint from that camera, including other camera settings like screen effects, color grading, etc.

Additional gems can in the future add an unknown quantity of additional screen effects of other components that affect what happens when you 'inhabit' a camera.

Would it not make more sense to bookmark cameras instead of some subset of camera properties? Or to copy those components to the current editor camera while using the bookmark? Or something?

nick-l-o3de commented 2 years ago

As for merge conflicts, its an interesting question, that is, where to save them. Its possible to save these bookmarks in user preference space instead of level, since the level already contains actual cameras, which is what is supposed to be saved. This would reduce conflicts but not allow sharing. A combo of some means of sharing, especially if it could be string encoded, as well as saving in user space might be ideal but is worth further thought.

HogJonny-AMZN commented 2 years ago

TL/DR

My gut tells me that this would be a more ideal default pattern for storage:

  1. I can define default global editor camera bookmarks (like 3/4 ortho split views)
  2. I can define the default file location rule for bookmark storage (nested under a level)
  3. I can promote a bookmark from User (local) to Persistent (shared), even if the first version is a manual copy/paste between files
  4. I can decide if a bookmark is serialized (into a Prefab) or stored externally (in a .camerabookmark.setreg)
  5. I can store locally relative 'hero view' bookmarks along with a Prefab (in the example above, the Witches Hut, or in a really large Castle.)

(valuable future) I can use the camera bookmark system in automation (generate dailies!); generate screenshots from multiple views in a level, upload/stash them on a S3 bucket, and build a confluence discussion page so a group can review, discuss, and perform collaborative group project planning. (this is a step towards high value production tracking workflows ... like integrating with Autodesk ShotGrid (used to be called Shotgun))


Notes: @gadams3 please weigh in

  1. I think this RFC needs to be a little bit more clear that this is a ethereal 'Editor Camera' only system (unless it is not.)
  2. "These entities are game objects that, if they are not removed, would be shipped to the final product." <-- All Entities have a 'Editor Only' state, the original intent was these are pruned from the exported level. We don't export levels anymore but we do still have this state (these are things we need to keep, there are other use cases for defining 'editor only' entities for authoring convenience - like having 'editor only' dynamic vegetation gradient signal node-graph to author a complex-composition in realtime but for optimization purposes generate a 'baked image gradient'; the only entity marked for runtime is the final 'Image Gradient' node that holds the baked representation via loading the generated (baked) image gradient asset. Any entity based system should respect these states IMO.
  3. My belief is that it is probably a P0-priority that I can easily save camera book marks based on cameras existing within the level, this would allow me to leave cameras parked as needed but quickly jump the view around. Ideally the bookmark could store a ref to the camera it was made from and allow for the user to easily/quickly select the camera related to the bookmark (and of course, warnings/errors if that camera was removed ... I can still go to the camera view but the 'select bookmark camera' button or other mechanism is disabled.)
  4. Another way to consider this might be to add a flag on camera entities 'persist camera as bookmark', so that I can aggregate loaded cameras selectively to participate in the bookmarking system and workflows (they might come from/with a prefab that is part of a segmented world, or a hero landmark prefab, etc.)
  5. How does this relate to multiple-viewports like a CAD style 4 way split (1 perspective, 1 top, 1 side, 1 front) and the ability for any viewport to easily change camera views?
  6. 'Be this Camera' is P0-priority and vital to workflows such as keyframed camera sequences that might for instance alter the FOV dynamically. So it feels like to me that even a nice bookmarking system may not keep us from encountering some of the other issues that are high friction? (so how will we address those things, a separate RFC or just treat them as 'user experience bugs'?)
  7. There is need to build things like a 'virtual spectrometer' that can be placed in a level to inspect and tune HDR lighting (exposure); the easiest (and only) way to build this today is "Entity.Prefab with: Sphere model with custom material, a camera with a view that maximizes screen coverage of the sphere, and the camera has a Exposure component that has the 'Exposure Histogram' enabled. I want to drop multiple of these around my level to inspect lighting, I need control over if they persist to runtime (for debug views) or are pruned as 'Editor only'. Just stating another type of use case where 'be this camera' and 'editor only' entity states are useful.
  8. A) Other data-driven mechanisms exist which provide the ability to define data for 'authoring convenience' and allow that data to be stored anywhere in the project (the core project, an asset gem, etc.) Two examples are the 'lightingpreset.azasset' files which is used to define HDR lighting environments that propagate to the material editor, and 'modelpreset.azasset' which allows me to define one of my existing 3d models to be used for preview in the material editor. Part of the value is that in addition to the defaults, I can specifically propagate project related data into my tools and workflows.
  9. B) There are systems with mechanisms in place to capture and store local / level specific data, the example I will use is 'baked specular reflection probes'. The interesting extension here, is that I can capture lighting specific to a location within a level, then use a 'lightingpreset.azasset' to propagate that lighting for use in the material editor (I can craft a library of lighting environments from my game levels into the material authoring workflow which is a super radical convenience for a group of material specialists.) What hinders this currently is that all probes are baked into a single folder at the project root (I have little control over where they are baked, and this doesn't scale well as the number of levels and number of probes within levels increases - it becomes a data management nightmare. What belongs to what? What is stale?) If I want to manage where probes are stored, like moving a capture to a gem as a default baked reflection cube map for modular assets (which will mean it's materials preview well in isolation outside of a level!), or turning them into 'lightingpreset.json' there is some manual work; like copying, renaming, etc, and adding baked probes as the default asset loaded within a 'reflection probe component' on a stored prefab (manual workflows to copy/move and reuse data are ok, but not storing level related data near the level ends up very cumbersome.)
  10. ^ What I am saying is that we really don't particularly like a default storage location to be at the project root, a folder nested under the level root would be better for level related data, but user choice in many cases is preferred (how one thinks about data organization should be team/project based and user choice, because the mental context and recall can make a huge difference for team efficiency during scaled production.)
  11. What many Level Designers and Design Technologist have asked for is a project based 'File Rule System' that allows me to define storage patterns for these type of systems that capture data. Such as mechanisms to choose where and how bookmarks are stored.
HogJonny-AMZN commented 2 years ago

It would be nice to be able to "share" a singular bookmark in some way so that another user could just cut/paste a singular string/value and have it add to their local bookmarks or maybe just update the camera to the new position/orientation as though it was a bookmark they clicked?

Yes I agree, this could be a nice stretch goal! We could generate an URL (something like this o3de://set_camera/e2xldmVsX25hbW) with base 64 encoding that could be shared between developers. For example, if a designer finds a missing texture in the level this URL could be generated and shared with the artist responsible, then when clicked, it will set the camera to that specific position in the level.

Yeah this is super useful for data stored in a bug! When I was at R*NY we had a pretty good bug system for GTA that automated the stored of metadata like this along with screenshots. (other metadata was stored like the level / sequence, and some gamestate data.)

HogJonny-AMZN commented 2 years ago

When I watched someone use this, they were using the existing level cameras to 'look through' so that they could get a WYSIWYG view of the viewpoint from that camera, including other camera settings like screen effects, color grading, etc.

Additional gems can in the future add an unknown quantity of additional screen effects of other components that affect what happens when you 'inhabit' a camera.

Would it not make more sense to bookmark cameras instead of some subset of camera properties? Or to copy those components to the current editor camera while using the bookmark? Or something?

See my comment but yes I agree, lets make sure we define and consider these critical use cases.

AMZN-daimini commented 2 years ago

When I watched someone use this, they were using the existing level cameras to 'look through' so that they could get a WYSIWYG view of the viewpoint from that camera, including other camera settings like screen effects, color grading, etc.

Additional gems can in the future add an unknown quantity of additional screen effects of other components that affect what happens when you 'inhabit' a camera.

Would it not make more sense to bookmark cameras instead of some subset of camera properties? Or to copy those components to the current editor camera while using the bookmark? Or something?

I think the discussion is starting to create some confusion between the purposes of this new system (Camera Bookmarks) and those of the existing one (Be This Camera).

The existing "Be This Camera" system allows users to "inhabit" existing camera gameobjects in the scene. While you "are" that camera, all changes to the view (both via keyboard inputs, mouse inputs on the viewport etc) are reflected on the gameobject transform and camera components until the user leaves the camera and returns to the editor camera. This system was created to allow users to more easily make changes to an in-game camera entity.

Subsequently, users started using this system for a purpose it was not built for. Users wanted to save multiple positions in the level and easily switch between them in the editor. Mind you, these are not meant to be in-game cameras that will be used in gameplay; alas, using the "Be This Camera" system for that objective creates artifacts that are saved to the level and loaded in-game. Creating them as editor-only would mitigate that, but objectively that's more of a workaround than a feature. Additionally, "Be This Camera" alters the camera entity's position when the camera is moved, meaning that those entities are constantly changing and don't provide fixed positions in the world, which kind of goes against the user's intention.

The new "Camera Bookmarks" feature is built to serve this new and different use case. Camera bookmarks are just data, and whenever the user selects one, it just moves the existing editor camera to that position. This would not remove "Be This Camera", which has a completely different purpose.

AMZN-daimini commented 2 years ago

It would be nice to be able to "share" a singular bookmark in some way so that another user could just cut/paste a singular string/value and have it add to their local bookmarks or maybe just update the camera to the new position/orientation as though it was a bookmark they clicked?

Yes I agree, this could be a nice stretch goal! We could generate an URL (something like this o3de://set_camera/e2xldmVsX25hbW) with base 64 encoding that could be shared between developers. For example, if a designer finds a missing texture in the level this URL could be generated and shared with the artist responsible, then when clicked, it will set the camera to that specific position in the level.

Agreed, although I'd be oriented towards just using json directly. We have discussed potentially doing something similar with entities in prefabs in the future (select an entity in the Outliner, press Ctrl+C, and that actually stores the json representing the entity in your clipboard.

AMZN-daimini commented 2 years ago

Honestly, I am somewhat partial to the idea of differentiating between local and shared camera bookmarks. I think it will just introduce confusion and be a source of complication on the UX side. Ideally, all saved bookmarks could be stored on the level (not including the transient ones of course). Not against the current setup if we can make the difference clear and transparent to the user though.

Another thing to keep in mind, then, is the fact that levels are prefabs, and viceversa. Will all prefabs possibly contain bookmarks? If so, how will they be handled in the UI?

Aside from the comments above, a few suggestions (which likely would be more of a stretch goal):

tjmichaels commented 2 years ago

It would be nice to be able to "share" a singular bookmark in some way so that another user could just cut/paste a singular string/value and have it add to their local bookmarks or maybe just update the camera to the new position/orientation as though it was a bookmark they clicked?

Yes I agree, this could be a nice stretch goal! We could generate an URL (something like this o3de://set_camera/e2xldmVsX25hbW) with base 64 encoding that could be shared between developers. For example, if a designer finds a missing texture in the level this URL could be generated and shared with the artist responsible, then when clicked, it will set the camera to that specific position in the level.

Agreed, although I'd be oriented towards just using json directly. We have discussed potentially doing something similar with entities in prefabs in the future (select an entity in the Outliner, press Ctrl+C, and that actually stores the json representing the entity in your clipboard.

I think having something compact is important here for numerous reasons (usability, transportability, etc) versus having something readable/editable

HogJonny-AMZN commented 2 years ago

Subsequently, users started using this system for a purpose it was not built for.

Uhm, we've been using it this way for many years, specifically we leveraged this A Lot while demoing at GDC, etc. We should lean into what users feel is intuitive and understand why they are in the frame of context.

AMZN-daimini commented 2 years ago

Subsequently, users started using this system for a purpose it was not built for.

Uhm, we've been using it this way for many years, specifically we leveraged this A Lot while demoing at GDC, etc. We should lean into what users feel is intuitive and understand why they are in the frame of context.

I don't think the fact that a tool has been erroneously used for a certain purpose for a long time means it's the most intuitive or best way to provide the same functionality. If anything, it highlights that the current UX for "Be This Camera" is very confusing, and that we don't provide the functionality the user wants in that context.

Adi-Amazon commented 2 years ago

This RFC is a good direction and proposal, and definitely an area O3DE does not give good answer.

Having said that I have several questions and observations:

  1. Moving the system from using entities is not mandatory in order to achieve its functionality, but by doing so you now lose the ability to use the editor 'as is' due to the manipulation of entities and transforms. Unless I misunderstood, this will force dedicated handling and overhead for implementation and maintain non-core functionality of the system.
  2. How will the system affect existing camera functionality and hooks or support equivalent? for example, a camera can be chosen as a script camera and take part in cut-scene and animation. The proposed system will need to support it properly and seamlessly.
  3. You mentioned that cameras 'cannot be grouped' today, yet in my mind, grouping is almost the same as setting all desired camera entities under the same sub-tree root node, and in level development, it is customary to create group root nodes according to desired themes (lighting, cameras, characters..) and set entities beneath them exactly for that purpose.
  4. Cameras are a very relevant per level necessity and feature and therefore saving cameras in a user registry file makes less sense to me towards correct workflow or at game production level.
  5. Sharing cameras a cross the production is a big win but this can be achieved also by creating a root node, adding cameras under it and saving as a prefab. This prefab is now be shared across the production. The usual conflict as per your suggestion for who saves and shares always arises, but the main question is - what is the benefit of using the proposed system over a prefab with all cameras?

All in all, the points that you raised in this RFC are very important and as per Guthrie I see them as P0-P1. What is not quite clear is whether there is a major win in creating such a system over adding a layer on top of the existing today to get the functionality you described that will now benefit from the generic functionality inherited by the entities, components and GUI.

HogJonny-AMZN commented 2 years ago

I will say I really like this bookmark RFC and the general direction (super valuable) I still have some skepticism to remove. One aspect of RFCs are for feeling out the questions and poke holes, or suggest potential alternatives. Not trying to confuse the discussion, just having a solid discussion that allows us to view the problem thought many facets to understand the implications and impact (we all want the same thing, which is great experience design.)

Users wanted to save multiple positions in the level and easily switch between them in the editor.

Agreed. There are SO many use cases for parking cameras (or views), from the obvious (persisting intended views), for workflows (multi-view editing, like this is vital to lighting), to the technical (level automation processes).

Bookmarking implies marking an object directly with pertinent locations (a page in a book); we are definitely talking about scene bookmarks. The RFC is saying the bookmarking system is better then how users use "Viewport Camera Selector" today (for a similar purpose) that sounds fine, give me something better (and I know people have desiring better camera workflows.)

I completely get the usefulness of breadcrumbing type mechanics, bookmarking locations in any viewport sounds great. (like bookmarking a particular viewpoint in a complex blueprint graph in unreal, helps you quickly hope around the canvas view and are stored with that graph.)

Should we also be talking about per-camera bookmarks (I think so)? I know several users who I know are familiar with several 3D DCC tools and likely then also familiar with their bookmarking systems, how do you know that something like this isn't what they are actually asking for? https://www.fxfx.net/how-to-bookmark-camera-angle-in-maya/

Or more like this? https://blendermarket.com/products/bookmark-view/docs Where I can log many bookmarks, then independently apply them to any viewport.

If any viewport could potentially "Be this camera" (Or any similar valuable use like, "Look Through Light")? And if users wanted to apply view bookmarks, to any viewport, which could be viewing any camera, what do we do?

In DCC tools (lets say CGi workflows), 'the stage' is composed of many layers data from referenced files. This is not really much different from levels and nested prefabs. Any of these referenced files may have their own sets of cameras, and each camera may have bookmarks. One nice thing Maya allows for, is to provide namespaces for references - the scene (level prefab) might have 'PerspectiveCamera' and the nested scene (prefab) might have 'Foo:PerspectiveCamera' (this helps distinguish ownership across complex scene data in a way you can grok on inspection)

Scenes get complex quickly, and so I feel like even with an MVP, we need to know what the North Star is (the outcome we are hopefully driving towards) So how do you think this is going to scale to larger sets of bookmark locations in a partitioned vast open worlds, etc.? Because I am definitely going to want to build a castle prefab and store bookmarks in that scene, and then place that castle in a larger level and bookmark locations in that scene as well.

Honestly most 3D tools just use a collection of the same stateful 'camera node' class, only a few are specially configured as default views for scene editing (in Maya a scene comes with a default set: Persp, and 3 ortho), these are just cameras:

So treating a camera (or collection of them) as 'Editor Only' would be the equivalent Not a workaround And so there is an alternative design here, which might be more straightforward, and better preserve user intent.

  1. Multiple viewports: 3/4 in a single layout for world editing, a floating panel looking through a camera shot, another looking through a light.
  2. Any viewport, can view any camera (or actually any object that it wants to look through, like a Light)
  3. Any camera can be bookmarked (or any objects transform being used as a view like a Light.)
  4. In the bookmarking tool panel*, I see a set of bookmarks for either:
    • the bookmarks for the camera of the viewport that is currently in focus
    • or is the set for a camera is currently explicitly selected (the selected camera takes priority)

So what I might consider is an alternative....

  1. Default scene views, are 'Editor Only' cameras (in a group under the level prefab)
  2. Move your design to a Entity Bookmark Component (per-camera bookmarks naturally serialize. But IMO this might be a case where I want the bookmark json as a sidecar data file.)
  3. MVP:
  4. User can manually add Bookmark Component to Camera
  5. User can manage Bookmarks within the Component (as some kind of list?)
  6. If a Camera is 'Editor Only' then it and it's bookmarks are naturally pruned
  7. If a Camera is in a nested prefab, great you get portable re-usable bookmarks (every level the Castle is placed in, I get access to Castle related camera view bookmarks)
  8. Possible Freebie 1: any entity gets transform bookmarking and feature set for free!
  9. Possible Freebie 2: bookmarking can be made use of in any custom workflows (python level automation, new python tools that are already being scripted against the entity/component system.)
  10. Possible Freebie 3: bookmarking data accessible at runtime FTW!!! (embedded and annotated views might be useful to Digital Twin actually)

Questions and thoughts I have:

  1. What is the overhead of cameras that are not pruned from runtime?
  2. What is the overhead if bookmarks at runtime?
  3. Embedded and persistent mechanisms feels like a feature, when I need it I need it (or want it)
  4. Physically placed cameras have a distinct locality to them, I can see them, select them, and inherently understand them.
  5. Pruning is an optimization, optimization is a task, how much or what is user choice; what I want is flexibility and choices.
  6. Ideally we are indexing on building core systems that are multi-purpose (we have good patterns in place, for layers of UX above core data models.)

it's not the most intuitive or best way to provide the same functionality.

  • from my POV apparently many people did intuit exactly this (and it wasn't always problematic), they didn't complain about it being unintuitive, they complained when it stopped functioning the way they expected (based on prior experience)
AMZN-daimini commented 2 years ago

So treating a camera (or collection of them) as 'Editor Only' would be the equivalent Not a workaround

I disagree with this, they are different operations with different results.

Let's say we establish this workflow:

  1. Open a level in the Editor;
  2. Use either method to get to a specific view in the viewport (let's call it VIEW1);
  3. Move the camera via the viewport (for example, use the scrollwheel to zoom);
  4. Use either method to get to a different view in the viewport;
  5. Use either method to go back to VIEW1.

If you do this with the current "Be This Camera" workflow, point 5 will result in the viewport displaying the position after the zoom operation. That's because changes to the camera done in the viewport are saved to the camera entity, and those result in the level being edited (even when the entity is editor-only, it is still saved in the level prefab). This is because "Be This Camera" is designed to offer an intuitive way to alter entities in the scene.

If you do the same workflow with the "Camera Bookmarks" system detailed above, point 5 will result in the viewport displaying the same exact view as point 2. Effectively, CameraBookmarks are not entities in the scene, they're pretty much the equivalent of using the "Go to Position" option, and avoid having to supply the string defining the position/rotation of the camera manually every time.

For what I can understand, we've been using the "Be This Camera" system to achieve results that are not really what it was intended to do. If anything, I think "Be This Camera" should actually be changed to be the Component Mode of the Camera Component - that would likely clarify the difference between the two systems, in my opinion.

cgalvan commented 2 years ago

As far as the UI for the proposed tool panel goes, we do have a bookmark panel in GraphCanvas that is used for both ScriptCanvas and LandscapeCanvas, that similarly lets you save (bookmark) positions in a graph and switch between them. It also has several different ways of switching between them, including shortcuts, and a search filter as well.

This system doesn't handle local vs. shared bookmarks though, so I believe it's more helpful for having a consistent UI experience across our different tools as opposed to how to handle the actual data on the backend.

GraphCanvasBookmarks

HogJonny-AMZN commented 2 years ago

As far as the UI for the proposed tool panel goes, we do have a bookmark panel in GraphCanvas that is used for both ScriptCanvas and LandscapeCanvas, that similarly lets you save (bookmark) positions in a graph and switch between them. It also has several different ways of switching between them, including shortcuts, and a search filter as well.

This system doesn't handle local vs. shared bookmarks though, so I believe it's more helpful for having a consistent UI experience across our different tools as opposed to how to handle the actual data on the backend.

This is like Blueprint canvas bookmarks and these bookmarks are owned by the graph. In world building then this would be something like.

A graph is a view : a camera is a view. A graph can store location bookmarks : a camera can bookmark view locations.

Q: Do bookmarks aggregate if graphs are nested? Do you think shared bookmarks in the canvas tool have an important use case?

HogJonny-AMZN commented 2 years ago

TL/DR

All cameras are a common class object, several controllers operate camera data (at edit, or runtime), one of which is a action based bookmarking system. IMO to me, anything special about 'editor cameras' (and their views) is either because of their default configuration/presentation, or the way a User decides to treat them (like naming conventions or groups). I haven't changed my mind that our 'Editor Only' state is a valid mechanism to sort one set of cameras from another (by intent purpose) and prune a set of those cameras from runtime.

Viewports, views, cameras and bookmarks, if they can't just fit the MVC model like this it makes me wonder why?

  1. model = scene data, ECS entities (cameras exist in a scene) (should viewpoint bookmarks be model data, or be an external
  2. controller(s) =
    • edit time = viewport controls, world space gizmos (or direct entity inspector parameter editing of the model data)
    • runtime = script canvas, lua, trackview
  3. view = viewport presentation

So treating a camera (or collection of them) as 'Editor Only' would be the equivalent Not a workaround I disagree with this, they are different operations with different results.

This is the same as common 3d tools with bookmarking systems:

  1. the natural flow which includes viewports as an active controller of a camera entity
  2. a camera with bookmarks

If you do this with the current "Be This Camera" workflow, point 5 will result in the viewport displaying the position after the zoom operation.

That feels intentional. (the viewport is now the controller, the camera is the data it is operating)

That's because changes to the camera done in the viewport are saved to the camera entity.

For camera's this is often exactly what you want. Like moving a camera view to line up a shot, then keyframe that position. This is exactly the simple basic camera entity design that most 3D tools use.

In Maya (as an example): If a parameter (lets say transform.x, .y, .z) are keyframed, the params change color (red) to indicate this. This doesn't stop you from selecting and moving the camera around (during edit time.) The red indicates the current transform value will be overridden by keyframe data. You NEED to be able to move it around, so you can CRUD keyframe data. When you activate dynamic mode(s) like 'animation playback' (like going in and out of game mode), the camera will jump to the keyframed position.

If a param is bound by logic (like an expression), it turns purple, this indicates that you can't keyframe it. In fact it's permanently bound to the logical expression in both edit and dynamic mode. (this is something Chris Roby and I talked about many times, which is to make expressive constructs at edit time - like seeing a Rock scale based on world position. Anyway another conversation.)

When you exit the dynamic mode it doesn't return to the position it was in before dynamic mode, it's just left wherever you stopped (it's current keyframe value.)

^ that is something I realize is desirable to improve for viewports/cameras in O3DE when entering/exiting game mode (a departure from my story here, but that's addressable in Maya - you'd script a callback to store and then restore which camera and it's state to return to.)

If you want a camera position/view to be immutable, you lock it's transform (or part of it). Thinks keeps user-interactions from further editing or altering its data (it does not lock keyframe or logical expression from operating that data, only users actions.)

The default editor camera views (Perspective, Top, Front, Side):

But all cameras are camera nodes of the same class, and viewports are just one controller (which has to layer with other potential controllers.) And any camera can be bookmarked. And bookmarks are just an action based controller of cameras.

Users decide a new cameras purpose, "it is a scene editing camera", "its a cinematic camera", "its a camera rigged in a character controller"

For what I can understand, we've been using the "Be This Camera" system to achieve results that are not really what it was intended to do.

People eat with chopsticks, but they are just sticks. But OK "be this camera" is what it is, it's important, and needs to work just as well. Both are ways a user interacts with camera data and view switching. Therefore the new design, should state it's indentions and impact on the other camera interactions and workflows.

"Be This Camera" should actually be changed to be the Component Mode of the Camera Component.

Cameras and camera view editing should not be gated behind a button like mechanism. (unless you really enjoy death by a thousand clicks, and are ok with creating flow state interruptions users are not accustomed to encountering.) This would be so foreign to 3D tool users, the flow interruption will cause friction (My gut is that this will just become a 'complaint flywheel'.)

The locking out of data generally should be in the users control... sure I might accidentally edit a camera, however on the flipside too many guide rails, barriers and gating mechanisms is just a different kind of compounded frustration. One I can learn to avoid, the other I can not.

Component Modes (CM) purpose is 'modal editing' so we can have 'new' mode states, along with new actions and mechanisms, like hotkeys, and advanced modes for data manipulation (like Whitebox mesh data.)

shawstar commented 2 years ago

This looks good. We can use these Runtime as well I assume. Could we have a Vector3 POS for a Camera Target as well. Also, Locking of some Transforms and Rotations? Maybe I want to script this camera to do some dynamic animation, or a user can use the camera in with some restrictions, like only can rotate around etc.

Infurael commented 2 years ago

Some notes to clear up several topics mentioned in this thread:

  1. There is a mechanic to transform, update or delete entities/components during the processing when a Prefab (editor entities) are converted to Spawnables (runtime entities). The "Editor Only"-components inherited from the Slice workflow still works, but the recommended approach is to add a node to the Prefab Processing Stack. This will allow an opportunity to update the provide Prefab directly, allowing for more complex processing. This can include looking up information from the Settings Registry to determine if a camera should be removed or kept in the runtime. Providing such options can be useful for debugging and demoing purposes for instance.
  2. There has been a lot said about "level prefabs", but from the perspective of the Prefabs there is no special "level prefab", just a prefab with level information. There are still some workflows that were inherited from Lumberyard such as the level folder and opening a "level". Over time though those will be isolated so other workflows beside a level-centric one can be provided. For the camera information it's better to think of individual prefabs having the information rather than only levels. In particular this can mean that the same camera information is duplicated when the same spawnable is nested multiple times.
  3. Camera information that's stored in the Settings Registry may need to have some awareness about the prefab it references. Camera positions in one scene are unlikely to also apply to another scene for instance. There will always be a level of indirection there, while storing the camera settings in the prefab itself avoids this indirection and is therefore more reliable. On the other hand storing in the prefab will cause the camera settings to always be shared, while the .setreg option allows both sharing and personal settings.
  4. .setreg files can only live in Registry folders, so putting them in say /MyProject/Levels/Camera.setreg will not load those camera settings into the settings registry.
  5. Storing information into the Settings Registry doesn't mean they can't be shared. In fact the majority of .setreg are shared. Roughly the order of load is "shared engine settings" -> "shared project settings" -> "personal settings". It's therefore entirely possible to store camera information in the Settings Registry and still share the information while also supporting personal settings that are not shared. It all depends on what folder the .setreg files are stored in. (As a side note, this would also allow other specialization vectors such as per-platform camera settings which may be helpful on mobile if those camera settings are used at runtime.)
  6. A use case I don't think has been mentioned is async reviewing. This camera bookmarking can also be used to leave review notes that can be cycled through, potentially with a filter option for artists/designers.
HogJonny-AMZN commented 2 years ago

I've condensed, I think these are the only point of my thoughts that are actually important considerations.

Personally I am 100% in favor of replacing "Viewport Camera Selector" with something like "Viewport Bookmarks", however, doing so does not alleviate my need to 1.select, 2.switch, 3.activate, and 4.control game cameras in a view.

Here is how it basically doesn't add up for me though ...

You are explicitly suggesting we take away something that has performed 1.2. and 3 (the "Viewport Camera Selector") to get you to the state of 4 quickly, but you are suggesting to replace it with something that does not do 1.2.3 or necessarily 4. (sniff test, are you actually solving the same problem here?)

Therefor we may not have the core of a MVP users need (or the design and RFC is indexing on the wrong issues and assumptions as the tent poles suggesting it is needed.)

User needs:

So I ponder, what is a "Main Editor Viewport" in a state of multiplicity!?

This design is around storing bookmarks and assigning them to the 'Main Editor Viewport' (only).

The suggested design hasn't solved the inter-op when Bookmarks applied to --> 'Main Editor Viewport' when --> it's the view and controller for an 'active camera'. (I do not see how this can be decoupled, or not answered in this design.)

A Bookmark design that operates on a "Viewport", can not assume a singleton view forever, so a more general design is needed (design with a North Star in mind.)

Many details and comments suggest that this system is independent of others, like "be this camera".

However, the User Experience of Viewports, Viewport Controls, Cameras (virtual cameras like editor views, or placed camera entities), and Camera and/or View bookmarking, are in fact to me the User the same experience and part of scene management and fluid workflows. Personally I use all of these fluidly and in the flow don't think about views, cameras and bookmarks separately ( I am simply navigating my scene/world with the best use of mechanisms and my understanding of them. )

1) Not all 3d apps have bookmarking, but generally all 3d apps do allow the user to place, configure and manage collections of cameras around the scene.

2) All 3d apps generally allow you to 'look through' any camera, from any viewport; if that camera is considered active (or locked, there are a few variations), then the viewport is the camera view controller. This is the common baseline, thus the most likely to be encountered and naturally intuitive way to operate,

3) Bookmarks are not ubiquitous, they are actually supplemental to any core needs related to cameras, view switching, and navigating complex scenes.

(Let's maybe hold off on building a roof, until we are sure we have the solid framing to support it.)

Nachomartgar commented 2 years ago

Hi everyone! first of all I want to thank you for all your valuable feedback I really appreciate the time you've taken to improve this RFC. I also want to clarify some general points and question that I have seen so far and hopefully end some the confusion that might have been generated.

GoToLocation RememberLocation

lsemp3d commented 2 years ago

We discussed this RFC in the sig-content monthly meeting on 1/27.

I support this proposal.

monroegm commented 2 years ago

Approved to move into the final 5-day comment period at SIG-Content Triage on February 8, 2022. Please raise any final concerns on or before February 13, 2022.

hultonha commented 2 years ago

@monroegm Quick follow-up, should this now be closed? Or is it expected to be left in the open state? Thanks!