Merge pull request #4849 from Web-eWorks/modelviewer-refactor

Refactor the ModelViewer to use PiGui
master
Webster Sheets 2020-04-06 15:51:48 -04:00 committed by GitHub
commit 08386499a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1673 additions and 1539 deletions

View File

@ -9,10 +9,17 @@
#include <array> #include <array>
void Input::Init(GameConfig *config) Input::Input(IniConfig *config) :
m_config(config),
m_capturingMouse(false),
mouseYInvert(false),
joystickEnabled(true),
keyModState(0),
mouseButton(),
mouseMotion()
{ {
joystickEnabled = (config->Int("EnableJoystick")) ? true : false; joystickEnabled = (m_config->Int("EnableJoystick")) ? true : false;
mouseYInvert = (config->Int("InvertMouseY")) ? true : false; mouseYInvert = (m_config->Int("InvertMouseY")) ? true : false;
InitJoysticks(); InitJoysticks();
} }
@ -22,8 +29,8 @@ void Input::InitGame()
//reset input states //reset input states
keyState.clear(); keyState.clear();
keyModState = 0; keyModState = 0;
std::fill(mouseButton, mouseButton + COUNTOF(mouseButton), 0); mouseButton.fill(0);
std::fill(mouseMotion, mouseMotion + COUNTOF(mouseMotion), 0); mouseMotion.fill(0);
for (std::map<SDL_JoystickID, JoystickState>::iterator stick = joysticks.begin(); stick != joysticks.end(); ++stick) { for (std::map<SDL_JoystickID, JoystickState>::iterator stick = joysticks.begin(); stick != joysticks.end(); ++stick) {
JoystickState &state = stick->second; JoystickState &state = stick->second;
std::fill(state.buttons.begin(), state.buttons.end(), false); std::fill(state.buttons.begin(), state.buttons.end(), false);
@ -32,6 +39,25 @@ void Input::InitGame()
} }
} }
void Input::NewFrame()
{
mouseMotion.fill(0);
mouseWheel = 0;
for (auto &k : keyState) {
auto &val = keyState[k.first];
switch (k.second) {
case 1: // if we were just pressed last frame, migrate to held state
val = 2;
break;
case 4: // if we were just released last frame, migrate to empty state
val = 0;
break;
default: // otherwise, no need to do anything
break;
}
}
}
InputResponse Input::InputFrame::ProcessSDLEvent(SDL_Event &event) InputResponse Input::InputFrame::ProcessSDLEvent(SDL_Event &event)
{ {
bool matched = false; bool matched = false;
@ -95,7 +121,7 @@ KeyBindings::ActionBinding *Input::AddActionBinding(std::string id, BindingGroup
group->bindings[id] = BindingGroup::ENTRY_ACTION; group->bindings[id] = BindingGroup::ENTRY_ACTION;
// Load from the config // Load from the config
std::string config_str = Pi::config->String(id.c_str()); std::string config_str = m_config->String(id.c_str());
if (config_str.length() > 0) binding.SetFromString(config_str); if (config_str.length() > 0) binding.SetFromString(config_str);
return &(actionBindings[id] = binding); return &(actionBindings[id] = binding);
@ -110,7 +136,7 @@ KeyBindings::AxisBinding *Input::AddAxisBinding(std::string id, BindingGroup *gr
group->bindings[id] = BindingGroup::ENTRY_AXIS; group->bindings[id] = BindingGroup::ENTRY_AXIS;
// Load from the config // Load from the config
std::string config_str = Pi::config->String(id.c_str()); std::string config_str = m_config->String(id.c_str());
if (config_str.length() > 0) binding.SetFromString(config_str); if (config_str.length() > 0) binding.SetFromString(config_str);
return &(axisBindings[id] = binding); return &(axisBindings[id] = binding);
@ -120,30 +146,33 @@ void Input::HandleSDLEvent(SDL_Event &event)
{ {
switch (event.type) { switch (event.type) {
case SDL_KEYDOWN: case SDL_KEYDOWN:
keyState[event.key.keysym.sym] = true; // Set key state to "just pressed"
keyState[event.key.keysym.sym] = 1;
keyModState = event.key.keysym.mod; keyModState = event.key.keysym.mod;
onKeyPress.emit(&event.key.keysym); onKeyPress.emit(&event.key.keysym);
break; break;
case SDL_KEYUP: case SDL_KEYUP:
keyState[event.key.keysym.sym] = false; // Set key state to "just released"
keyState[event.key.keysym.sym] = 4;
keyModState = event.key.keysym.mod; keyModState = event.key.keysym.mod;
onKeyRelease.emit(&event.key.keysym); onKeyRelease.emit(&event.key.keysym);
break; break;
case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONDOWN:
if (event.button.button < COUNTOF(mouseButton)) { if (event.button.button < mouseButton.size()) {
mouseButton[event.button.button] = 1; mouseButton[event.button.button] = 1;
onMouseButtonDown.emit(event.button.button, onMouseButtonDown.emit(event.button.button,
event.button.x, event.button.y); event.button.x, event.button.y);
} }
break; break;
case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONUP:
if (event.button.button < COUNTOF(mouseButton)) { if (event.button.button < mouseButton.size()) {
mouseButton[event.button.button] = 0; mouseButton[event.button.button] = 0;
onMouseButtonUp.emit(event.button.button, onMouseButtonUp.emit(event.button.button,
event.button.x, event.button.y); event.button.x, event.button.y);
} }
break; break;
case SDL_MOUSEWHEEL: case SDL_MOUSEWHEEL:
mouseWheel = event.wheel.y;
onMouseWheel.emit(event.wheel.y > 0); // true = up onMouseWheel.emit(event.wheel.y > 0); // true = up
break; break;
case SDL_MOUSEMOTION: case SDL_MOUSEMOTION:

View File

@ -8,18 +8,16 @@
#include "utils.h" #include "utils.h"
#include <algorithm> #include <algorithm>
#include <array>
class GameConfig; class IniConfig;
class Input { class Input {
// TODO: better decouple these two classes.
friend class Pi;
public: public:
Input() = default; Input(IniConfig *config);
void Init(GameConfig *config);
void InitGame(); void InitGame();
void HandleSDLEvent(SDL_Event &ev); void HandleSDLEvent(SDL_Event &ev);
void NewFrame();
// The Page->Group->Binding system serves as a thin veneer for the UI to make // The Page->Group->Binding system serves as a thin veneer for the UI to make
// sane reasonings about how to structure the Options dialog. // sane reasonings about how to structure the Options dialog.
@ -94,7 +92,17 @@ public:
return axisBindings.count(id) ? &axisBindings[id] : nullptr; return axisBindings.count(id) ? &axisBindings[id] : nullptr;
} }
bool KeyState(SDL_Keycode k) { return keyState[k]; } bool KeyState(SDL_Keycode k) { return IsKeyDown(k); }
// returns true if key K is currently pressed
bool IsKeyDown(SDL_Keycode k) { return keyState[k] & 0x3; }
// returns true if key K was pressed this frame
bool IsKeyPressed(SDL_Keycode k) { return keyState[k] == 1; }
// returns true if key K was released this frame
bool IsKeyReleased(SDL_Keycode k) { return keyState[k] == 4; }
int KeyModState() { return keyModState; } int KeyModState() { return keyModState; }
int JoystickButtonState(int joystick, int button); int JoystickButtonState(int joystick, int button);
@ -132,9 +140,11 @@ public:
void GetMouseMotion(int motion[2]) void GetMouseMotion(int motion[2])
{ {
memcpy(motion, mouseMotion, sizeof(int) * 2); std::copy_n(mouseMotion.data(), mouseMotion.size(), motion);
} }
int GetMouseWheel() { return mouseWheel; }
// Capturing the mouse hides the cursor, puts the mouse into relative mode, // Capturing the mouse hides the cursor, puts the mouse into relative mode,
// and passes all mouse inputs to the input system, regardless of whether // and passes all mouse inputs to the input system, regardless of whether
// ImGui is using them or not. // ImGui is using them or not.
@ -154,10 +164,13 @@ public:
private: private:
void InitJoysticks(); void InitJoysticks();
std::map<SDL_Keycode, bool> keyState; IniConfig *m_config;
std::map<SDL_Keycode, uint8_t> keyState;
int keyModState; int keyModState;
char mouseButton[6]; std::array<char, 6> mouseButton;
int mouseMotion[2]; std::array<int, 2> mouseMotion;
int mouseWheel;
bool m_capturingMouse; bool m_capturingMouse;
bool joystickEnabled; bool joystickEnabled;

File diff suppressed because it is too large Load Diff

View File

@ -3,66 +3,114 @@
#ifndef MODELVIEWER_H #ifndef MODELVIEWER_H
#define MODELVIEWER_H #define MODELVIEWER_H
#include "Input.h"
#include "NavLights.h" #include "NavLights.h"
#include "Shields.h" #include "Shields.h"
#include "core/GuiApplication.h"
#include "graphics/Drawables.h" #include "graphics/Drawables.h"
#include "graphics/Renderer.h" #include "graphics/Renderer.h"
#include "graphics/Texture.h" #include "graphics/Texture.h"
#include "libs.h" #include "libs.h"
#include "lua/LuaManager.h" #include "lua/LuaManager.h"
#include "pigui/PiGui.h"
#include "scenegraph/SceneGraph.h" #include "scenegraph/SceneGraph.h"
#include "ui/Context.h" #include "ui/Context.h"
class ModelViewer { #include <memory>
public:
ModelViewer(Graphics::Renderer *r, LuaManager *l);
~ModelViewer();
static void Run(const std::string &modelName); class Input;
class ModelViewer;
class ModelViewerApp : public GuiApplication {
public:
ModelViewerApp() :
GuiApplication("Model Viewer")
{}
void SetInitialModel(std::string &modelName) { m_modelName = modelName; }
std::string &GetModelName() { return m_modelName; }
protected:
void Startup() override;
void Shutdown() override;
void PreUpdate() override;
void PostUpdate() override;
friend class ModelViewer;
private:
std::string m_modelName;
std::unique_ptr<Input> m_input;
std::shared_ptr<ModelViewer> m_modelViewer;
};
class ModelViewer : public Application::Lifecycle {
public:
enum class CameraPreset : uint8_t {
Front,
Back,
Left,
Right,
Top,
Bottom
};
ModelViewer(ModelViewerApp *app, LuaManager *l);
void SetModel(const std::string &modelName);
bool SetRandomColor();
void ResetCamera();
void ChangeCameraPreset(CameraPreset preset);
protected:
void Start() override;
void Update(float deltaTime) override;
void End() override;
void SetupAxes();
void HandleInput();
private: private:
bool OnPickModel(UI::List *);
bool OnQuit();
bool OnReloadModel(UI::Widget *);
bool OnToggleCollMesh(UI::CheckBox *);
bool OnToggleShowShields(UI::CheckBox *);
bool OnToggleGrid(UI::Widget *);
bool OnToggleGuns(UI::CheckBox *);
bool OnRandomColor(UI::Widget *);
void UpdateShield();
bool OnHitIt(UI::Widget *);
void HitImpl();
void AddLog(const std::string &line); void AddLog(const std::string &line);
void ChangeCameraPreset(SDL_Keycode, SDL_Keymod);
void UpdateModelList();
void UpdateDecalList();
void UpdateShield();
void UpdateCamera(float deltaTime);
void UpdateLights();
void ReloadModel();
void SetAnimation(SceneGraph::Animation *anim);
void SetDecals(const std::string &file);
void OnModelChanged();
void ToggleCollMesh();
void ToggleShowShields();
void ToggleGrid();
void ToggleGuns();
void HitIt();
void ToggleViewControlMode(); void ToggleViewControlMode();
void ClearLog();
void ClearModel(); void ClearModel();
void CreateTestResources(); void CreateTestResources();
void DrawBackground(); void DrawBackground();
void DrawGrid(const matrix4x4f &trans, float radius); void DrawGrid(const matrix4x4f &trans, float radius);
void DrawModel(const matrix4x4f &mv); void DrawModel(const matrix4x4f &mv);
void MainLoop();
void OnAnimChanged(unsigned int, const std::string &);
void OnAnimSliderChanged(float);
void OnDecalChanged(unsigned int, const std::string &);
void OnLightPresetChanged(unsigned int index, const std::string &);
void OnModelColorsChanged(float);
void OnPatternChanged(unsigned int, const std::string &);
void OnThrustChanged(float);
void PollEvents();
void PopulateFilePicker();
void ResetCamera();
void ResetThrusters(); void ResetThrusters();
void Screenshot(); void Screenshot();
void SaveModelToBinary(); void SaveModelToBinary();
void SetModel(const std::string &name);
void SetupFilePicker();
void SetupUI();
void UpdateAnimList();
void UpdateCamera();
void UpdateLights();
void UpdatePatternList();
void DrawModelSelector();
void DrawModelOptions();
void DrawShipControls();
void DrawLog();
void DrawPiGui();
private:
//toggleable options //toggleable options
struct Options { struct Options {
bool attachGuns; bool attachGuns;
@ -77,17 +125,69 @@ private:
bool wireframe; bool wireframe;
bool mouselookEnabled; bool mouselookEnabled;
float gridInterval; float gridInterval;
int lightPreset; uint32_t lightPreset;
bool orthoView; bool orthoView;
Options(); Options();
}; };
bool m_done;
private:
ModelViewerApp *m_app;
Input *m_input;
PiGui::Instance *m_pigui;
KeyBindings::AxisBinding *m_moveForward;
KeyBindings::AxisBinding *m_moveLeft;
KeyBindings::AxisBinding *m_moveUp;
KeyBindings::AxisBinding *m_zoomAxis;
KeyBindings::AxisBinding *m_rotateViewLeft;
KeyBindings::AxisBinding *m_rotateViewUp;
KeyBindings::ActionBinding *m_viewTop;
KeyBindings::ActionBinding *m_viewLeft;
KeyBindings::ActionBinding *m_viewFront;
vector2f m_windowSize;
vector2f m_logWindowSize;
vector2f m_animWindowSize;
std::vector<std::string> m_log;
bool m_resetLogScroll = false;
vector3f m_linearThrust = {};
vector3f m_angularThrust = {};
// Model pattern colors
std::vector<Color> m_colors;
std::vector<std::string> m_fileNames;
std::string m_modelName;
std::string m_requestedModelName;
std::unique_ptr<SceneGraph::Model> m_model;
bool m_modelIsShip = false;
std::vector<SceneGraph::Animation *> m_animations;
SceneGraph::Animation *m_currentAnimation = nullptr;
bool m_modelSupportsPatterns = false;
std::vector<std::string> m_patterns;
uint32_t m_currentPattern = 0;
bool m_modelSupportsDecals = false;
std::vector<std::string> m_decals;
uint32_t m_currentDecal = 0;
bool m_modelHasShields = false;
std::unique_ptr<Shields> m_shields;
std::unique_ptr<NavLights> m_navLights;
std::unique_ptr<SceneGraph::Model> m_gunModel;
std::unique_ptr<SceneGraph::Model> m_scaleModel;
bool m_screenshotQueued; bool m_screenshotQueued;
bool m_shieldIsHit; bool m_shieldIsHit;
bool m_settingColourSliders; bool m_settingColourSliders;
float m_shieldHitPan; float m_shieldHitPan;
double m_frameTime;
Graphics::Renderer *m_renderer; Graphics::Renderer *m_renderer;
Graphics::Texture *m_decalTexture; Graphics::Texture *m_decalTexture;
vector3f m_viewPos; vector3f m_viewPos;
@ -95,41 +195,13 @@ private:
float m_rotX, m_rotY, m_zoom; float m_rotX, m_rotY, m_zoom;
float m_baseDistance; float m_baseDistance;
Random m_rng; Random m_rng;
SceneGraph::Animation *m_currentAnimation;
SceneGraph::Model *m_model;
Options m_options; Options m_options;
float m_landingMinOffset; float m_landingMinOffset;
std::unique_ptr<NavLights> m_navLights;
std::unique_ptr<Shields> m_shields;
std::unique_ptr<SceneGraph::Model> m_gunModel;
std::unique_ptr<SceneGraph::Model> m_scaleModel;
std::string m_modelName;
std::string m_requestedModelName;
RefCountedPtr<UI::Context> m_ui;
Graphics::RenderState *m_bgState; Graphics::RenderState *m_bgState;
RefCountedPtr<Graphics::VertexBuffer> m_bgBuffer; RefCountedPtr<Graphics::VertexBuffer> m_bgBuffer;
//undecided on this input stuff
//updating the states of all inputs during PollEvents
std::map<SDL_Keycode, bool> m_keyStates;
bool m_mouseButton[SDL_BUTTON_RIGHT + 1]; //buttons start at 1
int m_mouseMotion[2];
bool m_mouseWheelUp, m_mouseWheelDown;
//interface stuff that needs to be accessed later (unorganized)
UI::MultiLineText *m_log;
RefCountedPtr<UI::Scroller> m_logScroller;
UI::List *m_fileList;
UI::DropDown *animSelector;
UI::DropDown *patternSelector;
UI::DropDown *decalSelector;
UI::Label *nameLabel;
UI::Slider *animSlider;
UI::Label *animValue;
UI::Slider *colorSliders[9];
UI::Slider *thrustSliders[2 * 3]; //thruster sliders 2*xyz (linear & angular)
sigc::signal<void> onModelChanged; sigc::signal<void> onModelChanged;
Graphics::Drawables::Lines m_gridLines; Graphics::Drawables::Lines m_gridLines;

View File

@ -25,9 +25,11 @@
#include "NavLights.h" #include "NavLights.h"
#include "OS.h" #include "OS.h"
#include "core/GuiApplication.h" #include "core/GuiApplication.h"
#include "graphics/opengl/RendererGL.h"
#include "lua/Lua.h" #include "lua/Lua.h"
#include "lua/LuaConsole.h" #include "lua/LuaConsole.h"
#include "lua/LuaEvent.h" #include "lua/LuaEvent.h"
#include "lua/LuaPiGui.h"
#include "lua/LuaTimer.h" #include "lua/LuaTimer.h"
#include "profiler/Profiler.h" #include "profiler/Profiler.h"
#include "sound/AmbientSounds.h" #include "sound/AmbientSounds.h"
@ -133,7 +135,7 @@ float Pi::amountOfBackgroundStarsDisplayed = 1.0f;
bool Pi::DrawGUI = true; bool Pi::DrawGUI = true;
Graphics::Renderer *Pi::renderer; Graphics::Renderer *Pi::renderer;
RefCountedPtr<UI::Context> Pi::ui; RefCountedPtr<UI::Context> Pi::ui;
RefCountedPtr<PiGui> Pi::pigui; PiGui::Instance *Pi::pigui = nullptr;
ModelCache *Pi::modelCache; ModelCache *Pi::modelCache;
Intro *Pi::intro; Intro *Pi::intro;
SDLGraphics *Pi::sdl; SDLGraphics *Pi::sdl;
@ -361,8 +363,7 @@ void Pi::App::Startup()
Pi::rng.IncRefCount(); // so nothing tries to free it Pi::rng.IncRefCount(); // so nothing tries to free it
Pi::rng.seed(time(0)); Pi::rng.seed(time(0));
Pi::input = new Input(); Pi::input = StartupInput(config);
Pi::input->Init(Pi::config);
Pi::input->onKeyPress.connect(sigc::ptr_fun(&Pi::HandleKeyDown)); Pi::input->onKeyPress.connect(sigc::ptr_fun(&Pi::HandleKeyDown));
// we can only do bindings once joysticks are initialised. // we can only do bindings once joysticks are initialised.
@ -371,6 +372,8 @@ void Pi::App::Startup()
RegisterInputBindings(); RegisterInputBindings();
Pi::pigui = StartupPiGui();
// FIXME: move these into the appropriate class! // FIXME: move these into the appropriate class!
navTunnelDisplayed = (config->Int("DisplayNavTunnel")) ? true : false; navTunnelDisplayed = (config->Int("DisplayNavTunnel")) ? true : false;
speedLinesDisplayed = (config->Int("SpeedLines")) ? true : false; speedLinesDisplayed = (config->Int("SpeedLines")) ? true : false;
@ -438,9 +441,11 @@ void Pi::App::Shutdown()
BaseSphere::Uninit(); BaseSphere::Uninit();
FaceParts::Uninit(); FaceParts::Uninit();
Graphics::Uninit(); Graphics::Uninit();
Pi::pigui->Uninit();
PiGUI::Lua::Uninit();
ShutdownPiGui();
Pi::pigui = nullptr;
Pi::ui.Reset(0); Pi::ui.Reset(0);
Pi::pigui.Reset(0);
Lua::UninitModules(); Lua::UninitModules();
Lua::Uninit(); Lua::Uninit();
Gui::Uninit(); Gui::Uninit();
@ -452,6 +457,9 @@ void Pi::App::Shutdown()
ShutdownRenderer(); ShutdownRenderer();
Pi::renderer = nullptr; Pi::renderer = nullptr;
ShutdownInput();
Pi::input = nullptr;
delete Pi::config; delete Pi::config;
delete Pi::planner; delete Pi::planner;
asyncJobQueue.reset(); asyncJobQueue.reset();
@ -489,14 +497,12 @@ void LoadStep::Start()
// TODO: Get the lua state responsible for drawing the init progress up as fast as possible // TODO: Get the lua state responsible for drawing the init progress up as fast as possible
// Investigate using a pigui-only Lua state that we can initialize without depending on // Investigate using a pigui-only Lua state that we can initialize without depending on
// normal init flow, or drawing the init screen in C++ instead? // normal init flow, or drawing the init screen in C++ instead?
// Ideally we can initialize the ImGui related parts of pigui as soon as the renderer is online, // Loads just the PiGui class and PiGui-related modules
// and then load all the lua-related state once Lua's registered and online... PiGUI::Lua::Init();
Pi::pigui.Reset(new PiGui);
Pi::pigui->Init(Pi::renderer->GetSDLWindow());
// Don't render the first frame, just make sure all of our fonts are loaded // Don't render the first frame, just make sure all of our fonts are loaded
Pi::pigui->NewFrame(Pi::renderer->GetSDLWindow()); Pi::pigui->NewFrame();
Pi::pigui->RunHandler(0.01, "INIT"); PiGUI::RunHandler(0.01, "INIT");
Pi::pigui->EndFrame(); Pi::pigui->EndFrame();
AddStep("UI::AddContext", []() { AddStep("UI::AddContext", []() {
@ -608,8 +614,8 @@ void LoadStep::Update(float deltaTime)
Output("Loading [%02.f%%]: %s took %.2fms\n", progress * 100., Output("Loading [%02.f%%]: %s took %.2fms\n", progress * 100.,
loader.name.c_str(), timer.milliseconds()); loader.name.c_str(), timer.milliseconds());
Pi::pigui->NewFrame(Pi::renderer->GetSDLWindow()); Pi::pigui->NewFrame();
Pi::pigui->RunHandler(progress, "INIT"); PiGUI::RunHandler(progress, "INIT");
Pi::pigui->Render(); Pi::pigui->Render();
} else { } else {
@ -643,43 +649,12 @@ void MainMenu::Start()
void MainMenu::Update(float deltaTime) void MainMenu::Update(float deltaTime)
{ {
SDL_Event event; Pi::GetApp()->HandleEvents();
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT)
Pi::RequestQuit();
else {
Pi::pigui->ProcessEvent(&event);
if (Pi::pigui->WantCaptureMouse()) {
// don't process mouse event any further, imgui already handled it
switch (event.type) {
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEWHEEL:
case SDL_MOUSEMOTION:
continue;
default: break;
}
}
if (Pi::pigui->WantCaptureKeyboard()) {
// don't process keyboard event any further, imgui already handled it
switch (event.type) {
case SDL_KEYDOWN:
case SDL_KEYUP:
case SDL_TEXTINPUT:
continue;
default: break;
}
}
Pi::input->HandleSDLEvent(event);
}
}
Pi::intro->Draw(deltaTime); Pi::intro->Draw(deltaTime);
Pi::pigui->NewFrame(Pi::renderer->GetSDLWindow()); Pi::pigui->NewFrame();
Pi::pigui->RunHandler(deltaTime, "MAINMENU"); PiGUI::RunHandler(deltaTime, "MAINMENU");
Pi::pigui->Render(); Pi::pigui->Render();
@ -847,12 +822,13 @@ void Pi::HandleEscKey()
} }
} }
void Pi::App::HandleEvents() // Return true if the event has been handled and shouldn't be passed through
// to the normal input system.
bool Pi::App::HandleEvent(SDL_Event &event)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
SDL_Event event;
// XXX for most keypresses SDL will generate KEYUP/KEYDOWN and TEXTINPUT // HACK for most keypresses SDL will generate KEYUP/KEYDOWN and TEXTINPUT
// events. keybindings run off KEYUP/KEYDOWN. the console is opened/closed // events. keybindings run off KEYUP/KEYDOWN. the console is opened/closed
// via keybinding. the console TextInput widget uses TEXTINPUT events. thus // via keybinding. the console TextInput widget uses TEXTINPUT events. thus
// after switching the console, the stray TEXTINPUT event causes the // after switching the console, the stray TEXTINPUT event causes the
@ -860,64 +836,37 @@ void Pi::App::HandleEvents()
// this by setting this flag if the console was switched. if its set, we // this by setting this flag if the console was switched. if its set, we
// swallow the TEXTINPUT event this hack must remain until we have a // swallow the TEXTINPUT event this hack must remain until we have a
// unified input system // unified input system
bool skipTextInput = false; // This is safely able to be removed once GUI and newUI are gone
static bool skipTextInput = false;
Pi::input->mouseMotion[0] = Pi::input->mouseMotion[1] = 0; if (skipTextInput && event.type == SDL_TEXTINPUT) {
while (SDL_PollEvent(&event)) { skipTextInput = false;
if (event.type == SDL_QUIT) { return true;
Pi::RequestQuit();
}
Pi::pigui->ProcessEvent(&event);
// Input system takes priority over mouse events when capturing the mouse
if (Pi::pigui->WantCaptureMouse() && !Pi::input->IsCapturingMouse()) {
// don't process mouse event any further, imgui already handled it
switch (event.type) {
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEWHEEL:
case SDL_MOUSEMOTION:
continue;
default: break;
}
}
if (Pi::pigui->WantCaptureKeyboard()) {
// don't process keyboard event any further, imgui already handled it
switch (event.type) {
case SDL_KEYDOWN:
case SDL_KEYUP:
case SDL_TEXTINPUT:
continue;
default: break;
}
}
if (skipTextInput && event.type == SDL_TEXTINPUT) {
skipTextInput = false;
continue;
}
if (ui->DispatchSDLEvent(event))
continue;
bool consoleActive = Pi::IsConsoleActive();
if (!consoleActive) {
KeyBindings::DispatchSDLEvent(&event);
if (currentView)
currentView->HandleSDLEvent(event);
} else
KeyBindings::toggleLuaConsole.CheckSDLEventAndDispatch(&event);
if (consoleActive != Pi::IsConsoleActive()) {
skipTextInput = true;
continue;
}
if (Pi::IsConsoleActive())
continue;
Gui::HandleSDLEvent(&event);
input->HandleSDLEvent(event);
} }
if (ui->DispatchSDLEvent(event))
return true;
bool consoleActive = Pi::IsConsoleActive();
if (!consoleActive) {
KeyBindings::DispatchSDLEvent(&event);
if (currentView)
currentView->HandleSDLEvent(event);
} else {
KeyBindings::toggleLuaConsole.CheckSDLEventAndDispatch(&event);
}
if (consoleActive != Pi::IsConsoleActive()) {
skipTextInput = true;
return true;
}
if (Pi::IsConsoleActive())
return true;
Gui::HandleSDLEvent(&event);
return false;
} }
void Pi::App::HandleRequests() void Pi::App::HandleRequests()
@ -1126,10 +1075,16 @@ void GameLoop::Update(float deltaTime)
Pi::ui->Draw(); Pi::ui->Draw();
} }
// Ask ImGui to hide OS cursor if we're capturing it for input:
// it will do this if GetMouseCursor == ImGuiMouseCursor_None.
if (Pi::input->IsCapturingMouse()) {
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
}
// TODO: the escape menu depends on HandleEvents() being called before NewFrame() // TODO: the escape menu depends on HandleEvents() being called before NewFrame()
// Move HandleEvents to either the end of the loop or the very start of the loop // Move HandleEvents to either the end of the loop or the very start of the loop
// The goal is to be able to call imgui functions for debugging inside C++ code // The goal is to be able to call imgui functions for debugging inside C++ code
Pi::pigui->NewFrame(Pi::renderer->GetSDLWindow()); Pi::pigui->NewFrame();
if (Pi::game && !Pi::player->IsDead()) { if (Pi::game && !Pi::player->IsDead()) {
// FIXME: Always begin a camera frame because WorldSpaceToScreenSpace // FIXME: Always begin a camera frame because WorldSpaceToScreenSpace
@ -1137,7 +1092,7 @@ void GameLoop::Update(float deltaTime)
Pi::game->GetWorldView()->BeginCameraFrame(); Pi::game->GetWorldView()->BeginCameraFrame();
// FIXME: major hack to work around the fact that the console is in newUI and not pigui // FIXME: major hack to work around the fact that the console is in newUI and not pigui
if (!Pi::IsConsoleActive()) if (!Pi::IsConsoleActive())
Pi::pigui->RunHandler(deltaTime, "GAME"); PiGUI::RunHandler(deltaTime, "GAME");
Pi::game->GetWorldView()->EndCameraFrame(); Pi::game->GetWorldView()->EndCameraFrame();
} }

View File

@ -16,6 +16,10 @@
#include <string> #include <string>
#include <vector> #include <vector>
namespace PiGui {
class Instance;
} //namespace PiGui
class Game; class Game;
class GameConfig; class GameConfig;
@ -26,7 +30,6 @@ class LuaNameGen;
class LuaTimer; class LuaTimer;
class ModelCache; class ModelCache;
class ObjectViewerView; class ObjectViewerView;
class PiGui;
class Player; class Player;
class SystemPath; class SystemPath;
class TransferPlanner; class TransferPlanner;
@ -101,7 +104,7 @@ public:
void RunJobs(); void RunJobs();
void HandleRequests(); void HandleRequests();
void HandleEvents(); bool HandleEvent(SDL_Event &ev) override;
private: private:
// msgs/requests that can be posted which the game processes at the end of a game loop in HandleRequests // msgs/requests that can be posted which the game processes at the end of a game loop in HandleRequests
@ -170,7 +173,7 @@ public:
#endif #endif
static RefCountedPtr<UI::Context> ui; static RefCountedPtr<UI::Context> ui;
static RefCountedPtr<PiGui> pigui; static PiGui::Instance *pigui;
static Random rng; static Random rng;
static int statSceneTris; static int statSceneTris;

View File

@ -4,6 +4,7 @@
#include "Application.h" #include "Application.h"
#include "FileSystem.h" #include "FileSystem.h"
#include "OS.h" #include "OS.h"
#include "SDL.h"
#include "profiler/Profiler.h" #include "profiler/Profiler.h"
#include "utils.h" #include "utils.h"
@ -33,11 +34,14 @@ void Application::Startup()
#ifdef PIONEER_PROFILER #ifdef PIONEER_PROFILER
FileSystem::userFiles.MakeDirectory("profiler"); FileSystem::userFiles.MakeDirectory("profiler");
#endif #endif
SDL_Init(SDL_INIT_EVENTS);
} }
void Application::Shutdown() void Application::Shutdown()
{ {
FileSystem::Uninit(); FileSystem::Uninit();
SDL_Quit();
} }
bool Application::StartLifecycle() bool Application::StartLifecycle()
@ -138,7 +142,7 @@ void Application::Run()
EndFrame(); EndFrame();
if (m_activeLifecycle->m_endLifecycle) { if (m_activeLifecycle->m_endLifecycle || !m_applicationRunning) {
EndLifecycle(); EndLifecycle();
} }

View File

@ -40,8 +40,6 @@ public:
m_nextLifecycle = l; m_nextLifecycle = l;
} }
Application *GetApp();
private: private:
// set to true when you want to accumulate all updates in a lifecycle into a single profile frame // set to true when you want to accumulate all updates in a lifecycle into a single profile frame
bool m_profilerAccumulate = false; bool m_profilerAccumulate = false;

View File

@ -12,6 +12,7 @@
#include "graphics/RenderTarget.h" #include "graphics/RenderTarget.h"
#include "graphics/Renderer.h" #include "graphics/Renderer.h"
#include "graphics/Texture.h" #include "graphics/Texture.h"
#include "pigui/PiGui.h"
#include "utils.h" #include "utils.h"
#include "versioningInfo.h" #include "versioningInfo.h"
@ -31,7 +32,8 @@ void GuiApplication::BeginFrame()
void GuiApplication::DrawRenderTarget() void GuiApplication::DrawRenderTarget()
{ {
#if RTT #if RTT
m_renderer->BeginFrame(); m_renderer->SetRenderTarget(nullptr);
m_renderer->ClearScreen();
m_renderer->SetViewport(0, 0, Graphics::GetScreenWidth(), Graphics::GetScreenHeight()); m_renderer->SetViewport(0, 0, Graphics::GetScreenWidth(), Graphics::GetScreenHeight());
m_renderer->SetTransform(matrix4x4f::Identity()); m_renderer->SetTransform(matrix4x4f::Identity());
@ -60,9 +62,9 @@ void GuiApplication::DrawRenderTarget()
void GuiApplication::EndFrame() void GuiApplication::EndFrame()
{ {
#if RTT #if RTT
m_renderer->SetRenderTarget(nullptr);
DrawRenderTarget(); DrawRenderTarget();
#endif #endif
m_renderer->EndFrame(); m_renderer->EndFrame();
m_renderer->SwapBuffers(); m_renderer->SwapBuffers();
} }
@ -100,7 +102,55 @@ Graphics::RenderTarget *GuiApplication::CreateRenderTarget(const Graphics::Setti
return nullptr; return nullptr;
} }
Graphics::Renderer *GuiApplication::StartupRenderer(const GameConfig *config, bool hidden) void GuiApplication::HandleEvents()
{
PROFILE_SCOPED()
SDL_Event event;
// FIXME: input state is right before handling updates because
// legacy UI code needs to run before input does.
// When HandleEvents() is moved to BeginFrame / PreUpdate, this call should go with it
m_input->NewFrame();
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
RequestQuit();
}
m_pigui->ProcessEvent(&event);
// Input system takes priority over mouse events when capturing the mouse
if (PiGui::WantCaptureMouse() && !m_input->IsCapturingMouse()) {
// don't process mouse event any further, imgui already handled it
switch (event.type) {
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEWHEEL:
case SDL_MOUSEMOTION:
continue;
default: break;
}
}
if (PiGui::WantCaptureKeyboard()) {
// don't process keyboard event any further, imgui already handled it
switch (event.type) {
case SDL_KEYDOWN:
case SDL_KEYUP:
case SDL_TEXTINPUT:
continue;
default: break;
}
}
// TODO: virtual method dispatch for each event isn't great. Let's find a better solution
if (HandleEvent(event))
continue;
m_input->HandleSDLEvent(event);
}
}
Graphics::Renderer *GuiApplication::StartupRenderer(IniConfig *config, bool hidden)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
// Initialize SDL // Initialize SDL
@ -145,3 +195,28 @@ void GuiApplication::ShutdownRenderer()
SDL_QuitSubSystem(SDL_INIT_VIDEO); SDL_QuitSubSystem(SDL_INIT_VIDEO);
} }
Input *GuiApplication::StartupInput(IniConfig *config)
{
m_input.reset(new Input(config));
return m_input.get();
}
void GuiApplication::ShutdownInput()
{
m_input.reset();
}
PiGui::Instance *GuiApplication::StartupPiGui()
{
m_pigui.Reset(new PiGui::Instance());
m_pigui->Init(GetRenderer());
return m_pigui.Get();
}
void GuiApplication::ShutdownPiGui()
{
m_pigui->Uninit();
m_pigui.Reset();
}

