Compare commits

...

16 Commits

Author SHA1 Message Date
Webster Sheets 6092733940
Update Changelog.txt 2020-04-06 15:53:03 -04:00
Webster Sheets 08386499a9
Merge pull request #4849 from Web-eWorks/modelviewer-refactor
Refactor the ModelViewer to use PiGui
2020-04-06 15:51:48 -04:00
Webster Sheets 8e54a3be06 Fix some minor blunders.
Fixes #4853 (faulty initialization) and fixes #4681 (unsupported GLSL version).
2020-04-06 15:00:14 -04:00
Webster Sheets 9d719273fe
Update Changelog.txt 2020-04-04 15:53:54 -04:00
Webster Sheets 8e31d169ee
Merge pull request #4821 from Gliese852/ecraven-systemview-squashed
Move the System Map to PiGUI (#4821)
2020-04-04 15:53:12 -04:00
Andrew Copland 2310827da0 Added a few missing files to the VS build 2020-04-04 20:33:39 +01:00
Andrew Copland f6ebcbe966 Need this include as the array header doesn't appear to be included always 2020-04-04 20:33:18 +01:00
Gliese852 ac7e98248e Additional fixes related (almost) to System Map
- Now only those orbits whose radius is less than 1% of the screen width are not shown.
- Add smooth transition when click object.
- Improve system bodies orbits.
- Fix that ..apsis and lagrange points were displayed in the wrong place.

- After rescanning enums, an error was found in the pigui/Face.h, fixed. fixes #4801
- Some indentations to pass clang
- Added static_cast to Space.cpp, because it caused a warning at compilation.
2020-04-04 16:27:44 +03:00
Gliese852 af5d52cfd9 Move the System Map to PiGUI
The main idea of the interaction of lua side and C++ side is the same as
in the worldview - the C++ View Class builds three-dimensional objects,
some projected points are passed to the lua script, the lua script only
works with screen coordinates.

The difference between the system view and the world view is that not
all objects can have a physical body. If we examine a non-current star
system, we see only system bodies that do not have physical bodies.
Therefore, if for a world view you can simply create an array of
physical bodies, this will not work in a system view.

To solve this problem, struct "Projectable" was created, into which you
can write any point object, system body, ship, Lagrange point,
apocenter, pericenter, etc. Now all these objects can be processed in a
single array.

The names of the types of these objects are recorded in an enum, which
is exported to a enum_table.cpp and read by the script as integers at
the start. I thought it was better to avoid passing strings in such a
hot place in the code. (runs for every object in every frame) Therefore,
type names are written and transmitted as integers.

All colors are now imported from lua: color index names are listed in
the enum, which is exported to the enum_table.cpp. At event
"onGameStart" the lua script reads these names and sends color values to
the class. Now you can change colors at runtime.

* Improvements/fixes: *

- Fix zooming speeds.
- Fix ship's orbits in nonroot frame.
- Remove unused old gui objects.
- Landed ship are now spinning with the planet when rewinding time in a
    planner.
- Now you can center the view on any object, including the player’s ship.
- Add right-click context menu to center or set object as target.
    Therefore, to rotate the view, set the middle button.
- Add indicators for navigation target and combat target.

* Changes in the files explained *

    SystemView.cpp, SystemView.h, system-view-ui.lua, lang/core/en.json

The whole thing, lots of changes. Now the system view draws only the
orbits and the grid. In those places where the icons were drawn, instead
of drawing, they are stored in an array, which is then requested from
the lua.

    LuaEngine.cpp

Add a general function that returns the value of an any enum by name
from an enum_table.cpp (for calling from a lua)

    Space.cpp

Added a notification for the systemview when the ship is removed from
space, because it can be centered at this time, and an segmentation
fault will occur.

    LuaSystemBody.cpp

Added attributes to determine the shape of the icon and set the
navigation target.

    enum_table.cpp, enum_table.h

Rescanned enums to pass color settings and object types between Lua and
C++.

    LuaGame.cpp

The float does not have enough accuracy to display time up to seconds,
change to double.

    icons.svg, themes/default.lua

Add rotate view icon
2020-04-04 11:56:10 +03:00
Webster Sheets b754aeed2a Rewrite ModelViewer to use GuiApplication and PiGui
"Snap to Direction" keybinds don't work, due to Input not dispatching their event handlers.
Will be resolved in a later sprint on the Input system.
2020-04-01 17:04:52 -04:00
Webster Sheets 2ea32811f7 Always clean up the active lifecycle 2020-04-01 17:02:51 -04:00
Webster Sheets ca3fde811f Decouple Input from Pi, add key state tracking
Added IsKeyPressed / Released methods to track per-frame key state
Added GetMouseWheel so users don't have to track mouse wheel events themselves
Input keeps a pointer to a config instance, completely decoupling it from Pi
2020-04-01 17:02:12 -04:00
Webster Sheets 053ff32ef1 Decouple PiGui from Pi::renderer
Explicitly inject the renderer dependency, instead of relying on Pi::renderer existing
Made it Pi.cpp's responsibility to hide the mouse cursor.
2020-03-31 02:50:11 -04:00
Webster Sheets 7b3a69a385 Rename PiGui -> PiGui::Instance
Make PiGui a namespace for easier method scoping, allows us to be more flexible

PiGui::Instance only has the bare minimum of methods needed to run ImGui

Move PiGui RegisterClass call to pigui/PiGuiLua - we'll further unify LuaPiGui
and PiGuiLua later.
2020-03-31 02:08:54 -04:00
Webster Sheets e584780ce9 Move Input and PiGui handling to GuiApplication
At the moment, the lifecycle is responsible for calling HandleEvents and rendering PiGui.
This can change once legacy code no longer depends on HandleEvents running after View::Draw3D().

Added virtual HandleEvent() call to dispatch to legacy UI code.
This is suboptimal; we need a better way that doesn't involve virtual function calls.
Use std::function or a better, lower-overhead delegate library
2020-03-31 00:59:44 -04:00
Webster Sheets a1745867ff Move all Lua initialization out of PiGui.cpp
- Now LuaPiGui handles it, decoupling the PiGui implementation from lua
- Made all externally-facing symbols in LuaPiGui part of the PiGUI namespace
This will be renamed to PiGui and the class of the same name will become part of the namespace.
2020-03-30 23:32:52 -04:00
40 changed files with 3328 additions and 2301 deletions

View File

@ -1,3 +1,10 @@
April 2020
* New Features
* Move the System Map to PiGUI (#4821)
* Internal Changes
* Refactor the ModelViewer to use PiGui (#4849)
March 2020
* New Features
* Stars that are brighter are now bigger and have a brighter colour on the Starfield (#4833)

View File

@ -16,7 +16,7 @@
viewBox="0 0 3628.3465 3628.3464"
id="svg4446"
version="1.1"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="icons.svg">
<defs
id="defs4448">
@ -881,16 +881,16 @@
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="0.1767767"
inkscape:cx="1429.7092"
inkscape:cy="1859.5164"
inkscape:zoom="0.25000001"
inkscape:cx="2379.8651"
inkscape:cy="2002.5917"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1850"
inkscape:window-height="1057"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-width="1853"
inkscape:window-height="1016"
inkscape:window-x="67"
inkscape:window-y="27"
inkscape:window-maximized="1"
showguides="true"
inkscape:guide-bbox="true"
@ -943,7 +943,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@ -9014,5 +9014,29 @@
id="rect7847"
style="fill:none;stroke:none;stroke-width:10.69061661;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:10.69061666, 21.38123329;stroke-dashoffset:0" />
</g>
<g
inkscape:label="rotate_view"
transform="translate(-4762.2048,1133.8583)"
id="g2225">
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:21.25984192;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.5151515"
id="rect2223"
width="226.77151"
height="226.77162"
x="8163.7798"
y="-2575.9844" />
<path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:14.17322826;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1"
d="m 8203.6641,-2505.9607 -28.4385,-0.4182 49.7674,65.2412 56.877,-63.5684 -35.9663,-0.8364 c 0,0 8.0177,-38.3618 43.3419,-41.9946 28.4386,4.1822 18.5537,47.4314 18.5537,47.4314 0,0 7.1096,-5.4369 17.9831,-5.0187 10.8736,0.4183 15.8922,11.9192 15.8922,11.9192 0,0 4.182,-23.2109 -4.1822,-37.8484 -8.3642,-14.6374 -28.4385,-31.7841 -53.5313,-30.5295 -25.0928,1.2547 -44.3307,4.1821 -62.3138,23.8382 -14.6375,18.8196 -17.9832,31.7842 -17.9832,31.7842 z"
id="path2227"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccc" />
<path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:14.17322826;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1"
d="m 8234.8718,-2421.3714 50.2389,63.6822 0.3823,-35.7724 c 40.2329,6.163 71.2869,1.8276 85.669,-13.457 14.3821,-15.2847 15.0115,-40.8195 5.6423,-57.6389 -9.3693,-16.8194 -32.0947,-21.798 -32.0947,-21.798 0,0 6.1707,10.9709 6.3568,20.8718 0.1827,9.7237 -10.2431,16.3492 -10.2431,16.3492 0,0 17.5122,1.9115 20.8358,3.4682 3.3236,1.5567 5.762,1.9039 -0.5228,7.4519 -6.2848,5.548 -41.2332,12.4421 -74.9406,2.6361 l 0.8277,-38.7801 z"
id="path2231"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccczzcsczzccc" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 717 KiB

After

Width:  |  Height:  |  Size: 719 KiB

View File

@ -167,6 +167,10 @@
"description": "",
"message": "Cargo scoop attempted. Not enough room in cargo hold."
},
"CENTER": {
"description": "",
"message": "Center"
},
"CH4_ATMOSPHERE": {
"description": "",
"message": "\" Methane atmosphere\""
@ -1091,6 +1095,10 @@
"description": "",
"message": "Orbital starport"
},
"ORBIT_PLANNER": {
"description": "",
"message": "Orbit planner"
},
"OUTDOOR_AGRICULTURAL_WORLD": {
"description": "",
"message": "Outdoor agricultural world."
@ -1323,6 +1331,14 @@
"description": "",
"message": "Semi-major axis"
},
"SET_AS_COMBAT_TARGET": {
"description": "",
"message": "Set as combat target"
},
"SET_AS_TARGET": {
"description": "",
"message": "Set as navigation target"
},
"SET_HYPERSPACE_DESTINATION_TO": {
"description": "",
"message": "Set hyperspace destination to %system"

View File

@ -263,6 +263,10 @@
"description": "Player combat rating",
"message": "Deadly"
},
"DECREASE": {
"description": "Decrease something.",
"message": "Decrease"
},
"DELTA_V": {
"description": "",
"message": "Delta-v"
@ -963,6 +967,10 @@
"description": "For player reputation",
"message": "Incompetent"
},
"INCREASE": {
"description": "Increase something.",
"message": "Increase"
},
"INEXPERIENCED": {
"description": "For player reputation",
"message": "Inexperienced"
@ -1531,6 +1539,10 @@
"description": "",
"message": "Reward"
},
"ROTATE_VIEW": {
"description": "",
"message": "Rotate view"
},
"ROUTE_INFO": {
"description": "For hyperjump planner",
"message": "Route Info"
@ -1627,6 +1639,10 @@
"description": "",
"message": "Ship Repairs"
},
"SHIP_TYPE": {
"description": "",
"message": "Ship Type"
},
"SHIP_VIEWING_WAS_SOLD": {
"description": "",
"message": "The ship you were viewing has been sold"

View File

@ -0,0 +1,516 @@
local Game = require 'Game'
local Engine = require 'Engine'
local Event = require 'Event'
local Lang = require 'Lang'
local ui = require 'pigui'
local Format = require 'Format'
local Input = require 'Input'
local Vector2 = _G.Vector2
local lc = Lang.GetResource("core")
local luc = Lang.GetResource("ui-core")
local player = nil
local colors = ui.theme.colors
local icons = ui.theme.icons
local systemView
local mainButtonSize = ui.rescaleUI(Vector2(32,32), Vector2(1600, 900))
local mainButtonFramePadding = 3
local itemSpacing = Vector2(8, 4) -- couldn't get default from ui
local indicatorSize = Vector2(30 , 30)
local selectedObject -- object, centered in SystemView
local pionillium = ui.fonts.pionillium
local ASTEROID_RADIUS = 1500000 -- rocky planets smaller than this (in meters) are considered an asteroid, not a planet
--load enums Projectable::types and Projectable::bases in one table "Projectable"
local Projectable = {}
for _, key in pairs(Constants.ProjectableTypes) do Projectable[key] = Engine.GetEnumValue("ProjectableTypes", key) end
for _, key in pairs(Constants.ProjectableBases) do Projectable[key] = Engine.GetEnumValue("ProjectableBases", key) end
local svColor = {
BUTTON_BACK = colors.buttonBlue,
BUTTON_INK = colors.white,
COMBAT_TARGET = colors.combatTarget,
GRID = Color(25,25,25),
LAGRANGE = Color(0,214,226),
NAV_TARGET = colors.navTarget,
OBJECT = colors.frame,
PLANNER = Color(0,0,255),
PLANNER_ORBIT = Color(0,0,255),
PLAYER = Color(255,0,0),
PLAYER_ORBIT = Color(255,0,0),
SELECTED_SHIP_ORBIT = Color(0,186,255),
SHIP = colors.frame,
SHIP_ORBIT = Color(0,0,155),
SYSTEMBODY = Color(109,109,134),
SYSTEMBODY_ICON = colors.frame,
SYSTEMBODY_ORBIT = Color(0,155,0),
SYSTEMNAME_BACK = colors.transparent,
WINDOW_BACK = colors.lightBlackBackground,
UNKNOWN = Color(255,0,255)
}
local onGameStart = function ()
--connect to class SystemView
systemView = Game.systemView
--export several colors to class SystemView (only those which mentioned in the enum SystemViewColorIndex)
for _, key in pairs(Constants.SystemViewColorIndex) do
systemView:SetColor(key, svColor[key])
end
end
local function showDvLine(leftIcon, resetIcon, rightIcon, key, Formatter, leftTooltip, resetTooltip, rightTooltip)
local wheel = function()
if ui.isItemHovered() then
local w = ui.getMouseWheel()
if w ~= 0 then
systemView:TransferPlannerAdd(key, w * 10)
end
end
end
local press = ui.coloredSelectedIconButton(leftIcon, mainButtonSize, false, mainButtonFramePadding, svColor.BUTTON_BACK, svColor.BUTTON_INK, leftTooltip)
if press or (key ~= "factor" and ui.isItemActive()) then
systemView:TransferPlannerAdd(key, -10)
end
wheel()
ui.sameLine()
if ui.coloredSelectedIconButton(resetIcon, mainButtonSize, false, mainButtonFramePadding, svColor.BUTTON_BACK, svColor.BUTTON_INK, resetTooltip) then
systemView:TransferPlannerReset(key)
end
wheel()
ui.sameLine()
press = ui.coloredSelectedIconButton(rightIcon, mainButtonSize, false, mainButtonFramePadding, svColor.BUTTON_BACK, svColor.BUTTON_INK, rightTooltip)
if press or (key ~= "factor" and ui.isItemActive()) then
systemView:TransferPlannerAdd(key, 10)
end
wheel()
ui.sameLine()
local speed, speed_unit = Formatter(systemView:TransferPlannerGet(key))
ui.text(speed .. " " .. speed_unit)
return 0
end
local time_selected_button_icon = icons.time_center
local function timeButton(icon, tooltip, factor)
if ui.coloredSelectedIconButton(icon, mainButtonSize, false, mainButtonFramePadding, svColor.BUTTON_BACK, svColor.BUTTON_INK, tooltip) then
time_selected_button_icon = icon
end
local active = ui.isItemActive()
if active then
systemView:AccelerateTime(factor)
end
ui.sameLine()
return active
end
local function loop3items(a, b, c) return { [a] = b, [b] = c, [c] = a } end
local ship_drawing = "SHIPS_OFF"
local show_lagrange = "LAG_OFF"
local show_grid = "GRID_OFF"
local nextShipDrawings = loop3items("SHIPS_OFF", "SHIPS_ON", "SHIPS_ORBITS")
local nextShowLagrange = loop3items("LAG_OFF", "LAG_ICON", "LAG_ICONTEXT")
local nextShowGrid = loop3items("GRID_OFF", "GRID_ON", "GRID_AND_LEGS")
local function calcWindowWidth(buttons)
return (mainButtonSize.y + mainButtonFramePadding * 2) * buttons
+ itemSpacing.x * (buttons + 1)
end
local function calcWindowHeight(buttons, separators, texts)
return
(mainButtonSize.y + mainButtonFramePadding * 2) * buttons
+ separators * itemSpacing.y
+ texts * ui.fonts.pionillium.medium.size
+ (buttons + texts - 1) * itemSpacing.y
+ itemSpacing.x * 2
end
local orbitPlannerWindowPos = Vector2(ui.screenWidth - calcWindowWidth(7), ui.screenHeight - calcWindowHeight(7, 3, 2))
local function showOrbitPlannerWindow()
ui.setNextWindowPos(orbitPlannerWindowPos, "Always")
ui.withStyleColors({["WindowBg"] = svColor.WINDOW_BACK}, function()
ui.window("OrbitPlannerWindow", {"NoTitleBar", "NoResize", "NoFocusOnAppearing", "NoBringToFrontOnFocus", "NoSavedSettings", "AlwaysAutoResize"},
function()
ui.text(lc.ORBIT_PLANNER)
ui.separator()
if ui.coloredSelectedIconButton(icons.reset_view, mainButtonSize, showShips, mainButtonFramePadding, svColor.BUTTON_BACK, svColor.BUTTON_INK, lc.RESET_ORIENTATION_AND_ZOOM) then
systemView:SetVisibility("RESET_VIEW")
end
ui.sameLine()
if ui.coloredSelectedIconButton(icons.toggle_grid, mainButtonSize, showShips, mainButtonFramePadding, svColor.BUTTON_BACK, svColor.BUTTON_INK, lc.GRID_DISPLAY_MODE_TOGGLE) then
show_grid = nextShowGrid[show_grid]
systemView:SetVisibility(show_grid);
end
ui.sameLine()
if ui.coloredSelectedIconButton(icons.toggle_ships, mainButtonSize, showShips, mainButtonFramePadding, svColor.BUTTON_BACK, svColor.BUTTON_INK, lc.SHIPS_DISPLAY_MODE_TOGGLE) then
ship_drawing = nextShipDrawings[ship_drawing]
systemView:SetVisibility(ship_drawing);
end
ui.sameLine()
if ui.coloredSelectedIconButton(icons.toggle_lagrange, mainButtonSize, showLagrangePoints, mainButtonFramePadding, svColor.BUTTON_BACK, svColor.BUTTON_INK, lc.L4L5_DISPLAY_MODE_TOGGLE) then
show_lagrange = nextShowLagrange[show_lagrange]
systemView:SetVisibility(show_lagrange);
end
ui.sameLine()
ui.coloredSelectedIconButton(icons.search_lens,mainButtonSize, false, mainButtonFramePadding, svColor.BUTTON_BACK, svColor.BUTTON_INK, luc.ZOOM)
systemView:SetZoomMode(ui.isItemActive())
ui.sameLine()
ui.coloredSelectedIconButton(icons.rotate_view, mainButtonSize, false, mainButtonFramePadding, svColor.BUTTON_BACK, svColor.BUTTON_INK, luc.ROTATE_VIEW)
systemView:SetRotateMode(ui.isItemActive())
ui.separator()
showDvLine(icons.decrease, icons.delta, icons.increase, "factor", function(i) return i, "x" end, luc.DECREASE, lc.PLANNER_RESET_FACTOR, luc.INCREASE)
showDvLine(icons.decrease, icons.clock, icons.increase, "starttime",
function(i)
local now = Game.time
local start = systemView:GetOrbitPlannerStartTime()
if start then
return ui.Format.Duration(math.floor(start - now)), ""
else
return lc.NOW, ""
end
end,
luc.DECREASE, lc.PLANNER_RESET_START, luc.INCREASE)
showDvLine(icons.decrease, icons.orbit_prograde, icons.increase, "prograde", ui.Format.Speed, luc.DECREASE, lc.PLANNER_RESET_PROGRADE, luc.INCREASE)
showDvLine(icons.decrease, icons.orbit_normal, icons.increase, "normal", ui.Format.Speed, luc.DECREASE, lc.PLANNER_RESET_NORMAL, luc.INCREASE)
showDvLine(icons.decrease, icons.orbit_radial, icons.increase, "radial", ui.Format.Speed, luc.DECREASE, lc.PLANNER_RESET_RADIAL, luc.INCREASE)
ui.separator()
local t = systemView:GetOrbitPlannerTime()
ui.text(t and ui.Format.Datetime(t) or lc.NOW)
local r = false
r = timeButton(icons.time_backward_100x, "-10,000,000x",-10000000) or r
r = timeButton(icons.time_backward_10x, "-100,000x", -100000) or r
r = timeButton(icons.time_backward_1x, "-1,000x", -1000) or r
r = timeButton(icons.time_center, lc.NOW, nil) or r
r = timeButton(icons.time_forward_1x, "1,000x", 1000) or r
r = timeButton(icons.time_forward_10x, "100,000x", 100000) or r
r = timeButton(icons.time_forward_100x, "10,000,000x", 10000000) or r
if not r then
if time_selected_button_icon == icons.time_center then
systemView:AccelerateTime(nil)
else
systemView:AccelerateTime(0.0)
end
end
end)
end)
end
local function getBodyIcon(obj)
if obj.type == Projectable.APOAPSIS then return icons.apoapsis
elseif obj.type == Projectable.PERIAPSIS then return icons.periapsis
elseif obj.type == Projectable.L4 then return icons.lagrange_marker
elseif obj.type == Projectable.L5 then return icons.lagrange_marker
elseif obj.base == Projectable.PLAYER or obj.base == Projectable.PLANNER then
local shipClass = obj.ref:GetShipClass()
if icons[shipClass] then
return icons[shipClass]
else
return icons.ship
end
elseif obj.base == Projectable.SYSTEMBODY then
local body = obj.ref
local st = body.superType
local t = body.type
if st == "STARPORT" then
if t == "STARPORT_ORBITAL" then
return icons.spacestation
elseif body.type == "STARPORT_SURFACE" then
return icons.starport
end
elseif st == "GAS_GIANT" then
return icons.gas_giant
elseif st == "STAR" then
return icons.sun
elseif st == "ROCKY_PLANET" then
if body.IsMoon then
return icons.moon
else
if body.radius < ASTEROID_RADIUS then
return icons.asteroid_hollow
else
return icons.rocky_planet
end
end
end -- st
else
-- physical body
local body = obj.ref
if body:IsShip() then
local shipClass = body:GetShipClass()
if icons[shipClass] then
return icons[shipClass]
else
print("system-view-ui.lua: getBodyIcon unknown ship class " .. (shipClass and shipClass or "nil"))
return icons.ship -- TODO: better icon
end
elseif body:IsHyperspaceCloud() then
return icons.hyperspace -- TODO: better icon
elseif body:IsMissile() then
return icons.bullseye -- TODO: better icon
elseif body:IsCargoContainer() then
return icons.rocky_planet -- TODO: better icon
else
print("system-view-ui.lua: getBodyIcon not sure how to process body, supertype: " .. (st and st or "nil") .. ", type: " .. (t and t or "nil"))
--utils.print_r(body)
return icons.ship
end
end
end
local function getLabel(obj)
if obj.type == Projectable.OBJECT then
if obj.base == Projectable.SYSTEMBODY then return obj.ref.name
elseif obj.base == Projectable.PLANNER then return ""
else return obj.ref:GetLabel() end
elseif obj.type == Projectable.L4 and show_lagrange == "LAG_ICONTEXT" then return "L4"
elseif obj.type == Projectable.L5 and show_lagrange == "LAG_ICONTEXT" then return "L5"
else return ""
end
end
local function getColor(obj)
if obj.type == Projectable.OBJECT then
if obj.base == Projectable.SYSTEMBODY then return svColor.SYSTEMBODY_ICON
elseif obj.base == Projectable.SHIP then return svColor.SHIP
elseif obj.base == Projectable.PLAYER then return svColor.PLAYER
elseif obj.base == Projectable.PLANNER then return svColor.PLANNER
else return svColor.OBJECT
end
elseif obj.type == Projectable.APOAPSIS or obj.type == Projectable.PERIAPSIS then
if obj.base == Projectable.SYSTEMBODY then return svColor.SYSTEMBODY_ORBIT
elseif obj.base == Projectable.SHIP then
if obj.ref == selectedObject then return svColor.SELECTED_SHIP_ORBIT
else return svColor.SHIP_ORBIT
end
elseif obj.base == Projectable.PLAYER then return svColor.PLAYER_ORBIT
elseif obj.base == Projectable.PLANNER then return svColor.PLANNER_ORBIT
else return svColor.UNKNOWN -- unknown base
end
elseif obj.type == Projectable.L4 or obj.type == Projectable.L5 then return svColor.LAGRANGE
else return svColor.UNKNOWN
end
end
local function showSystemName()
ui.setNextWindowPos(Vector2(20, 20), "Always")
ui.withStyleColors({["WindowBg"] = svColor.SYSTEMNAME_BACK}, function()
ui.window("SystemName", {"NoTitleBar", "AlwaysAutoResize", "NoResize", "NoFocusOnAppearing", "NoBringToFrontOnFocus", "NoSavedSettings"},
function()
local path = Engine.GetSectorMapSelectedSystemPath()
local starsystem = path:GetStarSystem()
ui.text(starsystem.name .. " (" .. path.sectorX .. ", " .. path.sectorY .. ", " .. path.sectorZ .. ")")
end)
end)
end
-- forked from data/pigui/views/game.lua
local function displayOnScreenObjects()
local navTarget = player:GetNavTarget()
local combatTarget = player:GetCombatTarget()
local should_show_label = ui.shouldShowLabels()
local iconsize = Vector2(18 , 18)
local label_offset = 14 -- enough so that the target rectangle fits
local collapse = iconsize -- size of clusters to be collapsed into single bodies
local click_radius = collapse:length() * 0.5
-- make click_radius sufficiently smaller than the cluster size
-- to prevent overlap of selection regions
local objectCounter = 0
local objects_grouped = systemView:GetProjectedGrouped(collapse, 1e64)
if #objects_grouped == 0 then
ui.setNextWindowPos(Vector2(ui.screenWidth, ui.screenHeight) / 2 - ui.calcTextSize(lc.UNEXPLORED_SYSTEM_NO_SYSTEM_VIEW) / 2, "Always")
ui.withStyleColors({["WindowBg"] = svColor.SYSTEMNAME_BACK}, function()
ui.window("NoSystemView", {"NoTitleBar", "AlwaysAutoResize", "NoResize", "NoFocusOnAppearing", "NoBringToFrontOnFocus", "NoSavedSettings"},
function()
ui.text(lc.UNEXPLORED_SYSTEM_NO_SYSTEM_VIEW);
end)
end)
return
end
for _,group in ipairs(objects_grouped) do
local mainObject = group.mainObject
local mainCoords = Vector2(group.screenCoordinates.x, group.screenCoordinates.y)
-- indicators
local stackedSize = indicatorSize
local stackStep = Vector2(10, 10)
if group.hasPlayer then
ui.addIcon(mainCoords, icons.square, svColor.PLAYER, stackedSize, ui.anchor.center, ui.anchor.center)
stackedSize = stackedSize + stackStep
end
if group.hasNavTarget then
ui.addIcon(mainCoords, icons.square, svColor.NAV_TARGET, stackedSize, ui.anchor.center, ui.anchor.center)
stackedSize = stackedSize + stackStep
end
if group.hasCombatTarget then
ui.addIcon(mainCoords, icons.square, svColor.COMBAT_TARGET, stackedSize, ui.anchor.center, ui.anchor.center)
stackedSize = stackedSize + stackStep
end
if mainObject.type == Projectable.OBJECT and mainObject.base == Projectable.PLANNER then ui.addIcon(mainCoords, icons.square, svColor.PLANNER, indicatorSize, ui.anchor.center, ui.anchor.center) end
ui.addIcon(mainCoords, getBodyIcon(mainObject), getColor(mainObject), iconsize, ui.anchor.center, ui.anchor.center)
if should_show_label then
local label = getLabel(mainObject)
if group.objects then
label = label .. " (" .. #group.objects .. ")"
end
ui.addStyledText(mainCoords + Vector2(label_offset,0), ui.anchor.left, ui.anchor.center, label , getColor(mainObject), pionillium.small)
end
local mp = ui.getMousePos()
if mainObject.type == Projectable.OBJECT and (mainObject.base == Projectable.SYSTEMBODY or mainObject.base == Projectable.SHIP or mainObject.base == Projectable.PLAYER) then
-- mouse release handler for right button
if (mp - mainCoords):length() < click_radius then
if not ui.isAnyWindowHovered() and ui.isMouseReleased(1) then
ui.openPopup("target" .. objectCounter)
end
end
-- make popup
ui.popup("target" .. objectCounter, function()
local isObject = mainObject.type == Projectable.OBJECT
local isSystemBody = isObject and mainObject.base == Projectable.SYSTEMBODY
local isShip = isObject and not isSystemBody and mainObject.ref:IsShip()
ui.text(getLabel(mainObject))
ui.separator()
if ui.selectable(lc.CENTER, false, {}) then
systemView:SetSelectedObject(mainObject.type, mainObject.base, mainObject.ref)
end
if (isShip or isSystemBody and mainObject.ref.physicsBody) and ui.selectable(lc.SET_AS_TARGET, false, {}) then
if isSystemBody then
player:SetNavTarget(mainObject.ref.physicsBody)
else
if combatTarget == mainObject.ref then player:SetCombatTarget(nil) end
player:SetNavTarget(mainObject.ref)
end
end
if isShip and ui.selectable(lc.SET_AS_COMBAT_TARGET, false, {}) then
if navTarget == mainObject.ref then player:SetNavTarget(nil) end
player:SetCombatTarget(mainObject.ref)
end
end)
end
-- mouse release handler for left button
if (mp - mainCoords):length() < click_radius then
if not ui.isAnyWindowHovered() and ui.isMouseReleased(0) and mainObject.type == Projectable.OBJECT then
systemView:SetSelectedObject(mainObject.type, mainObject.base, mainObject.ref)
end
end
objectCounter = objectCounter + 1
end
end
local function tabular(data)
if data and #data > 0 then
ui.columns(2, "Attributes", true)
for _,item in pairs(data) do
if item.value then
ui.text(item.name)
ui.nextColumn()
ui.text(item.value)
ui.nextColumn()
end
end
ui.columns(1, "NoAttributes", false)
end
end
local function showTargetInfoWindow(obj)
if obj.type ~= Projectable.OBJECT or obj.base ~= Projectable.SHIP and obj.base ~= Projectable.SYSTEMBODY then return end
ui.setNextWindowSize(Vector2(ui.screenWidth / 5, 0), "Always")
ui.setNextWindowPos(Vector2(20, (ui.screenHeight / 5) * 2 + 20), "Always")
ui.withStyleColors({["WindowBg"] = svColor.WINDOW_BACK}, function()
ui.window("TargetInfoWindow", {"NoTitleBar", "AlwaysAutoResize", "NoResize", "NoFocusOnAppearing", "NoBringToFrontOnFocus", "NoSavedSettings"},
function()
local data
-- system body
if obj.type == Projectable.OBJECT and obj.base == Projectable.SYSTEMBODY then
local systemBody = obj.ref
local name = systemBody.name
local rp = systemBody.rotationPeriod * 24 * 60 * 60
local r = systemBody.radius
local radius = nil
if r and r > 0 then
local v,u = ui.Format.Distance(r)
radius = v .. u
end
local sma = systemBody.semiMajorAxis
local semimajoraxis = nil
if sma and sma > 0 then
local v,u = ui.Format.Distance(sma)
semimajoraxis = v .. u
end
local op = systemBody.orbitPeriod * 24 * 60 * 60
data = {
{ name = lc.NAME_OBJECT,
value = name },
{ name = lc.DAY_LENGTH .. lc.ROTATIONAL_PERIOD,
value = rp > 0 and ui.Format.Duration(rp, 2) or nil },
{ name = lc.RADIUS,
value = radius },
{ name = lc.SEMI_MAJOR_AXIS,
value = semimajoraxis },
{ name = lc.ORBITAL_PERIOD,
value = op and op > 0 and ui.Format.Duration(op, 2) or nil }
}
-- physical body
elseif obj.type == Projectable.OBJECT and obj.ref:IsShip() then
local body = obj.ref
local name = body.label
data = {{ name = lc.NAME_OBJECT, value = name }, }
-- TODO: the advanced target scanner should add additional data here,
-- but we really do not want to hardcode that here. there should be
-- some kind of hook that the target scanner can hook into to display
-- more info here.
-- This is what should be inserted:
table.insert(data, { name = luc.SHIP_TYPE, value = body:GetShipType() })
if player:GetEquipCountOccupied('target_scanner') > 0 or player:GetEquipCountOccupied('advanced_target_scanner') > 0 then
local hd = body:GetEquip("engine", 1)
table.insert(data, { name = lc.HYPERDRIVE, value = hd and hd:GetName() or lc.NO_HYPERDRIVE })
table.insert(data, { name = lc.MASS, value = Format.MassTonnes(body:GetStats().staticMass) })
table.insert(data, { name = lc.CARGO, value = Format.MassTonnes(body:GetStats().usedCargo) })
end
else
data = {}
end
tabular(data)
end)
end)
end
local function displaySystemViewUI()
player = Game.player
local current_view = Game.CurrentView()
if current_view == "system" and not Game.InHyperspace() then
selectedObject = systemView:GetSelectedObject()
displayOnScreenObjects()
ui.withFont(ui.fonts.pionillium.medium.name, ui.fonts.pionillium.medium.size, function()
showOrbitPlannerWindow()
showTargetInfoWindow(selectedObject)
end)
showSystemName()
end
end
Event.Register("onGameStart", onGameStart)
ui.registerModule("game", displaySystemViewUI)
return {}

View File

@ -160,6 +160,7 @@ theme.icons = {
shield = 92,
hull = 93,
temperature = 94,
rotate_view = 95,
-- seventh row
heavy_cargo_shuttle = 96,
medium_cargo_shuttle = 97,

View File

@ -373,8 +373,8 @@ def write_header(enums, fl):
fl.write('#ifndef HX_GEN_ENUM_TABLES\n')
fl.write('#define HX_GEN_ENUM_TABLES\n\n')
write_generation_header(fl)
fl.write('struct EnumItem { const char *name; int value; };\n')
fl.write('struct EnumTable { const char *name; const EnumItem *first; };\n\n')
fl.write('struct EnumItem {\n\tconst char *name;\n\tint value;\n};\n')
fl.write('struct EnumTable {\n\tconst char *name;\n\tconst EnumItem *first;\n};\n\n')
for e in enums:
e.write_c_header(fl)
fl.write('\n')

View File

@ -9,10 +9,17 @@
#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;
mouseYInvert = (config->Int("InvertMouseY")) ? true : false;
joystickEnabled = (m_config->Int("EnableJoystick")) ? true : false;
mouseYInvert = (m_config->Int("InvertMouseY")) ? true : false;
InitJoysticks();
}
@ -22,8 +29,8 @@ void Input::InitGame()
//reset input states
keyState.clear();
keyModState = 0;
std::fill(mouseButton, mouseButton + COUNTOF(mouseButton), 0);
std::fill(mouseMotion, mouseMotion + COUNTOF(mouseMotion), 0);
mouseButton.fill(0);
mouseMotion.fill(0);
for (std::map<SDL_JoystickID, JoystickState>::iterator stick = joysticks.begin(); stick != joysticks.end(); ++stick) {
JoystickState &state = stick->second;
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)
{
bool matched = false;
@ -95,7 +121,7 @@ KeyBindings::ActionBinding *Input::AddActionBinding(std::string id, BindingGroup
group->bindings[id] = BindingGroup::ENTRY_ACTION;
// 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);
return &(actionBindings[id] = binding);
@ -110,7 +136,7 @@ KeyBindings::AxisBinding *Input::AddAxisBinding(std::string id, BindingGroup *gr
group->bindings[id] = BindingGroup::ENTRY_AXIS;
// 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);
return &(axisBindings[id] = binding);
@ -120,30 +146,33 @@ void Input::HandleSDLEvent(SDL_Event &event)
{
switch (event.type) {
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;
onKeyPress.emit(&event.key.keysym);
break;
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;
onKeyRelease.emit(&event.key.keysym);
break;
case SDL_MOUSEBUTTONDOWN:
if (event.button.button < COUNTOF(mouseButton)) {
if (event.button.button < mouseButton.size()) {
mouseButton[event.button.button] = 1;
onMouseButtonDown.emit(event.button.button,
event.button.x, event.button.y);
}
break;
case SDL_MOUSEBUTTONUP:
if (event.button.button < COUNTOF(mouseButton)) {
if (event.button.button < mouseButton.size()) {
mouseButton[event.button.button] = 0;
onMouseButtonUp.emit(event.button.button,
event.button.x, event.button.y);
}
break;
case SDL_MOUSEWHEEL:
mouseWheel = event.wheel.y;
onMouseWheel.emit(event.wheel.y > 0); // true = up
break;
case SDL_MOUSEMOTION:

View File

@ -8,18 +8,16 @@
#include "utils.h"
#include <algorithm>
#include <array>
class GameConfig;
class IniConfig;
class Input {
// TODO: better decouple these two classes.
friend class Pi;
public:
Input() = default;
void Init(GameConfig *config);
Input(IniConfig *config);
void InitGame();
void HandleSDLEvent(SDL_Event &ev);
void NewFrame();
// The Page->Group->Binding system serves as a thin veneer for the UI to make
// sane reasonings about how to structure the Options dialog.
@ -94,7 +92,17 @@ public:
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 JoystickButtonState(int joystick, int button);
@ -132,9 +140,11 @@ public:
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,
// and passes all mouse inputs to the input system, regardless of whether
// ImGui is using them or not.
@ -154,10 +164,13 @@ public:
private:
void InitJoysticks();
std::map<SDL_Keycode, bool> keyState;
IniConfig *m_config;
std::map<SDL_Keycode, uint8_t> keyState;
int keyModState;
char mouseButton[6];
int mouseMotion[2];
std::array<char, 6> mouseButton;
std::array<int, 2> mouseMotion;
int mouseWheel;
bool m_capturingMouse;
bool joystickEnabled;

File diff suppressed because it is too large Load Diff

View File

@ -3,66 +3,114 @@
#ifndef MODELVIEWER_H
#define MODELVIEWER_H
#include "Input.h"
#include "NavLights.h"
#include "Shields.h"
#include "core/GuiApplication.h"
#include "graphics/Drawables.h"
#include "graphics/Renderer.h"
#include "graphics/Texture.h"
#include "libs.h"
#include "lua/LuaManager.h"
#include "pigui/PiGui.h"
#include "scenegraph/SceneGraph.h"
#include "ui/Context.h"
class ModelViewer {
public:
ModelViewer(Graphics::Renderer *r, LuaManager *l);
~ModelViewer();
#include <memory>
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:
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 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 ClearLog();
void ClearModel();
void CreateTestResources();
void DrawBackground();
void DrawGrid(const matrix4x4f &trans, float radius);
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 Screenshot();
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
struct Options {
bool attachGuns;
@ -77,17 +125,69 @@ private:
bool wireframe;
bool mouselookEnabled;
float gridInterval;
int lightPreset;
uint32_t lightPreset;
bool orthoView;
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_shieldIsHit;
bool m_settingColourSliders;
float m_shieldHitPan;
double m_frameTime;
Graphics::Renderer *m_renderer;
Graphics::Texture *m_decalTexture;
vector3f m_viewPos;
@ -95,41 +195,13 @@ private:
float m_rotX, m_rotY, m_zoom;
float m_baseDistance;
Random m_rng;
SceneGraph::Animation *m_currentAnimation;
SceneGraph::Model *m_model;
Options m_options;
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;
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;
Graphics::Drawables::Lines m_gridLines;

View File

@ -25,9 +25,11 @@
#include "NavLights.h"
#include "OS.h"
#include "core/GuiApplication.h"
#include "graphics/opengl/RendererGL.h"
#include "lua/Lua.h"
#include "lua/LuaConsole.h"
#include "lua/LuaEvent.h"
#include "lua/LuaPiGui.h"
#include "lua/LuaTimer.h"
#include "profiler/Profiler.h"
#include "sound/AmbientSounds.h"
@ -133,7 +135,7 @@ float Pi::amountOfBackgroundStarsDisplayed = 1.0f;
bool Pi::DrawGUI = true;
Graphics::Renderer *Pi::renderer;
RefCountedPtr<UI::Context> Pi::ui;
RefCountedPtr<PiGui> Pi::pigui;
PiGui::Instance *Pi::pigui = nullptr;
ModelCache *Pi::modelCache;
Intro *Pi::intro;
SDLGraphics *Pi::sdl;
@ -361,8 +363,7 @@ void Pi::App::Startup()
Pi::rng.IncRefCount(); // so nothing tries to free it
Pi::rng.seed(time(0));
Pi::input = new Input();
Pi::input->Init(Pi::config);
Pi::input = StartupInput(config);
Pi::input->onKeyPress.connect(sigc::ptr_fun(&Pi::HandleKeyDown));
// we can only do bindings once joysticks are initialised.
@ -371,6 +372,8 @@ void Pi::App::Startup()
RegisterInputBindings();
Pi::pigui = StartupPiGui();
// FIXME: move these into the appropriate class!
navTunnelDisplayed = (config->Int("DisplayNavTunnel")) ? true : false;
speedLinesDisplayed = (config->Int("SpeedLines")) ? true : false;
@ -438,9 +441,11 @@ void Pi::App::Shutdown()
BaseSphere::Uninit();
FaceParts::Uninit();
Graphics::Uninit();
Pi::pigui->Uninit();
PiGUI::Lua::Uninit();
ShutdownPiGui();
Pi::pigui = nullptr;
Pi::ui.Reset(0);
Pi::pigui.Reset(0);
Lua::UninitModules();
Lua::Uninit();
Gui::Uninit();
@ -452,6 +457,9 @@ void Pi::App::Shutdown()
ShutdownRenderer();
Pi::renderer = nullptr;
ShutdownInput();
Pi::input = nullptr;
delete Pi::config;
delete Pi::planner;
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
// 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?
// Ideally we can initialize the ImGui related parts of pigui as soon as the renderer is online,
// and then load all the lua-related state once Lua's registered and online...
Pi::pigui.Reset(new PiGui);
Pi::pigui->Init(Pi::renderer->GetSDLWindow());
// Loads just the PiGui class and PiGui-related modules
PiGUI::Lua::Init();
// Don't render the first frame, just make sure all of our fonts are loaded
Pi::pigui->NewFrame(Pi::renderer->GetSDLWindow());
Pi::pigui->RunHandler(0.01, "INIT");
Pi::pigui->NewFrame();
PiGUI::RunHandler(0.01, "INIT");
Pi::pigui->EndFrame();
AddStep("UI::AddContext", []() {
@ -608,8 +614,8 @@ void LoadStep::Update(float deltaTime)
Output("Loading [%02.f%%]: %s took %.2fms\n", progress * 100.,
loader.name.c_str(), timer.milliseconds());
Pi::pigui->NewFrame(Pi::renderer->GetSDLWindow());
Pi::pigui->RunHandler(progress, "INIT");
Pi::pigui->NewFrame();
PiGUI::RunHandler(progress, "INIT");
Pi::pigui->Render();
} else {
@ -643,43 +649,12 @@ void MainMenu::Start()
void MainMenu::Update(float deltaTime)
{
SDL_Event event;
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::GetApp()->HandleEvents();
Pi::intro->Draw(deltaTime);
Pi::pigui->NewFrame(Pi::renderer->GetSDLWindow());
Pi::pigui->RunHandler(deltaTime, "MAINMENU");
Pi::pigui->NewFrame();
PiGUI::RunHandler(deltaTime, "MAINMENU");
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()
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
// via keybinding. the console TextInput widget uses TEXTINPUT events. thus
// 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
// swallow the TEXTINPUT event this hack must remain until we have a
// 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;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
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 (skipTextInput && event.type == SDL_TEXTINPUT) {
skipTextInput = false;
return true;
}
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()
@ -1126,10 +1075,16 @@ void GameLoop::Update(float deltaTime)
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()
// 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
Pi::pigui->NewFrame(Pi::renderer->GetSDLWindow());
Pi::pigui->NewFrame();
if (Pi::game && !Pi::player->IsDead()) {
// FIXME: Always begin a camera frame because WorldSpaceToScreenSpace
@ -1137,7 +1092,7 @@ void GameLoop::Update(float deltaTime)
Pi::game->GetWorldView()->BeginCameraFrame();
// FIXME: major hack to work around the fact that the console is in newUI and not pigui
if (!Pi::IsConsoleActive())
Pi::pigui->RunHandler(deltaTime, "GAME");
PiGUI::RunHandler(deltaTime, "GAME");
Pi::game->GetWorldView()->EndCameraFrame();
}

View File

@ -16,6 +16,10 @@
#include <string>
#include <vector>
namespace PiGui {
class Instance;
} //namespace PiGui
class Game;
class GameConfig;
@ -26,7 +30,6 @@ class LuaNameGen;
class LuaTimer;
class ModelCache;
class ObjectViewerView;
class PiGui;
class Player;
class SystemPath;
class TransferPlanner;
@ -101,7 +104,7 @@ public:
void RunJobs();
void HandleRequests();
void HandleEvents();
bool HandleEvent(SDL_Event &ev) override;
private:
// 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
static RefCountedPtr<UI::Context> ui;
static RefCountedPtr<PiGui> pigui;
static PiGui::Instance *pigui;
static Random rng;
static int statSceneTris;

View File

@ -16,6 +16,7 @@
#include "Player.h"
#include "SpaceStation.h"
#include "Star.h"
#include "SystemView.h"
#include "collider/CollisionContact.h"
#include "collider/CollisionSpace.h"
#include "galaxy/Galaxy.h"
@ -427,7 +428,7 @@ static void RelocateStarportIfNecessary(SystemBody *sbody, Planet *planet, vecto
double bestVariation = 1e10; // any high value
matrix3x3d rotNotUnderwaterWithLeastVariation = rot;
vector3d posNotUnderwaterWithLeastVariation = pos;
const double heightVariationCheckThreshold = 0.008; // max variation to radius radius ratio to check for local slope, ganymede is around 0.01
const double heightVariationCheckThreshold = 0.008; // max variation to radius radius ratio to check for local slope, ganymede is around 0.01
const double terrainHeightVariation = planet->GetMaxFeatureRadius(); //in radii
//Output("%s: terrain height variation %f\n", sbody->name.c_str(), terrainHeightVariation);
@ -436,8 +437,8 @@ static void RelocateStarportIfNecessary(SystemBody *sbody, Planet *planet, vecto
// points must stay within max height variation to be accepted
// 1. delta should be chosen such that it a distance from the starport center that encloses landing pads for the largest starport
// 2. maxSlope should be set so maxHeightVariation is less than the height of the landing pads
const double delta = 20.0 / radius; // in radii
const double maxSlope = 0.2; // 0.0 to 1.0
const double delta = 20.0 / radius; // in radii
const double maxSlope = 0.2; // 0.0 to 1.0
const double maxHeightVariation = maxSlope * delta * radius; // in m
matrix3x3d rot_ = rot;
@ -1006,6 +1007,7 @@ void Space::UpdateBodies()
rmb->SetFrame(FrameId::Invalid);
for (Body *b : m_bodies)
b->NotifyRemoved(rmb);
if (Pi::GetView()) Pi::game->GetSystemView()->BodyInaccessible(rmb);
m_bodies.remove(rmb);
}
m_removeBodies.clear();
@ -1013,6 +1015,7 @@ void Space::UpdateBodies()
for (Body *killb : m_killBodies) {
for (Body *b : m_bodies)
b->NotifyRemoved(killb);
if (Pi::GetView()) Pi::game->GetSystemView()->BodyInaccessible(killb);
m_bodies.remove(killb);
delete killb;
}
@ -1030,7 +1033,7 @@ static void DebugDumpFrame(FrameId fId, bool details, unsigned int indent)
Frame *f = Frame::GetFrame(fId);
Frame *parent = Frame::GetFrame(f->GetParent());
Output("%.*s%2i) %p (%s)%s\n", indent, space, fId, static_cast<void *>(f), f->GetLabel().c_str(), f->IsRotFrame() ? " [rotating]" : " [non rotating]");
Output("%.*s%2i) %p (%s)%s\n", indent, space, static_cast<int>(fId), static_cast<void *>(f), f->GetLabel().c_str(), f->IsRotFrame() ? " [rotating]" : " [non rotating]");
if (f->GetParent().valid())
Output("%.*s parent %p (%s)\n", indent + 3, space, static_cast<void *>(parent), parent->GetLabel().c_str());
if (f->GetBody())

File diff suppressed because it is too large Load Diff

View File

@ -9,12 +9,15 @@
#include "graphics/Drawables.h"
#include "matrix4x4.h"
#include "vector3.h"
#include "enum_table.h"
#include "Frame.h"
class StarSystem;
class SystemBody;
class Orbit;
class Ship;
class Game;
class Body;
enum BurnDirection {
PROGRADE,
@ -40,6 +43,7 @@ enum ShowLagrange {
LAG_OFF
};
class TransferPlanner {
public:
TransferPlanner();
@ -51,7 +55,9 @@ public:
void IncreaseFactor(), ResetFactor(), DecreaseFactor();
void AddStartTime(double timeStep);
void ResetStartTime();
double GetFactor() const { return m_factor; }
void AddDv(BurnDirection d, double dv);
double GetDv(BurnDirection d);
void ResetDv(BurnDirection d);
void ResetDv();
std::string printDeltaTime();
@ -69,85 +75,130 @@ private:
double m_startTime;
};
class SystemView : public UIView {
struct Projectable
{
enum types { // <enum name=ProjectableTypes scope='Projectable' public>
NONE = 0, // empty projectable, don't try to get members
OBJECT = 1, // clickable space object, may be without phys.body (other starsystem)
L4 = 2,
L5 = 3,
APOAPSIS = 4,
PERIAPSIS = 5
} type;
enum bases { // <enum name=ProjectableBases scope='Projectable' public>
SYSTEMBODY = 0, // ref class SystemBody, may not have a physical body
BODY = 1, // generic body
SHIP = 2,
PLAYER = 3, // player's ship
PLANNER = 4 // player's ship planned by transfer planner, refers to player's object
} base;
union{
const Body* body;
const SystemBody* sbody;
} ref;
vector3d screenpos; // x,y - screen coordinate, z - in NDC
Projectable(const types t, const bases b, const Body* obj) : type(t), base(b)
{
ref.body = obj;
}
Projectable(const types t, const bases b, const SystemBody* obj) : type(t), base(b)
{
ref.sbody = obj;
}
Projectable() : type(NONE) {}
};
class SystemView : public UIView, public DeleteEmitter {
public:
SystemView(Game *game);
virtual ~SystemView();
virtual void Update();
virtual void Draw3D();
virtual void OnSwitchTo() { Update(); Draw3D(); }
Projectable* GetSelectedObject();
void SetSelectedObject(Projectable::types type, Projectable::bases base, SystemBody *sb);
void SetSelectedObject(Projectable::types type, Projectable::bases base, Body *b);
TransferPlanner* GetTransferPlanner() const { return m_planner; }
double GetOrbitPlannerStartTime() const { return m_planner->GetStartTime(); }
double GetOrbitPlannerTime() const { return m_time; }
void AccelerateTime(float step);
void SetRealTime();
std::vector<Projectable> GetProjected() const { return m_projected; }
void BodyInaccessible(Body *b);
void SetVisibility(std::string param);
void SetZoomMode(bool enable);
void SetRotateMode(bool enable);
double ProjectedSize(double size, vector3d pos);
// all used colors. defined in system-view-ui.lua
enum ColorIndex { // <enum name=SystemViewColorIndex scope='SystemView' public>
GRID = 0,
SYSTEMBODY = 1,
SYSTEMBODY_ORBIT = 2,
PLAYER_ORBIT = 3,
PLANNER_ORBIT = 4,
SELECTED_SHIP_ORBIT = 5,
SHIP_ORBIT = 6
};
Color svColor[7];
void SetColor(ColorIndex color_index, Color* color_value) { svColor[color_index] = *color_value; }
private:
bool m_rotateWithMouseButton = false;
bool m_rotateView = false;
bool m_zoomView = false;
std::vector<Projectable> m_projected;
static const double PICK_OBJECT_RECT_SIZE;
static const Uint16 N_VERTICES_MAX;
void PutOrbit(const Orbit *orb, const vector3d &offset, const Color &color, const double planetRadius = 0.0, const bool showLagrange = false);
const float CAMERA_FOV = 50.f;
const float CAMERA_FOV_RADIANS = CAMERA_FOV / 57.295779f;
matrix4x4f m_cameraSpace;
template <typename RefType>
void PutOrbit(Projectable::bases base, RefType *ref, const Orbit *orb, const vector3d &offset, const Color &color, const double planetRadius = 0.0, const bool showLagrange = false);
void PutBody(const SystemBody *b, const vector3d &offset, const matrix4x4f &trans);
void PutLabel(const SystemBody *b, const vector3d &offset);
void PutSelectionBox(const SystemBody *b, const vector3d &rootPos, const Color &col);
void PutSelectionBox(const vector3d &worldPos, const Color &col);
void GetTransformTo(const SystemBody *b, vector3d &pos);
void OnClickObject(const SystemBody *b);
void OnClickLagrange();
void OnClickAccel(float step);
void OnClickRealt();
void OnIncreaseFactorButtonClick(void), OnResetFactorButtonClick(void), OnDecreaseFactorButtonClick(void);
void OnIncreaseStartTimeButtonClick(void), OnResetStartTimeButtonClick(void), OnDecreaseStartTimeButtonClick(void);
void OnToggleShipsButtonClick(void);
void OnToggleGridButtonClick(void);
void OnToggleL4L5ButtonClick(Gui::MultiStateImageButton *);
void GetTransformTo(Projectable &p, vector3d &pos);
void ResetViewpoint();
void MouseWheel(bool up);
void RefreshShips(void);
void DrawShips(const double t, const vector3d &offset);
void PrepareGrid();
void DrawGrid();
void LabelShip(Ship *s, const vector3d &offset);
void OnClickShip(Ship *s);
template <typename T>
void AddProjected(Projectable::types type, Projectable::bases base, T *ref, vector3d &pos);
template <typename T>
void AddNotProjected(Projectable::types type, Projectable::bases base, T *ref, const vector3d &worldscaledpos);
void CalculateShipPositionAtTime(const Ship *s, Orbit o, double t, vector3d &pos);
void CalculateFramePositionAtTime(FrameId frameId, double t, vector3d &pos);
double GetOrbitTime(double t, const SystemBody* b);
double GetOrbitTime(double t, const Body* b);
Game *m_game;
RefCountedPtr<StarSystem> m_system;
const SystemBody *m_selectedObject;
Projectable m_selectedObject;
std::vector<SystemBody *> m_displayed_sbody;
bool m_unexplored;
ShowLagrange m_showL4L5;
TransferPlanner *m_planner;
std::list<std::pair<Ship *, Orbit>> m_contacts;
Gui::LabelSet *m_shipLabels;
ShipDrawing m_shipDrawing;
GridDrawing m_gridDrawing;
int m_grid_lines;
float m_rot_x, m_rot_z;
float m_rot_x_to, m_rot_z_to;
float m_rot_x, m_rot_y;
float m_rot_x_to, m_rot_y_to;
float m_zoom, m_zoomTo;
int m_animateTransition;
vector3d m_trans;
vector3d m_transTo;
double m_time;
bool m_realtime;
double m_timeStep;
Gui::ImageButton *m_zoomInButton;
Gui::ImageButton *m_zoomOutButton;
Gui::ImageButton *m_toggleShipsButton;
Gui::ImageButton *m_toggleGridButton;
Gui::ImageButton *m_ResetOrientButton;
Gui::MultiStateImageButton *m_toggleL4L5Button;
Gui::ImageButton *m_plannerIncreaseStartTimeButton, *m_plannerResetStartTimeButton, *m_plannerDecreaseStartTimeButton;
Gui::ImageButton *m_plannerIncreaseFactorButton, *m_plannerResetFactorButton, *m_plannerDecreaseFactorButton;
Gui::ImageButton *m_plannerAddProgradeVelButton;
Gui::ImageButton *m_plannerAddRetrogradeVelButton;
Gui::ImageButton *m_plannerAddNormalVelButton;
Gui::ImageButton *m_plannerAddAntiNormalVelButton;
Gui::ImageButton *m_plannerAddRadiallyInVelButton;
Gui::ImageButton *m_plannerAddRadiallyOutVelButton;
Gui::ImageButton *m_plannerZeroProgradeVelButton, *m_plannerZeroNormalVelButton, *m_plannerZeroRadialVelButton;
Gui::Label *m_timePoint;
Gui::Label *m_infoLabel;
Gui::Label *m_infoText;
Gui::Label *m_plannerFactorText, *m_plannerStartTimeText, *m_plannerProgradeDvText, *m_plannerNormalDvText, *m_plannerRadialDvText;
Gui::LabelSet *m_objectLabels;
sigc::connection m_onMouseWheelCon;
std::unique_ptr<Graphics::Drawables::Disk> m_bodyIcon;
std::unique_ptr<Gui::TexturedQuad> m_l4Icon;
std::unique_ptr<Gui::TexturedQuad> m_l5Icon;
std::unique_ptr<Gui::TexturedQuad> m_periapsisIcon;
std::unique_ptr<Gui::TexturedQuad> m_apoapsisIcon;
Graphics::RenderState *m_lineState;
Graphics::Drawables::Lines m_orbits;
Graphics::Drawables::Lines m_selectBox;
@ -157,7 +208,6 @@ private:
std::unique_ptr<Graphics::VertexArray> m_lineVerts;
Graphics::Drawables::Lines m_lines;
};
#endif /* _SYSTEMVIEW_H */

View File

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

View File

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

View File

@ -12,6 +12,7 @@
#include "graphics/RenderTarget.h"
#include "graphics/Renderer.h"
#include "graphics/Texture.h"
#include "pigui/PiGui.h"
#include "utils.h"
#include "versioningInfo.h"
@ -31,7 +32,8 @@ void GuiApplication::BeginFrame()
void GuiApplication::DrawRenderTarget()
{
#if RTT
m_renderer->BeginFrame();
m_renderer->SetRenderTarget(nullptr);
m_renderer->ClearScreen();
m_renderer->SetViewport(0, 0, Graphics::GetScreenWidth(), Graphics::GetScreenHeight());
m_renderer->SetTransform(matrix4x4f::Identity());
@ -60,9 +62,9 @@ void GuiApplication::DrawRenderTarget()
void GuiApplication::EndFrame()
{
#if RTT
m_renderer->SetRenderTarget(nullptr);
DrawRenderTarget();
#endif
m_renderer->EndFrame();
m_renderer->SwapBuffers();
}
@ -100,7 +102,55 @@ Graphics::RenderTarget *GuiApplication::CreateRenderTarget(const Graphics::Setti
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()
// Initialize SDL
@ -145,3 +195,28 @@ void GuiApplication::ShutdownRenderer()
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
#include "Application.h"
#include "GameConfig.h"
#include "Input.h"
#include "RefCounted.h"
#include "SDL_events.h"
#include "pigui/PiGui.h"
#include "graphics/RenderState.h"
#include "graphics/RenderTarget.h"
#include "graphics/Renderer.h"
class IniConfig;
class GuiApplication : public Application {
public:
GuiApplication(std::string title) :
Application(), m_applicationTitle(title)
{}
protected:
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
// framebuffer
void DrawRenderTarget();
// TODO: unify config handling, possibly make the config an Application member
// 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
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.
// If you override BeginFrame, make sure you call this.
void BeginFrame() override;
@ -38,9 +57,21 @@ protected:
// If you override EndFrame, make sure you call this.
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:
Graphics::RenderTarget *CreateRenderTarget(const Graphics::Settings &settings);
RefCountedPtr<PiGui::Instance> m_pigui;
std::unique_ptr<Input> m_input;
std::string m_applicationTitle;
std::unique_ptr<Graphics::Renderer> m_renderer;

View File

@ -10,15 +10,17 @@
#include "Ship.h"
#include "ShipAICmd.h"
#include "ShipType.h"
#include "SystemView.h"
#include "galaxy/Economy.h"
#include "galaxy/Polit.h"
#include "galaxy/StarSystem.h"
#include "galaxy/SystemBody.h"
#include "gameui/Face.h"
#include "lua/LuaEngine.h"
#include "lua/LuaFileSystem.h"
#include "pigui/Face.h"
#include "scenegraph/Model.h"
#include "ship/PlayerShipController.h"
#include "ship/Propulsion.h"
#include "ship/ShipController.h"
#include "ui/Align.h"
#include "ui/Animation.h"
#include "ui/Event.h"
@ -37,21 +39,6 @@ const struct EnumItem ENUM_ShipAIError[] = {
{ 0, 0 },
};
const struct EnumItem ENUM_DetailLevel[] = {
{ "VERY_LOW", int(LuaEngine::DETAIL_VERY_LOW) },
{ "LOW", int(LuaEngine::DETAIL_LOW) },
{ "MEDIUM", int(LuaEngine::DETAIL_MEDIUM) },
{ "HIGH", int(LuaEngine::DETAIL_HIGH) },
{ "VERY_HIGH", int(LuaEngine::DETAIL_VERY_HIGH) },
{ 0, 0 },
};
const struct EnumItem ENUM_FileSystemRoot[] = {
{ "USER", int(LuaFileSystem::ROOT_USER) },
{ "DATA", int(LuaFileSystem::ROOT_DATA) },
{ 0, 0 },
};
const struct EnumItem ENUM_PhysicsObjectType[] = {
{ "BODY", int(Object::BODY) },
{ "MODELBODY", int(Object::MODELBODY) },
@ -65,52 +52,6 @@ const struct EnumItem ENUM_PhysicsObjectType[] = {
{ 0, 0 },
};
const struct EnumItem ENUM_PolitEcon[] = {
{ "NONE", int(Polit::ECON_NONE) },
{ "VERY_CAPITALIST", int(Polit::ECON_VERY_CAPITALIST) },
{ "CAPITALIST", int(Polit::ECON_CAPITALIST) },
{ "MIXED", int(Polit::ECON_MIXED) },
{ "PLANNED", int(Polit::ECON_PLANNED) },
{ 0, 0 },
};
const struct EnumItem ENUM_PolitGovType[] = {
{ "NONE", int(Polit::GOV_NONE) },
{ "EARTHCOLONIAL", int(Polit::GOV_EARTHCOLONIAL) },
{ "EARTHDEMOC", int(Polit::GOV_EARTHDEMOC) },
{ "EMPIRERULE", int(Polit::GOV_EMPIRERULE) },
{ "CISLIBDEM", int(Polit::GOV_CISLIBDEM) },
{ "CISSOCDEM", int(Polit::GOV_CISSOCDEM) },
{ "LIBDEM", int(Polit::GOV_LIBDEM) },
{ "CORPORATE", int(Polit::GOV_CORPORATE) },
{ "SOCDEM", int(Polit::GOV_SOCDEM) },
{ "EARTHMILDICT", int(Polit::GOV_EARTHMILDICT) },
{ "MILDICT1", int(Polit::GOV_MILDICT1) },
{ "MILDICT2", int(Polit::GOV_MILDICT2) },
{ "EMPIREMILDICT", int(Polit::GOV_EMPIREMILDICT) },
{ "COMMUNIST", int(Polit::GOV_COMMUNIST) },
{ "PLUTOCRATIC", int(Polit::GOV_PLUTOCRATIC) },
{ "DISORDER", int(Polit::GOV_DISORDER) },
{ 0, 0 },
};
const struct EnumItem ENUM_ShipTypeThruster[] = {
{ "REVERSE", int(Thruster::THRUSTER_REVERSE) },
{ "FORWARD", int(Thruster::THRUSTER_FORWARD) },
{ "UP", int(Thruster::THRUSTER_UP) },
{ "DOWN", int(Thruster::THRUSTER_DOWN) },
{ "LEFT", int(Thruster::THRUSTER_LEFT) },
{ "RIGHT", int(Thruster::THRUSTER_RIGHT) },
{ 0, 0 },
};
const struct EnumItem ENUM_PropulsionFuelStatus[] = {
{ "OK", int(Propulsion::FUEL_OK) },
{ "WARNING", int(Propulsion::FUEL_WARNING) },
{ "EMPTY", int(Propulsion::FUEL_EMPTY) },
{ 0, 0 },
};
const struct EnumItem ENUM_ShipFlightState[] = {
{ "FLYING", int(Ship::FLYING) },
{ "DOCKING", int(Ship::DOCKING) },
@ -138,6 +79,7 @@ const struct EnumItem ENUM_ShipAlertStatus[] = {
{ "NONE", int(Ship::ALERT_NONE) },
{ "SHIP_NEARBY", int(Ship::ALERT_SHIP_NEARBY) },
{ "SHIP_FIRING", int(Ship::ALERT_SHIP_FIRING) },
{ "MISSILE_DETECTED", int(Ship::ALERT_MISSILE_DETECTED) },
{ 0, 0 },
};
@ -153,20 +95,6 @@ const struct EnumItem ENUM_ShipAICmdName[] = {
{ 0, 0 },
};
const struct EnumItem ENUM_ShipControllerFlightControlState[] = {
{ "CONTROL_MANUAL", int(FlightControlState::CONTROL_MANUAL) },
{ "CONTROL_FIXSPEED", int(FlightControlState::CONTROL_FIXSPEED) },
{ "CONTROL_FIXHEADING_FORWARD", int(FlightControlState::CONTROL_FIXHEADING_FORWARD) },
{ "CONTROL_FIXHEADING_BACKWARD", int(FlightControlState::CONTROL_FIXHEADING_BACKWARD) },
{ "CONTROL_FIXHEADING_NORMAL", int(FlightControlState::CONTROL_FIXHEADING_NORMAL) },
{ "CONTROL_FIXHEADING_ANTINORMAL", int(FlightControlState::CONTROL_FIXHEADING_ANTINORMAL) },
{ "CONTROL_FIXHEADING_RADIALLY_INWARD", int(FlightControlState::CONTROL_FIXHEADING_RADIALLY_INWARD) },
{ "CONTROL_FIXHEADING_RADIALLY_OUTWARD", int(FlightControlState::CONTROL_FIXHEADING_RADIALLY_OUTWARD) },
{ "CONTROL_FIXHEADING_KILLROT", int(FlightControlState::CONTROL_FIXHEADING_KILLROT) },
{ "CONTROL_AUTOPILOT", int(FlightControlState::CONTROL_AUTOPILOT) },
{ 0, 0 },
};
const struct EnumItem ENUM_DualLaserOrientation[] = {
{ "HORIZONTAL", int(ShipType::DUAL_LASERS_HORIZONTAL) },
{ "VERTICAL", int(ShipType::DUAL_LASERS_VERTICAL) },
@ -181,6 +109,36 @@ const struct EnumItem ENUM_ShipTypeTag[] = {
{ 0, 0 },
};
const struct EnumItem ENUM_ProjectableTypes[] = {
{ "NONE", int(Projectable::NONE) },
{ "OBJECT", int(Projectable::OBJECT) },
{ "L4", int(Projectable::L4) },
{ "L5", int(Projectable::L5) },
{ "APOAPSIS", int(Projectable::APOAPSIS) },
{ "PERIAPSIS", int(Projectable::PERIAPSIS) },
{ 0, 0 },
};
const struct EnumItem ENUM_ProjectableBases[] = {
{ "SYSTEMBODY", int(Projectable::SYSTEMBODY) },
{ "BODY", int(Projectable::BODY) },
{ "SHIP", int(Projectable::SHIP) },
{ "PLAYER", int(Projectable::PLAYER) },
{ "PLANNER", int(Projectable::PLANNER) },
{ 0, 0 },
};
const struct EnumItem ENUM_SystemViewColorIndex[] = {
{ "GRID", int(SystemView::GRID) },
{ "SYSTEMBODY", int(SystemView::SYSTEMBODY) },
{ "SYSTEMBODY_ORBIT", int(SystemView::SYSTEMBODY_ORBIT) },
{ "PLAYER_ORBIT", int(SystemView::PLAYER_ORBIT) },
{ "PLANNER_ORBIT", int(SystemView::PLANNER_ORBIT) },
{ "SELECTED_SHIP_ORBIT", int(SystemView::SELECTED_SHIP_ORBIT) },
{ "SHIP_ORBIT", int(SystemView::SHIP_ORBIT) },
{ 0, 0 },
};
const struct EnumItem ENUM_EconType[] = {
{ "MINING", int(GalacticEconomy::ECON_MINING) },
{ "AGRICULTURE", int(GalacticEconomy::ECON_AGRICULTURE) },
@ -223,6 +181,35 @@ const struct EnumItem ENUM_CommodityType[] = {
{ 0, 0 },
};
const struct EnumItem ENUM_PolitEcon[] = {
{ "NONE", int(Polit::ECON_NONE) },
{ "VERY_CAPITALIST", int(Polit::ECON_VERY_CAPITALIST) },
{ "CAPITALIST", int(Polit::ECON_CAPITALIST) },
{ "MIXED", int(Polit::ECON_MIXED) },
{ "PLANNED", int(Polit::ECON_PLANNED) },
{ 0, 0 },
};
const struct EnumItem ENUM_PolitGovType[] = {
{ "NONE", int(Polit::GOV_NONE) },
{ "EARTHCOLONIAL", int(Polit::GOV_EARTHCOLONIAL) },
{ "EARTHDEMOC", int(Polit::GOV_EARTHDEMOC) },
{ "EMPIRERULE", int(Polit::GOV_EMPIRERULE) },
{ "CISLIBDEM", int(Polit::GOV_CISLIBDEM) },
{ "CISSOCDEM", int(Polit::GOV_CISSOCDEM) },
{ "LIBDEM", int(Polit::GOV_LIBDEM) },
{ "CORPORATE", int(Polit::GOV_CORPORATE) },
{ "SOCDEM", int(Polit::GOV_SOCDEM) },
{ "EARTHMILDICT", int(Polit::GOV_EARTHMILDICT) },
{ "MILDICT1", int(Polit::GOV_MILDICT1) },
{ "MILDICT2", int(Polit::GOV_MILDICT2) },
{ "EMPIREMILDICT", int(Polit::GOV_EMPIREMILDICT) },
{ "COMMUNIST", int(Polit::GOV_COMMUNIST) },
{ "PLUTOCRATIC", int(Polit::GOV_PLUTOCRATIC) },
{ "DISORDER", int(Polit::GOV_DISORDER) },
{ 0, 0 },
};
const struct EnumItem ENUM_BodyType[] = {
{ "GRAVPOINT", int(SystemBody::TYPE_GRAVPOINT) },
{ "BROWN_DWARF", int(SystemBody::TYPE_BROWN_DWARF) },
@ -286,6 +273,29 @@ const struct EnumItem ENUM_GameUIFaceFlags[] = {
{ 0, 0 },
};
const struct EnumItem ENUM_DetailLevel[] = {
{ "VERY_LOW", int(LuaEngine::DETAIL_VERY_LOW) },
{ "LOW", int(LuaEngine::DETAIL_LOW) },
{ "MEDIUM", int(LuaEngine::DETAIL_MEDIUM) },
{ "HIGH", int(LuaEngine::DETAIL_HIGH) },
{ "VERY_HIGH", int(LuaEngine::DETAIL_VERY_HIGH) },
{ 0, 0 },
};
const struct EnumItem ENUM_FileSystemRoot[] = {
{ "USER", int(LuaFileSystem::ROOT_USER) },
{ "DATA", int(LuaFileSystem::ROOT_DATA) },
{ 0, 0 },
};
const struct EnumItem ENUM_PiGUIFaceFlags[] = {
{ "RAND", int(PiGUI::Face::RAND) },
{ "MALE", int(PiGUI::Face::MALE) },
{ "FEMALE", int(PiGUI::Face::FEMALE) },
{ "ARMOUR", int(PiGUI::Face::ARMOUR) },
{ 0, 0 },
};
const struct EnumItem ENUM_ModelDebugFlags[] = {
{ "NONE", int(SceneGraph::Model::DEBUG_NONE) },
{ "BBOX", int(SceneGraph::Model::DEBUG_BBOX) },
@ -296,6 +306,37 @@ const struct EnumItem ENUM_ModelDebugFlags[] = {
{ 0, 0 },
};
const struct EnumItem ENUM_ShipTypeThruster[] = {
{ "REVERSE", int(Thruster::THRUSTER_REVERSE) },
{ "FORWARD", int(Thruster::THRUSTER_FORWARD) },
{ "UP", int(Thruster::THRUSTER_UP) },
{ "DOWN", int(Thruster::THRUSTER_DOWN) },
{ "LEFT", int(Thruster::THRUSTER_LEFT) },
{ "RIGHT", int(Thruster::THRUSTER_RIGHT) },
{ 0, 0 },
};
const struct EnumItem ENUM_PropulsionFuelStatus[] = {
{ "OK", int(Propulsion::FUEL_OK) },
{ "WARNING", int(Propulsion::FUEL_WARNING) },
{ "EMPTY", int(Propulsion::FUEL_EMPTY) },
{ 0, 0 },
};
const struct EnumItem ENUM_ShipControllerFlightControlState[] = {
{ "CONTROL_MANUAL", int(FlightControlState::CONTROL_MANUAL) },
{ "CONTROL_FIXSPEED", int(FlightControlState::CONTROL_FIXSPEED) },
{ "CONTROL_FIXHEADING_FORWARD", int(FlightControlState::CONTROL_FIXHEADING_FORWARD) },
{ "CONTROL_FIXHEADING_BACKWARD", int(FlightControlState::CONTROL_FIXHEADING_BACKWARD) },
{ "CONTROL_FIXHEADING_NORMAL", int(FlightControlState::CONTROL_FIXHEADING_NORMAL) },
{ "CONTROL_FIXHEADING_ANTINORMAL", int(FlightControlState::CONTROL_FIXHEADING_ANTINORMAL) },
{ "CONTROL_FIXHEADING_RADIALLY_INWARD", int(FlightControlState::CONTROL_FIXHEADING_RADIALLY_INWARD) },
{ "CONTROL_FIXHEADING_RADIALLY_OUTWARD", int(FlightControlState::CONTROL_FIXHEADING_RADIALLY_OUTWARD) },
{ "CONTROL_FIXHEADING_KILLROT", int(FlightControlState::CONTROL_FIXHEADING_KILLROT) },
{ "CONTROL_AUTOPILOT", int(FlightControlState::CONTROL_AUTOPILOT) },
{ 0, 0 },
};
const struct EnumItem ENUM_UIAlignDirection[] = {
{ "TOP_LEFT", int(UI::Align::TOP_LEFT) },
{ "TOP", int(UI::Align::TOP) },
@ -479,26 +520,30 @@ const struct EnumItem ENUM_UIFont[] = {
const struct EnumTable ENUM_TABLES[] = {
{ "ShipAIError", ENUM_ShipAIError },
{ "DetailLevel", ENUM_DetailLevel },
{ "FileSystemRoot", ENUM_FileSystemRoot },
{ "PhysicsObjectType", ENUM_PhysicsObjectType },
{ "PolitEcon", ENUM_PolitEcon },
{ "PolitGovType", ENUM_PolitGovType },
{ "ShipTypeThruster", ENUM_ShipTypeThruster },
{ "PropulsionFuelStatus", ENUM_PropulsionFuelStatus },
{ "ShipFlightState", ENUM_ShipFlightState },
{ "ShipJumpStatus", ENUM_ShipJumpStatus },
{ "ShipAlertStatus", ENUM_ShipAlertStatus },
{ "ShipAICmdName", ENUM_ShipAICmdName },
{ "ShipControllerFlightControlState", ENUM_ShipControllerFlightControlState },
{ "DualLaserOrientation", ENUM_DualLaserOrientation },
{ "ShipTypeTag", ENUM_ShipTypeTag },
{ "ProjectableTypes", ENUM_ProjectableTypes },
{ "ProjectableBases", ENUM_ProjectableBases },
{ "SystemViewColorIndex", ENUM_SystemViewColorIndex },
{ "EconType", ENUM_EconType },
{ "CommodityType", ENUM_CommodityType },
{ "PolitEcon", ENUM_PolitEcon },
{ "PolitGovType", ENUM_PolitGovType },
{ "BodyType", ENUM_BodyType },
{ "BodySuperType", ENUM_BodySuperType },
{ "GameUIFaceFlags", ENUM_GameUIFaceFlags },
{ "DetailLevel", ENUM_DetailLevel },
{ "FileSystemRoot", ENUM_FileSystemRoot },
{ "PiGUIFaceFlags", ENUM_PiGUIFaceFlags },
{ "ModelDebugFlags", ENUM_ModelDebugFlags },
{ "ShipTypeThruster", ENUM_ShipTypeThruster },
{ "PropulsionFuelStatus", ENUM_PropulsionFuelStatus },
{ "ShipControllerFlightControlState", ENUM_ShipControllerFlightControlState },
{ "UIAlignDirection", ENUM_UIAlignDirection },
{ "UIAnimationType", ENUM_UIAnimationType },
{ "UIAnimationEasing", ENUM_UIAnimationEasing },
@ -523,26 +568,30 @@ const struct EnumTable ENUM_TABLES[] = {
const struct EnumTable ENUM_TABLES_PUBLIC[] = {
{ "ShipAIError", ENUM_ShipAIError },
{ "DetailLevel", ENUM_DetailLevel },
{ "FileSystemRoot", ENUM_FileSystemRoot },
{ "PhysicsObjectType", ENUM_PhysicsObjectType },
{ "PolitEcon", ENUM_PolitEcon },
{ "PolitGovType", ENUM_PolitGovType },
{ "ShipTypeThruster", ENUM_ShipTypeThruster },
{ "PropulsionFuelStatus", ENUM_PropulsionFuelStatus },
{ "ShipFlightState", ENUM_ShipFlightState },
{ "ShipJumpStatus", ENUM_ShipJumpStatus },
{ "ShipAlertStatus", ENUM_ShipAlertStatus },
{ "ShipAICmdName", ENUM_ShipAICmdName },
{ "ShipControllerFlightControlState", ENUM_ShipControllerFlightControlState },
{ "DualLaserOrientation", ENUM_DualLaserOrientation },
{ "ShipTypeTag", ENUM_ShipTypeTag },
{ "ProjectableTypes", ENUM_ProjectableTypes },
{ "ProjectableBases", ENUM_ProjectableBases },
{ "SystemViewColorIndex", ENUM_SystemViewColorIndex },
{ "EconType", ENUM_EconType },
{ "CommodityType", ENUM_CommodityType },
{ "PolitEcon", ENUM_PolitEcon },
{ "PolitGovType", ENUM_PolitGovType },
{ "BodyType", ENUM_BodyType },
{ "BodySuperType", ENUM_BodySuperType },
{ "GameUIFaceFlags", ENUM_GameUIFaceFlags },
{ "DetailLevel", ENUM_DetailLevel },
{ "FileSystemRoot", ENUM_FileSystemRoot },
{ "PiGUIFaceFlags", ENUM_PiGUIFaceFlags },
{ "ModelDebugFlags", ENUM_ModelDebugFlags },
{ "ShipTypeThruster", ENUM_ShipTypeThruster },
{ "PropulsionFuelStatus", ENUM_PropulsionFuelStatus },
{ "ShipControllerFlightControlState", ENUM_ShipControllerFlightControlState },
{ "UIAlignDirection", ENUM_UIAlignDirection },
{ "UIAnimationType", ENUM_UIAnimationType },
{ "UIAnimationEasing", ENUM_UIAnimationEasing },

View File

@ -17,26 +17,30 @@ struct EnumTable {
};
extern const struct EnumItem ENUM_ShipAIError[];
extern const struct EnumItem ENUM_DetailLevel[];
extern const struct EnumItem ENUM_FileSystemRoot[];
extern const struct EnumItem ENUM_PhysicsObjectType[];
extern const struct EnumItem ENUM_PolitEcon[];
extern const struct EnumItem ENUM_PolitGovType[];
extern const struct EnumItem ENUM_ShipTypeThruster[];
extern const struct EnumItem ENUM_PropulsionFuelStatus[];
extern const struct EnumItem ENUM_ShipFlightState[];
extern const struct EnumItem ENUM_ShipJumpStatus[];
extern const struct EnumItem ENUM_ShipAlertStatus[];
extern const struct EnumItem ENUM_ShipAICmdName[];
extern const struct EnumItem ENUM_ShipControllerFlightControlState[];
extern const struct EnumItem ENUM_DualLaserOrientation[];
extern const struct EnumItem ENUM_ShipTypeTag[];
extern const struct EnumItem ENUM_ProjectableTypes[];
extern const struct EnumItem ENUM_ProjectableBases[];
extern const struct EnumItem ENUM_SystemViewColorIndex[];
extern const struct EnumItem ENUM_EconType[];
extern const struct EnumItem ENUM_CommodityType[];
extern const struct EnumItem ENUM_PolitEcon[];
extern const struct EnumItem ENUM_PolitGovType[];
extern const struct EnumItem ENUM_BodyType[];
extern const struct EnumItem ENUM_BodySuperType[];
extern const struct EnumItem ENUM_GameUIFaceFlags[];
extern const struct EnumItem ENUM_DetailLevel[];
extern const struct EnumItem ENUM_FileSystemRoot[];
extern const struct EnumItem ENUM_PiGUIFaceFlags[];
extern const struct EnumItem ENUM_ModelDebugFlags[];
extern const struct EnumItem ENUM_ShipTypeThruster[];
extern const struct EnumItem ENUM_PropulsionFuelStatus[];
extern const struct EnumItem ENUM_ShipControllerFlightControlState[];
extern const struct EnumItem ENUM_UIAlignDirection[];
extern const struct EnumItem ENUM_UIAnimationType[];
extern const struct EnumItem ENUM_UIAnimationEasing[];

View File

@ -31,6 +31,7 @@
#include "Ship.h"
#include "SpaceStation.h"
#include "Star.h"
#include "SystemView.h"
#include "galaxy/StarSystem.h"
#include "gameui/Lua.h"
@ -71,6 +72,7 @@ namespace Lua {
LuaObject<StarSystem>::RegisterClass();
LuaObject<SystemPath>::RegisterClass();
LuaObject<SystemView>::RegisterClass();
LuaObject<SystemBody>::RegisterClass();
LuaObject<Random>::RegisterClass();
LuaObject<Faction>::RegisterClass();
@ -107,9 +109,6 @@ namespace Lua {
GameUI::Lua::Init();
SceneGraph::Lua::Init();
LuaObject<PiGui>::RegisterClass();
PiGUI::Lua::Init();
// XXX load everything. for now, just modules
lua_State *l = Lua::manager->GetLuaState();
pi_lua_dofile(l, "libs/autoload.lua");

View File

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

View File

@ -11,6 +11,7 @@
#include "Intro.h"
#include "KeyBindings.h"
#include "Lang.h"
#include "LuaColor.h"
#include "LuaConstants.h"
#include "LuaObject.h"
#include "LuaPiGui.h"
@ -116,7 +117,7 @@ static int l_engine_attr_ui(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;
}
@ -336,6 +337,14 @@ static int l_engine_set_fullscreen(lua_State *l)
return 0;
}
static int l_engine_get_enum_value(lua_State *l)
{
auto enum_name = LuaPull<const char *>(l, 1);
auto enum_tag = LuaPull<const char *>(l, 2);
LuaPush<int>(l, EnumStrings::GetValue(enum_name, enum_tag));
return 1;
}
static int l_engine_get_disable_screenshot_info(lua_State *l)
{
LuaPush<bool>(l, Pi::config->Int("DisableScreenshotInfo") != 0);
@ -785,7 +794,7 @@ static int l_engine_world_space_to_screen_space(lua_State *l)
{
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<vector2d>(l, res._screenPosition);
@ -1077,7 +1086,7 @@ static int l_engine_get_sector_map_factions(lua_State *l)
lua_setfield(l, -2, "faction");
lua_pushboolean(l, hidden.count(f) == 0);
lua_setfield(l, -2, "visible"); // inner table
lua_settable(l, -3); // outer table
lua_settable(l, -3); // outer table
}
return 1;
}
@ -1195,16 +1204,14 @@ void LuaEngine::Register()
{ "SectorMapMoveRouteItemUp", l_engine_sector_map_move_route_item_up },
{ "SectorMapMoveRouteItemDown", l_engine_sector_map_move_route_item_down },
{ "SectorMapRemoveRouteItem", l_engine_sector_map_remove_route_item },
{ "SectorMapClearRoute", l_engine_sector_map_clear_route },
{ "SectorMapAddToRoute", l_engine_sector_map_add_to_route },
{ "SearchNearbyStarSystemsByName", l_engine_search_nearby_star_systems_by_name },
{ "ShipSpaceToScreenSpace", l_engine_ship_space_to_screen_space },
{ "CameraSpaceToScreenSpace", l_engine_camera_space_to_screen_space },
{ "WorldSpaceToScreenSpace", l_engine_world_space_to_screen_space },
{ "WorldSpaceToShipSpace", l_engine_world_space_to_ship_space },
{ "GetEnumValue", l_engine_get_enum_value },
{ 0, 0 }
};

View File

@ -336,6 +336,28 @@ static int l_game_attr_system(lua_State *l)
return 1;
}
/*
* Attribute: systemView
*
* The <SystemView> object for the system map view class
*
* Availability:
*
* February 2020
*
* Status:
*
* experiment
*/
static int l_game_attr_systemview(lua_State *l)
{
if (!Pi::game)
lua_pushnil(l);
else
LuaObject<SystemView>::PushToLua(Pi::game->GetSystemView());
return 1;
}
/*
* Attribute: time
*
@ -613,7 +635,7 @@ static int l_game_get_hyperspace_travelled_percentage(lua_State *l)
static int l_game_get_parts_from_date_time(lua_State *l)
{
float time = LuaPull<float>(l, 1);
double time = LuaPull<double>(l, 1);
Time::DateTime t(time);
int year, month, day, hour, minute, second;
t.GetDateParts(&year, &month, &day);
@ -662,6 +684,7 @@ void LuaGame::Register()
static const luaL_Reg l_attrs[] = {
{ "player", l_game_attr_player },
{ "system", l_game_attr_system },
{ "systemView", l_game_attr_systemview },
{ "time", l_game_attr_time },
{ "paused", l_game_attr_paused },
{ 0, 0 }

View File

@ -18,6 +18,7 @@
#include "graphics/Graphics.h"
#include "pigui/LuaFlags.h"
#include "pigui/PiGui.h"
#include "pigui/PiGuiLua.h"
#include "ship/PlayerShipController.h"
#include "sound/Sound.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);
}
int pushOnScreenPositionDirection(lua_State *l, vector3d position)
int PiGUI::pushOnScreenPositionDirection(lua_State *l, vector3d position)
{
PROFILE_SCOPED()
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)
{
PROFILE_SCOPED()
PiGui *pigui = LuaObject<PiGui>::CheckFromLua(1);
PiGui::Instance *pigui = LuaObject<PiGui::Instance>::CheckFromLua(1);
std::string fontname = LuaPull<std::string>(l, 2);
int size = LuaPull<int>(l, 3);
ImFont *font = pigui->GetFont(fontname, size);
@ -1560,7 +1561,7 @@ static int l_pigui_get_mouse_clicked_pos(lua_State *l)
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()
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 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) {
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 {
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()
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 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) {
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 {
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();
@ -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 ship_max_distance = LuaPull<double>(l, 2);
TSS_vector filtered;
PiGUI::TSS_vector filtered;
filtered.reserve(Pi::game->GetSpace()->GetNumBodies());
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::SHIP &&
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;
filtered.emplace_back(res);
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 *setspeed_target = Pi::game->GetPlayer()->GetSetSpeedTarget();
for (TScreenSpace &obj : filtered) {
for (PiGUI::TScreenSpace &obj : filtered) {
bool inserted = false;
// 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_mainBody = obj._body;
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_screenCoords = obj._screenPosition;
}
@ -1797,7 +1798,7 @@ static int l_pigui_get_projected_bodies_grouped(lua_State *l)
for (GroupInfo &group : groups) {
std::sort(begin(group.m_bodies), end(group.m_bodies),
[](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)
{
PROFILE_SCOPED()
TSS_vector filtered;
PiGUI::TSS_vector filtered;
filtered.reserve(Pi::game->GetSpace()->GetNumBodies());
for (Body *body : Pi::game->GetSpace()->GetBodies()) {
if (body == Pi::game->GetPlayer()) 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;
filtered.emplace_back(res);
filtered.back()._body = body;
}
LuaTable result(l, 0, filtered.size());
for (TScreenSpace &res : filtered) {
for (PiGUI::TScreenSpace &res : filtered) {
LuaTable object(l, 0, 3);
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)
{
PROFILE_SCOPED()
PiGui *pigui = LuaObject<PiGui>::CheckFromLua(1);
pigui->GetHandlers().PushCopyToStack();
PiGui::Instance *pigui = LuaObject<PiGui::Instance>::CheckFromLua(1);
PiGUI::GetHandlers().PushCopyToStack();
return 1;
}
static int l_attr_keys(lua_State *l)
{
PROFILE_SCOPED()
PiGui *pigui = LuaObject<PiGui>::CheckFromLua(1);
pigui->GetKeys().PushCopyToStack();
PiGui::Instance *pigui = LuaObject<PiGui::Instance>::CheckFromLua(1);
PiGUI::GetKeys().PushCopyToStack();
return 1;
}
static int l_attr_screen_width(lua_State *l)
{
PROFILE_SCOPED()
// PiGui *pigui = LuaObject<PiGui>::CheckFromLua(1);
// PiGui::Instance *pigui = LuaObject<PiGui::Instance>::CheckFromLua(1);
LuaPush<int>(l, Graphics::GetScreenWidth());
return 1;
}
@ -1993,7 +1994,7 @@ static int l_attr_key_alt(lua_State *l)
static int l_attr_screen_height(lua_State *l)
{
PROFILE_SCOPED()
// PiGui *pigui = LuaObject<PiGui>::CheckFromLua(1);
// PiGui::Instance *pigui = LuaObject<PiGui::Instance>::CheckFromLua(1);
LuaPush<int>(l, Graphics::GetScreenHeight());
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)
{
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);
int width = LuaPull<int>(l, 3);
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);
lua_pushlightuserdata(l, id);
return 1;
@ -2467,11 +2468,21 @@ static int l_pigui_push_text_wrap_pos(lua_State *l)
return 0;
}
template <>
const char *LuaObject<PiGui>::s_type = "PiGui";
void PiGUI::RunHandler(double delta, std::string handler)
{
PROFILE_SCOPED()
ScopedTable t(GetHandlers());
if (t.Get<bool>(handler)) {
t.Call<bool>(handler, delta);
Pi::renderer->CheckRenderErrors(__FUNCTION__, __LINE__);
}
}
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[] = {
{ "Begin", l_pigui_begin },

View File

@ -3,6 +3,7 @@
#ifndef _LUAPIGUI_H
#define _LUAPIGUI_H
#include "LuaObject.h"
#include "LuaPushPull.h"
@ -11,19 +12,25 @@
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
{
TScreenSpace(const bool onScreen, const vector2d &screenPos, const vector3d &direction) : _onScreen(onScreen), _screenPosition(screenPos), _direction(direction) {}
bool _onScreen;
vector2d _screenPosition;
vector3d _direction;
Body *_body;
};
struct TScreenSpace {
TScreenSpace(const bool onScreen, const vector2d &screenPos, const vector3d &direction) :
_onScreen(onScreen), _screenPosition(screenPos), _direction(direction) {}
bool _onScreen;
vector2d _screenPosition;
vector3d _direction;
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

View File

@ -7,6 +7,7 @@
#include "LuaObject.h"
#include "LuaUtils.h"
#include "Pi.h"
#include "SectorView.h"
#include "Space.h"
#include "galaxy/Galaxy.h"
#include "galaxy/StarSystem.h"
@ -655,6 +656,27 @@ static int l_sbody_attr_children(lua_State *l)
return 1;
}
static int l_sbody_attr_is_moon(lua_State *l)
{
LuaPush<bool>(l, LuaObject<SystemBody>::CheckFromLua(1)->IsMoon());
return 1;
}
static int l_sbody_attr_physics_body(lua_State *l)
{
SystemBody *b = LuaObject<SystemBody>::CheckFromLua(1);
Body *physbody = nullptr;
SystemPath headpath = Pi::game->GetSectorView()->GetSelected().SystemOnly();
SystemPath gamepath = Pi::game->GetSpace()->GetStarSystem()->GetPath();
if (headpath == gamepath) {
RefCountedPtr<StarSystem> ss = Pi::game->GetGalaxy()->GetStarSystem(headpath);
SystemPath path = ss->GetPathOf(b);
physbody = Pi::game->GetSpace()->FindBodyForPath(&path);
}
LuaObject<Body>::PushToLua(physbody);
return 1;
}
template <>
const char *LuaObject<SystemBody>::s_type = "SystemBody";
@ -694,6 +716,8 @@ void LuaObject<SystemBody>::RegisterClass()
{ "path", l_sbody_attr_path },
{ "body", l_sbody_attr_body },
{ "children", l_sbody_attr_children },
{ "isMoon", l_sbody_attr_is_moon },
{ "physicsBody", l_sbody_attr_physics_body },
{ 0, 0 }
};

436
src/lua/LuaSystemView.cpp Normal file
View File

@ -0,0 +1,436 @@
// Copyright © 2008-2020 Pioneer Developers. See AUTHORS.txt for details
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
#include "EnumStrings.h"
#include "Game.h"
#include "LuaColor.h"
#include "LuaObject.h"
#include "LuaVector.h"
#include "LuaVector2.h"
#include "Pi.h"
#include "Player.h"
#include "SystemView.h"
#include "graphics/Graphics.h"
LuaTable projectable_to_lua_row(Projectable &p, lua_State *l)
{
LuaTable proj_table(l, 0, 3);
proj_table.Set("type", int(p.type));
if (p.type == Projectable::NONE) return proj_table;
proj_table.Set("base", int(p.base));
if (p.base == Projectable::SYSTEMBODY)
proj_table.Set("ref", const_cast<SystemBody *>(p.ref.sbody));
else
proj_table.Set("ref", const_cast<Body *>(p.ref.body));
return proj_table;
}
static int l_systemview_set_color(lua_State *l)
{
SystemView *sv = LuaObject<SystemView>::CheckFromLua(1);
auto color_index = static_cast<SystemView::ColorIndex>(EnumStrings::GetValue("SystemViewColorIndex", LuaPull<const char *>(l, 2)));
auto color_value = LuaColor::CheckFromLua(l, 3);
sv->SetColor(color_index, color_value);
return 0;
}
static int l_systemview_set_selected_object(lua_State *l)
{
SystemView *sv = LuaObject<SystemView>::CheckFromLua(1);
Projectable::types type = static_cast<Projectable::types>(luaL_checkinteger(l, 2));
Projectable::bases base = static_cast<Projectable::bases>(luaL_checkinteger(l, 3));
if (base == Projectable::SYSTEMBODY)
sv->SetSelectedObject(type, base, LuaObject<SystemBody>::CheckFromLua(4));
else
sv->SetSelectedObject(type, base, LuaObject<Body>::CheckFromLua(4));
return 0;
}
bool too_near(const vector3d &a, const vector3d &b, const vector2d &gain)
{
return std::abs(a.x - b.x) < gain.x && std::abs(a.y - b.y) < gain.y
// we dont want to group objects that simply overlap and are located at different distances
// therefore, depth is also taken into account, we have z_NDC (normalized device coordinates)
// in order to make a strict translation of delta z_NDC into delta "pixels", one also needs to know
// the z coordinates in the camera space. I plan to implement this later
// at the moment I just picked up a number that works well (6.0)
&& std::abs(a.z - b.z) * Graphics::GetScreenWidth() * 6.0 < gain.x;
};
/*
* Method: GetProjectedGrouped
*
* Get a table of projected point objects from the SystemView class, having
* previously grouped them.
*
* In the process of rendering the current frame in a SystemView class, an
* array of visible point objects (centers of planets, ships, special points
* of orbits) is created. This function creates a lua table based on this
* array, after grouping them according to certain criteria.
*
* Resulting table:
*
* {
* *** ORBIT ICONS (apoapsis, periapsis) ***
*
* { screenCoordinates: vector2d
* mainObject (see struct Projectable in SystemView.h)
* { type: Projectable::types
* base: Projectable::bases
* ref: SystemBody* or Body*
* }
* objects (if multiple only, main object is duplicated there)
* {
* {type, base, ref}
* {type, base, ref}
* ...
* }
* hasPlayer: boolean
* hasCombatTarget: boolean
* hasNavTarget: boolean
* }
* { screencoordinates, mainobject, ... }
* { screencoordinates, mainobject, ... }
* ...
*
* *** LAGRANGE ICONS ***
* { screenCoordinates, mainObject, ... }
* { screenCoordinates, mainObject, ... }
* ...
*
* *** BODY ICONS (stars, planets, ships) ***
* { screenCoordinates, mainObject, ... }
* { screenCoordinates, mainObject, ... }
* ...
*
* }
*
*
* Availability:
*
* 2020-03
*
* Status:
*
* experimental
*/
static int l_systemview_get_projected_grouped(lua_State *l)
{
SystemView *sv = LuaObject<SystemView>::CheckFromLua(1);
const vector2d gap = LuaPull<vector2d>(l, 2);
std::vector<Projectable> projected = sv->GetProjected();
// types of special object
const int NOT_SPECIAL = -1;
const int SO_PLAYER = 0;
const int SO_COMBATTARGET = 1;
const int SO_NAVTARGET = 2;
const int NUMBER_OF_SO_TYPES = 3;
Projectable *special_object[NUMBER_OF_SO_TYPES] = { nullptr, nullptr, nullptr };
const char *special_object_lua_name[NUMBER_OF_SO_TYPES] = { "hasPlayer", "hasCombatTarget", "hasNavTarget" };
struct GroupInfo {
Projectable m_mainObject;
std::vector<Projectable> m_objects;
bool m_hasSpecialObject[NUMBER_OF_SO_TYPES] = { false, false, false };
GroupInfo(Projectable p) :
m_mainObject(p)
{
m_objects.push_back(p);
}
};
// use forward_list to concatenate
std::vector<GroupInfo> bodyIcons;
std::vector<GroupInfo> orbitIcons;
std::vector<GroupInfo> lagrangeIcons;
const Body *nav_target = Pi::game->GetPlayer()->GetNavTarget();
const Body *combat_target = Pi::game->GetPlayer()->GetCombatTarget();
const Body *player_body = static_cast<Body *>(Pi::game->GetPlayer());
for (Projectable &p : projected) {
// --- icons---
if (p.type == Projectable::APOAPSIS || p.type == Projectable::PERIAPSIS)
// orbit icons - just take all
orbitIcons.push_back(GroupInfo(p));
else if (p.type == Projectable::L4 || p.type == Projectable::L5) {
// lagrange icons - take only those who don't intersect with other lagrange icons
bool intersect = false;
for (GroupInfo &group : lagrangeIcons) {
if (too_near(p.screenpos, group.m_mainObject.screenpos, gap)) {
intersect = true;
break;
}
}
if (!intersect) lagrangeIcons.push_back(GroupInfo(p));
} else {
// --- real objects ---
int object_type = NOT_SPECIAL;
bool inserted = false;
// check if p is special object, and remember it's type
if (p.base == Projectable::SYSTEMBODY) {
if (nav_target && p.ref.sbody == nav_target->GetSystemBody()) object_type = SO_NAVTARGET;
//system body can't be a combat target and can't be a player
} else {
if (nav_target && p.ref.body == nav_target)
object_type = SO_NAVTARGET;
else if (combat_target && p.ref.body == combat_target)
object_type = SO_COMBATTARGET;
else if (p.base == Projectable::PLAYER)
object_type = SO_PLAYER;
}
for (GroupInfo &group : bodyIcons) {
if (too_near(p.screenpos, group.m_mainObject.screenpos, gap)) {
// object inside group boundaries
// special object is not added
// special objects must be added to nearest group, this group could be not nearest
if (object_type == NOT_SPECIAL) {
group.m_objects.push_back(p);
} else
// remember it separately
special_object[object_type] = &p;
inserted = true;
break;
}
}
if (!inserted) {
// object is not inside a group
// special object is nearest to itself, so we remember it as a new group
// create new group
GroupInfo newgroup(p);
if (object_type != NOT_SPECIAL) newgroup.m_hasSpecialObject[object_type] = true;
bodyIcons.push_back(std::move(newgroup));
}
}
} // for (Projectable &p : projected)
// adding overlapping special object to nearest group
for (int object_type = 0; object_type < NUMBER_OF_SO_TYPES; object_type++)
if (special_object[object_type]) {
std::vector<GroupInfo *> touchedGroups;
// first we get all groups, touched this object
for (GroupInfo &group : bodyIcons)
if (too_near(special_object[object_type]->screenpos, group.m_mainObject.screenpos, gap))
// object inside group boundaries: remember this group
touchedGroups.push_back(&group);
//now select the nearest group (if have)
if (touchedGroups.size()) {
GroupInfo *nearest;
double min_length = 1e64;
for (GroupInfo *&g : touchedGroups) {
double this_length = (g->m_mainObject.screenpos - special_object[object_type]->screenpos).Length();
if (this_length < min_length) {
nearest = g;
min_length = this_length;
}
}
nearest->m_hasSpecialObject[object_type] = true;
nearest->m_objects.push_back(*special_object[object_type]);
} else {
//don't touching any group, create a new one
GroupInfo newgroup(*special_object[object_type]);
newgroup.m_hasSpecialObject[object_type] = true;
bodyIcons.push_back(std::move(newgroup));
}
}
//no need to sort, because the bodies are so recorded in good order
//because they are written recursively starting from the root
//body of the system, and ships go after the system bodies
LuaTable result(l, orbitIcons.size() + lagrangeIcons.size() + bodyIcons.size(), 0);
int index = 1;
//the sooner is displayed, the more in the background
// so it goes orbitIcons->lagrangeIcons->bodies
for (auto groups : { orbitIcons, lagrangeIcons, bodyIcons }) {
for (GroupInfo &group : groups) {
LuaTable info_table(l, 0, 6);
info_table.Set("screenCoordinates", group.m_mainObject.screenpos);
info_table.Set("mainObject", projectable_to_lua_row(group.m_mainObject, l));
lua_pop(l, 1);
if (group.m_objects.size() > 1) {
LuaTable objects_table(l, group.m_objects.size(), 0);
int objects_table_index = 1;
for (Projectable &pj : group.m_objects) {
objects_table.Set(objects_table_index++, projectable_to_lua_row(pj, l));
lua_pop(l, 1);
}
info_table.Set("objects", objects_table);
lua_pop(l, 1);
}
for (int object_type = 0; object_type < NUMBER_OF_SO_TYPES; object_type++)
info_table.Set(special_object_lua_name[object_type], group.m_hasSpecialObject[object_type]);
result.Set(index++, info_table);
lua_pop(l, 1);
}
}
LuaPush(l, result);
return 1;
}
static int l_systemview_get_selected_object(lua_State *l)
{
SystemView *sv = LuaObject<SystemView>::CheckFromLua(1);
Projectable *p = sv->GetSelectedObject();
LuaPush(l, projectable_to_lua_row(*p, l));
return 1;
}
static int l_systemview_set_visibility(lua_State *l)
{
SystemView *sv = LuaObject<SystemView>::CheckFromLua(1);
sv->SetVisibility(LuaPull<std::string>(l, 2));
return 0;
}
static int l_systemview_get_orbit_planner_start_time(lua_State *l)
{
SystemView *sv = LuaObject<SystemView>::CheckFromLua(1);
double t = sv->GetOrbitPlannerStartTime();
if (std::fabs(t) < 1.)
lua_pushnil(l);
else
LuaPush<double>(l, t);
return 1;
}
static int l_systemview_get_orbit_planner_time(lua_State *l)
{
SystemView *sv = LuaObject<SystemView>::CheckFromLua(1);
double t = sv->GetOrbitPlannerTime();
LuaPush<double>(l, t);
return 1;
}
static int l_systemview_accelerate_time(lua_State *l)
{
SystemView *sv = LuaObject<SystemView>::CheckFromLua(1);
if (lua_isnil(l, 2))
sv->SetRealTime();
else {
double step = LuaPull<double>(l, 2);
sv->AccelerateTime(step);
}
return 0;
}
static int l_systemview_set_rotate_mode(lua_State *l)
{
SystemView *sv = LuaObject<SystemView>::CheckFromLua(1);
bool b = LuaPull<bool>(l, 2);
sv->SetRotateMode(b);
return 0;
}
static int l_systemview_set_zoom_mode(lua_State *l)
{
SystemView *sv = LuaObject<SystemView>::CheckFromLua(1);
bool b = LuaPull<bool>(l, 2);
sv->SetZoomMode(b);
return 0;
}
static int l_systemview_transfer_planner_get(lua_State *l)
{
SystemView *sv = LuaObject<SystemView>::CheckFromLua(1);
std::string key = LuaPull<std::string>(l, 2);
TransferPlanner *planner = sv->GetTransferPlanner();
if (key == "prograde") {
LuaPush<double>(l, planner->GetDv(BurnDirection::PROGRADE));
} else if (key == "normal") {
LuaPush<double>(l, planner->GetDv(BurnDirection::NORMAL));
} else if (key == "radial") {
LuaPush<double>(l, planner->GetDv(BurnDirection::RADIAL));
} else if (key == "starttime") {
LuaPush<double>(l, planner->GetStartTime());
} else if (key == "factor") {
LuaPush<double>(l, planner->GetFactor() * 10); // factor is shown as "x 10"
} else {
Warning("Unknown transfer planner key %s\n", key.c_str());
lua_pushnil(l);
}
return 1;
}
static int l_systemview_transfer_planner_add(lua_State *l)
{
SystemView *sv = LuaObject<SystemView>::CheckFromLua(1);
std::string key = LuaPull<std::string>(l, 2);
double delta = LuaPull<double>(l, 3);
TransferPlanner *planner = sv->GetTransferPlanner();
if (key == "prograde") {
planner->AddDv(BurnDirection::PROGRADE, delta);
} else if (key == "normal") {
planner->AddDv(BurnDirection::NORMAL, delta);
} else if (key == "radial") {
planner->AddDv(BurnDirection::RADIAL, delta);
} else if (key == "starttime") {
planner->AddStartTime(delta);
} else if (key == "factor") {
if (delta > 0)
planner->IncreaseFactor();
else
planner->DecreaseFactor();
} else {
Warning("Unknown transfer planner key %s\n", key.c_str());
}
return 0;
}
static int l_systemview_transfer_planner_reset(lua_State *l)
{
SystemView *sv = LuaObject<SystemView>::CheckFromLua(1);
std::string key = LuaPull<std::string>(l, 2);
TransferPlanner *planner = sv->GetTransferPlanner();
if (key == "prograde") {
planner->ResetDv(BurnDirection::PROGRADE);
} else if (key == "normal") {
planner->ResetDv(BurnDirection::NORMAL);
} else if (key == "radial") {
planner->ResetDv(BurnDirection::RADIAL);
} else if (key == "starttime") {
planner->ResetStartTime();
} else if (key == "factor") {
planner->ResetFactor();
} else {
Warning("Unknown transfer planner key %s\n", key.c_str());
}
return 0;
}
template <>
const char *LuaObject<SystemView>::s_type = "SystemView";
template <>
void LuaObject<SystemView>::RegisterClass()
{
static const luaL_Reg l_methods[] = {
{ "GetProjectedGrouped", l_systemview_get_projected_grouped },
{ "GetSelectedObject", l_systemview_get_selected_object },
{ "GetOrbitPlannerStartTime", l_systemview_get_orbit_planner_start_time },
{ "GetOrbitPlannerTime", l_systemview_get_orbit_planner_time },
{ "AccelerateTime", l_systemview_accelerate_time },
{ "SetSelectedObject", l_systemview_set_selected_object },
{ "SetVisibility", l_systemview_set_visibility },
{ "SetColor", l_systemview_set_color },
{ "SetRotateMode", l_systemview_set_rotate_mode },
{ "SetZoomMode" , l_systemview_set_zoom_mode },
{ "TransferPlannerAdd", l_systemview_transfer_planner_add },
{ "TransferPlannerGet", l_systemview_transfer_planner_get },
{ "TransferPlannerReset", l_systemview_transfer_planner_reset },
{ NULL, NULL }
};
LuaObjectBase::CreateClass(s_type, nullptr, l_methods, 0, 0);
}

View File

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

View File

@ -5,8 +5,8 @@
#define PIGUI_FACE_H
#include "FaceParts.h"
#include "SmartPtr.h"
#include "Pi.h"
#include "SmartPtr.h"
#include "graphics/Drawables.h"
#include "graphics/Texture.h"
@ -14,12 +14,12 @@ namespace PiGUI {
class Face : public RefCounted {
public:
Face(FaceParts::FaceDescriptor& face, Uint32 seed = 0);
Face(FaceParts::FaceDescriptor &face, Uint32 seed = 0);
Uint32 GetTextureId();
vector2f GetTextureSize();
enum Flags { // <enum scope='GameUI::Face' name=GameUIFaceFlags public>
enum Flags { // <enum scope='PiGUI::Face' name=PiGUIFaceFlags public>
RAND = 0,
MALE = (1 << 0),
FEMALE = (1 << 1),

View File

@ -5,6 +5,9 @@
#include "Input.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 "imgui/imgui.h"
@ -12,9 +15,6 @@
#define IMGUI_IMPL_OPENGL_LOADER_GLEW 1
#include "imgui/examples/imgui_impl_opengl3.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 <stdio.h>
@ -24,38 +24,36 @@
#define NANOSVGRAST_IMPLEMENTATION
#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 (key & ~SDLK_SCANCODE_MASK) | 0x100;
}*/
return key;
return m_svg_textures;
}
static std::vector<std::pair<std::string, int>> keycodes = {
{ "left", to_keycode(SDLK_LEFT) },
{ "right", to_keycode(SDLK_RIGHT) },
{ "up", to_keycode(SDLK_UP) },
{ "down", to_keycode(SDLK_DOWN) },
{ "escape", to_keycode(SDLK_ESCAPE) },
{ "f1", to_keycode(SDLK_F1) },
{ "f2", to_keycode(SDLK_F2) },
{ "f3", to_keycode(SDLK_F3) },
{ "f4", to_keycode(SDLK_F4) },
{ "f5", to_keycode(SDLK_F5) },
{ "f6", to_keycode(SDLK_F6) },
{ "f7", to_keycode(SDLK_F7) },
{ "f8", to_keycode(SDLK_F8) },
{ "f9", to_keycode(SDLK_F9) },
{ "f10", to_keycode(SDLK_F10) },
{ "f11", to_keycode(SDLK_F11) },
{ "f12", to_keycode(SDLK_F12) },
{ "tab", to_keycode(SDLK_TAB) },
};
static ImTextureID makeTexture(Graphics::Renderer *renderer, unsigned char *pixels, int width, int height)
{
PROFILE_SCOPED()
// this is not very pretty code
// 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 = 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);
PiGui::GetSVGTextures().push_back(pTex); // store for cleanup later
return reinterpret_cast<ImTextureID>(uintptr_t(pTex->GetTextureID()));
}
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()
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);
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()
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;
}
void PiGui::AddGlyph(ImFont *font, unsigned short glyph)
void Instance::AddGlyph(ImFont *font, unsigned short glyph)
{
PROFILE_SCOPED()
// 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);
}
ImFont *PiGui::AddFont(const std::string &name, int size)
ImFont *Instance::AddFont(const std::string &name, int size)
{
PROFILE_SCOPED()
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)];
}
void PiGui::RefreshFontsTexture()
void Instance::RefreshFontsTexture()
{
PROFILE_SCOPED()
// 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.
}
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()
m_handlers.Unref();
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);
}
m_renderer = renderer;
IMGUI_CHECKVERSION();
ImGui::CreateContext();
@ -211,14 +233,18 @@ void PiGui::Init(SDL_Window *window)
// TODO: FIXME before upgrading! The sdl_gl_context parameter is currently
// unused, but that is slated to change very soon.
// We will need to fill this with a valid pointer to the OpenGL context.
ImGui_ImplSDL2_InitForOpenGL(window, NULL);
switch (Pi::renderer->GetRendererType()) {
ImGui_ImplSDL2_InitForOpenGL(m_renderer->GetSDLWindow(), NULL);
switch (m_renderer->GetRendererType()) {
default:
case Graphics::RENDERER_DUMMY:
Error("RENDERER_DUMMY is not a valid renderer, aborting.");
return;
case Graphics::RENDERER_OPENGL_3x:
#ifdef __APPLE__
ImGui_ImplOpenGL3_Init("#version 140");
#else
ImGui_ImplOpenGL3_Init();
#endif
break;
}
@ -237,156 +263,18 @@ void PiGui::Init(SDL_Window *window)
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)
{
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)
bool Instance::ProcessEvent(SDL_Event *event)
{
PROFILE_SCOPED()
ImGui_ImplSDL2_ProcessEvent(event);
return false;
}
void *PiGui::makeTexture(unsigned char *pixels, int width, int height)
{
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)
void Instance::NewFrame()
{
PROFILE_SCOPED()
// 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);
}
switch (Pi::renderer->GetRendererType()) {
switch (m_renderer->GetRendererType()) {
default:
case Graphics::RENDERER_DUMMY:
Error("RENDERER_DUMMY is not a valid renderer, aborting.");
@ -395,24 +283,14 @@ void PiGui::NewFrame(SDL_Window *window)
ImGui_ImplOpenGL3_NewFrame();
break;
}
ImGui_ImplSDL2_NewFrame(window);
ImGui_ImplSDL2_NewFrame(m_renderer->GetSDLWindow());
ImGui::NewFrame();
Pi::renderer->CheckRenderErrors(__FUNCTION__, __LINE__);
m_renderer->CheckRenderErrors(__FUNCTION__, __LINE__);
ImGui::SetMouseCursor(ImGuiMouseCursor_Arrow);
}
void PiGui::RunHandler(double delta, std::string handler)
{
PROFILE_SCOPED()
ScopedTable t(m_handlers);
if (t.Get<bool>(handler)) {
t.Call<bool>(handler, delta);
Pi::renderer->CheckRenderErrors(__FUNCTION__, __LINE__);
}
}
void PiGui::EndFrame()
void Instance::EndFrame()
{
PROFILE_SCOPED()
@ -439,14 +317,14 @@ void PiGui::EndFrame()
}
}
void PiGui::Render()
void Instance::Render()
{
PROFILE_SCOPED()
EndFrame();
ImGui::Render();
switch (Pi::renderer->GetRendererType()) {
switch (m_renderer->GetRendererType()) {
default:
case Graphics::RENDERER_DUMMY:
return;
@ -456,7 +334,7 @@ void PiGui::Render()
}
}
void PiGui::ClearFonts()
void Instance::ClearFonts()
{
PROFILE_SCOPED()
ImGuiIO &io = ImGui::GetIO();
@ -466,7 +344,7 @@ void PiGui::ClearFonts()
io.Fonts->Clear();
}
void PiGui::BakeFont(PiFont &font)
void Instance::BakeFont(PiFont &font)
{
PROFILE_SCOPED()
ImGuiIO &io = ImGui::GetIO();
@ -507,7 +385,7 @@ void PiGui::BakeFont(PiFont &font)
imfont->MissingGlyphs.clear();
}
void PiGui::BakeFonts()
void Instance::BakeFonts()
{
PROFILE_SCOPED()
// Output("Baking fonts\n");
@ -534,199 +412,14 @@ void PiGui::BakeFonts()
RefreshFontsTexture();
}
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;
}
void PiGui::Cleanup()
void Instance::Uninit()
{
PROFILE_SCOPED()
for (auto tex : m_svg_textures) {
delete tex;
}
switch (Pi::renderer->GetRendererType()) {
switch (m_renderer->GetRendererType()) {
default:
case Graphics::RENDERER_DUMMY:
return;
@ -739,25 +432,9 @@ void PiGui::Cleanup()
ImGui::DestroyContext();
}
PiGui::PiGui() :
m_should_bake_fonts(true)
{
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);
};
//
// PiGui::PiFace
//
const bool PiFace::isValidGlyph(unsigned short glyph) const
{

View File

@ -1,140 +1,149 @@
// Copyright © 2008-2020 Pioneer Developers. See AUTHORS.txt for details
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
#pragma once
#include "FileSystem.h"
#include "RefCounted.h"
#include "graphics/opengl/RendererGL.h"
#include "imgui/imgui.h"
#include "lua/Lua.h"
#include "lua/LuaRef.h"
#include "lua/LuaTable.h"
#include "utils.h"
#include <unordered_set>
class PiFace {
friend class PiGui; // need acces to some private data
std::string m_ttfname; // only the ttf name, it is automatically sought in data/fonts/
float m_sizefactor; // the requested pixelsize is multiplied by this factor
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;
namespace Graphics {
class Texture;
class Renderer;
} // namespace Graphics
public:
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;
};
namespace PiGui {
class PiFont {
std::string m_name;
std::vector<PiFace> m_faces;
int m_pixelsize;
class PiFace {
public:
using UsedRange = std::pair<uint16_t, uint16_t>;
PiFace(const std::string &ttfname, float sizefactor) :
m_ttfname(ttfname),
m_sizefactor(sizefactor) {}
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());
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<UsedRange> &used_ranges() const { return m_used_ranges; }
const bool isValidGlyph(unsigned short glyph) const;
void addGlyph(unsigned short glyph);
void sortUsedRanges() const;
private:
friend class Instance; // need access to some private data
std::string m_ttfname; // only the ttf name, it is automatically sought in data/fonts/
float m_sizefactor; // the requested pixelsize is multiplied by this factor
std::unordered_set<unsigned short> m_invalid_glyphs;
mutable std::vector<UsedRange> m_used_ranges;
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. */
class PiGui : public RefCounted {
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;
private:
std::string m_name;
std::vector<PiFace> m_faces;
int m_pixelsize;
};
std::map<std::string, PiFont> m_font_definitions;
/* Class to wrap ImGui. */
class Instance : public RefCounted {
public:
Instance();
void BakeFonts();
void BakeFont(PiFont &font);
void AddFontDefinition(const PiFont &font) { m_font_definitions[font.name()] = font; }
void ClearFonts();
void Init(Graphics::Renderer *renderer);
void Uninit();
public:
PiGui();
// Call at the start of every frame. Calls ImGui::NewFrame() internally.
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 NewFrame(SDL_Window *window);
void AddGlyph(ImFont *font, unsigned short glyph);
// Call at the end of a frame that you're not going to render the results of
void EndFrame();
bool ProcessEvent(SDL_Event *event);
// Calls ImGui::EndFrame() internally and does book-keeping before rendering.
void Render();
void RefreshFontsTexture();
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()
{
Cleanup();
m_handlers.Unref();
m_keys.Unref();
}
ImFont *AddFont(const std::string &name, int size);
std::map<std::string, PiFont> m_font_definitions;
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);
static bool WantCaptureMouse()
inline bool WantCaptureMouse()
{
return ImGui::GetIO().WantCaptureMouse;
}
static bool WantCaptureKeyboard()
inline bool 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();
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);
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);
std::vector<Graphics::Texture *> &GetSVGTextures();
ImTextureID RenderSVG(Graphics::Renderer *renderer, std::string svgFilename, int width, int height);
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);
private:
LuaRef m_handlers;
LuaRef m_keys;
static std::vector<Graphics::Texture *> m_svg_textures;
};
} //namespace PiGui

View File

@ -5,17 +5,65 @@
#include "Face.h"
#include "Image.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 Lua {
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::Face>::RegisterClass();
LuaObject<PiGUI::ModelSpinner>::RegisterClass();
RegisterSandbox();
}
void Uninit()
{
m_handlers.Unref();
m_keys.Unref();
}
} // namespace Lua
LuaRef GetHandlers() { return m_handlers; }
LuaRef GetKeys() { return m_keys; }
} // namespace PiGUI

View File

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

View File

@ -132,7 +132,7 @@ luaL_Reg l_stack_functions[] = {
{ NULL, NULL }
};
void PiGUI::RegisterSandbox()
void PiGUI::Lua::RegisterSandbox()
{
lua_State *L = ::Lua::manager->GetLuaState();
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\LuaFace.cpp" />
<ClCompile Include="..\..\src\pigui\PiGuiSandbox.cpp" />
<ClCompile Include="..\..\src\pigui\Widgets.cpp" />
<ClCompile Include="..\..\src\Plane.cpp" />
<ClCompile Include="..\..\src\Planet.cpp" />
<ClCompile Include="..\..\src\Player.cpp" />
@ -679,10 +680,12 @@
<ClInclude Include="..\..\src\Pi.h" />
<ClInclude Include="..\..\src\pigui\Face.h" />
<ClInclude Include="..\..\src\pigui\Image.h" />
<ClInclude Include="..\..\src\pigui\LuaFlags.h" />
<ClInclude Include="..\..\src\pigui\ModelSpinner.h" />
<ClInclude Include="..\..\src\pigui\PerfInfo.h" />
<ClInclude Include="..\..\src\pigui\PiGui.h" />
<ClInclude Include="..\..\src\pigui\PiGuiLua.h" />
<ClInclude Include="..\..\src\pigui\View.h" />
<ClInclude Include="..\..\src\Plane.h" />
<ClInclude Include="..\..\src\Planet.h" />
<ClInclude Include="..\..\src\Player.h" />

View File

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