// Copyright (c) 2008-2023 the Urho3D project // License: MIT #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Sample::Sample(Context* context) : Application(context), yaw_(0.0f), pitch_(0.0f), touchEnabled_(false), useMouseMode_(MM_ABSOLUTE), screenJoystickIndex_(M_MAX_UNSIGNED), screenJoystickSettingsIndex_(M_MAX_UNSIGNED), paused_(false) { } void Sample::Setup() { // Modify engine startup parameters engineParameters_[EP_WINDOW_TITLE] = GetTypeName(); engineParameters_[EP_LOG_NAME] = GetSubsystem()->GetAppPreferencesDir("urho3d", "logs") + GetTypeName() + ".log"; engineParameters_[EP_FULL_SCREEN] = false; engineParameters_[EP_HEADLESS] = false; engineParameters_[EP_SOUND] = false; // Construct a search path to find the resource prefix with two entries: // The first entry is an empty path which will be substituted with program/bin directory -- this entry is for binary when it is still in build tree // The second and third entries are possible relative paths from the installed program/bin directory to the asset directory -- these entries are for binary when it is in the Urho3D SDK installation location if (!engineParameters_.Contains(EP_RESOURCE_PREFIX_PATHS)) engineParameters_[EP_RESOURCE_PREFIX_PATHS] = ";../share/Resources;../share/Urho3D/Resources"; } void Sample::Start() { if (GetPlatform() == "Android" || GetPlatform() == "iOS") // On mobile platform, enable touch by adding a screen joystick InitTouchInput(); else if (GetSubsystem()->GetNumJoysticks() == 0) // On desktop platform, do not detect touch when we already got a joystick SubscribeToEvent(E_TOUCHBEGIN, URHO3D_HANDLER(Sample, HandleTouchBegin)); // Create logo CreateLogo(); // Set custom window Title & Icon SetWindowTitleAndIcon(); // Create console and debug HUD CreateConsoleAndDebugHud(); // Subscribe key down event SubscribeToEvent(E_KEYDOWN, URHO3D_HANDLER(Sample, HandleKeyDown)); // Subscribe key up event SubscribeToEvent(E_KEYUP, URHO3D_HANDLER(Sample, HandleKeyUp)); // Subscribe scene update event SubscribeToEvent(E_SCENEUPDATE, URHO3D_HANDLER(Sample, HandleSceneUpdate)); } void Sample::Stop() { engine_->DumpResources(true); } void Sample::InitTouchInput() { touchEnabled_ = true; ResourceCache* cache = GetSubsystem(); Input* input = GetSubsystem(); XMLFile* layout = cache->GetResource("UI/ScreenJoystick_Samples.xml"); const String& patchString = GetScreenJoystickPatchString(); if (!patchString.Empty()) { // Patch the screen joystick layout further on demand SharedPtr patchFile(new XMLFile(context_)); if (patchFile->FromString(patchString)) layout->Patch(patchFile); } screenJoystickIndex_ = (unsigned)input->AddScreenJoystick(layout, cache->GetResource("UI/DefaultStyle.xml")); input->SetScreenJoystickVisible(screenJoystickSettingsIndex_, true); } void Sample::InitMouseMode(MouseMode mode) { useMouseMode_ = mode; Input* input = GetSubsystem(); if (GetPlatform() != "Web") { if (useMouseMode_ == MM_FREE) input->SetMouseVisible(true); Console* console = GetSubsystem(); if (useMouseMode_ != MM_ABSOLUTE) { input->SetMouseMode(useMouseMode_); if (console && console->IsVisible()) input->SetMouseMode(MM_ABSOLUTE, true); } } else { input->SetMouseVisible(true); SubscribeToEvent(E_MOUSEBUTTONDOWN, URHO3D_HANDLER(Sample, HandleMouseModeRequest)); SubscribeToEvent(E_MOUSEMODECHANGED, URHO3D_HANDLER(Sample, HandleMouseModeChange)); } } void Sample::SetLogoVisible(bool enable) { if (logoSprite_) logoSprite_->SetVisible(enable); } void Sample::CreateLogo() { // Get logo texture ResourceCache* cache = GetSubsystem(); Texture2D* logoTexture = cache->GetResource("Textures/FishBoneLogo.png"); if (!logoTexture) return; // Create logo sprite and add to the UI layout UI* ui = GetSubsystem(); logoSprite_ = ui->GetRoot()->CreateChild(); // Set logo sprite texture logoSprite_->SetTexture(logoTexture); int textureWidth = logoTexture->GetWidth(); int textureHeight = logoTexture->GetHeight(); // Set logo sprite scale logoSprite_->SetScale(256.0f / textureWidth); // Set logo sprite size logoSprite_->SetSize(textureWidth, textureHeight); // Set logo sprite hot spot logoSprite_->SetHotSpot(textureWidth, textureHeight); // Set logo sprite alignment logoSprite_->SetAlignment(HA_RIGHT, VA_BOTTOM); // Make logo not fully opaque to show the scene underneath logoSprite_->SetOpacity(0.9f); // Set a low priority for the logo so that other UI elements can be drawn on top logoSprite_->SetPriority(-100); } void Sample::SetWindowTitleAndIcon() { ResourceCache* cache = GetSubsystem(); Graphics* graphics = GetSubsystem(); Image* icon = cache->GetResource("Textures/UrhoIcon.png"); graphics->SetWindowIcon(icon); graphics->SetWindowTitle("Urho3D Sample"); } void Sample::CreateConsoleAndDebugHud() { // Get default style ResourceCache* cache = GetSubsystem(); XMLFile* xmlFile = cache->GetResource("UI/DefaultStyle.xml"); // Create console Console* console = engine_->CreateConsole(); console->SetDefaultStyle(xmlFile); console->GetBackground()->SetOpacity(0.8f); // Create debug HUD. DebugHud* debugHud = engine_->CreateDebugHud(); debugHud->SetDefaultStyle(xmlFile); } void Sample::HandleKeyUp(StringHash /*eventType*/, VariantMap& eventData) { using namespace KeyUp; int key = eventData[P_KEY].GetI32(); // Close console (if open) or exit when ESC is pressed if (key == KEY_ESCAPE) { Console* console = GetSubsystem(); if (console->IsVisible()) console->SetVisible(false); else { if (GetPlatform() == "Web") { GetSubsystem()->SetMouseVisible(true); if (useMouseMode_ != MM_ABSOLUTE) GetSubsystem()->SetMouseMode(MM_FREE); } else engine_->Exit(); } } } void Sample::HandleKeyDown(StringHash /*eventType*/, VariantMap& eventData) { using namespace KeyDown; int key = eventData[P_KEY].GetI32(); // Toggle console with F1 if (key == KEY_F1) GetSubsystem()->Toggle(); // Toggle debug HUD with F2 else if (key == KEY_F2) GetSubsystem()->ToggleAll(); // Common rendering quality controls, only when UI has no focused element else if (!GetSubsystem()->GetFocusElement()) { Renderer* renderer = GetSubsystem(); // Preferences / Pause if (key == KEY_SELECT && touchEnabled_) { paused_ = !paused_; Input* input = GetSubsystem(); if (screenJoystickSettingsIndex_ == M_MAX_UNSIGNED) { // Lazy initialization ResourceCache* cache = GetSubsystem(); screenJoystickSettingsIndex_ = (unsigned)input->AddScreenJoystick(cache->GetResource("UI/ScreenJoystickSettings_Samples.xml"), cache->GetResource("UI/DefaultStyle.xml")); } else input->SetScreenJoystickVisible(screenJoystickSettingsIndex_, paused_); } // Texture quality else if (key == '1') { auto quality = (unsigned)renderer->GetTextureQuality(); ++quality; if (quality > QUALITY_HIGH) quality = QUALITY_LOW; renderer->SetTextureQuality((MaterialQuality)quality); } // Material quality else if (key == '2') { auto quality = (unsigned)renderer->GetMaterialQuality(); ++quality; if (quality > QUALITY_HIGH) quality = QUALITY_LOW; renderer->SetMaterialQuality((MaterialQuality)quality); } // Specular lighting else if (key == '3') renderer->SetSpecularLighting(!renderer->GetSpecularLighting()); // Shadow rendering else if (key == '4') renderer->SetDrawShadows(!renderer->GetDrawShadows()); // Shadow map resolution else if (key == '5') { int shadowMapSize = renderer->GetShadowMapSize(); shadowMapSize *= 2; if (shadowMapSize > 2048) shadowMapSize = 512; renderer->SetShadowMapSize(shadowMapSize); } // Shadow depth and filtering quality else if (key == '6') { ShadowQuality quality = renderer->GetShadowQuality(); quality = (ShadowQuality)(quality + 1); if (quality > SHADOWQUALITY_BLUR_VSM) quality = SHADOWQUALITY_SIMPLE_16BIT; renderer->SetShadowQuality(quality); } // Occlusion culling else if (key == '7') { bool occlusion = renderer->GetMaxOccluderTriangles() > 0; occlusion = !occlusion; renderer->SetMaxOccluderTriangles(occlusion ? 5000 : 0); } // Instancing else if (key == '8') renderer->SetDynamicInstancing(!renderer->GetDynamicInstancing()); // Take screenshot else if (key == '9') { Graphics* graphics = GetSubsystem(); Image screenshot(context_); graphics->TakeScreenShot(screenshot); // Here we save in the Data folder with date and time appended screenshot.SavePNG(GetSubsystem()->GetProgramDir() + "Data/Screenshot_" + Time::GetTimeStamp().Replaced(':', '_').Replaced('.', '_').Replaced(' ', '_') + ".png"); } } } void Sample::HandleSceneUpdate(StringHash /*eventType*/, VariantMap& eventData) { // Move the camera by touch, if the camera node is initialized by descendant sample class if (touchEnabled_ && cameraNode_) { Input* input = GetSubsystem(); for (i32 i = 0; i < input->GetNumTouches(); ++i) { TouchState* state = input->GetTouch(i); if (!state->touchedElement_) // Touch on empty space { if (state->delta_.x_ ||state->delta_.y_) { Camera* camera = cameraNode_->GetComponent(); if (!camera) return; Graphics* graphics = GetSubsystem(); yaw_ += TOUCH_SENSITIVITY * camera->GetFov() / graphics->GetHeight() * state->delta_.x_; pitch_ += TOUCH_SENSITIVITY * camera->GetFov() / graphics->GetHeight() * state->delta_.y_; // Construct new orientation for the camera scene node from yaw and pitch; roll is fixed to zero cameraNode_->SetRotation(Quaternion(pitch_, yaw_, 0.0f)); } else { // Move the cursor to the touch position Cursor* cursor = GetSubsystem()->GetCursor(); if (cursor && cursor->IsVisible()) cursor->SetPosition(state->position_); } } } } } void Sample::HandleTouchBegin(StringHash /*eventType*/, VariantMap& eventData) { // On some platforms like Windows the presence of touch input can only be detected dynamically InitTouchInput(); UnsubscribeFromEvent("TouchBegin"); } // If the user clicks the canvas, attempt to switch to relative mouse mode on web platform void Sample::HandleMouseModeRequest(StringHash /*eventType*/, VariantMap& eventData) { Console* console = GetSubsystem(); if (console && console->IsVisible()) return; Input* input = GetSubsystem(); if (useMouseMode_ == MM_ABSOLUTE) input->SetMouseVisible(false); else if (useMouseMode_ == MM_FREE) input->SetMouseVisible(true); input->SetMouseMode(useMouseMode_); } void Sample::HandleMouseModeChange(StringHash /*eventType*/, VariantMap& eventData) { Input* input = GetSubsystem(); bool mouseLocked = eventData[MouseModeChanged::P_MOUSELOCKED].GetBool(); input->SetMouseVisible(!mouseLocked); }