View File

@ -4,32 +4,51 @@
#pragma once #pragma once
#include "Application.h" #include "Application.h"
#include "GameConfig.h" #include "Input.h"
#include "RefCounted.h" #include "RefCounted.h"
#include "SDL_events.h"
#include "pigui/PiGui.h"
#include "graphics/RenderState.h" #include "graphics/RenderState.h"
#include "graphics/RenderTarget.h" #include "graphics/RenderTarget.h"
#include "graphics/Renderer.h" #include "graphics/Renderer.h"
class IniConfig;
class GuiApplication : public Application { class GuiApplication : public Application {
public: public:
GuiApplication(std::string title) : GuiApplication(std::string title) :
Application(), m_applicationTitle(title) Application(), m_applicationTitle(title)
{} {}
protected:
Graphics::Renderer *GetRenderer() { return m_renderer.get(); } Graphics::Renderer *GetRenderer() { return m_renderer.get(); }
Input *GetInput() { return m_input.get(); }
PiGui::Instance *GetPiGui() { return m_pigui.Get(); }
protected:
// Called at the end of the frame automatically, blits the RT onto the application // Called at the end of the frame automatically, blits the RT onto the application
// framebuffer // framebuffer
void DrawRenderTarget(); void DrawRenderTarget();
// TODO: unify config handling, possibly make the config an Application member
// Call this from your Startup() method // Call this from your Startup() method
Graphics::Renderer *StartupRenderer(const GameConfig *config, bool hidden = false); Graphics::Renderer *StartupRenderer(IniConfig *config, bool hidden = false);
// Call this from your Startup() method
Input *StartupInput(IniConfig *config);
// Call this from your Startup() method
PiGui::Instance *StartupPiGui();
// Call this from your Shutdown() method // Call this from your Shutdown() method
void ShutdownRenderer(); void ShutdownRenderer();
// Call this from your Shutdown() method
void ShutdownInput();
// Call this from your shutdown() method
void ShutdownPiGui();
// Hook to bind the RT and clear the screen. // Hook to bind the RT and clear the screen.
// If you override BeginFrame, make sure you call this. // If you override BeginFrame, make sure you call this.
void BeginFrame() override; void BeginFrame() override;
@ -38,9 +57,21 @@ protected:
// If you override EndFrame, make sure you call this. // If you override EndFrame, make sure you call this.
void EndFrame() override; void EndFrame() override;
// Consume events from SDL and dispatch to pigui / input
void HandleEvents();
// Override point for classes to add custom event handling
virtual bool HandleEvent(SDL_Event &ev) { return false; }
// Override point to handle an application quit notification
virtual void HandleQuit(SDL_QuitEvent &ev) { RequestQuit(); }
private: private:
Graphics::RenderTarget *CreateRenderTarget(const Graphics::Settings &settings); Graphics::RenderTarget *CreateRenderTarget(const Graphics::Settings &settings);
RefCountedPtr<PiGui::Instance> m_pigui;
std::unique_ptr<Input> m_input;
std::string m_applicationTitle; std::string m_applicationTitle;
std::unique_ptr<Graphics::Renderer> m_renderer; std::unique_ptr<Graphics::Renderer> m_renderer;

View File

@ -109,9 +109,6 @@ namespace Lua {
GameUI::Lua::Init(); GameUI::Lua::Init();
SceneGraph::Lua::Init(); SceneGraph::Lua::Init();
LuaObject<PiGui>::RegisterClass();
PiGUI::Lua::Init();
// XXX load everything. for now, just modules // XXX load everything. for now, just modules
lua_State *l = Lua::manager->GetLuaState(); lua_State *l = Lua::manager->GetLuaState();
pi_lua_dofile(l, "libs/autoload.lua"); pi_lua_dofile(l, "libs/autoload.lua");

View File

@ -24,11 +24,12 @@
#include "Ship.h" #include "Ship.h"
#include "SpaceStation.h" #include "SpaceStation.h"
#include "Star.h" #include "Star.h"
#include "HyperspaceCloud.h"
// Defined in LuaPiGui.h namespace PiGUI {
extern bool first_body_is_more_important_than(Body*, Body*); // Defined in LuaPiGui.h
extern int pushOnScreenPositionDirection(lua_State *l, vector3d position); extern bool first_body_is_more_important_than(Body *, Body *);
extern int pushOnScreenPositionDirection(lua_State *l, vector3d position);
} // namespace PiGUI
/* /*
* Class: Body * Class: Body
@ -246,7 +247,7 @@ static int l_body_is_more_important_than(lua_State *l)
LuaPush<bool>(l, false); LuaPush<bool>(l, false);
return 1; return 1;
} }
LuaPush<bool>(l, first_body_is_more_important_than(body, other)); LuaPush<bool>(l, PiGUI::first_body_is_more_important_than(body, other));
return 1; return 1;
} }
/* /*
@ -653,7 +654,7 @@ static int l_body_get_projected_screen_position(lua_State *l)
Body *b = LuaObject<Body>::CheckFromLua(1); Body *b = LuaObject<Body>::CheckFromLua(1);
WorldView *wv = Pi::game->GetWorldView(); WorldView *wv = Pi::game->GetWorldView();
vector3d p = wv->WorldSpaceToScreenSpace(b); vector3d p = wv->WorldSpaceToScreenSpace(b);
return pushOnScreenPositionDirection(l, p); return PiGUI::pushOnScreenPositionDirection(l, p);
} }
static int l_body_get_atmospheric_state(lua_State *l) static int l_body_get_atmospheric_state(lua_State *l)
@ -685,7 +686,7 @@ static int l_body_get_target_indicator_screen_position(lua_State *l)
Body *b = LuaObject<Body>::CheckFromLua(1); Body *b = LuaObject<Body>::CheckFromLua(1);
WorldView *wv = Pi::game->GetWorldView(); WorldView *wv = Pi::game->GetWorldView();
vector3d p = wv->GetTargetIndicatorScreenPosition(b); vector3d p = wv->GetTargetIndicatorScreenPosition(b);
return pushOnScreenPositionDirection(l, p); return PiGUI::pushOnScreenPositionDirection(l, p);
} }
static bool push_body_to_lua(Body *body) static bool push_body_to_lua(Body *body)

View File

@ -117,7 +117,7 @@ static int l_engine_attr_ui(lua_State *l)
*/ */
static int l_engine_attr_pigui(lua_State *l) static int l_engine_attr_pigui(lua_State *l)
{ {
LuaObject<PiGui>::PushToLua(Pi::pigui.Get()); LuaObject<PiGui::Instance>::PushToLua(Pi::pigui);
return 1; return 1;
} }
@ -794,7 +794,7 @@ static int l_engine_world_space_to_screen_space(lua_State *l)
{ {
vector3d pos = LuaPull<vector3d>(l, 1); vector3d pos = LuaPull<vector3d>(l, 1);
TScreenSpace res = lua_world_space_to_screen_space(pos); // defined in LuaPiGui.cpp PiGUI::TScreenSpace res = PiGUI::lua_world_space_to_screen_space(pos); // defined in LuaPiGui.cpp
LuaPush<bool>(l, res._onScreen); LuaPush<bool>(l, res._onScreen);
LuaPush<vector2d>(l, res._screenPosition); LuaPush<vector2d>(l, res._screenPosition);

View File

@ -18,6 +18,7 @@
#include "graphics/Graphics.h" #include "graphics/Graphics.h"
#include "pigui/LuaFlags.h" #include "pigui/LuaFlags.h"
#include "pigui/PiGui.h" #include "pigui/PiGui.h"
#include "pigui/PiGuiLua.h"
#include "ship/PlayerShipController.h" #include "ship/PlayerShipController.h"
#include "sound/Sound.h" #include "sound/Sound.h"
#include "ui/Context.h" #include "ui/Context.h"
@ -77,7 +78,7 @@ void pi_lua_generic_pull(lua_State *l, int index, ImVec2 &vec)
vec = ImVec2(tr.x, tr.y); vec = ImVec2(tr.x, tr.y);
} }
int pushOnScreenPositionDirection(lua_State *l, vector3d position) int PiGUI::pushOnScreenPositionDirection(lua_State *l, vector3d position)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
const int width = Graphics::GetScreenWidth(); const int width = Graphics::GetScreenWidth();
@ -1381,7 +1382,7 @@ static int l_pigui_is_mouse_clicked(lua_State *l)
static int l_pigui_push_font(lua_State *l) static int l_pigui_push_font(lua_State *l)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
PiGui *pigui = LuaObject<PiGui>::CheckFromLua(1); PiGui::Instance *pigui = LuaObject<PiGui::Instance>::CheckFromLua(1);
std::string fontname = LuaPull<std::string>(l, 2); std::string fontname = LuaPull<std::string>(l, 2);
int size = LuaPull<int>(l, 3); int size = LuaPull<int>(l, 3);
ImFont *font = pigui->GetFont(fontname, size); ImFont *font = pigui->GetFont(fontname, size);
@ -1560,7 +1561,7 @@ static int l_pigui_get_mouse_clicked_pos(lua_State *l)
return 1; return 1;
} }
TScreenSpace lua_world_space_to_screen_space(const vector3d &pos) PiGUI::TScreenSpace PiGUI::lua_world_space_to_screen_space(const vector3d &pos)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
const WorldView *wv = Pi::game->GetWorldView(); const WorldView *wv = Pi::game->GetWorldView();
@ -1569,13 +1570,13 @@ TScreenSpace lua_world_space_to_screen_space(const vector3d &pos)
const int height = Graphics::GetScreenHeight(); const int height = Graphics::GetScreenHeight();
const vector3d direction = (p - vector3d(width / 2, height / 2, 0)).Normalized(); const vector3d direction = (p - vector3d(width / 2, height / 2, 0)).Normalized();
if (vector3d(0, 0, 0) == p || p.x < 0 || p.y < 0 || p.x > width || p.y > height || p.z > 0) { if (vector3d(0, 0, 0) == p || p.x < 0 || p.y < 0 || p.x > width || p.y > height || p.z > 0) {
return TScreenSpace(false, vector2d(0, 0), direction * (p.z > 0 ? -1 : 1)); return PiGUI::TScreenSpace(false, vector2d(0, 0), direction * (p.z > 0 ? -1 : 1));
} else { } else {
return TScreenSpace(true, vector2d(p.x, p.y), direction); return PiGUI::TScreenSpace(true, vector2d(p.x, p.y), direction);
} }
} }
TScreenSpace lua_world_space_to_screen_space(const Body *body) PiGUI::TScreenSpace lua_world_space_to_screen_space(const Body *body)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
const WorldView *wv = Pi::game->GetWorldView(); const WorldView *wv = Pi::game->GetWorldView();
@ -1584,13 +1585,13 @@ TScreenSpace lua_world_space_to_screen_space(const Body *body)
const int height = Graphics::GetScreenHeight(); const int height = Graphics::GetScreenHeight();
const vector3d direction = (p - vector3d(width / 2, height / 2, 0)).Normalized(); const vector3d direction = (p - vector3d(width / 2, height / 2, 0)).Normalized();
if (vector3d(0, 0, 0) == p || p.x < 0 || p.y < 0 || p.x > width || p.y > height || p.z > 0) { if (vector3d(0, 0, 0) == p || p.x < 0 || p.y < 0 || p.x > width || p.y > height || p.z > 0) {
return TScreenSpace(false, vector2d(0, 0), direction * (p.z > 0 ? -1 : 1)); return PiGUI::TScreenSpace(false, vector2d(0, 0), direction * (p.z > 0 ? -1 : 1));
} else { } else {
return TScreenSpace(true, vector2d(p.x, p.y), direction); return PiGUI::TScreenSpace(true, vector2d(p.x, p.y), direction);
} }
} }
bool first_body_is_more_important_than(Body *body, Body *other) bool PiGUI::first_body_is_more_important_than(Body *body, Body *other)
{ {
Object::Type a = body->GetType(); Object::Type a = body->GetType();
@ -1720,7 +1721,7 @@ static int l_pigui_get_projected_bodies_grouped(lua_State *l)
const double cluster_size = LuaPull<double>(l, 1); const double cluster_size = LuaPull<double>(l, 1);
const double ship_max_distance = LuaPull<double>(l, 2); const double ship_max_distance = LuaPull<double>(l, 2);
TSS_vector filtered; PiGUI::TSS_vector filtered;
filtered.reserve(Pi::game->GetSpace()->GetNumBodies()); filtered.reserve(Pi::game->GetSpace()->GetNumBodies());
for (Body *body : Pi::game->GetSpace()->GetBodies()) { for (Body *body : Pi::game->GetSpace()->GetBodies()) {
@ -1728,7 +1729,7 @@ static int l_pigui_get_projected_bodies_grouped(lua_State *l)
if (body->GetType() == Object::PROJECTILE) continue; if (body->GetType() == Object::PROJECTILE) continue;
if (body->GetType() == Object::SHIP && if (body->GetType() == Object::SHIP &&
body->GetPositionRelTo(Pi::player).Length() > ship_max_distance) continue; body->GetPositionRelTo(Pi::player).Length() > ship_max_distance) continue;
const TScreenSpace res = lua_world_space_to_screen_space(body); // defined in LuaPiGui.cpp const PiGUI::TScreenSpace res = lua_world_space_to_screen_space(body); // defined in LuaPiGui.cpp
if (!res._onScreen) continue; if (!res._onScreen) continue;
filtered.emplace_back(res); filtered.emplace_back(res);
filtered.back()._body = body; filtered.back()._body = body;
@ -1757,7 +1758,7 @@ static int l_pigui_get_projected_bodies_grouped(lua_State *l)
const Body *combat_target = Pi::game->GetPlayer()->GetCombatTarget(); const Body *combat_target = Pi::game->GetPlayer()->GetCombatTarget();
const Body *setspeed_target = Pi::game->GetPlayer()->GetSetSpeedTarget(); const Body *setspeed_target = Pi::game->GetPlayer()->GetSetSpeedTarget();
for (TScreenSpace &obj : filtered) { for (PiGUI::TScreenSpace &obj : filtered) {
bool inserted = false; bool inserted = false;
// never collapse combat target // never collapse combat target
@ -1773,7 +1774,7 @@ static int l_pigui_get_projected_bodies_grouped(lua_State *l)
group.m_hasNavTarget = true; group.m_hasNavTarget = true;
group.m_mainBody = obj._body; group.m_mainBody = obj._body;
group.m_screenCoords = obj._screenPosition; group.m_screenCoords = obj._screenPosition;
} else if (!group.m_hasNavTarget && first_body_is_more_important_than(obj._body, group.m_mainBody)) { } else if (!group.m_hasNavTarget && PiGUI::first_body_is_more_important_than(obj._body, group.m_mainBody)) {
group.m_mainBody = obj._body; group.m_mainBody = obj._body;
group.m_screenCoords = obj._screenPosition; group.m_screenCoords = obj._screenPosition;
} }
@ -1797,7 +1798,7 @@ static int l_pigui_get_projected_bodies_grouped(lua_State *l)
for (GroupInfo &group : groups) { for (GroupInfo &group : groups) {
std::sort(begin(group.m_bodies), end(group.m_bodies), std::sort(begin(group.m_bodies), end(group.m_bodies),
[](Body *a, Body *b) { [](Body *a, Body *b) {
return first_body_is_more_important_than(a, b); return PiGUI::first_body_is_more_important_than(a, b);
}); });
} }
@ -1826,19 +1827,19 @@ static int l_pigui_get_projected_bodies_grouped(lua_State *l)
static int l_pigui_get_projected_bodies(lua_State *l) static int l_pigui_get_projected_bodies(lua_State *l)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
TSS_vector filtered; PiGUI::TSS_vector filtered;
filtered.reserve(Pi::game->GetSpace()->GetNumBodies()); filtered.reserve(Pi::game->GetSpace()->GetNumBodies());
for (Body *body : Pi::game->GetSpace()->GetBodies()) { for (Body *body : Pi::game->GetSpace()->GetBodies()) {
if (body == Pi::game->GetPlayer()) continue; if (body == Pi::game->GetPlayer()) continue;
if (body->GetType() == Object::PROJECTILE) continue; if (body->GetType() == Object::PROJECTILE) continue;
const TScreenSpace res = lua_world_space_to_screen_space(body); // defined in LuaPiGui.cpp const PiGUI::TScreenSpace res = lua_world_space_to_screen_space(body); // defined in LuaPiGui.cpp
if (!res._onScreen) continue; if (!res._onScreen) continue;
filtered.emplace_back(res); filtered.emplace_back(res);
filtered.back()._body = body; filtered.back()._body = body;
} }
LuaTable result(l, 0, filtered.size()); LuaTable result(l, 0, filtered.size());
for (TScreenSpace &res : filtered) { for (PiGUI::TScreenSpace &res : filtered) {
LuaTable object(l, 0, 3); LuaTable object(l, 0, 3);
object.Set("onscreen", res._onScreen); object.Set("onscreen", res._onScreen);
@ -1941,23 +1942,23 @@ static int l_pigui_should_show_labels(lua_State *l)
static int l_attr_handlers(lua_State *l) static int l_attr_handlers(lua_State *l)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
PiGui *pigui = LuaObject<PiGui>::CheckFromLua(1); PiGui::Instance *pigui = LuaObject<PiGui::Instance>::CheckFromLua(1);
pigui->GetHandlers().PushCopyToStack(); PiGUI::GetHandlers().PushCopyToStack();
return 1; return 1;
} }
static int l_attr_keys(lua_State *l) static int l_attr_keys(lua_State *l)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
PiGui *pigui = LuaObject<PiGui>::CheckFromLua(1); PiGui::Instance *pigui = LuaObject<PiGui::Instance>::CheckFromLua(1);
pigui->GetKeys().PushCopyToStack(); PiGUI::GetKeys().PushCopyToStack();
return 1; return 1;
} }
static int l_attr_screen_width(lua_State *l) static int l_attr_screen_width(lua_State *l)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
// PiGui *pigui = LuaObject<PiGui>::CheckFromLua(1); // PiGui::Instance *pigui = LuaObject<PiGui::Instance>::CheckFromLua(1);
LuaPush<int>(l, Graphics::GetScreenWidth()); LuaPush<int>(l, Graphics::GetScreenWidth());
return 1; return 1;
} }
@ -1993,7 +1994,7 @@ static int l_attr_key_alt(lua_State *l)
static int l_attr_screen_height(lua_State *l) static int l_attr_screen_height(lua_State *l)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
// PiGui *pigui = LuaObject<PiGui>::CheckFromLua(1); // PiGui::Instance *pigui = LuaObject<PiGui::Instance>::CheckFromLua(1);
LuaPush<int>(l, Graphics::GetScreenHeight()); LuaPush<int>(l, Graphics::GetScreenHeight());
return 1; return 1;
} }
@ -2365,11 +2366,11 @@ static int l_pigui_add_convex_poly_filled(lua_State *l)
static int l_pigui_load_texture_from_svg(lua_State *l) static int l_pigui_load_texture_from_svg(lua_State *l)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
PiGui *pigui = LuaObject<PiGui>::CheckFromLua(1); PiGui::Instance *pigui = LuaObject<PiGui::Instance>::CheckFromLua(1);
std::string svg_filename = LuaPull<std::string>(l, 2); std::string svg_filename = LuaPull<std::string>(l, 2);
int width = LuaPull<int>(l, 3); int width = LuaPull<int>(l, 3);
int height = LuaPull<int>(l, 4); int height = LuaPull<int>(l, 4);
ImTextureID id = pigui->RenderSVG(svg_filename, width, height); ImTextureID id = PiGui::RenderSVG(Pi::renderer, svg_filename, width, height);
// LuaPush(l, id); // LuaPush(l, id);
lua_pushlightuserdata(l, id); lua_pushlightuserdata(l, id);
return 1; return 1;
@ -2467,11 +2468,21 @@ static int l_pigui_push_text_wrap_pos(lua_State *l)
return 0; return 0;
} }
template <> void PiGUI::RunHandler(double delta, std::string handler)
const char *LuaObject<PiGui>::s_type = "PiGui"; {
PROFILE_SCOPED()
ScopedTable t(GetHandlers());
if (t.Get<bool>(handler)) {
t.Call<bool>(handler, delta);
Pi::renderer->CheckRenderErrors(__FUNCTION__, __LINE__);
}
}
template <> template <>
void LuaObject<PiGui>::RegisterClass() const char *LuaObject<PiGui::Instance>::s_type = "PiGui";
template <>
void LuaObject<PiGui::Instance>::RegisterClass()
{ {
static const luaL_Reg l_methods[] = { static const luaL_Reg l_methods[] = {
{ "Begin", l_pigui_begin }, { "Begin", l_pigui_begin },

View File

@ -3,6 +3,7 @@
#ifndef _LUAPIGUI_H #ifndef _LUAPIGUI_H
#define _LUAPIGUI_H #define _LUAPIGUI_H
#include "LuaObject.h" #include "LuaObject.h"
#include "LuaPushPull.h" #include "LuaPushPull.h"
@ -11,19 +12,25 @@
class Body; class Body;
bool first_body_is_more_important_than(Body* body, Body* other); namespace PiGUI {
bool first_body_is_more_important_than(Body *body, Body *other);
struct TScreenSpace struct TScreenSpace {
{ TScreenSpace(const bool onScreen, const vector2d &screenPos, const vector3d &direction) :
TScreenSpace(const bool onScreen, const vector2d &screenPos, const vector3d &direction) : _onScreen(onScreen), _screenPosition(screenPos), _direction(direction) {} _onScreen(onScreen), _screenPosition(screenPos), _direction(direction) {}
bool _onScreen; bool _onScreen;
vector2d _screenPosition; vector2d _screenPosition;
vector3d _direction; vector3d _direction;
Body *_body; Body *_body;
}; };
typedef std::vector<TScreenSpace> TSS_vector; typedef std::vector<TScreenSpace> TSS_vector;
int pushOnScreenPositionDirection(lua_State *l, vector3d position);
TScreenSpace lua_world_space_to_screen_space(const vector3d &pos);
// Run a lua PiGui handler.
void RunHandler(double delta, std::string handler = "GAME");
} // namespace PiGUI
int pushOnScreenPositionDirection(lua_State *l, vector3d position);
TScreenSpace lua_world_space_to_screen_space(const vector3d &pos);
#endif #endif

View File

@ -205,7 +205,9 @@ start:
std::string modelName; std::string modelName;
if (argc > 2) if (argc > 2)
modelName = argv[2]; modelName = argv[2];
ModelViewer::Run(modelName); auto modelViewer = ModelViewerApp();
modelViewer.SetInitialModel(modelName);
modelViewer.Run();
break; break;
} }

View File

@ -5,6 +5,9 @@
#include "Input.h" #include "Input.h"
#include "Pi.h" #include "Pi.h"
#include "graphics/Graphics.h"
#include "graphics/Texture.h"
#include "graphics/opengl/RendererGL.h"
#include "graphics/opengl/TextureGL.h" // nasty, usage of GL is implementation specific #include "graphics/opengl/TextureGL.h" // nasty, usage of GL is implementation specific
#include "imgui/imgui.h" #include "imgui/imgui.h"
@ -12,9 +15,6 @@
#define IMGUI_IMPL_OPENGL_LOADER_GLEW 1 #define IMGUI_IMPL_OPENGL_LOADER_GLEW 1
#include "imgui/examples/imgui_impl_opengl3.h" #include "imgui/examples/imgui_impl_opengl3.h"
#include "imgui/examples/imgui_impl_sdl.h" #include "imgui/examples/imgui_impl_sdl.h"
// to get ImVec2 + ImVec2
#define IMGUI_DEFINE_MATH_OPERATORS true
#include "imgui/imgui_internal.h"
#include <float.h> #include <float.h>
#include <stdio.h> #include <stdio.h>
@ -24,38 +24,36 @@
#define NANOSVGRAST_IMPLEMENTATION #define NANOSVGRAST_IMPLEMENTATION
#include "nanosvg/nanosvgrast.h" #include "nanosvg/nanosvgrast.h"
std::vector<Graphics::Texture *> PiGui::m_svg_textures; using namespace PiGui;
static int to_keycode(int key) std::vector<Graphics::Texture *> m_svg_textures;
std::vector<Graphics::Texture *> &PiGui::GetSVGTextures()
{ {
/*if(key & SDLK_SCANCODE_MASK) { return m_svg_textures;
return (key & ~SDLK_SCANCODE_MASK) | 0x100;
}*/
return key;
} }
static std::vector<std::pair<std::string, int>> keycodes = { static ImTextureID makeTexture(Graphics::Renderer *renderer, unsigned char *pixels, int width, int height)
{ "left", to_keycode(SDLK_LEFT) }, {
{ "right", to_keycode(SDLK_RIGHT) }, PROFILE_SCOPED()
{ "up", to_keycode(SDLK_UP) }, // this is not very pretty code
{ "down", to_keycode(SDLK_DOWN) }, // Texture descriptor defines the size, type.
{ "escape", to_keycode(SDLK_ESCAPE) }, // Gone for LINEAR_CLAMP here and RGBA like the original code
{ "f1", to_keycode(SDLK_F1) }, const vector2f texSize(1.0f, 1.0f);
{ "f2", to_keycode(SDLK_F2) }, const vector3f dataSize(width, height, 0.0f);
{ "f3", to_keycode(SDLK_F3) }, const Graphics::TextureDescriptor texDesc(Graphics::TEXTURE_RGBA_8888,
{ "f4", to_keycode(SDLK_F4) }, dataSize, texSize, Graphics::LINEAR_CLAMP,
{ "f5", to_keycode(SDLK_F5) }, false, false, false, 0, Graphics::TEXTURE_2D);
{ "f6", to_keycode(SDLK_F6) }, // Create the texture, calling it via renderer directly avoids the caching call of TextureBuilder
{ "f7", to_keycode(SDLK_F7) }, // However interestingly this gets called twice which would have been a WIN for the TextureBuilder :/
{ "f8", to_keycode(SDLK_F8) }, Graphics::Texture *pTex = renderer->CreateTexture(texDesc);
{ "f9", to_keycode(SDLK_F9) }, // Update it with the actual pixels, this is a two step process due to legacy code
{ "f10", to_keycode(SDLK_F10) }, pTex->Update(pixels, dataSize, Graphics::TEXTURE_RGBA_8888);
{ "f11", to_keycode(SDLK_F11) }, PiGui::GetSVGTextures().push_back(pTex); // store for cleanup later
{ "f12", to_keycode(SDLK_F12) }, return reinterpret_cast<ImTextureID>(uintptr_t(pTex->GetTextureID()));
{ "tab", to_keycode(SDLK_TAB) }, }
};
ImTextureID PiGui::RenderSVG(std::string svgFilename, int width, int height) ImTextureID PiGui::RenderSVG(Graphics::Renderer *renderer, std::string svgFilename, int width, int height)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
Output("nanosvg: %s %dx%d\n", svgFilename.c_str(), width, height); Output("nanosvg: %s %dx%d\n", svgFilename.c_str(), width, height);
@ -111,10 +109,44 @@ ImTextureID PiGui::RenderSVG(std::string svgFilename, int width, int height)
} }
nsvgDeleteRasterizer(rast); nsvgDeleteRasterizer(rast);
nsvgDelete(image); nsvgDelete(image);
return makeTexture(img, W, H); return makeTexture(renderer, img, W, H);
} }
ImFont *PiGui::GetFont(const std::string &name, int size) //
// PiGui::Instance
//
Instance::Instance() :
m_should_bake_fonts(true)
{
// TODO: clang-format doesn't like list initializers inside function calls
// clang-format off
PiFont uiheading("orbiteer", {
PiFace("DejaVuSans.ttf", /*18.0/20.0*/ 1.2),
PiFace("wqy-microhei.ttc", 1.0),
PiFace("Orbiteer-Bold.ttf", 1.0) // imgui only supports 0xffff, not 0x10ffff
});
AddFontDefinition(uiheading);
PiFont guifont("pionillium", {
PiFace("DejaVuSans.ttf", 13.0 / 14.0),
PiFace("wqy-microhei.ttc", 1.0),
PiFace("PionilliumText22L-Medium.ttf", 1.0)
});
AddFontDefinition(guifont);
// clang-format on
// Output("Fonts:\n");
for (auto entry : m_font_definitions) {
// Output(" entry %s:\n", entry.first.c_str());
entry.second.describe();
}
// ensure the tooltip font exists
GetFont("pionillium", 14);
};
ImFont *Instance::GetFont(const std::string &name, int size)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
auto iter = m_fonts.find(std::make_pair(name, size)); auto iter = m_fonts.find(std::make_pair(name, size));
@ -126,7 +158,7 @@ ImFont *PiGui::GetFont(const std::string &name, int size)
return font; return font;
} }
void PiGui::AddGlyph(ImFont *font, unsigned short glyph) void Instance::AddGlyph(ImFont *font, unsigned short glyph)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
// range glyph..glyph // range glyph..glyph
@ -151,7 +183,7 @@ void PiGui::AddGlyph(ImFont *font, unsigned short glyph)
Error("No face in font %s handles glyph %i\n", pifont.name().c_str(), glyph); Error("No face in font %s handles glyph %i\n", pifont.name().c_str(), glyph);
} }
ImFont *PiGui::AddFont(const std::string &name, int size) ImFont *Instance::AddFont(const std::string &name, int size)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
auto iter = m_font_definitions.find(name); auto iter = m_font_definitions.find(name);
@ -175,7 +207,7 @@ ImFont *PiGui::AddFont(const std::string &name, int size)
return m_fonts[std::make_pair(name, size)]; return m_fonts[std::make_pair(name, size)];
} }
void PiGui::RefreshFontsTexture() void Instance::RefreshFontsTexture()
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
// TODO: fix this, do the right thing, don't just re-create *everything* :) // TODO: fix this, do the right thing, don't just re-create *everything* :)
@ -189,21 +221,11 @@ void PiDefaultStyle(ImGuiStyle &style)
style.WindowBorderSize = 0.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. style.WindowBorderSize = 0.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
} }
void PiGui::Init(SDL_Window *window) // TODO: this isn't very RAII friendly, are we sure we need to call Init() seperately from creating the instance?
void Instance::Init(Graphics::Renderer *renderer)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
m_handlers.Unref(); m_renderer = renderer;
lua_State *l = Lua::manager->GetLuaState();
lua_newtable(l);
m_handlers = LuaRef(l, -1);
lua_newtable(l);
m_keys = LuaRef(l, -1);
LuaTable keys(l, -1);
for (auto p : keycodes) {
keys.Set(p.first, p.second);
}
IMGUI_CHECKVERSION(); IMGUI_CHECKVERSION();
ImGui::CreateContext(); ImGui::CreateContext();
@ -211,8 +233,8 @@ void PiGui::Init(SDL_Window *window)
// TODO: FIXME before upgrading! The sdl_gl_context parameter is currently // TODO: FIXME before upgrading! The sdl_gl_context parameter is currently
// unused, but that is slated to change very soon. // unused, but that is slated to change very soon.
// We will need to fill this with a valid pointer to the OpenGL context. // We will need to fill this with a valid pointer to the OpenGL context.
ImGui_ImplSDL2_InitForOpenGL(window, NULL); ImGui_ImplSDL2_InitForOpenGL(m_renderer->GetSDLWindow(), NULL);
switch (Pi::renderer->GetRendererType()) { switch (m_renderer->GetRendererType()) {
default: default:
case Graphics::RENDERER_DUMMY: case Graphics::RENDERER_DUMMY:
Error("RENDERER_DUMMY is not a valid renderer, aborting."); Error("RENDERER_DUMMY is not a valid renderer, aborting.");
@ -241,156 +263,18 @@ void PiGui::Init(SDL_Window *window)
io.IniFilename = ioIniFilename; io.IniFilename = ioIniFilename;
} }
int PiGui::RadialPopupSelectMenu(const ImVec2 &center, std::string popup_id, int mouse_button, std::vector<ImTextureID> tex_ids, std::vector<std::pair<ImVec2, ImVec2>> uvs, unsigned int size, std::vector<std::string> tooltips) bool Instance::ProcessEvent(SDL_Event *event)
{
PROFILE_SCOPED()
// return:
// 0 - n for item selected
// -1 for nothing chosen, but menu open
// -2 for menu closed without an icon chosen
// -3 for menu not open
int ret = -3;
// FIXME: Missing a call to query if Popup is open so we can move the PushStyleColor inside the BeginPopupBlock (e.g. IsPopupOpen() in imgui.cpp)
// FIXME: Our PathFill function only handle convex polygons, so we can't have items spanning an arc too large else inner concave edge artifact is too visible, hence the ImMax(7,items_count)
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
if (ImGui::BeginPopup(popup_id.c_str())) {
ret = -1;
const ImVec2 drag_delta = ImVec2(ImGui::GetIO().MousePos.x - center.x, ImGui::GetIO().MousePos.y - center.y);
const float drag_dist2 = drag_delta.x * drag_delta.x + drag_delta.y * drag_delta.y;
const ImGuiStyle &style = ImGui::GetStyle();
const float RADIUS_MIN = 20.0f;
const float RADIUS_MAX = 90.0f;
const float RADIUS_INTERACT_MIN = 20.0f;
const int ITEMS_MIN = 4;
const float border_inout = 12.0f;
const float border_thickness = 4.0f;
ImDrawList *draw_list = ImGui::GetWindowDrawList();
draw_list->PushClipRectFullScreen();
draw_list->PathArcTo(center, (RADIUS_MIN + RADIUS_MAX) * 0.5f, 0.0f, IM_PI * 2.0f * 0.99f, 64); // FIXME: 0.99f look like full arc with closed thick stroke has a bug now
draw_list->PathStroke(ImColor(18, 44, 67, 210), true, RADIUS_MAX - RADIUS_MIN);
const float item_arc_span = 2 * IM_PI / ImMax<int>(ITEMS_MIN, tex_ids.size());
float drag_angle = atan2f(drag_delta.y, drag_delta.x);
if (drag_angle < -0.5f * item_arc_span)
drag_angle += 2.0f * IM_PI;
int item_hovered = -1;
int item_n = 0;
for (ImTextureID tex_id : tex_ids) {
const char *tooltip = tooltips.at(item_n).c_str();
const float inner_spacing = style.ItemInnerSpacing.x / RADIUS_MIN / 2;
const float item_inner_ang_min = item_arc_span * (item_n - 0.5f + inner_spacing);
const float item_inner_ang_max = item_arc_span * (item_n + 0.5f - inner_spacing);
const float item_outer_ang_min = item_arc_span * (item_n - 0.5f + inner_spacing * (RADIUS_MIN / RADIUS_MAX));
const float item_outer_ang_max = item_arc_span * (item_n + 0.5f - inner_spacing * (RADIUS_MIN / RADIUS_MAX));
bool hovered = false;
if (drag_dist2 >= RADIUS_INTERACT_MIN * RADIUS_INTERACT_MIN) {
if (drag_angle >= item_inner_ang_min && drag_angle < item_inner_ang_max)
hovered = true;
}
bool selected = false;
int arc_segments = static_cast<int>((64 * item_arc_span / (2 * IM_PI))) + 1;
draw_list->PathArcTo(center, RADIUS_MAX - border_inout, item_outer_ang_min, item_outer_ang_max, arc_segments);
draw_list->PathArcTo(center, RADIUS_MIN + border_inout, item_inner_ang_max, item_inner_ang_min, arc_segments);
draw_list->PathFillConvex(hovered ? ImColor(102, 147, 189) : selected ? ImColor(48, 81, 111) : ImColor(48, 81, 111));
if (hovered) {
// draw outer / inner extra segments
draw_list->PathArcTo(center, RADIUS_MAX - border_thickness, item_outer_ang_min, item_outer_ang_max, arc_segments);
draw_list->PathStroke(ImColor(102, 147, 189), false, border_thickness);
draw_list->PathArcTo(center, RADIUS_MIN + border_thickness, item_outer_ang_min, item_outer_ang_max, arc_segments);
draw_list->PathStroke(ImColor(102, 147, 189), false, border_thickness);
}
ImVec2 text_size = ImVec2(size, size);
ImVec2 text_pos = ImVec2(
center.x + cosf((item_inner_ang_min + item_inner_ang_max) * 0.5f) * (RADIUS_MIN + RADIUS_MAX) * 0.5f - text_size.x * 0.5f,
center.y + sinf((item_inner_ang_min + item_inner_ang_max) * 0.5f) * (RADIUS_MIN + RADIUS_MAX) * 0.5f - text_size.y * 0.5f);
draw_list->AddImage(tex_id, text_pos, ImVec2(text_pos.x + size, text_pos.y + size), uvs[item_n].first, uvs[item_n].second);
ImGui::SameLine();
if (hovered) {
item_hovered = item_n;
ImGui::SetTooltip("%s", tooltip);
}
item_n++;
}
draw_list->PopClipRect();
if (ImGui::IsMouseReleased(mouse_button)) {
ImGui::CloseCurrentPopup();
if (item_hovered == -1)
ret = -2;
else
ret = item_hovered;
}
ImGui::EndPopup();
} else {
// Output("WARNING: RadialPopupSelectMenu BeginPopup failed: %s\n", popup_id.c_str());
}
ImGui::PopStyleColor(3);
return ret;
}
bool PiGui::CircularSlider(const ImVec2 &center, float *v, float v_min, float v_max)
{
PROFILE_SCOPED()
ImDrawList *draw_list = ImGui::GetWindowDrawList();
ImGuiWindow *window = ImGui::GetCurrentWindow();
const ImGuiID id = window->GetID("circularslider");
draw_list->AddCircle(center, 17, ImColor(100, 100, 100), 128, 12.0);
draw_list->PathArcTo(center, 17, 0, M_PI * 2.0 * (*v - v_min) / (v_max - v_min), 64);
draw_list->PathStroke(ImColor(200, 200, 200), false, 12.0);
ImRect grab_bb;
return ImGui::SliderBehavior(ImRect(center.x - 17, center.y - 17, center.x + 17, center.y + 17),
id, ImGuiDataType_Float, v, &v_min, &v_max, "%.4f", 1.0, ImGuiSliderFlags_None, &grab_bb);
}
bool PiGui::ProcessEvent(SDL_Event *event)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
ImGui_ImplSDL2_ProcessEvent(event); ImGui_ImplSDL2_ProcessEvent(event);
return false; return false;
} }
void *PiGui::makeTexture(unsigned char *pixels, int width, int height) void Instance::NewFrame()
{
PROFILE_SCOPED()
// this is not very pretty code and uses the Graphics::TextureGL class directly
// Texture descriptor defines the size, type.
// Gone for LINEAR_CLAMP here and RGBA like the original code
const vector2f texSize(1.0f, 1.0f);
const vector3f dataSize(width, height, 0.0f);
const Graphics::TextureDescriptor texDesc(Graphics::TEXTURE_RGBA_8888,
dataSize, texSize, Graphics::LINEAR_CLAMP,
false, false, false, 0, Graphics::TEXTURE_2D);
// Create the texture, calling it via renderer directly avoids the caching call of TextureBuilder
// However interestingly this gets called twice which would have been a WIN for the TextureBuilder :/
Graphics::Texture *pTex = Pi::renderer->CreateTexture(texDesc);
// Update it with the actual pixels, this is a two step process due to legacy code
pTex->Update(pixels, dataSize, Graphics::TEXTURE_RGBA_8888);
// nasty bit as I invoke the TextureGL
Graphics::OGL::TextureGL *pGLTex = reinterpret_cast<Graphics::OGL::TextureGL *>(pTex);
Uint32 result = pGLTex->GetTextureID();
m_svg_textures.push_back(pTex); // store for cleanup later
return reinterpret_cast<void *>(result);
}
void PiGui::NewFrame(SDL_Window *window)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
// Ask ImGui to hide OS cursor if we're capturing it for input: switch (m_renderer->GetRendererType()) {
// it will do this if GetMouseCursor == ImGuiMouseCursor_None.
if (Pi::input->IsCapturingMouse()) {
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
}
switch (Pi::renderer->GetRendererType()) {
default: default:
case Graphics::RENDERER_DUMMY: case Graphics::RENDERER_DUMMY:
Error("RENDERER_DUMMY is not a valid renderer, aborting."); Error("RENDERER_DUMMY is not a valid renderer, aborting.");
@ -399,24 +283,14 @@ void PiGui::NewFrame(SDL_Window *window)
ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplOpenGL3_NewFrame();
break; break;
} }
ImGui_ImplSDL2_NewFrame(window); ImGui_ImplSDL2_NewFrame(m_renderer->GetSDLWindow());
ImGui::NewFrame(); ImGui::NewFrame();
Pi::renderer->CheckRenderErrors(__FUNCTION__, __LINE__); m_renderer->CheckRenderErrors(__FUNCTION__, __LINE__);
ImGui::SetMouseCursor(ImGuiMouseCursor_Arrow); ImGui::SetMouseCursor(ImGuiMouseCursor_Arrow);
} }
void PiGui::RunHandler(double delta, std::string handler) void Instance::EndFrame()
{
PROFILE_SCOPED()
ScopedTable t(m_handlers);
if (t.Get<bool>(handler)) {
t.Call<bool>(handler, delta);
Pi::renderer->CheckRenderErrors(__FUNCTION__, __LINE__);
}
}
void PiGui::EndFrame()
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
@ -443,14 +317,14 @@ void PiGui::EndFrame()
} }
} }
void PiGui::Render() void Instance::Render()
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
EndFrame(); EndFrame();
ImGui::Render(); ImGui::Render();
switch (Pi::renderer->GetRendererType()) { switch (m_renderer->GetRendererType()) {
default: default:
case Graphics::RENDERER_DUMMY: case Graphics::RENDERER_DUMMY:
return; return;
@ -460,7 +334,7 @@ void PiGui::Render()
} }
} }
void PiGui::ClearFonts() void Instance::ClearFonts()
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
ImGuiIO &io = ImGui::GetIO(); ImGuiIO &io = ImGui::GetIO();
@ -470,7 +344,7 @@ void PiGui::ClearFonts()
io.Fonts->Clear(); io.Fonts->Clear();
} }
void PiGui::BakeFont(PiFont &font) void Instance::BakeFont(PiFont &font)
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
ImGuiIO &io = ImGui::GetIO(); ImGuiIO &io = ImGui::GetIO();
@ -511,7 +385,7 @@ void PiGui::BakeFont(PiFont &font)
imfont->MissingGlyphs.clear(); imfont->MissingGlyphs.clear();
} }
void PiGui::BakeFonts() void Instance::BakeFonts()
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
// Output("Baking fonts\n"); // Output("Baking fonts\n");
@ -538,199 +412,14 @@ void PiGui::BakeFonts()
RefreshFontsTexture(); RefreshFontsTexture();
} }
static void drawThrust(ImDrawList *draw_list, const ImVec2 &center, const ImVec2 &up, float value, const ImColor &fg, const ImColor &bg) void Instance::Uninit()
{
PROFILE_SCOPED()
float factor = 0.1; // how much to offset from center
const ImVec2 step(up.x * 0.5, up.y * 0.5);
const ImVec2 left(-step.y * (1.0 - factor), step.x * (1.0 - factor));
const ImVec2 u(up.x * (1.0 - factor), up.y * (1.0 - factor));
const ImVec2 c(center + ImVec2(u.x * factor, u.y * factor));
const ImVec2 right(-left.x, -left.y);
const ImVec2 leftmiddle = c + step + left;
const ImVec2 rightmiddle = c + step + right;
const ImVec2 bb_lowerright = c + right;
const ImVec2 bb_upperleft = c + left + ImVec2(u.x * value, u.y * value);
const ImVec2 lefttop = c + u + left;
const ImVec2 righttop = c + u + right;
const ImVec2 minimum(fmin(bb_upperleft.x, bb_lowerright.x), fmin(bb_upperleft.y, bb_lowerright.y));
const ImVec2 maximum(fmax(bb_upperleft.x, bb_lowerright.x), fmax(bb_upperleft.y, bb_lowerright.y));
ImVec2 points[] = { c, leftmiddle, lefttop, righttop, rightmiddle };
draw_list->AddConvexPolyFilled(points, 5, bg);
draw_list->PushClipRect(minimum - ImVec2(1, 1), maximum + ImVec2(1, 1));
draw_list->AddConvexPolyFilled(points, 5, fg);
draw_list->PopClipRect();
}
void PiGui::ThrustIndicator(const std::string &id_string, const ImVec2 &size_arg, const ImVec4 &thrust, const ImVec4 &velocity, const ImVec4 &bg_col, int frame_padding, ImColor vel_fg, ImColor vel_bg, ImColor thrust_fg, ImColor thrust_bg)
{
PROFILE_SCOPED()
ImGuiWindow *window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return;
ImGuiContext &g = *GImGui;
const ImGuiStyle &style = g.Style;
const ImGuiID id = window->GetID(id_string.c_str());
ImVec2 pos = window->DC.CursorPos;
// if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
// pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
ImVec2 size = ImGui::CalcItemSize(size_arg, style.FramePadding.x * 2.0f, style.FramePadding.y * 2.0f);
const ImVec2 padding = (frame_padding >= 0) ? ImVec2(static_cast<float>(frame_padding), static_cast<float>(frame_padding)) : style.FramePadding;
const ImRect bb(pos, pos + size + padding * 2);
const ImRect inner_bb(pos + padding, pos + padding + size);
ImGui::ItemSize(bb, style.FramePadding.y);
if (!ImGui::ItemAdd(bb, id))
return;
// Render
const ImU32 col = ImGui::GetColorU32(static_cast<ImGuiCol>(ImGuiCol_Button));
ImGui::RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
ImDrawList *draw_list = ImGui::GetWindowDrawList();
if (bg_col.w > 0.0f)
draw_list->AddRectFilled(inner_bb.Min, inner_bb.Max, ImGui::GetColorU32(bg_col));
const ImVec2 leftupper = inner_bb.Min;
const ImVec2 rightlower = inner_bb.Max;
const ImVec2 rightcenter((rightlower.x - leftupper.x) * 0.8 + leftupper.x, (rightlower.y + leftupper.y) / 2);
const ImVec2 leftcenter((rightlower.x - leftupper.x) * 0.35 + leftupper.x, (rightlower.y + leftupper.y) / 2);
const ImVec2 up(0, -std::abs(leftupper.y - rightlower.y) * 0.4);
const ImVec2 left(-up.y, up.x);
float thrust_fwd = fmax(thrust.z, 0);
float thrust_bwd = fmax(-thrust.z, 0);
float thrust_left = fmax(-thrust.x, 0);
float thrust_right = fmax(thrust.x, 0);
float thrust_up = fmax(-thrust.y, 0);
float thrust_down = fmax(thrust.y, 0);
// actual thrust
drawThrust(draw_list, rightcenter, up, thrust_fwd, thrust_fg, thrust_bg);
drawThrust(draw_list, rightcenter, ImVec2(-up.x, -up.y), thrust_bwd, thrust_fg, thrust_bg);
drawThrust(draw_list, leftcenter, up, thrust_up, thrust_fg, thrust_bg);
drawThrust(draw_list, leftcenter, ImVec2(-up.x, -up.y), thrust_down, thrust_fg, thrust_bg);
drawThrust(draw_list, leftcenter, left, thrust_left, thrust_fg, thrust_bg);
drawThrust(draw_list, leftcenter, ImVec2(-left.x, -left.y), thrust_right, thrust_fg, thrust_bg);
// forward/back velocity
draw_list->AddLine(rightcenter + up, rightcenter - up, vel_bg, 3);
draw_list->AddLine(rightcenter, rightcenter - up * velocity.z, vel_fg, 3);
// left/right velocity
draw_list->AddLine(leftcenter + left, leftcenter - left, vel_bg, 3);
draw_list->AddLine(leftcenter, leftcenter + left * velocity.x, vel_fg, 3);
// up/down velocity
draw_list->AddLine(leftcenter + up, leftcenter - up, vel_bg, 3);
draw_list->AddLine(leftcenter, leftcenter + up * velocity.y, vel_fg, 3);
// Automatically close popups
//if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
// CloseCurrentPopup();
}
bool PiGui::LowThrustButton(const char *id_string, const ImVec2 &size_arg, int thrust_level, const ImVec4 &bg_col, int frame_padding, ImColor gauge_fg, ImColor gauge_bg)
{
PROFILE_SCOPED()
std::string label = std::to_string(thrust_level);
ImGuiWindow *window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext &g = *GImGui;
const ImGuiStyle &style = g.Style;
const ImGuiID id = window->GetID(id_string);
const ImVec2 label_size = ImGui::CalcTextSize(label.c_str(), NULL, true);
ImVec2 pos = window->DC.CursorPos;
// if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
// pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
const ImVec2 padding = (frame_padding >= 0) ? ImVec2(static_cast<float>(frame_padding), static_cast<float>(frame_padding)) : style.FramePadding;
const ImRect bb(pos, pos + size + padding * 2);
const ImRect inner_bb(pos + padding, pos + padding + size);
ImGui::ItemSize(bb, style.FramePadding.y);
if (!ImGui::ItemAdd(bb, id))
return false;
// if (window->DC.ButtonRepeat) flags |= ImGuiButtonFlags_Repeat;
bool hovered, held;
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, 0); // flags
// Render
const ImU32 col = ImGui::GetColorU32(static_cast<ImGuiCol>((hovered && held) ? ImGuiCol_ButtonActive : (hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button)));
ImGui::RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
const ImVec2 center = (inner_bb.Min + inner_bb.Max) / 2;
float radius = (inner_bb.Max.x - inner_bb.Min.x) * 0.4;
float thickness = 4;
ImDrawList *draw_list = ImGui::GetWindowDrawList();
if (bg_col.w > 0.0f)
draw_list->AddRectFilled(inner_bb.Min, inner_bb.Max, ImGui::GetColorU32(bg_col));
draw_list->PathArcTo(center, radius, 0, IM_PI * 2, 16);
draw_list->PathStroke(gauge_bg, false, thickness);
draw_list->PathArcTo(center, radius, IM_PI, IM_PI + IM_PI * 2 * (thrust_level / 100.0), 16);
draw_list->PathStroke(gauge_fg, false, thickness);
ImGui::RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label.c_str(), NULL, &label_size, style.ButtonTextAlign, &bb);
// Automatically close popups
//if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
// CloseCurrentPopup();
return pressed;
}
// frame_padding < 0: uses FramePadding from style (default)
// frame_padding = 0: no framing
// frame_padding > 0: set framing size
// The color used are the button colors.
bool PiGui::ButtonImageSized(ImTextureID user_texture_id, const ImVec2 &size, const ImVec2 &imgSize, const ImVec2 &uv0, const ImVec2 &uv1, int frame_padding, const ImVec4 &bg_col, const ImVec4 &tint_col)
{
ImGuiWindow *window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext &g = *GImGui;
const ImGuiStyle &style = g.Style;
// Default to using texture ID as ID. User can still push string/integer prefixes.
// We could hash the size/uv to create a unique ID but that would prevent the user from animating UV.
ImGui::PushID((void *)user_texture_id);
const ImGuiID id = window->GetID("#image");
ImGui::PopID();
ImVec2 imgPadding = (size - imgSize) / 2;
imgPadding.x = imgPadding.x < 0 || imgSize.x <= 0 ? 0 : imgPadding.x;
imgPadding.y = imgPadding.y < 0 || imgSize.y <= 0 ? 0 : imgPadding.y;
const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding;
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2);
const ImRect image_bb(window->DC.CursorPos + padding + imgPadding, window->DC.CursorPos + padding + size - imgPadding);
ImGui::ItemSize(bb);
if (!ImGui::ItemAdd(bb, id))
return false;
bool hovered, held;
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held);
// Render
const ImU32 col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
ImGui::RenderNavHighlight(bb, id);
ImGui::RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding));
if (bg_col.w > 0.0f)
window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, ImGui::GetColorU32(bg_col));
window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, ImGui::GetColorU32(tint_col));
return pressed;
}
void PiGui::Cleanup()
{ {
PROFILE_SCOPED() PROFILE_SCOPED()
for (auto tex : m_svg_textures) { for (auto tex : m_svg_textures) {
delete tex; delete tex;
} }
switch (Pi::renderer->GetRendererType()) { switch (m_renderer->GetRendererType()) {
default: default:
case Graphics::RENDERER_DUMMY: case Graphics::RENDERER_DUMMY:
return; return;
@ -743,25 +432,9 @@ void PiGui::Cleanup()
ImGui::DestroyContext(); ImGui::DestroyContext();
} }
PiGui::PiGui() : //
m_should_bake_fonts(true) // PiGui::PiFace
{ //
PiFont uiheading("orbiteer", {
PiFace("DejaVuSans.ttf", /*18.0/20.0*/ 1.2), PiFace("wqy-microhei.ttc", 1.0), PiFace("Orbiteer-Bold.ttf", 1.0) // imgui only supports 0xffff, not 0x10ffff
});
PiFont guifont("pionillium", { PiFace("DejaVuSans.ttf", 13.0 / 14.0), PiFace("wqy-microhei.ttc", 1.0), PiFace("PionilliumText22L-Medium.ttf", 1.0) });
AddFontDefinition(uiheading);
AddFontDefinition(guifont);
// Output("Fonts:\n");
for (auto entry : m_font_definitions) {
// Output(" entry %s:\n", entry.first.c_str());
entry.second.describe();
}
// ensure the tooltip font exists
GetFont("pionillium", 14);
};
const bool PiFace::isValidGlyph(unsigned short glyph) const const bool PiFace::isValidGlyph(unsigned short glyph) const
{ {

View File

@ -1,140 +1,149 @@
// Copyright © 2008-2020 Pioneer Developers. See AUTHORS.txt for details // Copyright © 2008-2020 Pioneer Developers. See AUTHORS.txt for details
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
#pragma once
#include "FileSystem.h" #include "FileSystem.h"
#include "RefCounted.h" #include "RefCounted.h"
#include "graphics/opengl/RendererGL.h"
#include "imgui/imgui.h" #include "imgui/imgui.h"
#include "lua/Lua.h"
#include "lua/LuaRef.h" #include "utils.h"
#include "lua/LuaTable.h"
#include <unordered_set> #include <unordered_set>
class PiFace { namespace Graphics {
friend class PiGui; // need acces to some private data class Texture;
std::string m_ttfname; // only the ttf name, it is automatically sought in data/fonts/ class Renderer;
float m_sizefactor; // the requested pixelsize is multiplied by this factor } // namespace Graphics
std::unordered_set<unsigned short> m_invalid_glyphs;
mutable std::vector<std::pair<unsigned short, unsigned short>> m_used_ranges;
ImVector<ImWchar> m_imgui_ranges;
public: namespace PiGui {
PiFace(const std::string &ttfname, float sizefactor) :
m_ttfname(ttfname),
m_sizefactor(sizefactor) {}
const std::string &ttfname() const { return m_ttfname; }
const float sizefactor() const { return m_sizefactor; }
//std::unordered_map<unsigned short, unsigned short> &invalid_glyphs() const { return m_invalid_glyphs; }
const std::vector<std::pair<unsigned short, unsigned short>> &used_ranges() const { return m_used_ranges; }
const bool isValidGlyph(unsigned short glyph) const;
void addGlyph(unsigned short glyph);
void sortUsedRanges() const;
};
class PiFont { class PiFace {
std::string m_name; public:
std::vector<PiFace> m_faces; using UsedRange = std::pair<uint16_t, uint16_t>;
int m_pixelsize; PiFace(const std::string &ttfname, float sizefactor) :
m_ttfname(ttfname),
m_sizefactor(sizefactor) {}
public: const std::string &ttfname() const { return m_ttfname; }
PiFont(const std::string &name) :
m_name(name) {} const float sizefactor() const { return m_sizefactor; }
PiFont(const std::string &name, const std::vector<PiFace> &faces) :
m_name(name), //std::unordered_map<unsigned short, unsigned short> &invalid_glyphs() const { return m_invalid_glyphs; }
m_faces(faces) {} const std::vector<UsedRange> &used_ranges() const { return m_used_ranges; }
PiFont(const PiFont &other) :
m_name(other.name()), const bool isValidGlyph(unsigned short glyph) const;
m_faces(other.faces()) {} void addGlyph(unsigned short glyph);
PiFont() : void sortUsedRanges() const;
m_name("unknown") {}
const std::vector<PiFace> &faces() const { return m_faces; } private:
std::vector<PiFace> &faces() { return m_faces; } friend class Instance; // need access to some private data
const std::string &name() const { return m_name; }
int pixelsize() const { return m_pixelsize; } std::string m_ttfname; // only the ttf name, it is automatically sought in data/fonts/
void setPixelsize(int pixelsize) { m_pixelsize = pixelsize; } float m_sizefactor; // the requested pixelsize is multiplied by this factor
void describe() const
{ std::unordered_set<unsigned short> m_invalid_glyphs;
Output("font %s:\n", name().c_str()); mutable std::vector<UsedRange> m_used_ranges;
for (const PiFace &face : faces()) {
Output("- %s %f\n", face.ttfname().c_str(), face.sizefactor()); ImVector<ImWchar> m_imgui_ranges;
};
class PiFont {
public:
PiFont(const std::string &name) :
m_name(name) {}
PiFont(const std::string &name, const std::vector<PiFace> &faces) :
m_name(name),
m_faces(faces) {}
PiFont(const PiFont &other) :
m_name(other.name()),
m_faces(other.faces()) {}
PiFont() :
m_name("unknown") {}
const std::vector<PiFace> &faces() const { return m_faces; }
std::vector<PiFace> &faces() { return m_faces; }
const std::string &name() const { return m_name; }
int pixelsize() const { return m_pixelsize; }
void setPixelsize(int pixelsize) { m_pixelsize = pixelsize; }
void describe() const
{
Output("font %s:\n", name().c_str());
for (const PiFace &face : faces()) {
Output("- %s %f\n", face.ttfname().c_str(), face.sizefactor());
}
} }
}
};
/* Class to wrap ImGui. */ private:
class PiGui : public RefCounted { std::string m_name;
std::map<std::pair<std::string, int>, ImFont *> m_fonts; std::vector<PiFace> m_faces;
std::map<ImFont *, std::pair<std::string, int>> m_im_fonts; int m_pixelsize;
std::map<std::pair<std::string, int>, PiFont> m_pi_fonts; };
bool m_should_bake_fonts;
std::map<std::string, PiFont> m_font_definitions; /* Class to wrap ImGui. */
class Instance : public RefCounted {
public:
Instance();
void BakeFonts(); void Init(Graphics::Renderer *renderer);
void BakeFont(PiFont &font); void Uninit();
void AddFontDefinition(const PiFont &font) { m_font_definitions[font.name()] = font; }
void ClearFonts();
public: // Call at the start of every frame. Calls ImGui::NewFrame() internally.
PiGui(); void NewFrame();
LuaRef GetHandlers() const { return m_handlers; } // Call at the end of a frame that you're not going to render the results of
void EndFrame();
LuaRef GetKeys() const { return m_keys; } // Calls ImGui::EndFrame() internally and does book-keeping before rendering.
void Render();
void RunHandler(double delta, std::string handler = "GAME"); ImFont *AddFont(const std::string &name, int size);
ImFont *GetFont(const std::string &name, int size);
// Call at the start of every frame. Calls ImGui::NewFrame() internally. void AddGlyph(ImFont *font, unsigned short glyph);
void NewFrame(SDL_Window *window);
// Call at the end of a frame that you're not going to render the results of bool ProcessEvent(SDL_Event *event);
void EndFrame();
// Calls ImGui::EndFrame() internally and does book-keeping before rendering. void RefreshFontsTexture();
void Render();
void Init(SDL_Window *window); private:
Graphics::Renderer *m_renderer;
ImFont *GetFont(const std::string &name, int size); std::map<std::pair<std::string, int>, ImFont *> m_fonts;
std::map<ImFont *, std::pair<std::string, int>> m_im_fonts;
std::map<std::pair<std::string, int>, PiFont> m_pi_fonts;
bool m_should_bake_fonts;
void Uninit() std::map<std::string, PiFont> m_font_definitions;
{
Cleanup();
m_handlers.Unref();
m_keys.Unref();
}
ImFont *AddFont(const std::string &name, int size);
void AddGlyph(ImFont *font, unsigned short glyph); void BakeFonts();
void BakeFont(PiFont &font);
void AddFontDefinition(const PiFont &font) { m_font_definitions[font.name()] = font; }
void ClearFonts();
};
static ImTextureID RenderSVG(std::string svgFilename, int width, int height); int RadialPopupSelectMenu(const ImVec2 &center, std::string popup_id, int mouse_button, std::vector<ImTextureID> tex_ids, std::vector<std::pair<ImVec2, ImVec2>> uvs, unsigned int size, std::vector<std::string> tooltips);
bool CircularSlider(const ImVec2 &center, float *v, float v_min, float v_max);
static bool ProcessEvent(SDL_Event *event); bool LowThrustButton(const char *label, const ImVec2 &size_arg, int thrust_level, const ImVec4 &bg_col, int frame_padding, ImColor gauge_fg, ImColor gauge_bg);
bool ButtonImageSized(ImTextureID user_texture_id, const ImVec2 &size, const ImVec2 &imgSize, const ImVec2 &uv0, const ImVec2 &uv1, int frame_padding, const ImVec4 &bg_col, const ImVec4 &tint_col);
void RefreshFontsTexture(); void ThrustIndicator(const std::string &id_string, const ImVec2 &size, const ImVec4 &thrust, const ImVec4 &velocity, const ImVec4 &bg_col, int frame_padding, ImColor vel_fg, ImColor vel_bg, ImColor thrust_fg, ImColor thrust_bg);
static void *makeTexture(unsigned char *pixels, int width, int height); inline bool WantCaptureMouse()
static bool WantCaptureMouse()
{ {
return ImGui::GetIO().WantCaptureMouse; return ImGui::GetIO().WantCaptureMouse;
} }
static bool WantCaptureKeyboard() inline bool WantCaptureKeyboard()
{ {
return ImGui::GetIO().WantCaptureKeyboard; return ImGui::GetIO().WantCaptureKeyboard;
} }
static int RadialPopupSelectMenu(const ImVec2 &center, std::string popup_id, int mouse_button, std::vector<ImTextureID> tex_ids, std::vector<std::pair<ImVec2, ImVec2>> uvs, unsigned int size, std::vector<std::string> tooltips);
static bool CircularSlider(const ImVec2 &center, float *v, float v_min, float v_max);
void Cleanup(); std::vector<Graphics::Texture *> &GetSVGTextures();
static bool LowThrustButton(const char *label, const ImVec2 &size_arg, int thrust_level, const ImVec4 &bg_col, int frame_padding, ImColor gauge_fg, ImColor gauge_bg); ImTextureID RenderSVG(Graphics::Renderer *renderer, std::string svgFilename, int width, int height);
static bool ButtonImageSized(ImTextureID user_texture_id, const ImVec2 &size, const ImVec2 &imgSize, const ImVec2 &uv0, const ImVec2 &uv1, int frame_padding, const ImVec4 &bg_col, const ImVec4 &tint_col);
static void ThrustIndicator(const std::string &id_string, const ImVec2 &size, const ImVec4 &thrust, const ImVec4 &velocity, const ImVec4 &bg_col, int frame_padding, ImColor vel_fg, ImColor vel_bg, ImColor thrust_fg, ImColor thrust_bg); } //namespace PiGui
private:
LuaRef m_handlers;
LuaRef m_keys;
static std::vector<Graphics::Texture *> m_svg_textures;
};

View File

@ -5,17 +5,65 @@
#include "Face.h" #include "Face.h"
#include "Image.h" #include "Image.h"
#include "ModelSpinner.h" #include "ModelSpinner.h"
#include "lua/LuaTable.h"
static std::vector<std::pair<std::string, int>> m_keycodes = {
{ "left", SDLK_LEFT },
{ "right", SDLK_RIGHT },
{ "up", SDLK_UP },
{ "down", SDLK_DOWN },
{ "escape", SDLK_ESCAPE },
{ "f1", SDLK_F1 },
{ "f2", SDLK_F2 },
{ "f3", SDLK_F3 },
{ "f4", SDLK_F4 },
{ "f5", SDLK_F5 },
{ "f6", SDLK_F6 },
{ "f7", SDLK_F7 },
{ "f8", SDLK_F8 },
{ "f9", SDLK_F9 },
{ "f10", SDLK_F10 },
{ "f11", SDLK_F11 },
{ "f12", SDLK_F12 },
{ "tab", SDLK_TAB },
};
static LuaRef m_handlers;
static LuaRef m_keys;
namespace PiGUI { namespace PiGUI {
namespace Lua { namespace Lua {
void Init() void Init()
{ {
LuaObject<PiGui::Instance>::RegisterClass();
lua_State *l = ::Lua::manager->GetLuaState();
lua_newtable(l);
m_handlers = LuaRef(l, -1);
lua_newtable(l);
m_keys = LuaRef(l, -1);
LuaTable keys(l, -1);
for (auto p : m_keycodes) {
keys.Set(p.first, p.second);
}
LuaObject<PiGUI::Image>::RegisterClass(); LuaObject<PiGUI::Image>::RegisterClass();
LuaObject<PiGUI::Face>::RegisterClass(); LuaObject<PiGUI::Face>::RegisterClass();
LuaObject<PiGUI::ModelSpinner>::RegisterClass(); LuaObject<PiGUI::ModelSpinner>::RegisterClass();
RegisterSandbox(); RegisterSandbox();
} }
void Uninit()
{
m_handlers.Unref();
m_keys.Unref();
}
} // namespace Lua } // namespace Lua
LuaRef GetHandlers() { return m_handlers; }
LuaRef GetKeys() { return m_keys; }
} // namespace PiGUI } // namespace PiGUI

View File

@ -7,12 +7,18 @@
#include "lua/LuaObject.h" #include "lua/LuaObject.h"
namespace PiGUI { namespace PiGUI {
void RegisterSandbox();
// Get registered PiGui handlers.
LuaRef GetHandlers();
// Get a table of key name to SDL-keycode mappings
LuaRef GetKeys();
namespace Lua { namespace Lua {
void RegisterSandbox();
void Init(); void Init();
} void Uninit();
} // namespace Lua
} // namespace PiGUI } // namespace PiGUI
#endif #endif

View File

@ -132,7 +132,7 @@ luaL_Reg l_stack_functions[] = {
{ NULL, NULL } { NULL, NULL }
}; };
void PiGUI::RegisterSandbox() void PiGUI::Lua::RegisterSandbox()
{ {
lua_State *L = ::Lua::manager->GetLuaState(); lua_State *L = ::Lua::manager->GetLuaState();
LUA_DEBUG_START(L); LUA_DEBUG_START(L);

303
src/pigui/Widgets.cpp Normal file
View File

@ -0,0 +1,303 @@
// Copyright © 2008-2020 Pioneer Developers. See AUTHORS.txt for details
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
#include "PiGui.h"
#include "imgui/imgui.h"
// to get ImVec2 + ImVec2
#define IMGUI_DEFINE_MATH_OPERATORS true
#include "imgui/imgui_internal.h"
int PiGui::RadialPopupSelectMenu(const ImVec2 &center, std::string popup_id, int mouse_button, std::vector<ImTextureID> tex_ids, std::vector<std::pair<ImVec2, ImVec2>> uvs, unsigned int size, std::vector<std::string> tooltips)
{
PROFILE_SCOPED()
// return:
// 0 - n for item selected
// -1 for nothing chosen, but menu open
// -2 for menu closed without an icon chosen
// -3 for menu not open
int ret = -3;
// FIXME: Missing a call to query if Popup is open so we can move the PushStyleColor inside the BeginPopupBlock (e.g. IsPopupOpen() in imgui.cpp)
// FIXME: Our PathFill function only handle convex polygons, so we can't have items spanning an arc too large else inner concave edge artifact is too visible, hence the ImMax(7,items_count)
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
if (ImGui::BeginPopup(popup_id.c_str())) {
ret = -1;
const ImVec2 drag_delta = ImVec2(ImGui::GetIO().MousePos.x - center.x, ImGui::GetIO().MousePos.y - center.y);
const float drag_dist2 = drag_delta.x * drag_delta.x + drag_delta.y * drag_delta.y;
const ImGuiStyle &style = ImGui::GetStyle();
const float RADIUS_MIN = 20.0f;
const float RADIUS_MAX = 90.0f;
const float RADIUS_INTERACT_MIN = 20.0f;
const int ITEMS_MIN = 4;
const float border_inout = 12.0f;
const float border_thickness = 4.0f;
ImDrawList *draw_list = ImGui::GetWindowDrawList();
draw_list->PushClipRectFullScreen();
draw_list->PathArcTo(center, (RADIUS_MIN + RADIUS_MAX) * 0.5f, 0.0f, IM_PI * 2.0f * 0.99f, 64); // FIXME: 0.99f look like full arc with closed thick stroke has a bug now
draw_list->PathStroke(ImColor(18, 44, 67, 210), true, RADIUS_MAX - RADIUS_MIN);
const float item_arc_span = 2 * IM_PI / ImMax<int>(ITEMS_MIN, tex_ids.size());
float drag_angle = atan2f(drag_delta.y, drag_delta.x);
if (drag_angle < -0.5f * item_arc_span)
drag_angle += 2.0f * IM_PI;
int item_hovered = -1;
int item_n = 0;
for (ImTextureID tex_id : tex_ids) {
const char *tooltip = tooltips.at(item_n).c_str();
const float inner_spacing = style.ItemInnerSpacing.x / RADIUS_MIN / 2;
const float item_inner_ang_min = item_arc_span * (item_n - 0.5f + inner_spacing);
const float item_inner_ang_max = item_arc_span * (item_n + 0.5f - inner_spacing);
const float item_outer_ang_min = item_arc_span * (item_n - 0.5f + inner_spacing * (RADIUS_MIN / RADIUS_MAX));
const float item_outer_ang_max = item_arc_span * (item_n + 0.5f - inner_spacing * (RADIUS_MIN / RADIUS_MAX));
bool hovered = false;
if (drag_dist2 >= RADIUS_INTERACT_MIN * RADIUS_INTERACT_MIN) {
if (drag_angle >= item_inner_ang_min && drag_angle < item_inner_ang_max)
hovered = true;
}
bool selected = false;
int arc_segments = static_cast<int>((64 * item_arc_span / (2 * IM_PI))) + 1;
draw_list->PathArcTo(center, RADIUS_MAX - border_inout, item_outer_ang_min, item_outer_ang_max, arc_segments);
draw_list->PathArcTo(center, RADIUS_MIN + border_inout, item_inner_ang_max, item_inner_ang_min, arc_segments);
draw_list->PathFillConvex(hovered ? ImColor(102, 147, 189) : selected ? ImColor(48, 81, 111) : ImColor(48, 81, 111));
if (hovered) {
// draw outer / inner extra segments
draw_list->PathArcTo(center, RADIUS_MAX - border_thickness, item_outer_ang_min, item_outer_ang_max, arc_segments);
draw_list->PathStroke(ImColor(102, 147, 189), false, border_thickness);
draw_list->PathArcTo(center, RADIUS_MIN + border_thickness, item_outer_ang_min, item_outer_ang_max, arc_segments);
draw_list->PathStroke(ImColor(102, 147, 189), false, border_thickness);
}
ImVec2 text_size = ImVec2(size, size);
ImVec2 text_pos = ImVec2(
center.x + cosf((item_inner_ang_min + item_inner_ang_max) * 0.5f) * (RADIUS_MIN + RADIUS_MAX) * 0.5f - text_size.x * 0.5f,
center.y + sinf((item_inner_ang_min + item_inner_ang_max) * 0.5f) * (RADIUS_MIN + RADIUS_MAX) * 0.5f - text_size.y * 0.5f);
draw_list->AddImage(tex_id, text_pos, ImVec2(text_pos.x + size, text_pos.y + size), uvs[item_n].first, uvs[item_n].second);
ImGui::SameLine();
if (hovered) {
item_hovered = item_n;
ImGui::SetTooltip("%s", tooltip);
}
item_n++;
}
draw_list->PopClipRect();
if (ImGui::IsMouseReleased(mouse_button)) {
ImGui::CloseCurrentPopup();
if (item_hovered == -1)
ret = -2;
else
ret = item_hovered;
}
ImGui::EndPopup();
} else {
// Output("WARNING: RadialPopupSelectMenu BeginPopup failed: %s\n", popup_id.c_str());
}
ImGui::PopStyleColor(3);
return ret;
}
bool PiGui::CircularSlider(const ImVec2 &center, float *v, float v_min, float v_max)
{
PROFILE_SCOPED()
ImDrawList *draw_list = ImGui::GetWindowDrawList();
ImGuiWindow *window = ImGui::GetCurrentWindow();
const ImGuiID id = window->GetID("circularslider");
draw_list->AddCircle(center, 17, ImColor(100, 100, 100), 128, 12.0);
draw_list->PathArcTo(center, 17, 0, M_PI * 2.0 * (*v - v_min) / (v_max - v_min), 64);
draw_list->PathStroke(ImColor(200, 200, 200), false, 12.0);
ImRect grab_bb;
return ImGui::SliderBehavior(ImRect(center.x - 17, center.y - 17, center.x + 17, center.y + 17),
id, ImGuiDataType_Float, v, &v_min, &v_max, "%.4f", 1.0, ImGuiSliderFlags_None, &grab_bb);
}
static void drawThrust(ImDrawList *draw_list, const ImVec2 &center, const ImVec2 &up, float value, const ImColor &fg, const ImColor &bg)
{
PROFILE_SCOPED()
float factor = 0.1; // how much to offset from center
const ImVec2 step(up.x * 0.5, up.y * 0.5);
const ImVec2 left(-step.y * (1.0 - factor), step.x * (1.0 - factor));
const ImVec2 u(up.x * (1.0 - factor), up.y * (1.0 - factor));
const ImVec2 c(center + ImVec2(u.x * factor, u.y * factor));
const ImVec2 right(-left.x, -left.y);
const ImVec2 leftmiddle = c + step + left;
const ImVec2 rightmiddle = c + step + right;
const ImVec2 bb_lowerright = c + right;
const ImVec2 bb_upperleft = c + left + ImVec2(u.x * value, u.y * value);
const ImVec2 lefttop = c + u + left;
const ImVec2 righttop = c + u + right;
const ImVec2 minimum(fmin(bb_upperleft.x, bb_lowerright.x), fmin(bb_upperleft.y, bb_lowerright.y));
const ImVec2 maximum(fmax(bb_upperleft.x, bb_lowerright.x), fmax(bb_upperleft.y, bb_lowerright.y));
ImVec2 points[] = { c, leftmiddle, lefttop, righttop, rightmiddle };
draw_list->AddConvexPolyFilled(points, 5, bg);
draw_list->PushClipRect(minimum - ImVec2(1, 1), maximum + ImVec2(1, 1));
draw_list->AddConvexPolyFilled(points, 5, fg);
draw_list->PopClipRect();
}
void PiGui::ThrustIndicator(const std::string &id_string, const ImVec2 &size_arg, const ImVec4 &thrust, const ImVec4 &velocity, const ImVec4 &bg_col, int frame_padding, ImColor vel_fg, ImColor vel_bg, ImColor thrust_fg, ImColor thrust_bg)
{
PROFILE_SCOPED()
ImGuiWindow *window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return;
ImGuiContext &g = *GImGui;
const ImGuiStyle &style = g.Style;
const ImGuiID id = window->GetID(id_string.c_str());
ImVec2 pos = window->DC.CursorPos;
// if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
// pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
ImVec2 size = ImGui::CalcItemSize(size_arg, style.FramePadding.x * 2.0f, style.FramePadding.y * 2.0f);
const ImVec2 padding = (frame_padding >= 0) ? ImVec2(static_cast<float>(frame_padding), static_cast<float>(frame_padding)) : style.FramePadding;
const ImRect bb(pos, pos + size + padding * 2);
const ImRect inner_bb(pos + padding, pos + padding + size);
ImGui::ItemSize(bb, style.FramePadding.y);
if (!ImGui::ItemAdd(bb, id))
return;
// Render
const ImU32 col = ImGui::GetColorU32(static_cast<ImGuiCol>(ImGuiCol_Button));
ImGui::RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
ImDrawList *draw_list = ImGui::GetWindowDrawList();
if (bg_col.w > 0.0f)
draw_list->AddRectFilled(inner_bb.Min, inner_bb.Max, ImGui::GetColorU32(bg_col));
const ImVec2 leftupper = inner_bb.Min;
const ImVec2 rightlower = inner_bb.Max;
const ImVec2 rightcenter((rightlower.x - leftupper.x) * 0.8 + leftupper.x, (rightlower.y + leftupper.y) / 2);
const ImVec2 leftcenter((rightlower.x - leftupper.x) * 0.35 + leftupper.x, (rightlower.y + leftupper.y) / 2);
const ImVec2 up(0, -std::abs(leftupper.y - rightlower.y) * 0.4);
const ImVec2 left(-up.y, up.x);
float thrust_fwd = fmax(thrust.z, 0);
float thrust_bwd = fmax(-thrust.z, 0);
float thrust_left = fmax(-thrust.x, 0);
float thrust_right = fmax(thrust.x, 0);
float thrust_up = fmax(-thrust.y, 0);
float thrust_down = fmax(thrust.y, 0);
// actual thrust
drawThrust(draw_list, rightcenter, up, thrust_fwd, thrust_fg, thrust_bg);
drawThrust(draw_list, rightcenter, ImVec2(-up.x, -up.y), thrust_bwd, thrust_fg, thrust_bg);
drawThrust(draw_list, leftcenter, up, thrust_up, thrust_fg, thrust_bg);
drawThrust(draw_list, leftcenter, ImVec2(-up.x, -up.y), thrust_down, thrust_fg, thrust_bg);
drawThrust(draw_list, leftcenter, left, thrust_left, thrust_fg, thrust_bg);
drawThrust(draw_list, leftcenter, ImVec2(-left.x, -left.y), thrust_right, thrust_fg, thrust_bg);
// forward/back velocity
draw_list->AddLine(rightcenter + up, rightcenter - up, vel_bg, 3);
draw_list->AddLine(rightcenter, rightcenter - up * velocity.z, vel_fg, 3);
// left/right velocity
draw_list->AddLine(leftcenter + left, leftcenter - left, vel_bg, 3);
draw_list->AddLine(leftcenter, leftcenter + left * velocity.x, vel_fg, 3);
// up/down velocity
draw_list->AddLine(leftcenter + up, leftcenter - up, vel_bg, 3);
draw_list->AddLine(leftcenter, leftcenter + up * velocity.y, vel_fg, 3);
// Automatically close popups
//if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
// CloseCurrentPopup();
}
bool PiGui::LowThrustButton(const char *id_string, const ImVec2 &size_arg, int thrust_level, const ImVec4 &bg_col, int frame_padding, ImColor gauge_fg, ImColor gauge_bg)
{
PROFILE_SCOPED()
std::string label = std::to_string(thrust_level);
ImGuiWindow *window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext &g = *GImGui;
const ImGuiStyle &style = g.Style;
const ImGuiID id = window->GetID(id_string);
const ImVec2 label_size = ImGui::CalcTextSize(label.c_str(), NULL, true);
ImVec2 pos = window->DC.CursorPos;
// if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
// pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
const ImVec2 padding = (frame_padding >= 0) ? ImVec2(static_cast<float>(frame_padding), static_cast<float>(frame_padding)) : style.FramePadding;
const ImRect bb(pos, pos + size + padding * 2);
const ImRect inner_bb(pos + padding, pos + padding + size);
ImGui::ItemSize(bb, style.FramePadding.y);
if (!ImGui::ItemAdd(bb, id))
return false;
// if (window->DC.ButtonRepeat) flags |= ImGuiButtonFlags_Repeat;
bool hovered, held;
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, 0); // flags
// Render
const ImU32 col = ImGui::GetColorU32(static_cast<ImGuiCol>((hovered && held) ? ImGuiCol_ButtonActive : (hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button)));
ImGui::RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
const ImVec2 center = (inner_bb.Min + inner_bb.Max) / 2;
float radius = (inner_bb.Max.x - inner_bb.Min.x) * 0.4;
float thickness = 4;
ImDrawList *draw_list = ImGui::GetWindowDrawList();
if (bg_col.w > 0.0f)
draw_list->AddRectFilled(inner_bb.Min, inner_bb.Max, ImGui::GetColorU32(bg_col));
draw_list->PathArcTo(center, radius, 0, IM_PI * 2, 16);
draw_list->PathStroke(gauge_bg, false, thickness);
draw_list->PathArcTo(center, radius, IM_PI, IM_PI + IM_PI * 2 * (thrust_level / 100.0), 16);
draw_list->PathStroke(gauge_fg, false, thickness);
ImGui::RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label.c_str(), NULL, &label_size, style.ButtonTextAlign, &bb);
// Automatically close popups
//if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
// CloseCurrentPopup();
return pressed;
}
// frame_padding < 0: uses FramePadding from style (default)
// frame_padding = 0: no framing
// frame_padding > 0: set framing size
// The color used are the button colors.
bool PiGui::ButtonImageSized(ImTextureID user_texture_id, const ImVec2 &size, const ImVec2 &imgSize, const ImVec2 &uv0, const ImVec2 &uv1, int frame_padding, const ImVec4 &bg_col, const ImVec4 &tint_col)
{
ImGuiWindow *window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext &g = *GImGui;
const ImGuiStyle &style = g.Style;
// Default to using texture ID as ID. User can still push string/integer prefixes.
// We could hash the size/uv to create a unique ID but that would prevent the user from animating UV.
ImGui::PushID((void *)user_texture_id);
const ImGuiID id = window->GetID("#image");
ImGui::PopID();
ImVec2 imgPadding = (size - imgSize) / 2;
imgPadding.x = imgPadding.x < 0 || imgSize.x <= 0 ? 0 : imgPadding.x;
imgPadding.y = imgPadding.y < 0 || imgSize.y <= 0 ? 0 : imgPadding.y;
const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding;
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2);
const ImRect image_bb(window->DC.CursorPos + padding + imgPadding, window->DC.CursorPos + padding + size - imgPadding);
ImGui::ItemSize(bb);
if (!ImGui::ItemAdd(bb, id))
return false;
bool hovered, held;
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held);
// Render
const ImU32 col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
ImGui::RenderNavHighlight(bb, id);
ImGui::RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding));
if (bg_col.w > 0.0f)
window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, ImGui::GetColorU32(bg_col));
window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, ImGui::GetColorU32(tint_col));
return pressed;
}

View File

@ -488,6 +488,7 @@
<ClCompile Include="..\..\src\pigui\PiGuiLua.cpp" /> <ClCompile Include="..\..\src\pigui\PiGuiLua.cpp" />
<ClCompile Include="..\..\src\pigui\LuaFace.cpp" /> <ClCompile Include="..\..\src\pigui\LuaFace.cpp" />
<ClCompile Include="..\..\src\pigui\PiGuiSandbox.cpp" /> <ClCompile Include="..\..\src\pigui\PiGuiSandbox.cpp" />
<ClCompile Include="..\..\src\pigui\Widgets.cpp" />
<ClCompile Include="..\..\src\Plane.cpp" /> <ClCompile Include="..\..\src\Plane.cpp" />
<ClCompile Include="..\..\src\Planet.cpp" /> <ClCompile Include="..\..\src\Planet.cpp" />
<ClCompile Include="..\..\src\Player.cpp" /> <ClCompile Include="..\..\src\Player.cpp" />
@ -679,10 +680,12 @@
<ClInclude Include="..\..\src\Pi.h" /> <ClInclude Include="..\..\src\Pi.h" />
<ClInclude Include="..\..\src\pigui\Face.h" /> <ClInclude Include="..\..\src\pigui\Face.h" />
<ClInclude Include="..\..\src\pigui\Image.h" /> <ClInclude Include="..\..\src\pigui\Image.h" />
<ClInclude Include="..\..\src\pigui\LuaFlags.h" />
<ClInclude Include="..\..\src\pigui\ModelSpinner.h" /> <ClInclude Include="..\..\src\pigui\ModelSpinner.h" />
<ClInclude Include="..\..\src\pigui\PerfInfo.h" /> <ClInclude Include="..\..\src\pigui\PerfInfo.h" />
<ClInclude Include="..\..\src\pigui\PiGui.h" /> <ClInclude Include="..\..\src\pigui\PiGui.h" />
<ClInclude Include="..\..\src\pigui\PiGuiLua.h" /> <ClInclude Include="..\..\src\pigui\PiGuiLua.h" />
<ClInclude Include="..\..\src\pigui\View.h" />
<ClInclude Include="..\..\src\Plane.h" /> <ClInclude Include="..\..\src\Plane.h" />
<ClInclude Include="..\..\src\Planet.h" /> <ClInclude Include="..\..\src\Planet.h" />
<ClInclude Include="..\..\src\Player.h" /> <ClInclude Include="..\..\src\Player.h" />

View File

@ -561,6 +561,9 @@
<ClCompile Include="..\..\src\core\Application.cpp"> <ClCompile Include="..\..\src\core\Application.cpp">
<Filter>src\core</Filter> <Filter>src\core</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\pigui\Widgets.cpp">
<Filter>src\pigui</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\src\Aabb.h"> <ClInclude Include="..\..\src\Aabb.h">
@ -1106,6 +1109,12 @@
<ClInclude Include="..\..\src\core\GuiApplication.h"> <ClInclude Include="..\..\src\core\GuiApplication.h">
<Filter>src\core</Filter> <Filter>src\core</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\pigui\View.h">
<Filter>src\pigui</Filter>
</ClInclude>
<ClInclude Include="..\..\src\pigui\LuaFlags.h">
<Filter>src\pigui</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="..\..\src\win32\pioneer.rc"> <ResourceCompile Include="..\..\src\win32\pioneer.rc">