416 lines
14 KiB
C++
416 lines
14 KiB
C++
//
|
|
// Copyright (c) 2008-2021 the Urho3D project.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
//
|
|
|
|
#include <Urho3D/Engine/Application.h>
|
|
#include <Urho3D/Graphics/Camera.h>
|
|
#include <Urho3D/Engine/Console.h>
|
|
#include <Urho3D/UI/Cursor.h>
|
|
#include <Urho3D/Engine/DebugHud.h>
|
|
#include <Urho3D/Engine/Engine.h>
|
|
#include <Urho3D/Engine/EngineDefs.h>
|
|
#include <Urho3D/IO/FileSystem.h>
|
|
#include <Urho3D/Graphics/Graphics.h>
|
|
#include <Urho3D/Input/Input.h>
|
|
#include <Urho3D/Input/InputEvents.h>
|
|
#include <Urho3D/Graphics/Renderer.h>
|
|
#include <Urho3D/Resource/ResourceCache.h>
|
|
#include <Urho3D/Scene/Scene.h>
|
|
#include <Urho3D/Scene/SceneEvents.h>
|
|
#include <Urho3D/UI/Sprite.h>
|
|
#include <Urho3D/Graphics/Texture2D.h>
|
|
#include <Urho3D/Core/Timer.h>
|
|
#include <Urho3D/UI/UI.h>
|
|
#include <Urho3D/Resource/XMLFile.h>
|
|
#include <Urho3D/IO/Log.h>
|
|
|
|
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<FileSystem>()->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<Input>()->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<ResourceCache>();
|
|
Input* input = GetSubsystem<Input>();
|
|
XMLFile* layout = cache->GetResource<XMLFile>("UI/ScreenJoystick_Samples.xml");
|
|
const String& patchString = GetScreenJoystickPatchString();
|
|
if (!patchString.Empty())
|
|
{
|
|
// Patch the screen joystick layout further on demand
|
|
SharedPtr<XMLFile> patchFile(new XMLFile(context_));
|
|
if (patchFile->FromString(patchString))
|
|
layout->Patch(patchFile);
|
|
}
|
|
screenJoystickIndex_ = (unsigned)input->AddScreenJoystick(layout, cache->GetResource<XMLFile>("UI/DefaultStyle.xml"));
|
|
input->SetScreenJoystickVisible(screenJoystickSettingsIndex_, true);
|
|
}
|
|
|
|
void Sample::InitMouseMode(MouseMode mode)
|
|
{
|
|
useMouseMode_ = mode;
|
|
|
|
Input* input = GetSubsystem<Input>();
|
|
|
|
if (GetPlatform() != "Web")
|
|
{
|
|
if (useMouseMode_ == MM_FREE)
|
|
input->SetMouseVisible(true);
|
|
|
|
Console* console = GetSubsystem<Console>();
|
|
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<ResourceCache>();
|
|
Texture2D* logoTexture = cache->GetResource<Texture2D>("Textures/FishBoneLogo.png");
|
|
if (!logoTexture)
|
|
return;
|
|
|
|
// Create logo sprite and add to the UI layout
|
|
UI* ui = GetSubsystem<UI>();
|
|
logoSprite_ = ui->GetRoot()->CreateChild<Sprite>();
|
|
|
|
// 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<ResourceCache>();
|
|
Graphics* graphics = GetSubsystem<Graphics>();
|
|
Image* icon = cache->GetResource<Image>("Textures/UrhoIcon.png");
|
|
graphics->SetWindowIcon(icon);
|
|
graphics->SetWindowTitle("Urho3D Sample");
|
|
}
|
|
|
|
void Sample::CreateConsoleAndDebugHud()
|
|
{
|
|
// Get default style
|
|
ResourceCache* cache = GetSubsystem<ResourceCache>();
|
|
XMLFile* xmlFile = cache->GetResource<XMLFile>("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].GetInt();
|
|
|
|
// Close console (if open) or exit when ESC is pressed
|
|
if (key == KEY_ESCAPE)
|
|
{
|
|
Console* console = GetSubsystem<Console>();
|
|
if (console->IsVisible())
|
|
console->SetVisible(false);
|
|
else
|
|
{
|
|
if (GetPlatform() == "Web")
|
|
{
|
|
GetSubsystem<Input>()->SetMouseVisible(true);
|
|
if (useMouseMode_ != MM_ABSOLUTE)
|
|
GetSubsystem<Input>()->SetMouseMode(MM_FREE);
|
|
}
|
|
else
|
|
engine_->Exit();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sample::HandleKeyDown(StringHash /*eventType*/, VariantMap& eventData)
|
|
{
|
|
using namespace KeyDown;
|
|
|
|
int key = eventData[P_KEY].GetInt();
|
|
|
|
// Toggle console with F1
|
|
if (key == KEY_F1)
|
|
GetSubsystem<Console>()->Toggle();
|
|
|
|
// Toggle debug HUD with F2
|
|
else if (key == KEY_F2)
|
|
GetSubsystem<DebugHud>()->ToggleAll();
|
|
|
|
// Common rendering quality controls, only when UI has no focused element
|
|
else if (!GetSubsystem<UI>()->GetFocusElement())
|
|
{
|
|
Renderer* renderer = GetSubsystem<Renderer>();
|
|
|
|
// Preferences / Pause
|
|
if (key == KEY_SELECT && touchEnabled_)
|
|
{
|
|
paused_ = !paused_;
|
|
|
|
Input* input = GetSubsystem<Input>();
|
|
if (screenJoystickSettingsIndex_ == M_MAX_UNSIGNED)
|
|
{
|
|
// Lazy initialization
|
|
ResourceCache* cache = GetSubsystem<ResourceCache>();
|
|
screenJoystickSettingsIndex_ = (unsigned)input->AddScreenJoystick(cache->GetResource<XMLFile>("UI/ScreenJoystickSettings_Samples.xml"), cache->GetResource<XMLFile>("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<Graphics>();
|
|
Image screenshot(context_);
|
|
graphics->TakeScreenShot(screenshot);
|
|
// Here we save in the Data folder with date and time appended
|
|
screenshot.SavePNG(GetSubsystem<FileSystem>()->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<Input>();
|
|
for (unsigned 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<Camera>();
|
|
if (!camera)
|
|
return;
|
|
|
|
Graphics* graphics = GetSubsystem<Graphics>();
|
|
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<UI>()->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<Console>();
|
|
if (console && console->IsVisible())
|
|
return;
|
|
Input* input = GetSubsystem<Input>();
|
|
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<Input>();
|
|
bool mouseLocked = eventData[MouseModeChanged::P_MOUSELOCKED].GetBool();
|
|
input->SetMouseVisible(!mouseLocked);
|
|
}
|