diff --git a/content/editor/ui/menu.html b/content/editor/ui/menu.html index 40b4b723..713f50b4 100644 --- a/content/editor/ui/menu.html +++ b/content/editor/ui/menu.html @@ -11,7 +11,7 @@ Help Test diff --git a/src/editor/content/content_opener/model_open_history.go b/src/editor/content/content_opener/model_open_history.go index 24ed1084..95eed920 100644 --- a/src/editor/content/content_opener/model_open_history.go +++ b/src/editor/content/content_opener/model_open_history.go @@ -39,36 +39,25 @@ package content_opener import ( "kaiju/engine" - "kaiju/rendering" ) type modelOpenHistory struct { - host *engine.Host - entity *engine.Entity - drawings []rendering.Drawing + host *engine.Host + entity *engine.Entity } func (h *modelOpenHistory) Redo() { h.entity.Activate() - for i := range h.drawings { - h.drawings[i].ShaderData.Activate() - } h.host.AddEntity(h.entity) } func (h *modelOpenHistory) Undo() { h.entity.Deactivate() - for i := range h.drawings { - h.drawings[i].ShaderData.Deactivate() - } h.host.RemoveEntity(h.entity) } func (h *modelOpenHistory) Delete() { h.entity.Destroy() - for i := range h.drawings { - h.drawings[i].ShaderData.Destroy() - } } func (h *modelOpenHistory) Exit() {} diff --git a/src/editor/content/content_opener/obj_opener.go b/src/editor/content/content_opener/obj_opener.go index e9828290..05ca9eaf 100644 --- a/src/editor/content/content_opener/obj_opener.go +++ b/src/editor/content/content_opener/obj_opener.go @@ -46,7 +46,6 @@ import ( "kaiju/engine" "kaiju/matrix" "kaiju/rendering" - "slices" ) type ObjOpener struct{} @@ -101,6 +100,9 @@ func load(host *engine.Host, adi asset_info.AssetDatabaseInfo, e *engine.Entity) } host.Drawings.AddDrawing(&drawing) e.EditorBindings.AddDrawing(drawing) + e.OnActivate.Add(func() { data.Activate() }) + e.OnDeactivate.Add(func() { data.Deactivate() }) + e.OnDestroy.Add(func() { data.Destroy() }) return nil } @@ -114,9 +116,8 @@ func (o ObjOpener) Open(adi asset_info.AssetDatabaseInfo, ed interfaces.Editor) } } ed.History().Add(&modelOpenHistory{ - host: host, - entity: e, - drawings: slices.Clone(e.EditorBindings.Drawings()), + host: host, + entity: e, }) host.Window.Focus() return nil diff --git a/src/editor/editor.go b/src/editor/editor.go index fce2387a..7ed2d710 100644 --- a/src/editor/editor.go +++ b/src/editor/editor.go @@ -103,7 +103,7 @@ func New(container *host_container.Container) *Editor { editorDir: filepath.Dir(klib.MustReturn(os.Executable())), history: memento.NewHistory(100), } - ed.stageManager = stages.NewManager(host, &ed.assetImporters) + ed.stageManager = stages.NewManager(host, &ed.assetImporters, &ed.history) ed.selection = selection.New(host, &ed.history) ed.assetImporters.Register(asset_importer.OBJImporter{}) ed.assetImporters.Register(asset_importer.PNGImporter{}) @@ -227,8 +227,10 @@ func (ed *Editor) update(delta float64) { ed.history.Undo() } else if kb.KeyDown(hid.KeyboardKeyY) { ed.history.Redo() - } else if kb.KeyDown(hid.KeyboardKeySpace) { + } else if kb.KeyUp(hid.KeyboardKeySpace) { content_window.New(&ed.contentOpener, ed) + } else if kb.KeyUp(hid.KeyboardKeyS) { + ed.stageManager.Save() } } else if kb.KeyDown(hid.KeyboardKeyDelete) { deleter.DeleteSelected(&ed.history, &ed.selection, diff --git a/src/editor/memento/history.go b/src/editor/memento/history.go index 9844b5f8..819bc6c5 100644 --- a/src/editor/memento/history.go +++ b/src/editor/memento/history.go @@ -81,3 +81,11 @@ func (h *History) Redo() { m.Redo() h.position++ } + +func (h *History) Clear() { + for i := 0; i < len(h.undoStack); i++ { + h.undoStack[i].Exit() + } + h.undoStack = h.undoStack[:0] + h.position = 0 +} diff --git a/src/editor/stages/manager.go b/src/editor/stages/manager.go index 5eda366e..f1f3038d 100644 --- a/src/editor/stages/manager.go +++ b/src/editor/stages/manager.go @@ -43,6 +43,7 @@ import ( "kaiju/assets/asset_info" "kaiju/editor/alert" "kaiju/editor/editor_config" + "kaiju/editor/memento" "kaiju/engine" "kaiju/filesystem" "kaiju/klib" @@ -53,14 +54,31 @@ import ( type Manager struct { host *engine.Host registry *asset_importer.ImportRegistry + history *memento.History stage string } -func NewManager(host *engine.Host, registry *asset_importer.ImportRegistry) Manager { +func NewManager(host *engine.Host, registry *asset_importer.ImportRegistry, + history *memento.History) Manager { + return Manager{ host: host, registry: registry, + history: history, + } +} + +func (m *Manager) confirmCheck() bool { + return <-alert.New("Save Changes", "You are changing stages, any unsaved changes will be lost. Are you sure you wish to continue?", "Yes", "No") +} + +func (m *Manager) New() { + if !m.confirmCheck() { + return } + m.stage = "" + m.history.Clear() + m.host.ClearEntities() } func (m *Manager) Save() error { @@ -90,10 +108,11 @@ func (m *Manager) Save() error { } func (m *Manager) Load(adi asset_info.AssetDatabaseInfo, host *engine.Host) error { - ok := <-alert.New("Save Changes", "You are changing stages, any unsaved changes will be lost. Are you sure you wish to continue?", "Yes", "No") - if !ok { + if !m.confirmCheck() { return nil } + m.history.Clear() + m.host.ClearEntities() m.stage = adi.Path data, err := filesystem.ReadFile(m.stage) if err != nil { diff --git a/src/editor/ui/menu/editor_menu.go b/src/editor/ui/menu/editor_menu.go index 22fe4f53..0864317f 100644 --- a/src/editor/ui/menu/editor_menu.go +++ b/src/editor/ui/menu/editor_menu.go @@ -111,6 +111,10 @@ func (m *Menu) openContentWindow(*document.DocElement) { content_window.New(m.contentOpener, m.editor) } +func (m *Menu) newStage(*document.DocElement) { + m.editor.StageManager().New() +} + func (m *Menu) saveStage(*document.DocElement) { if err := m.editor.StageManager().Save(); err != nil { slog.Error("Save stage failed", slog.String("error", err.Error())) @@ -134,6 +138,7 @@ func New(container *host_container.Container, "openLogWindow": m.openLogWindow, "openRepository": openRepository, "openAbout": openAbout, + "newStage": m.newStage, "saveStage": m.saveStage, "openContentWindow": m.openContentWindow, "sampleInfo": func(*document.DocElement) { slog.Info("This is some info") }, diff --git a/src/engine/host.go b/src/engine/host.go index ff809f3b..4e45e56e 100644 --- a/src/engine/host.go +++ b/src/engine/host.go @@ -116,8 +116,8 @@ func NewHost(name string, logStream *logging.LogStream) *Host { assetDatabase: assets.NewDatabase(), Drawings: rendering.NewDrawings(), OnClose: events.New(), - CloseSignal: make(chan struct{}), - Camera: cameras.NewStandardCamera(w, h, matrix.Vec3{0, 0, 1}), + CloseSignal: make(chan struct{}, 1), + Camera: cameras.NewStandardCamera(w, h, matrix.Vec3Backward()), UICamera: cameras.NewStandardCameraOrthographic(w, h, matrix.Vec3{0, 0, 250}), LogStream: logStream, frameRunner: make([]frameRun, 0), @@ -301,8 +301,11 @@ func (host *Host) Render() { host.shaderCache.CreatePending() host.textureCache.CreatePending() host.meshCache.CreatePending() - host.Window.Renderer.ReadyFrame(host.Camera, host.UICamera, float32(host.Runtime())) - host.Drawings.Render(host.Window.Renderer) + if host.Drawings.HasDrawings() { + host.Window.Renderer.ReadyFrame(host.Camera, + host.UICamera, float32(host.Runtime())) + host.Drawings.Render(host.Window.Renderer) + } host.Window.SwapBuffers() // TODO: Thread this or make the dirty on demand, and have a flag for the dirty frame for _, e := range host.entities { diff --git a/src/rendering/drawing.go b/src/rendering/drawing.go index 2ffef61b..c3bddea6 100644 --- a/src/rendering/drawing.go +++ b/src/rendering/drawing.go @@ -88,6 +88,8 @@ func NewDrawings() Drawings { } } +func (d *Drawings) HasDrawings() bool { return len(d.draws) > 0 } + func (d *Drawings) findRenderTargetDraw(target Canvas) (*RenderTargetDraw, bool) { for i := range d.draws { if d.draws[i].Target == target { diff --git a/src/tests/rendering_tests/rendering_tests.go b/src/tests/rendering_tests/rendering_tests.go index d7a7b52f..b6690ed8 100644 --- a/src/tests/rendering_tests/rendering_tests.go +++ b/src/tests/rendering_tests/rendering_tests.go @@ -268,7 +268,7 @@ func drawBasicMesh(host *engine.Host, res load_result.Result) { func testMonkeyOBJ(host *engine.Host) { const monkeyObj = "meshes/monkey.obj" - host.Camera.SetPosition(matrix.Vec3{0, 0, 3}) + host.Camera.SetPosition(matrix.Vec3Backward().Scale(3)) monkeyData := klib.MustReturn(host.AssetDatabase().ReadText(monkeyObj)) res := loaders.OBJ(monkeyData) if !res.IsValid() || len(res.Meshes) != 1 { @@ -280,7 +280,7 @@ func testMonkeyOBJ(host *engine.Host) { func testMonkeyGLTF(host *engine.Host) { const monkeyGLTF = "meshes/monkey.gltf" - host.Camera.SetPosition(matrix.Vec3{0, 0, 3}) + host.Camera.SetPosition(matrix.Vec3Backward().Scale(3)) res := klib.MustReturn(loaders.GLTF(host.Window.Renderer, monkeyGLTF, host.AssetDatabase())) if !res.IsValid() || len(res.Meshes) != 1 { slog.Error("Expected 1 mesh") @@ -291,7 +291,7 @@ func testMonkeyGLTF(host *engine.Host) { func testMonkeyGLB(host *engine.Host) { const monkeyGLTF = "meshes/monkey.glb" - host.Camera.SetPosition(matrix.Vec3{0, 0, 3}) + host.Camera.SetPosition(matrix.Vec3Backward().Scale(3)) res := klib.MustReturn(loaders.GLTF(host.Window.Renderer, monkeyGLTF, host.AssetDatabase())) if !res.IsValid() || len(res.Meshes) != 1 { slog.Error("Expected 1 mesh") @@ -337,7 +337,7 @@ func SetupConsole(host *engine.Host) { c := host_container.New("Test "+t, nil) go c.Run(engine.DefaultWindowWidth, engine.DefaultWindowHeight) <-c.PrepLock - c.Host.Camera.SetPosition(matrix.Vec3{0, 0, 2}) + c.Host.Camera.SetPosition(matrix.Vec3Backward().Scale(2)) testFunc(c.Host) } return "Running test" diff --git a/src/ui/input.go b/src/ui/input.go index dada3ddd..9d039324 100644 --- a/src/ui/input.go +++ b/src/ui/input.go @@ -148,8 +148,8 @@ func (p *Panel) ConvertToInput(placeholderText string) *Input { p.AddChild(data.highlight) data.highlight.entity.Deactivate() - input.AddEvent(EventTypeEnter, input.onHover) - input.AddEvent(EventTypeExit, input.onBlur) + input.AddEvent(EventTypeEnter, input.onEnter) + input.AddEvent(EventTypeExit, input.onExit) input.AddEvent(EventTypeDown, input.onDown) input.AddEvent(EventTypeClick, input.onClick) input.AddEvent(EventTypeMiss, input.onMiss) @@ -427,12 +427,12 @@ func (input *Input) onRebuild() { input.updateCursorPosition() } -func (input *Input) onHover() { - input.Host().Window.CursorIbeam() +func (input *Input) onEnter() { + input.host.Window.CursorIbeam() } -func (input *Input) onBlur() { - input.Host().Window.CursorStandard() +func (input *Input) onExit() { + input.host.Window.CursorStandard() } func (input *Input) onDown() { diff --git a/src/windowing/win32.c b/src/windowing/win32.c index 6220bad2..7f9fbbe3 100644 --- a/src/windowing/win32.c +++ b/src/windowing/win32.c @@ -141,18 +141,22 @@ void process_message(SharedMem* sm, MSG *msg) { setMouseEvent(sm->evt, msg->lParam, -1); break; case WM_LBUTTONDOWN: + SetCapture(msg->hwnd); case WM_LBUTTONUP: setMouseEvent(sm->evt, msg->lParam, MOUSE_BUTTON_LEFT); break; case WM_MBUTTONDOWN: + SetCapture(msg->hwnd); case WM_MBUTTONUP: setMouseEvent(sm->evt, msg->lParam, MOUSE_BUTTON_MIDDLE); break; case WM_RBUTTONDOWN: + SetCapture(msg->hwnd); case WM_RBUTTONUP: setMouseEvent(sm->evt, msg->lParam, MOUSE_BUTTON_RIGHT); break; case WM_XBUTTONDOWN: + SetCapture(msg->hwnd); case WM_XBUTTONUP: if (msg->wParam & 0x0010000) { setMouseEvent(sm->evt, msg->lParam, MOUSE_BUTTON_X1); @@ -221,7 +225,7 @@ void window_main(const wchar_t* windowTitle, int width, int height, void* evtSha // Register the window class. HMODULE hInstance = GetModuleHandle(NULL); const wchar_t className[] = L"Kaiju Window Class"; - WNDCLASS wc = { }; + WNDCLASS wc = { 0 }; wc.lpfnWndProc = window_proc; wc.hInstance = hInstance; wc.lpszClassName = className; @@ -274,7 +278,7 @@ uint32_t window_poll_controller(void* hwnd) { uint32_t window_poll(void* hwnd) { SharedMem* sm = (SharedMem*)GetWindowLongPtrA(hwnd, GWLP_USERDATA); // Run the message loop. - MSG msg = {}; + MSG msg = { 0 }; if (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE) > 0) { TranslateMessage(&msg); // TODO: Window resize happens in here, but would be clobbered by &msg which is different diff --git a/src/windowing/window.go b/src/windowing/window.go index f95c3eb5..4db9abdd 100644 --- a/src/windowing/window.go +++ b/src/windowing/window.go @@ -108,7 +108,7 @@ func New(windowName string, width, height int) (*Window, error) { } w.Cursor = hid.NewCursor(&w.Mouse, &w.Touch, &w.Stylus) // TODO: Pass in width and height - createWindow(windowName, w.width, w.height, w.evtSharedMem) + createWindow(windowName+"\x00\x00", w.width, w.height, w.evtSharedMem) if w.evtSharedMem.IsFatal() { return nil, errors.New(w.evtSharedMem.FatalMessage()) } @@ -299,4 +299,7 @@ func (w *Window) Destroy() { w.destroy() } -func (w *Window) Focus() { w.focus() } +func (w *Window) Focus() { + w.focus() + w.cursorStandard() +}