Thursday, December 17, 2020

Dusting Off

It's obviously been a while since my last post. Life has happened, and honestly I burned myself out. I hope everyone that may read this has been safe over this past 1/2 year or so.

So what's been going on with Integral in the mean time?

  • In case this post didn't indicate, I've picked it back up. To build up my momentum again I cleaned up some code and made sure things still work as I remember them.
  • Quality of life issues have been addressed.
  • There is a functional World Space Manager that can be used to add, delete, and rename world spaces.
  • There is a functional Command History Manager that can be used to undo/redo commands with the click of a button.
  • Mouse gesture detection and handling have been rewritten.
  • World Spaces now acknowledge the bounds of their parents as physical constraints.
  • The renderer Angular component pipeline has been streamlined.
  • The application menu system is in the process of being converted from Angular-component-based to native Electron-based.

Quality of Life

Three button mouse gestures are supported. Middle button dragging pans the camera, and mouse wheel zooms in and out. This behavior is proving to be problematic with trackpads in development mode, as the Electron-level handling of right-clicking interrupts the gesture logic flow. That will be disabled for releases, but trackpads probably shouldn't be used in real-world scenarios either way.

When long-clicking (holding the left mouse button for at least 1 second and then releasing) on handles that overlap, a context menu is displayed allowing the user to choose which single handle should be selected. This addresses the likely scenario of selection handles becoming unclickable without objects that mask them being disabled.

World Space Manager

The World Space Manager is crude, but mostly functional. World Spaces can be created, deleted, and renamed. They can also be disabled (no selection or editing behavior) or made invisible. The screenshot below demonstrates the handle selection context menu with a non-default-named Zone and Sector (and others) as options.

World Space Manager on the left, handle selection in the center

Zones contain Sectors, Sectors contain Rooms, and Room contain Rooms. Zones also contain Background and Foreground Layers, and Layers contain Rooms. Room management isn't fully implemented, but as it stands it's possible to create Zones and Sectors and Layers within those Zones.

The Sector is now invisible, but its position handle can still be manipulated

The Sector is now inactive, leaving it visible but unable to be manipulated (its position handle is gone)

Command History Manager

There is a class-based, formal notion of a Command in the Integral editor. When executed, Commands would produce a CommandInstance that would be placed on a stack for the Command History. Each CommandInstance had specific logic for how to undo and redo its operation and was parameterized with the data needed to make those operations correct.

Originally I wanted key binding to work the same between the editor and stand-alone game runner, but that had issues. A lot of work was put into unifying different types of input into a command model. Mouse buttons and movement were encoded consistently with modifier keys (ctrl, alt, etc.) and regular keyboard input, and eventual controller support was planned. There were problems with this approach though, especially when handling mouse input. For example, if an Application wanted "mouse movement" updates with optional modifier keys, every combination of mouse movement + modifier key had to be registered to the handling Command for the system to pipe it correctly.

Currently, at the top-level there are two components that listen for all accepted input: the singular Web GL canvas, and the root Angular editor component. Input is broken down as follows:
  • Down events
  • Up events
Additionally, both sets of these events are tracked in terms of:
  • What happened last frame (down and up)
  • What happened this frame (down and up)
  • What is currently down (essentially, what was down last frame that isn't up this frame)
All input captured in the Web GL canvas is piped into the active Application, which decides whether or not the input should proceed on to the "system command handler." Similarly, all input captured NOT in the Web GL canvas is piped into the "system command hander". This allows the active Application to handle all input exactly as it sees fit and to govern if system commands should be interpreted.

Commands are managed on named Command Logs, which contain both a "history" and a "future". The history is the stack of commands that can be undone, and the "future" is the stack of commands that can be redone. Beyond the CommandInstance-specific logic, the acts of undoing and redoing are basically popping the CommandInstance from one stack and pushing it on to the other. The idea of having named Command Logs is to allow Applications to divide what Commands can be undone/redone into logically separate contexts; this will be useful for things like separating system preference changes from world structure changes or keeping model editing changes separate from scripting changes.

World Editor Command History


Command History, Partial Undo

Mouse Gesture Detection + Handling

Originally, the logic for detecting mouse gestures was completely intertwined with the logic for handling those gestures. It worked well and was even unit-tested, but it quickly fell apart when I started exploring adding more functionality to the editor. Specifically, clicking and dragging around handles as a single input model works well for data that already exists, but it failed to accommodate basic things like clicking on nothing to add a new data point--i.e. drawing a polyline.

There is now a core mouse gesture detector. It tracks and teases apart mouse movement, clicking for different mouse buttons, and dragging for different mouse buttons. This core mouse gesture detector accepts a mouse input mode that responds to these core mouse gestures accordingly.

This code is much cleaner and easier to maintain, and these two pieces are now more independent. Different mouse input modes can be plugged into the core gesture detector without affecting how the core gestures are detected. This arrangement naturally lends itself to having UI behavior logically- and domain-aligned to the code the supports and drives it, i.e. "Select Mode" vs "Resize Mode" vs different "Edit Modes".

World Space Bounds

World Spaces within other World Spaces are restricted to the bounds of those parent World Spaces. When resizing is implemented, those bounds will also be applied as constraints. The peers of World Spaces--i.e. Zone 1 > Sectors A and B--do not currently serve as constraints to each other; this is planned functionality for the near future.
The Sector (green) is constrained to be inside of the Zone (red)

Parent World Spaces move their children World Spaces as well, establishing a type of hierarchical scene graph.
Moving the Zone (red) also moved its child Sector (green)

Conclusion

There is obviously a lot of work to do. Most of the work, in fact--and this is just the editor. My goal is to build each feature solid and with forethought so functionality can be progressively implemented without having to redo previous work. I have been getting better at capturing bugs and new desired functionality in our ticketing system, while maintaining focus on my current tasking.

My next post will probably be an attempt at a high-level roadmap, with some possible emphasis on specific features I need to do a brain dump about.

No comments:

Post a Comment