Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ If undo/redo is bolted into each feature separately, the project will accumulate

### Decision

Implement scale before rotate.
Implement scale before rotate, defer snapping to the grid until we have a better understanding of tools, undo/redo, and inspector behaviour.

### Why

Expand Down
53 changes: 37 additions & 16 deletions NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,11 @@ Scale fits the current object model because Prune already has width/height and r

Initial scope:

- Single selected authored object
- Corner or edge handle
- Minimum size clamp
- Inspector updates immediately
- Undo/redo command recorded when the drag completes
- [x] Single selected authored object
- [x] Corner or edge handle
- [x] Minimum size clamp
- [x] Inspector updates immediately
- [x] Undo/redo command recorded when the drag completes

### 7. Basic audio and event hooks

Expand All @@ -148,22 +148,43 @@ Preferred direction:

Initial scope:

- One or two hard-coded sound resources
- Fire event
- Hit/destroy event
- Global enable/disable toggle
- [ ] One or two hard-coded sound resources
- [ ] Fire event
- [ ] Hit/destroy event
- [ ] Global enable/disable toggle

## Later
## Medium term, in no particular order

- Z-Index ordering
- Rotate tool
- Grouping
- Copy/paste
- Prefabs/templates
- Delete action and command
- Background image support
- Simplified asset management
- Copy command
- Text rendering
- Paste command
- Behaviour toggles for authored objects
- Polished sample scenes
- Asset browser
- Animation/facing support
- Animated sprites/facing support
- Scene file versioning
- Grid snapping support on all tools and UI option, not in settings
- Runtime reset and reload, not just pause/resume
- Object locking and protection
- More robust collision shapes and collision options

## Later term, in no particular order

- Scene layering, rendering and collision etc.
- Grouping
- Input mapping and rebinding
- Scene settings panel for background, music, and other scene-wide options
- Game UI panels
- Pathfinding support
- Full audio mixer
- Many more tools
- Prefabs/templates
- Native playable export
- WebAssembly playable export
- Card scene
- Puzzle scene
- More robust collision shapes and collision options
- Many more scene tools
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@ Current priorities:
- [x] Command history panel
- [x] Multi-select
- [x] Multi-select move/delete
- [ ] Tool mode state
- [ ] Scale tool
- [x] Tool mode state
- [x] Scale tool
- [ ] Basic audio hooks

My development plan is tracked in [NOTES.md](NOTES.md), check the file for more details on what each of these points mean as well as what is included - this is what I will be working on in the next development phase.
Expand All @@ -204,18 +204,18 @@ My development plan is tracked in [NOTES.md](NOTES.md), check the file for more

I will consider Prune ready for users when the below is ready, this is the minimum I think users expect from a live editor/runtime prototype:

- Open app.
- Pick a scene type.
- Select object in viewport.
- Move with handle.
- Resize with scale tool.
- Duplicate it.
- Delete it.
- Undo/redo all of that.
- Save.
- Load.
- Behaviour still works.
- Runtime objects do not get accidentally edited or saved.
- [x] Open app.
- [ ] Pick a scene type. (Deferred for now, initial scene type is hardcoded to Platformer, others in new dropdown.)
- [x] Select object in viewport.
- [x] Move with handle.
- [x] Resize with scale tool.
- [ ] Duplicate it.
- [ ] Delete it.
- [x] Undo/redo all of that.
- [x] Save.
- [x] Load.
- [x] Behaviour still works.
- [x] Runtime objects do not get accidentally edited or saved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

## Documentation stance

Expand Down
10 changes: 9 additions & 1 deletion src/prune/editor/editor_tool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ namespace prune {

enum class EditorTool {
Select = 0,
Move
Move,
Scale
};

[[nodiscard]] inline constexpr const char* editor_tool_label(EditorTool tool) noexcept
Expand All @@ -14,6 +15,8 @@ namespace prune {
return "Select";
case EditorTool::Move:
return "Move";
case EditorTool::Scale:
return "Scale";
}

return "Unknown";
Expand All @@ -24,4 +27,9 @@ namespace prune {
return tool == EditorTool::Move;
}

[[nodiscard]] inline constexpr bool editor_tool_allows_scale(EditorTool tool) noexcept
{
return tool == EditorTool::Scale;
}

}
97 changes: 97 additions & 0 deletions src/prune/editor/tools/transform_gizmo.hpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
#pragma once

#include <array>

#include <SDL2/SDL.h>

