From df4bd55ba14fee98707186f20cd7975e4a545fff Mon Sep 17 00:00:00 2001 From: Turanszki Janos Date: Sat, 9 May 2020 18:48:14 +0100 Subject: [PATCH] UWP platform updates --- Editor/App.cpp | 42 +++++- Editor/App.h | 4 + Editor/Editor.cpp | 123 +++++++---------- Editor/Editor_UWP.vcxproj | 5 + Editor/LightWindow.cpp | 10 +- Editor/MaterialWindow.cpp | 50 ++----- Editor/MeshWindow.cpp | 8 +- Editor/Package.appxmanifest | 3 +- Editor/PostprocessWindow.cpp | 32 ++--- Editor/SoundWindow.cpp | 8 +- Editor/WeatherWindow.cpp | 9 +- Editor/images/logo_small.png | Bin 0 -> 42716 bytes Editor/main.cpp | 37 ++--- Template_UWP/App.cpp | 40 +++++- Template_UWP/App.h | 4 + Template_UWP/Template_UWP.vcxproj | 5 + Template_Windows/main.cpp | 37 ++--- Tests/main.cpp | 37 ++--- WickedEngine/MainComponent.cpp | 3 +- WickedEngine/wiBackLog.cpp | 30 +++++ WickedEngine/wiGraphicsDevice_DX11.cpp | 8 +- WickedEngine/wiGraphicsDevice_DX12.cpp | 4 +- WickedEngine/wiHelper.cpp | 180 ++++++++++++++++++------- WickedEngine/wiHelper.h | 8 +- WickedEngine/wiPlatform.h | 36 ++++- WickedEngine/wiScene_Serializers.cpp | 5 +- WickedEngine/wiVersion.cpp | 2 +- 27 files changed, 412 insertions(+), 318 deletions(-) create mode 100644 Editor/images/logo_small.png diff --git a/Editor/App.cpp b/Editor/App.cpp index 09dd0a8bb..28d1ab571 100644 --- a/Editor/App.cpp +++ b/Editor/App.cpp @@ -77,6 +77,9 @@ void App::SetWindow(CoreWindow^ window) DisplayInformation::DisplayContentsInvalidated += ref new TypedEventHandler(this, &App::OnDisplayContentsInvalidated); + window->KeyDown += ref new TypedEventHandler(this, &App::OnKeyDown); + window->CharacterReceived += ref new TypedEventHandler(this, &App::OnCharacterReceived); + editor.SetWindow(wiPlatform::window_type(window)); } @@ -169,10 +172,7 @@ void App::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args) void App::OnDpiChanged(DisplayInformation^ sender, Object^ args) { - // Note: The value for LogicalDpi retrieved here may not match the effective DPI of the app - // if it is being scaled for high resolution devices. Once the DPI is set on DeviceResources, - // you should always retrieve it using the GetDpi method. - // See DeviceResources.cpp for more details. + wiPlatform::GetWindowState().dpi = (int)sender->LogicalDpi; } void App::OnOrientationChanged(DisplayInformation^ sender, Object^ args) @@ -181,4 +181,36 @@ void App::OnOrientationChanged(DisplayInformation^ sender, Object^ args) void App::OnDisplayContentsInvalidated(DisplayInformation^ sender, Object^ args) { -} \ No newline at end of file +} + +// Input event handlers +void App::OnKeyDown(CoreWindow^ sender, KeyEventArgs^ key) +{ +} +void App::OnCharacterReceived(CoreWindow^ sender, CharacterReceivedEventArgs^ key) +{ + + switch (key->KeyCode) + { + case (unsigned int)VirtualKey::Back: + if (wiBackLog::isActive()) + wiBackLog::deletefromInput(); + wiTextInputField::DeleteFromInput(); + break; + + case (unsigned int)VirtualKey::Enter: + break; + + default: + { + const char c = (const char)key->KeyCode; + if (wiBackLog::isActive()) + { + wiBackLog::input(c); + } + wiTextInputField::AddInput(c); + } + break; + } + +} diff --git a/Editor/App.h b/Editor/App.h index 858673c09..5619c6390 100644 --- a/Editor/App.h +++ b/Editor/App.h @@ -34,6 +34,10 @@ namespace Template_UWP void OnOrientationChanged(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args); void OnDisplayContentsInvalidated(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args); + // Input handlers + void OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ key); + void OnCharacterReceived(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ key); + private: Editor editor; bool m_windowClosed; diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index c57474dac..6af9bb5f1 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -67,7 +67,7 @@ void EditorLoadingScreen::Load() WIFALIGN_CENTER, WIFALIGN_CENTER)); AddFont(&font); - sprite = wiSprite("../images/logo_small.png"); + sprite = wiSprite("images/logo_small.png"); sprite.anim.opa = 1; sprite.anim.repeatable = true; sprite.params.pos = XMFLOAT3(wiRenderer::GetDevice()->GetScreenWidth()*0.5f, wiRenderer::GetDevice()->GetScreenHeight()*0.5f - font.textHeight(), 0); @@ -610,14 +610,11 @@ void EditorComponent::Load() saveButton->OnClick([=](wiEventArgs args) { wiHelper::FileDialogParams params; - wiHelper::FileDialogResult result; params.type = wiHelper::FileDialogParams::SAVE; params.description = "Wicked Scene"; params.extensions.push_back("wiscene"); - wiHelper::FileDialog(params, result); + wiHelper::FileDialog(params, [this](std::string fileName) { - if (result.ok) { - string fileName = result.filenames.front(); if (fileName.substr(fileName.length() - 8).compare(".wiscene") != 0) { fileName += ".wiscene"; @@ -635,7 +632,7 @@ void EditorComponent::Load() { wiHelper::messageBox("Could not create " + fileName + "!"); } - } + }); }); GetGUI().AddWidget(saveButton); @@ -645,56 +642,48 @@ void EditorComponent::Load() modelButton->SetColor(wiColor(0, 89, 255, 180), wiWidget::WIDGETSTATE::IDLE); modelButton->SetColor(wiColor(112, 155, 255, 255), wiWidget::WIDGETSTATE::FOCUS); modelButton->OnClick([=](wiEventArgs args) { - thread([&] { + wiHelper::FileDialogParams params; + params.type = wiHelper::FileDialogParams::OPEN; + params.description = "Model formats (.wiscene, .obj, .gltf, .glb)"; + params.extensions.push_back("wiscene"); + params.extensions.push_back("obj"); + params.extensions.push_back("gltf"); + params.extensions.push_back("glb"); + wiHelper::FileDialog(params, [&](std::string fileName) { - wiHelper::FileDialogParams params; - wiHelper::FileDialogResult result; - params.type = wiHelper::FileDialogParams::OPEN; - params.description = "Model formats (.wiscene, .obj, .gltf, .glb)"; - params.extensions.push_back("wiscene"); - params.extensions.push_back("obj"); - params.extensions.push_back("gltf"); - params.extensions.push_back("glb"); - wiHelper::FileDialog(params, result); + main->loader->addLoadingFunction([=] { + string extension = wiHelper::toUpper(wiHelper::GetExtensionFromFileName(fileName)); - if (result.ok) - { - string fileName = result.filenames.front(); - - main->loader->addLoadingFunction([=] { - string extension = wiHelper::toUpper(wiHelper::GetExtensionFromFileName(fileName)); - - if (!extension.compare("WISCENE")) // engine-serialized - { - wiScene::LoadModel(fileName); - } - else if (!extension.compare("OBJ")) // wavefront-obj - { - Scene scene; - ImportModel_OBJ(fileName, scene); - wiScene::GetScene().Merge(scene); - } - else if (!extension.compare("GLTF")) // text-based gltf - { - Scene scene; - ImportModel_GLTF(fileName, scene); - wiScene::GetScene().Merge(scene); - } - else if (!extension.compare("GLB")) // binary gltf - { - Scene scene; - ImportModel_GLTF(fileName, scene); - wiScene::GetScene().Merge(scene); - } - }); - main->loader->onFinished([=] { - main->ActivatePath(this, 0.2f, wiColor::Black()); - weatherWnd->Update(); - }); - main->ActivatePath(main->loader.get(), 0.2f, wiColor::Black()); - ResetHistory(); - } - }).detach(); + if (!extension.compare("WISCENE")) // engine-serialized + { + wiScene::LoadModel(fileName); + } + else if (!extension.compare("OBJ")) // wavefront-obj + { + Scene scene; + ImportModel_OBJ(fileName, scene); + wiScene::GetScene().Merge(scene); + } + else if (!extension.compare("GLTF")) // text-based gltf + { + Scene scene; + ImportModel_GLTF(fileName, scene); + wiScene::GetScene().Merge(scene); + } + else if (!extension.compare("GLB")) // binary gltf + { + Scene scene; + ImportModel_GLTF(fileName, scene); + wiScene::GetScene().Merge(scene); + } + }); + main->loader->onFinished([=] { + main->ActivatePath(this, 0.2f, wiColor::Black()); + weatherWnd->Update(); + }); + main->ActivatePath(main->loader.get(), 0.2f, wiColor::Black()); + ResetHistory(); + }); }); GetGUI().AddWidget(modelButton); @@ -704,21 +693,13 @@ void EditorComponent::Load() scriptButton->SetColor(wiColor(255, 33, 140, 180), wiWidget::WIDGETSTATE::IDLE); scriptButton->SetColor(wiColor(255, 100, 140, 255), wiWidget::WIDGETSTATE::FOCUS); scriptButton->OnClick([=](wiEventArgs args) { - thread([&] { - - wiHelper::FileDialogParams params; - wiHelper::FileDialogResult result; - params.type = wiHelper::FileDialogParams::OPEN; - params.description = "Lua script"; - params.extensions.push_back("lua"); - wiHelper::FileDialog(params, result); - - if (result.ok) { - string fileName = result.filenames.front(); - wiLua::GetGlobal()->RunFile(fileName); - } - }).detach(); - + wiHelper::FileDialogParams params; + params.type = wiHelper::FileDialogParams::OPEN; + params.description = "Lua script"; + params.extensions.push_back("lua"); + wiHelper::FileDialog(params, [](std::string fileName) { + wiLua::GetGlobal()->RunFile(fileName); + }); }); GetGUI().AddWidget(scriptButton); @@ -821,7 +802,7 @@ void EditorComponent::Load() exitButton->SetColor(wiColor(190, 0, 0, 180), wiWidget::WIDGETSTATE::IDLE); exitButton->SetColor(wiColor(255, 0, 0, 255), wiWidget::WIDGETSTATE::FOCUS); exitButton->OnClick([this](wiEventArgs args) { - exit(0); + wiPlatform::Exit(); }); GetGUI().AddWidget(exitButton); @@ -856,7 +837,7 @@ void EditorComponent::Load() GetGUI().AddWidget(cinemaModeCheckBox); - sceneGraphView = new wiTreeList("Scene graph view (WIP)"); + sceneGraphView = new wiTreeList("Scene graph view"); sceneGraphView->OnSelect([this](wiEventArgs args) { translator.selected.clear(); diff --git a/Editor/Editor_UWP.vcxproj b/Editor/Editor_UWP.vcxproj index 07baa26d4..7fadec5e9 100644 --- a/Editor/Editor_UWP.vcxproj +++ b/Editor/Editor_UWP.vcxproj @@ -367,6 +367,11 @@ xcopy /Y /E /I $(SolutionDir)Editor\images $(OutDir)AppX\images Designer + + + {60da258f-e95f-4cf4-a46b-17d80644464b} + + diff --git a/Editor/LightWindow.cpp b/Editor/LightWindow.cpp index 795539cdb..17980721a 100644 --- a/Editor/LightWindow.cpp +++ b/Editor/LightWindow.cpp @@ -266,24 +266,20 @@ LightWindow::LightWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) else { wiHelper::FileDialogParams params; - wiHelper::FileDialogResult result; params.type = wiHelper::FileDialogParams::OPEN; params.description = "Texture"; params.extensions.push_back("dds"); params.extensions.push_back("png"); params.extensions.push_back("jpg"); params.extensions.push_back("tga"); - wiHelper::FileDialog(params, result); - - if (result.ok) { - std::string fileName = result.filenames.front(); + wiHelper::FileDialog(params, [this, light, i](std::string fileName) { light->lensFlareRimTextures[i] = wiResourceManager::Load(fileName); light->lensFlareNames[i] = fileName; fileName = wiHelper::GetFileNameFromPath(fileName); lensflare_Button[i]->SetText(fileName); - } + }); } - }); + }); lightWindow->AddWidget(lensflare_Button[i]); } diff --git a/Editor/MaterialWindow.cpp b/Editor/MaterialWindow.cpp index bb6023947..c2d054aa6 100644 --- a/Editor/MaterialWindow.cpp +++ b/Editor/MaterialWindow.cpp @@ -416,23 +416,19 @@ MaterialWindow::MaterialWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) else { wiHelper::FileDialogParams params; - wiHelper::FileDialogResult result; params.type = wiHelper::FileDialogParams::OPEN; params.description = "Texture"; params.extensions.push_back("dds"); params.extensions.push_back("png"); params.extensions.push_back("jpg"); params.extensions.push_back("tga"); - wiHelper::FileDialog(params, result); - - if (result.ok) { - string fileName = result.filenames.front(); + wiHelper::FileDialog(params, [this, material](std::string fileName) { material->baseColorMap = wiResourceManager::Load(fileName); material->baseColorMapName = fileName; material->SetDirty(); fileName = wiHelper::GetFileNameFromPath(fileName); texture_baseColor_Button->SetText(fileName); - } + }); } }); materialWindow->AddWidget(texture_baseColor_Button); @@ -478,23 +474,19 @@ MaterialWindow::MaterialWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) else { wiHelper::FileDialogParams params; - wiHelper::FileDialogResult result; params.type = wiHelper::FileDialogParams::OPEN; params.description = "Texture"; params.extensions.push_back("dds"); params.extensions.push_back("png"); params.extensions.push_back("jpg"); params.extensions.push_back("tga"); - wiHelper::FileDialog(params, result); - - if (result.ok) { - string fileName = result.filenames.front(); + wiHelper::FileDialog(params, [this, material](std::string fileName) { material->normalMap = wiResourceManager::Load(fileName); material->normalMapName = fileName; material->SetDirty(); fileName = wiHelper::GetFileNameFromPath(fileName); texture_normal_Button->SetText(fileName); - } + }); } }); materialWindow->AddWidget(texture_normal_Button); @@ -540,23 +532,19 @@ MaterialWindow::MaterialWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) else { wiHelper::FileDialogParams params; - wiHelper::FileDialogResult result; params.type = wiHelper::FileDialogParams::OPEN; params.description = "Texture"; params.extensions.push_back("dds"); params.extensions.push_back("png"); params.extensions.push_back("jpg"); params.extensions.push_back("tga"); - wiHelper::FileDialog(params, result); - - if (result.ok) { - string fileName = result.filenames.front(); + wiHelper::FileDialog(params, [this, material](std::string fileName) { material->surfaceMap = wiResourceManager::Load(fileName); material->surfaceMapName = fileName; material->SetDirty(); fileName = wiHelper::GetFileNameFromPath(fileName); texture_surface_Button->SetText(fileName); - } + }); } }); materialWindow->AddWidget(texture_surface_Button); @@ -602,25 +590,21 @@ MaterialWindow::MaterialWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) else { wiHelper::FileDialogParams params; - wiHelper::FileDialogResult result; params.type = wiHelper::FileDialogParams::OPEN; params.description = "Texture"; params.extensions.push_back("dds"); params.extensions.push_back("png"); params.extensions.push_back("jpg"); params.extensions.push_back("tga"); - wiHelper::FileDialog(params, result); - - if (result.ok) { - string fileName = result.filenames.front(); + wiHelper::FileDialog(params, [this, material](std::string fileName) { material->emissiveMap = wiResourceManager::Load(fileName); material->emissiveMapName = fileName; material->SetDirty(); fileName = wiHelper::GetFileNameFromPath(fileName); texture_emissive_Button->SetText(fileName); - } + }); } - }); + }); materialWindow->AddWidget(texture_emissive_Button); texture_emissive_uvset_Field = new wiTextInputField("uvset_emissive"); @@ -664,23 +648,19 @@ MaterialWindow::MaterialWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) else { wiHelper::FileDialogParams params; - wiHelper::FileDialogResult result; params.type = wiHelper::FileDialogParams::OPEN; params.description = "Texture"; params.extensions.push_back("dds"); params.extensions.push_back("png"); params.extensions.push_back("jpg"); params.extensions.push_back("tga"); - wiHelper::FileDialog(params, result); - - if (result.ok) { - string fileName = result.filenames.front(); + wiHelper::FileDialog(params, [this, material](std::string fileName) { material->displacementMap = wiResourceManager::Load(fileName); material->displacementMapName = fileName; material->SetDirty(); fileName = wiHelper::GetFileNameFromPath(fileName); texture_displacement_Button->SetText(fileName); - } + }); } }); materialWindow->AddWidget(texture_displacement_Button); @@ -727,23 +707,19 @@ MaterialWindow::MaterialWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) else { wiHelper::FileDialogParams params; - wiHelper::FileDialogResult result; params.type = wiHelper::FileDialogParams::OPEN; params.description = "Texture"; params.extensions.push_back("dds"); params.extensions.push_back("png"); params.extensions.push_back("jpg"); params.extensions.push_back("tga"); - wiHelper::FileDialog(params, result); - - if (result.ok) { - string fileName = result.filenames.front(); + wiHelper::FileDialog(params, [this, material](std::string fileName) { material->occlusionMap = wiResourceManager::Load(fileName); material->occlusionMapName = fileName; material->SetDirty(); fileName = wiHelper::GetFileNameFromPath(fileName); texture_occlusion_Button->SetText(fileName); - } + }); } }); materialWindow->AddWidget(texture_occlusion_Button); diff --git a/Editor/MeshWindow.cpp b/Editor/MeshWindow.cpp index 9930527c9..c980aaa8b 100644 --- a/Editor/MeshWindow.cpp +++ b/Editor/MeshWindow.cpp @@ -461,17 +461,13 @@ MeshWindow::MeshWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) heightmapButton->OnClick([=](wiEventArgs args) { wiHelper::FileDialogParams params; - wiHelper::FileDialogResult result; params.type = wiHelper::FileDialogParams::OPEN; params.description = "Texture"; params.extensions.push_back("dds"); params.extensions.push_back("png"); params.extensions.push_back("jpg"); params.extensions.push_back("tga"); - wiHelper::FileDialog(params, result); - - if (result.ok) { - string fileName = result.filenames.front(); + wiHelper::FileDialog(params, [=](std::string fileName) { if (this->rgb != nullptr) { @@ -483,7 +479,7 @@ MeshWindow::MeshWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) this->rgb = stbi_load(fileName.c_str(), &this->width, &this->height, &bpp, channelCount); generate_mesh(width, height, rgb, channelCount, dimYSlider->GetValue()); - } + }); }); terrainGenWindow->AddWidget(heightmapButton); diff --git a/Editor/Package.appxmanifest b/Editor/Package.appxmanifest index 56c6f5524..1e480d625 100644 --- a/Editor/Package.appxmanifest +++ b/Editor/Package.appxmanifest @@ -4,6 +4,7 @@ xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" + xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" IgnorableNamespaces="uap mp"> - + \ No newline at end of file diff --git a/Editor/PostprocessWindow.cpp b/Editor/PostprocessWindow.cpp index 2df261cb3..e1ecb1b72 100644 --- a/Editor/PostprocessWindow.cpp +++ b/Editor/PostprocessWindow.cpp @@ -217,26 +217,20 @@ PostprocessWindow::PostprocessWindow(EditorComponent* editor) : GUI(&editor->Get if (x == nullptr) { - thread([&] { - wiHelper::FileDialogParams params; - wiHelper::FileDialogResult result; - params.type = wiHelper::FileDialogParams::OPEN; - params.description = "Texture"; - params.extensions.push_back("dds"); - params.extensions.push_back("png"); - params.extensions.push_back("jpg"); - params.extensions.push_back("tga"); - wiHelper::FileDialog(params, result); - - if (result.ok) { - string fileName = result.filenames.front(); - editor->renderPath->setColorGradingTexture(wiResourceManager::Load(fileName)); - if (editor->renderPath->getColorGradingTexture() != nullptr) - { - colorGradingButton->SetText(fileName); - } + wiHelper::FileDialogParams params; + params.type = wiHelper::FileDialogParams::OPEN; + params.description = "Texture"; + params.extensions.push_back("dds"); + params.extensions.push_back("png"); + params.extensions.push_back("jpg"); + params.extensions.push_back("tga"); + wiHelper::FileDialog(params, [=](std::string fileName) { + editor->renderPath->setColorGradingTexture(wiResourceManager::Load(fileName)); + if (editor->renderPath->getColorGradingTexture() != nullptr) + { + colorGradingButton->SetText(fileName); } - }).detach(); + }); } else { diff --git a/Editor/SoundWindow.cpp b/Editor/SoundWindow.cpp index b9bc5d02b..e20fddbc0 100644 --- a/Editor/SoundWindow.cpp +++ b/Editor/SoundWindow.cpp @@ -69,19 +69,15 @@ SoundWindow::SoundWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) addButton->SetSize(XMFLOAT2(80, 30)); addButton->OnClick([=](wiEventArgs args) { wiHelper::FileDialogParams params; - wiHelper::FileDialogResult result; params.type = wiHelper::FileDialogParams::OPEN; params.description = "Sound"; params.extensions.push_back("wav"); - wiHelper::FileDialog(params, result); - - if (result.ok) { - string fileName = result.filenames.front(); + wiHelper::FileDialog(params, [=](std::string fileName) { Entity entity = GetScene().Entity_CreateSound("editorSound", fileName); editor->ClearSelected(); editor->AddSelected(entity); SetEntity(entity); - } + }); }); soundWindow->AddWidget(addButton); diff --git a/Editor/WeatherWindow.cpp b/Editor/WeatherWindow.cpp index 092ec5a04..c3da0428b 100644 --- a/Editor/WeatherWindow.cpp +++ b/Editor/WeatherWindow.cpp @@ -128,18 +128,15 @@ WeatherWindow::WeatherWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) if (weather.skyMap == nullptr) { wiHelper::FileDialogParams params; - wiHelper::FileDialogResult result; params.type = wiHelper::FileDialogParams::OPEN; params.description = "Cubemap texture"; params.extensions.push_back("dds"); - wiHelper::FileDialog(params, result); - - if (result.ok) { - string fileName = result.filenames.front(); + wiHelper::FileDialog(params, [=](std::string fileName) { + auto& weather = GetWeather(); weather.skyMapName = fileName; weather.skyMap = wiResourceManager::Load(fileName); skyButton->SetText(fileName); - } + }); } else { diff --git a/Editor/images/logo_small.png b/Editor/images/logo_small.png new file mode 100644 index 0000000000000000000000000000000000000000..9f69ce0564d26d1fb6e37da4914933f33438bd93 GIT binary patch literal 42716 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 HizogM%zNoq literal 0 HcmV?d00001 diff --git a/Editor/main.cpp b/Editor/main.cpp index 752e0924e..89821f496 100644 --- a/Editor/main.cpp +++ b/Editor/main.cpp @@ -211,32 +211,15 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } } break; - case WM_KEYDOWN: - switch (wParam) - { - case VK_HOME: - wiBackLog::Toggle(); - break; - case VK_UP: - if (wiBackLog::isActive()) - wiBackLog::historyPrev(); - break; - case VK_DOWN: - if (wiBackLog::isActive()) - wiBackLog::historyNext(); - break; - case VK_NEXT: - if (wiBackLog::isActive()) - wiBackLog::Scroll(10); - break; - case VK_PRIOR: - if (wiBackLog::isActive()) - wiBackLog::Scroll(-10); - break; - default: - break; - } - break; + case WM_DPICHANGED: + { + int dpi_x = LOWORD(wParam); + int dpi_y = HIWORD(wParam); + assert(dpi_x == dpi_y); + RECT* size = (RECT*)lParam; + wiPlatform::GetWindowState().dpi = dpi_x; + } + break; case WM_HOTKEY: switch (wParam) { @@ -258,8 +241,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) wiTextInputField::DeleteFromInput(); break; case VK_RETURN: - if (wiBackLog::isActive()) - wiBackLog::acceptInput(); break; default: { diff --git a/Template_UWP/App.cpp b/Template_UWP/App.cpp index 2049de9a6..04edf8555 100644 --- a/Template_UWP/App.cpp +++ b/Template_UWP/App.cpp @@ -82,6 +82,9 @@ void App::SetWindow(CoreWindow^ window) DisplayInformation::DisplayContentsInvalidated += ref new TypedEventHandler(this, &App::OnDisplayContentsInvalidated); + window->KeyDown += ref new TypedEventHandler(this, &App::OnKeyDown); + window->CharacterReceived += ref new TypedEventHandler(this, &App::OnCharacterReceived); + main.SetWindow(wiPlatform::window_type(window)); } @@ -174,10 +177,7 @@ void App::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args) void App::OnDpiChanged(DisplayInformation^ sender, Object^ args) { - // Note: The value for LogicalDpi retrieved here may not match the effective DPI of the app - // if it is being scaled for high resolution devices. Once the DPI is set on DeviceResources, - // you should always retrieve it using the GetDpi method. - // See DeviceResources.cpp for more details. + wiPlatform::GetWindowState().dpi = (int)sender->LogicalDpi; } void App::OnOrientationChanged(DisplayInformation^ sender, Object^ args) @@ -186,4 +186,36 @@ void App::OnOrientationChanged(DisplayInformation^ sender, Object^ args) void App::OnDisplayContentsInvalidated(DisplayInformation^ sender, Object^ args) { +} + +// Input event handlers +void App::OnKeyDown(CoreWindow^ sender, KeyEventArgs^ key) +{ +} +void App::OnCharacterReceived(CoreWindow^ sender, CharacterReceivedEventArgs^ key) +{ + + switch (key->KeyCode) + { + case (unsigned int)VirtualKey::Back: + if (wiBackLog::isActive()) + wiBackLog::deletefromInput(); + wiTextInputField::DeleteFromInput(); + break; + + case (unsigned int)VirtualKey::Enter: + break; + + default: + { + const char c = (const char)key->KeyCode; + if (wiBackLog::isActive()) + { + wiBackLog::input(c); + } + wiTextInputField::AddInput(c); + } + break; + } + } \ No newline at end of file diff --git a/Template_UWP/App.h b/Template_UWP/App.h index 82f6fd305..9502124a6 100644 --- a/Template_UWP/App.h +++ b/Template_UWP/App.h @@ -33,6 +33,10 @@ namespace Template_UWP void OnOrientationChanged(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args); void OnDisplayContentsInvalidated(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args); + // Input handlers + void OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ key); + void OnCharacterReceived(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ key); + private: MainComponent main; bool m_windowClosed; diff --git a/Template_UWP/Template_UWP.vcxproj b/Template_UWP/Template_UWP.vcxproj index a4d81e9a4..8acc74199 100644 --- a/Template_UWP/Template_UWP.vcxproj +++ b/Template_UWP/Template_UWP.vcxproj @@ -399,6 +399,11 @@ xcopy /Y /E /I $(SolutionDir)WickedEngine\fonts $(OutDir)AppX\fonts Designer + + + {60da258f-e95f-4cf4-a46b-17d80644464b} + + diff --git a/Template_Windows/main.cpp b/Template_Windows/main.cpp index ed68523ae..b514726cf 100644 --- a/Template_Windows/main.cpp +++ b/Template_Windows/main.cpp @@ -172,32 +172,15 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } } break; - case WM_KEYDOWN: - switch (wParam) - { - case VK_HOME: - wiBackLog::Toggle(); - break; - case VK_UP: - if (wiBackLog::isActive()) - wiBackLog::historyPrev(); - break; - case VK_DOWN: - if (wiBackLog::isActive()) - wiBackLog::historyNext(); - break; - case VK_NEXT: - if (wiBackLog::isActive()) - wiBackLog::Scroll(10); - break; - case VK_PRIOR: - if (wiBackLog::isActive()) - wiBackLog::Scroll(-10); - break; - default: - break; - } - break; + case WM_DPICHANGED: + { + int dpi_x = LOWORD(wParam); + int dpi_y = HIWORD(wParam); + assert(dpi_x == dpi_y); + RECT* size = (RECT*)lParam; + wiPlatform::GetWindowState().dpi = dpi_x; + } + break; case WM_CHAR: switch (wParam) { @@ -207,8 +190,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) wiTextInputField::DeleteFromInput(); break; case VK_RETURN: - if (wiBackLog::isActive()) - wiBackLog::acceptInput(); break; default: { diff --git a/Tests/main.cpp b/Tests/main.cpp index 1a5937ffa..d795904f0 100644 --- a/Tests/main.cpp +++ b/Tests/main.cpp @@ -165,32 +165,15 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } } break; - case WM_KEYDOWN: - switch (wParam) - { - case VK_HOME: - wiBackLog::Toggle(); - break; - case VK_UP: - if (wiBackLog::isActive()) - wiBackLog::historyPrev(); - break; - case VK_DOWN: - if (wiBackLog::isActive()) - wiBackLog::historyNext(); - break; - case VK_NEXT: - if (wiBackLog::isActive()) - wiBackLog::Scroll(10); - break; - case VK_PRIOR: - if (wiBackLog::isActive()) - wiBackLog::Scroll(-10); - break; - default: - break; - } - break; + case WM_DPICHANGED: + { + int dpi_x = LOWORD(wParam); + int dpi_y = HIWORD(wParam); + assert(dpi_x == dpi_y); + RECT* size = (RECT*)lParam; + wiPlatform::GetWindowState().dpi = dpi_x; + } + break; case WM_CHAR: switch (wParam) { @@ -200,8 +183,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) wiTextInputField::DeleteFromInput(); break; case VK_RETURN: - if (wiBackLog::isActive()) - wiBackLog::acceptInput(); break; default: { diff --git a/WickedEngine/MainComponent.cpp b/WickedEngine/MainComponent.cpp index 7a1b7ab17..8efab7e3c 100644 --- a/WickedEngine/MainComponent.cpp +++ b/WickedEngine/MainComponent.cpp @@ -332,6 +332,7 @@ void MainComponent::Compose(CommandList cmd) void MainComponent::SetWindow(wiPlatform::window_type window) { - wiPlatform::GetWindow() = window; + wiPlatform::GetWindowState().window = window; + wiPlatform::InitDPI(); } diff --git a/WickedEngine/wiBackLog.cpp b/WickedEngine/wiBackLog.cpp index 6f79d81e3..ec9b083bf 100644 --- a/WickedEngine/wiBackLog.cpp +++ b/WickedEngine/wiBackLog.cpp @@ -8,6 +8,7 @@ #include "wiSpriteFont.h" #include "wiImage.h" #include "wiLua.h" +#include "wiInput.h" #include #include @@ -60,6 +61,35 @@ namespace wiBackLog } void Update() { + if (wiInput::Press(wiInput::KEYBOARD_BUTTON_HOME)) + { + Toggle(); + } + + if (isActive) + { + if (wiInput::Press(wiInput::KEYBOARD_BUTTON_UP)) + { + historyPrev(); + } + if (wiInput::Press(wiInput::KEYBOARD_BUTTON_DOWN)) + { + historyNext(); + } + if (wiInput::Press(wiInput::KEYBOARD_BUTTON_ENTER)) + { + acceptInput(); + } + if (wiInput::Down(wiInput::KEYBOARD_BUTTON_PAGEUP)) + { + Scroll(10); + } + if (wiInput::Down(wiInput::KEYBOARD_BUTTON_PAGEDOWN)) + { + Scroll(-10); + } + } + if (state == DEACTIVATING) pos -= speed; else if (state == ACTIVATING) diff --git a/WickedEngine/wiGraphicsDevice_DX11.cpp b/WickedEngine/wiGraphicsDevice_DX11.cpp index c9892abf8..5423340a7 100644 --- a/WickedEngine/wiGraphicsDevice_DX11.cpp +++ b/WickedEngine/wiGraphicsDevice_DX11.cpp @@ -1241,7 +1241,7 @@ GraphicsDevice_DX11::GraphicsDevice_DX11(wiPlatform::window_type window, bool fu std::stringstream ss(""); ss << "Failed to create the graphics device! ERROR: " << std::hex << hr; wiHelper::messageBox(ss.str(), "Error!"); - exit(1); + wiPlatform::Exit(); } ComPtr pDXGIDevice; @@ -1287,7 +1287,7 @@ GraphicsDevice_DX11::GraphicsDevice_DX11(wiPlatform::window_type window, bool fu if (FAILED(hr)) { wiHelper::messageBox("Failed to create a swapchain for the graphics device!", "Error!"); - exit(1); + wiPlatform::Exit(); } // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and @@ -1341,13 +1341,13 @@ void GraphicsDevice_DX11::CreateBackBufferResources() hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), &backBuffer); if (FAILED(hr)) { wiHelper::messageBox("BackBuffer creation Failed!", "Error!"); - exit(1); + wiPlatform::Exit(); } hr = device->CreateRenderTargetView(backBuffer.Get(), nullptr, &renderTargetView); if (FAILED(hr)) { wiHelper::messageBox("Main Rendertarget creation Failed!", "Error!"); - exit(1); + wiPlatform::Exit(); } } diff --git a/WickedEngine/wiGraphicsDevice_DX12.cpp b/WickedEngine/wiGraphicsDevice_DX12.cpp index 6652edd9a..854c3b374 100644 --- a/WickedEngine/wiGraphicsDevice_DX12.cpp +++ b/WickedEngine/wiGraphicsDevice_DX12.cpp @@ -1577,7 +1577,7 @@ using namespace DX12_Internal; ss << "Failed to create the graphics device! ERROR: " << std::hex << hr; wiHelper::messageBox(ss.str(), "Error!"); assert(0); - exit(1); + wiPlatform::Exit(); } D3D12MA::ALLOCATOR_DESC allocatorDesc = {}; @@ -1647,7 +1647,7 @@ using namespace DX12_Internal; { wiHelper::messageBox("Failed to create a swapchain for the graphics device!", "Error!"); assert(0); - exit(1); + wiPlatform::Exit(); } hr = _swapChain.As(&swapChain); diff --git a/WickedEngine/wiHelper.cpp b/WickedEngine/wiHelper.cpp index f3a1fddf5..bf64a5ef8 100644 --- a/WickedEngine/wiHelper.cpp +++ b/WickedEngine/wiHelper.cpp @@ -5,6 +5,7 @@ #include "Utility/stb_image_write.h" +#include #include #include #include @@ -13,8 +14,13 @@ #include #include // string conversion +#ifdef PLATFORM_UWP +#include +#include +#else #include // openfile #include +#endif // PLATFORM_UWP using namespace std; @@ -343,71 +349,145 @@ namespace wiHelper return exists; } - void FileDialog(const FileDialogParams& params, FileDialogResult& result) + void FileDialog(const FileDialogParams& params, std::function onSuccess) { #ifdef _WIN32 #ifndef PLATFORM_UWP - char szFile[256]; + std::thread([=] { - OPENFILENAMEA ofn; - ZeroMemory(&ofn, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = nullptr; - ofn.lpstrFile = szFile; - // Set lpstrFile[0] to '\0' so that GetOpenFileName does not - // use the contents of szFile to initialize itself. - ofn.lpstrFile[0] = '\0'; - ofn.nMaxFile = sizeof(szFile); - ofn.lpstrFileTitle = NULL; - ofn.nMaxFileTitle = 0; - ofn.lpstrInitialDir = NULL; - ofn.nFilterIndex = 1; + char szFile[256]; - // Slightly convoluted way to create the filter. - // First string is description, ended by '\0' - // Second string is extensions, each separated by ';' and at the end of all, a '\0' - // Then the whole container string is closed with an other '\0' - // For example: "model files\0*.model;*.obj;\0" <-- this string literal has "model files" as description and two accepted extensions "model" and "obj" - std::vector filter; - filter.reserve(256); - { - for (auto& x : params.description) + OPENFILENAMEA ofn; + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = nullptr; + ofn.lpstrFile = szFile; + // Set lpstrFile[0] to '\0' so that GetOpenFileName does not + // use the contents of szFile to initialize itself. + ofn.lpstrFile[0] = '\0'; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.nFilterIndex = 1; + + // Slightly convoluted way to create the filter. + // First string is description, ended by '\0' + // Second string is extensions, each separated by ';' and at the end of all, a '\0' + // Then the whole container string is closed with an other '\0' + // For example: "model files\0*.model;*.obj;\0" <-- this string literal has "model files" as description and two accepted extensions "model" and "obj" + std::vector filter; + filter.reserve(256); { - filter.push_back(x); - } - filter.push_back(0); - - for (auto& x : params.extensions) - { - filter.push_back('*'); - filter.push_back('.'); - for (auto& y : x) + for (auto& x : params.description) { - filter.push_back(y); + filter.push_back(x); } - filter.push_back(';'); + filter.push_back(0); + + for (auto& x : params.extensions) + { + filter.push_back('*'); + filter.push_back('.'); + for (auto& y : x) + { + filter.push_back(y); + } + filter.push_back(';'); + } + filter.push_back(0); + filter.push_back(0); } - filter.push_back(0); - filter.push_back(0); - } - ofn.lpstrFilter = filter.data(); + ofn.lpstrFilter = filter.data(); + + + BOOL ok = FALSE; + switch (params.type) + { + case FileDialogParams::OPEN: + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + ok = GetOpenFileNameA(&ofn) == TRUE; + break; + case FileDialogParams::SAVE: + ofn.Flags = OFN_OVERWRITEPROMPT; + ok = GetSaveFileNameA(&ofn) == TRUE; + break; + } + + if (ok) + { + onSuccess(ofn.lpstrFile); + } + + }).detach(); + +#else + + using namespace concurrency; + using namespace Platform; + using namespace Windows::Storage; + using namespace Windows::Storage::Pickers; + using namespace Windows::UI::Xaml; + using namespace Windows::UI::Xaml::Controls; + using namespace Windows::UI::Xaml::Navigation; switch (params.type) { case FileDialogParams::OPEN: - ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; - result.ok = GetOpenFileNameA(&ofn) == TRUE; - break; - case FileDialogParams::SAVE: - ofn.Flags = OFN_OVERWRITEPROMPT; - result.ok = GetSaveFileNameA(&ofn) == TRUE; - break; - } - - if (result.ok) { - result.filenames.push_back(ofn.lpstrFile); + FileOpenPicker^ picker = ref new FileOpenPicker(); + picker->ViewMode = PickerViewMode::List; + picker->SuggestedStartLocation = PickerLocationId::ComputerFolder; + + for (auto& x : params.extensions) + { + wstring wstr; + StringConvert(x, wstr); + wstr = L"." + wstr; + picker->FileTypeFilter->Append(ref new String(wstr.c_str())); + } + + create_task(picker->PickSingleFileAsync()).then([=](StorageFile^ file) { + + if (file) + { + wstring wstr = file->Path->Data(); + string str; + StringConvert(wstr, str); + onSuccess(str); + } + }); + } + break; + case FileDialogParams::SAVE: + { + FileSavePicker^ picker = ref new FileSavePicker(); + picker->SuggestedStartLocation = PickerLocationId::ComputerFolder; + + for (auto& x : params.extensions) + { + wstring wstr; + StringConvert(x, wstr); + wstr = L"." + wstr; + auto plainTextExtensions = ref new Platform::Collections::Vector(); + plainTextExtensions->Append(ref new String(wstr.c_str())); + StringConvert(params.description, wstr); + picker->FileTypeChoices->Insert(ref new String(wstr.c_str()), plainTextExtensions); + } + + create_task(picker->PickSaveFileAsync()).then([=](StorageFile^ file) { + + if (file) + { + wstring wstr = file->Path->Data(); + string str; + StringConvert(wstr, str); + onSuccess(str); + } + }); + } + break; } #endif // PLATFORM_UWP diff --git a/WickedEngine/wiHelper.h b/WickedEngine/wiHelper.h index ced5273af..c2308261b 100644 --- a/WickedEngine/wiHelper.h +++ b/WickedEngine/wiHelper.h @@ -4,6 +4,7 @@ #include #include +#include namespace wiHelper { @@ -78,12 +79,7 @@ namespace wiHelper std::string description; std::vector extensions; }; - struct FileDialogResult - { - bool ok = false; - std::vector filenames; - }; - void FileDialog(const FileDialogParams& params, FileDialogResult& result); + void FileDialog(const FileDialogParams& params, std::function onSuccess); void StringConvert(const std::string from, std::wstring& to); diff --git a/WickedEngine/wiPlatform.h b/WickedEngine/wiPlatform.h index 51e5db3b6..f1240e3c6 100644 --- a/WickedEngine/wiPlatform.h +++ b/WickedEngine/wiPlatform.h @@ -26,10 +26,20 @@ namespace wiPlatform #endif // PLATFORM_UWP #endif // _WIN32 + struct WindowState + { + window_type window; + int dpi = 96; + }; + inline WindowState& GetWindowState() + { + static WindowState state; + return state; + } + inline window_type& GetWindow() { - static window_type window; - return window; + return GetWindowState().window; } inline bool IsWindowActive() { @@ -43,20 +53,32 @@ namespace wiPlatform return true; #endif // _WIN32 } - inline int GetDPI() + inline void InitDPI() { #ifdef _WIN32 #ifndef PLATFORM_UWP - return (int)GetDpiForWindow(GetWindow()); + GetWindowState().dpi = (int)GetDpiForWindow(GetWindow()); #else - return (int)Windows::Graphics::Display::DisplayInformation::GetForCurrentView()->LogicalDpi; + GetWindowState().dpi = (int)Windows::Graphics::Display::DisplayInformation::GetForCurrentView()->LogicalDpi; #endif // PLATFORM_UWP -#else - return 96; #endif // _WIN32 } + inline int GetDPI() + { + return GetWindowState().dpi; + } inline float GetDPIScaling() { return (float)GetDPI() / 96.0f; } + inline void Exit() + { +#ifdef _WIN32 +#ifndef PLATFORM_UWP + PostQuitMessage(0); +#else + Windows::ApplicationModel::Core::CoreApplication::Exit(); +#endif // PLATFORM_UWP +#endif // _WIN32 + } } diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp index 5f544f538..b0d7bda60 100644 --- a/WickedEngine/wiScene_Serializers.cpp +++ b/WickedEngine/wiScene_Serializers.cpp @@ -733,7 +733,10 @@ namespace wiScene if (archive.GetVersion() >= 32) { archive >> skyMapName; - skyMap = wiResourceManager::Load(dir + skyMapName); + if (!skyMapName.empty()) + { + skyMap = wiResourceManager::Load(dir + skyMapName); + } } if (archive.GetVersion() >= 40) { diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 6d9da6dbb..8281121ba 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wiVersion // minor features, major updates const int minor = 41; // minor bug fixes, alterations, refactors, updates - const int revision = 8; + const int revision = 9; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);