Project Raygine #16
Rough week. I added plenty of new code to do stuff, but superficially, it doesn't look like much. BUT, at least the movement tool is now working in a not-totally-sucking way:
It's mimiccking the Blender movement tool: Pressing g to start moving, escape for cancel, enter or mouse click to comfirm. X, Y, Z for constraining the movement to the respective axis and shift for constraining it to the plane. Control for snapping. I am displaying various information visually for the movement, like a grid box to show the snapping grid, the axis that is currently constrained, the plane that is currently constrained. I think at some point I also want to draw text for the current position and so on, but that's way down the line.
One issue I am deferring for now is that my hotkeys are keyboard layout independent - that's annoying because I am working with a German keyboard here and "y" is mapped to "z", so the movement tool axis constraining is actually feeling like using blender since "z" is up this way 🙈. I briefly looked into this, but it looks like it's not easily fixed... so I'll have to live with it for now. I am sidetracked too much in total anyway already.
I got also clearer on how things are working and set up a command system that includes a searchable command list popup:
Pressing CTRL+SHIFT+P opens this popup (same shortcut as in VS Code). It shows a list of commands including its shortcut and short description. The list depends on the context, so when the user is in the scene view, it shows the commands that are available for the scene view. Currently that's only the movement tool, but more is about to come.
So commands are now the interface to the user to manipulate the editor. But now another thing is missing: Actions. An action is going to be an instruction that modifies the actual content. Currently, every change done is directly modifying the content, but I need to change this and utilize actions everywhere. The main benefit is, that this way I'll get undo and redo operations as well, but there's more that I think it could be used for: Repeating actions. The idea is, that simple repetitive tasks could be recorded and then be repeated. A simple example: Dublicate an entity, move it 1 unit to the right, repeat 10 times. I believe this would be really powerful.
When funnelling all changes through actions, the recording should be a little smarter than just recording the actions. Action agregation is something I gave some thought as well; when the movement tool is moving an object around, it should operate entirely through actions, but ultimately, it should be recorded as a single action. So the idea is, that actions can have a merge function that is called when the same action is repeated. For the movement action, this is simply adding the movement vector to the previous one. The undo system would be a simple snapshot utility that when a script triggers a "set undo marker" action, it simply places that marker onto the undo stack. If every bit of the editor uses actions, that will make utilizing undo and redo very easy, since it's simply a "mark_undo()" call.
I have to implement the action system next, because otherwise it'll become a huge refactoring to get it in... currently, only the movement tool and the inspector is actually changing the content; apart from some automatically created assets like the project settings in case they don't exist.
But the topic also scratches a topic that I've been trying to avoid dealing with: Avoiding reloads. This is real tricky. Currently, when a new script appears, I simply reload the editor. What should then happen with the undo recordings? I could serialize the undo recordings and then reload them (and I think this isn't too difficult either), but changed scripts may result in changed variable namings etc. So if a script's property called "Foo" get renamed to "Bar" and the action recordings recorded actions changing "Foo", after a script reload, they would be invalid. Probably for now it's better to trash the undo stack upon reload. Thing is, I also reload when models get updated - which has actually the same impact as scripts (which is why I opted for the reload).
Currently editor reloads are super cheap. It's taking less than a fraction of a second - something like 100ms. That's another reason why I am not feeling guilty of (ab-)using them. I have to watch how that works out. Eventually I need to become more careful with reloads, but for now, I think it's fine.
One issue I encountered and that ate quite a bit of my time was btw the feature of drawing stuff that is behind other objects in a transparent fashion; technically it's simple: Disable z-testing, draw transparent, enable z-testing, draw opaque version. Easy. But my code didn't work until I figured out that the draw commands are batched. There's no flush command in raylib, but I found a hack to triggered flushing. Here's the code to do that:
1 Camera2D camera = {0};
2 BeginMode2D(camera);
3 EndMode2D();
Yes, it's just like that. It looks like the "official" solution as well since raysan5 suggested it somewhere on that topic. I am OK with that, it's just annoying I have to do it 😅