namespace prune::editor::tools::transform_gizmo {

constexpr int k_outline_padding = 2;
constexpr int k_move_handle_size = 12;
constexpr int k_scale_handle_size = 8;

enum class ScaleHandle {
None = 0,
TopLeft,
Top,
TopRight,
Right,
BottomRight,
Bottom,
BottomLeft,
Left
};

inline constexpr std::array<ScaleHandle, 8> k_scale_handles{
ScaleHandle::TopLeft,
ScaleHandle::Top,
ScaleHandle::TopRight,
ScaleHandle::Right,
ScaleHandle::BottomRight,
ScaleHandle::Bottom,
ScaleHandle::BottomLeft,
ScaleHandle::Left
};

[[nodiscard]] inline SDL_Rect selected_outline_rect(const SDL_Rect& object_rect) noexcept
{
Expand All @@ -27,6 +53,38 @@ namespace prune::editor::tools::transform_gizmo {
};
}

[[nodiscard]] inline SDL_Rect scale_handle_rect(const SDL_Rect& selected_outline, ScaleHandle handle) noexcept
{
const int half_size = k_scale_handle_size / 2;
const int center_x = selected_outline.x + (selected_outline.w / 2);
const int center_y = selected_outline.y + (selected_outline.h / 2);
const int right = selected_outline.x + selected_outline.w;
const int bottom = selected_outline.y + selected_outline.h;

switch (handle) {
case ScaleHandle::TopLeft:
return SDL_Rect{ selected_outline.x - half_size, selected_outline.y - half_size, k_scale_handle_size, k_scale_handle_size };
case ScaleHandle::Top:
return SDL_Rect{ center_x - half_size, selected_outline.y - half_size, k_scale_handle_size, k_scale_handle_size };
case ScaleHandle::TopRight:
return SDL_Rect{ right - half_size, selected_outline.y - half_size, k_scale_handle_size, k_scale_handle_size };
case ScaleHandle::Right:
return SDL_Rect{ right - half_size, center_y - half_size, k_scale_handle_size, k_scale_handle_size };
case ScaleHandle::BottomRight:
return SDL_Rect{ right - half_size, bottom - half_size, k_scale_handle_size, k_scale_handle_size };
case ScaleHandle::Bottom:
return SDL_Rect{ center_x - half_size, bottom - half_size, k_scale_handle_size, k_scale_handle_size };
case ScaleHandle::BottomLeft:
return SDL_Rect{ selected_outline.x - half_size, bottom - half_size, k_scale_handle_size, k_scale_handle_size };
case ScaleHandle::Left:
return SDL_Rect{ selected_outline.x - half_size, center_y - half_size, k_scale_handle_size, k_scale_handle_size };
case ScaleHandle::None:
break;
}

return SDL_Rect{};
}

[[nodiscard]] inline bool contains_point(const SDL_Rect& rect, int x, int y) noexcept
{
return x >= rect.x &&
Expand All @@ -35,4 +93,43 @@ namespace prune::editor::tools::transform_gizmo {
y < rect.y + rect.h;
}

[[nodiscard]] inline ScaleHandle scale_handle_at_point(const SDL_Rect& selected_outline, int x, int y) noexcept
{
for (const ScaleHandle handle : k_scale_handles) {
if (contains_point(scale_handle_rect(selected_outline, handle), x, y)) {
return handle;
}
}

return ScaleHandle::None;
}

[[nodiscard]] inline bool scale_handle_resizes_left(ScaleHandle handle) noexcept
{
return handle == ScaleHandle::TopLeft ||
handle == ScaleHandle::BottomLeft ||
handle == ScaleHandle::Left;
}

[[nodiscard]] inline bool scale_handle_resizes_right(ScaleHandle handle) noexcept
{
return handle == ScaleHandle::TopRight ||
handle == ScaleHandle::BottomRight ||
handle == ScaleHandle::Right;
}

[[nodiscard]] inline bool scale_handle_resizes_top(ScaleHandle handle) noexcept
{
return handle == ScaleHandle::TopLeft ||
handle == ScaleHandle::TopRight ||
handle == ScaleHandle::Top;
}

[[nodiscard]] inline bool scale_handle_resizes_bottom(ScaleHandle handle) noexcept
{
return handle == ScaleHandle::BottomLeft ||
handle == ScaleHandle::BottomRight ||
handle == ScaleHandle::Bottom;
}

}
5 changes: 5 additions & 0 deletions src/prune/scene/scene.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ namespace prune {
object.editor.movable;
}

[[nodiscard]] virtual bool object_is_scalable(const GameObject& object) const
{
return object_is_selectable(object) && object_is_editable(object);
}

[[nodiscard]] virtual ObjectConcept object_concept_for(const GameObject& object) const
{
return object_concepts::fallback_for(object);
Expand Down
Loading
Loading