66
77namespace GamepadRenderer {
88
9- // ── palette ──────────────────────────────────────────────────
9+ // ── layout coordinates (Xbox: stick upper-left, D-pad lower-left; Sony: D-pad upper-left, stick lower-left) ──
10+
11+ struct LayoutCoords {
12+ float dpadX, dpadY;
13+ float faceX, faceY;
14+ float leftStickX, leftStickY;
15+ float rightStickX, rightStickY;
16+ float lbTlX, lbTlY, lbBrX, lbBrY;
17+ float rbTlX, rbTlY, rbBrX, rbBrY;
18+ float ltTlX, ltTlY, ltBrX, ltBrY;
19+ float rtTlX, rtTlY, rtBrX, rtBrY;
20+ float backX, backY, guideX, guideY, startX, startY;
21+ const char * faceLabelA;
22+ const char * faceLabelB;
23+ const char * faceLabelX;
24+ const char * faceLabelY;
25+ const char * backLabel; // Back / Share
26+ const char * guideLabel; // Guide / PS
27+ const char * startLabel; // Start / Options
28+ };
29+
30+ static const LayoutCoords XBOX_LAYOUT = {
31+ 160 .f , 154 .f , // D-pad lower-left
32+ 275 .f , 85 .f , // face buttons
33+ 125 .f , 85 .f , // left stick upper-left
34+ 252 .f , 158 .f , // right stick
35+ 72 .f , 32 .f , 162 .f , 50 .f , // left bumper
36+ 238 .f , 32 .f , 328 .f , 50 .f , // right bumper
37+ 58 .f , 10 .f , 88 .f , 36 .f , // left trigger
38+ 312 .f , 10 .f , 342 .f , 36 .f , // right trigger
39+ 182 .f , 88 .f , 200 .f , 100 .f , 218 .f , 88 .f ,
40+ " A" , " B" , " X" , " Y" ,
41+ " Back" , " Guide" , " Start"
42+ };
43+
44+ // PlayStation: Circle (A), Cross (B), Square (X), Triangle (Y) — ASCII so default font works
45+ static const char * SONY_FACE_A = " O" ; // Circle
46+ static const char * SONY_FACE_B = " X" ; // Cross
47+ static const char * SONY_FACE_X = " S" ; // Square
48+ static const char * SONY_FACE_Y = " T" ; // Triangle
49+
50+ static const LayoutCoords SONY_LAYOUT = {
51+ 114 .f , 92 .5f , // D-pad upper-left (Sony symmetrical)
52+ 285 .f , 96 .f , // face buttons
53+ 160 .f , 130 .f , // left stick lower-left (Sony symmetrical)
54+ 240 .f , 130 .f , // right stick
55+ 72 .f , 32 .f , 162 .f , 50 .f ,
56+ 238 .f , 32 .f , 328 .f , 50 .f ,
57+ 58 .f , 10 .f , 88 .f , 36 .f ,
58+ 312 .f , 10 .f , 342 .f , 36 .f ,
59+ 182 .f , 88 .f , 200 .f , 100 .f , 218 .f , 88 .f ,
60+ SONY_FACE_A , SONY_FACE_B , SONY_FACE_X , SONY_FACE_Y ,
61+ " Share" , " PS" , " Options"
62+ };
63+
64+ /* *
65+ * @brief Selects the layout coordinates for the specified layout type.
66+ *
67+ * @param t Layout type indicating which coordinate set to use (e.g., LayoutType::Sony).
68+ * @return const LayoutCoords& Reference to the corresponding LayoutCoords constant: SONY_LAYOUT when t is LayoutType::Sony, otherwise XBOX_LAYOUT.
69+ */
70+ static const LayoutCoords& GetLayoutCoords (LayoutType t) {
71+ return t == LayoutType::Sony ? SONY_LAYOUT : XBOX_LAYOUT ;
72+ }
73+
74+ /* *
75+ * @brief Color used for the "A" face button.
76+ *
77+ * @return ImU32 Packed RGBA color equal to approximately RGB(96, 202, 56) with alpha 255.
78+ */
1079
1180static ImU32 ColorA () { return IM_COL32 ( 96 , 202 , 56 , 255 ); }
1281static ImU32 ColorB () { return IM_COL32 (228 , 56 , 56 , 255 ); }
@@ -19,7 +88,16 @@ static ImU32 Body() { return IM_COL32( 50, 50, 55, 255); }
1988static ImU32 Outline () { return IM_COL32 (120 , 120 , 130 , 255 ); }
2089static ImU32 BgDim () { return IM_COL32 ( 35 , 35 , 38 , 180 ); }
2190
22- // ── small drawing helpers ────────────────────────────────────
91+ /* *
92+ * @brief Draws the controller body and its left/right side panels using the provided layout.
93+ *
94+ * Renders filled, rounded rectangles with outlines to represent the main controller shell
95+ * and the two side panels. Positions, sizes, corner radii and stroke widths are derived
96+ * from the supplied Layout instance and the module's color helpers.
97+ *
98+ * @param dl ImGui draw list used for rendering.
99+ * @param L Layout providing coordinate transforms and scaling for placement and sizes.
100+ */
23101
24102static void DrawBody (ImDrawList* dl, const Layout& L) {
25103 ImVec2 tl = L.P (40 , 30 );
@@ -38,8 +116,20 @@ static void DrawBody(ImDrawList* dl, const Layout& L) {
38116 dl->AddRect (grtl, grbr, Outline (), L.S (20 ), 0 , L.S (2 ));
39117}
40118
41- static void DrawDPad (ImDrawList* dl, const Layout& L, const GamepadState& gs) {
42- ImVec2 center = L.P (120 , 155 );
119+ /* *
120+ * @brief Renders a D-pad at the given logical center, showing directional presses.
121+ *
122+ * Draws a cross-shaped D-pad centered at (cx, cy) in layout coordinates and highlights
123+ * individual directions when the corresponding D-pad buttons in gs are pressed.
124+ *
125+ * @param dl ImGui draw list used to issue drawing commands.
126+ * @param L Layout helper providing coordinate transforms and scaling.
127+ * @param gs Current gamepad state used to query directional button presses.
128+ * @param cx X coordinate of the D-pad center in logical layout space.
129+ * @param cy Y coordinate of the D-pad center in logical layout space.
130+ */
131+ static void DrawDPad (ImDrawList* dl, const Layout& L, const GamepadState& gs, float cx, float cy) {
132+ ImVec2 center = L.P (cx, cy);
43133 float arm = L.S (18 );
44134 float w = L.S (12 );
45135 float rnd = L.S (3 );
@@ -74,17 +164,38 @@ static void DrawDPad(ImDrawList* dl, const Layout& L, const GamepadState& gs) {
74164 ImVec2 (center.x + arm, center.y + w * 0 .5f ), Lit (), rnd);
75165}
76166
77- static void DrawFaceButtons (ImDrawList* dl, const Layout& L, const GamepadState& gs) {
78- ImVec2 center = L.P (290 , 110 );
167+ /* *
168+ * @brief Render the four face buttons (A/B/X/Y) with colors, labels, and pressed-state visuals.
169+ *
170+ * Draws four circular face buttons positioned around the given center using the supplied Layout for
171+ * scaling and positioning. Each button is filled with its face color when pressed (or a dim fill when
172+ * not), has a colored outline, and displays the provided label centered on the button. Button press
173+ * state is read from the provided GamepadState.
174+ *
175+ * @param dl ImGui draw list used to issue the rendering commands.
176+ * @param L Layout providing coordinate transforms and scaling.
177+ * @param gs Current gamepad state used to determine which face buttons are pressed.
178+ * @param cx X coordinate (logical) of the face-button group center.
179+ * @param cy Y coordinate (logical) of the face-button group center.
180+ * @param labelA Label text to render on the A button.
181+ * @param labelB Label text to render on the B button.
182+ * @param labelX Label text to render on the X button.
183+ * @param labelY Label text to render on the Y button.
184+ */
185+ static void DrawFaceButtons (ImDrawList* dl, const Layout& L, const GamepadState& gs,
186+ float cx, float cy,
187+ const char * labelA, const char * labelB,
188+ const char * labelX, const char * labelY) {
189+ ImVec2 center = L.P (cx, cy);
79190 float spread = L.S (18 );
80191 float r = L.S (11 );
81192
82193 struct FaceBtn { float dx; float dy; Button btn; ImU32 col; const char * label; };
83194 std::array btns = {
84- FaceBtn{ 0 , spread, Button::A, ColorA (), " A " },
85- FaceBtn{ spread, 0 , Button::B, ColorB (), " B " },
86- FaceBtn{ -spread, 0 , Button::X, ColorX (), " X " },
87- FaceBtn{ 0 , -spread, Button::Y, ColorY (), " Y " },
195+ FaceBtn{ 0 , spread, Button::A, ColorA (), labelA },
196+ FaceBtn{ spread, 0 , Button::B, ColorB (), labelB },
197+ FaceBtn{ -spread, 0 , Button::X, ColorX (), labelX },
198+ FaceBtn{ 0 , -spread, Button::Y, ColorY (), labelY },
88199 };
89200
90201 for (auto & fb : btns) {
@@ -150,27 +261,37 @@ static void DrawSmallButton(ImDrawList* dl, const Layout& L,
150261}
151262
152263/* *
153- * @brief Render a controller panel that visualizes a gamepad's layout and state.
264+ * @brief Render a framed controller panel visualizing a gamepad's layout and current state.
154265 *
155- * Renders a framed panel at the given position/size containing a header (built from the player
156- * slot, optional display name, and backend name), a connection-status pill, and a scaled
157- * representation of the controller showing body, D-pad, face buttons, thumbsticks, bumpers,
158- * triggers, and small buttons. When the gamepad is not connected, displays a centered
159- * "No controller detected" message instead of the controls.
266+ * Renders a header containing the player slot, optional display name, and backend name plus a
267+ * connection-status pill, and draws a scaled representation of the controller (body, D‑pad,
268+ * face buttons, thumbsticks, bumpers, triggers, and small buttons) inside the panel. If the
269+ * gamepad is not connected, displays a centered "No controller detected" message instead of
270+ * the controls. If a body texture is provided, it is used for the controller background;
271+ * otherwise the body is drawn procedurally.
160272 *
161- * @param dl ImGui draw list to use for low-level drawing operations.
273+ * @param dl ImGui draw list used for low-level drawing operations.
162274 * @param panelPos Top-left corner of the panel in screen coordinates.
163275 * @param panelSize Width and height of the panel in screen coordinates.
164276 * @param gs Current gamepad state (buttons, sticks, triggers, connection).
165277 * @param slotIndex Zero-based player slot index used in the header label.
166278 * @param backendName Null-terminated string identifying the input backend (shown in header).
167279 * @param displayName Optional null-terminated display name for the controller; when non-empty
168280 * it is included in the header as "Player N - DisplayName [backend]".
281+ * @param bodyTexture Optional ImGui texture ID to render as the controller body; when null,
282+ * the procedural body is drawn instead.
283+ * @param textureSizeLogical Logical size (width, height) of the provided bodyTexture in the
284+ * same coordinate space used by the renderer.
285+ * @param layoutType LayoutType selecting the coordinate/label set (e.g., Xbox or Sony) used to
286+ * position and label controls within the rendered controller.
169287 */
170288
171289void DrawGamepad (ImDrawList* dl, ImVec2 panelPos, ImVec2 panelSize,
172290 const GamepadState& gs, int slotIndex, const char * backendName,
173- const char * displayName) {
291+ const char * displayName,
292+ ImTextureID bodyTexture,
293+ ImVec2 textureSizeLogical,
294+ LayoutType layoutType) {
174295 dl->AddRectFilled (panelPos,
175296 ImVec2 (panelPos.x + panelSize.x , panelPos.y + panelSize.y ),
176297 BgDim (), 8 .0f );
@@ -228,26 +349,35 @@ void DrawGamepad(ImDrawList* dl, ImVec2 panelPos, ImVec2 panelSize,
228349 return ;
229350 }
230351
231- DrawBody (dl, L);
232- DrawDPad (dl, L, gs);
233- DrawFaceButtons (dl, L, gs);
352+ if (bodyTexture != GamepadRenderer::kNoBodyTexture ) {
353+ ImVec2 tl = L.P (0 .f , 0 .f );
354+ ImVec2 br = L.P (textureSizeLogical.x , textureSizeLogical.y );
355+ dl->AddImage (bodyTexture, tl, br, ImVec2 (0 .f , 0 .f ), ImVec2 (1 .f , 1 .f ));
356+ } else {
357+ DrawBody (dl, L);
358+ }
359+
360+ const LayoutCoords& c = GetLayoutCoords (layoutType);
361+ DrawDPad (dl, L, gs, c.dpadX , c.dpadY );
362+ DrawFaceButtons (dl, L, gs, c.faceX , c.faceY ,
363+ c.faceLabelA , c.faceLabelB , c.faceLabelX , c.faceLabelY );
234364
235- DrawThumbstick (dl, L, L.P (170 , 170 ), gs.leftStickX , gs.leftStickY ,
365+ DrawThumbstick (dl, L, L.P (c. leftStickX , c. leftStickY ), gs.leftStickX , gs.leftStickY ,
236366 gs.IsPressed (Button::LeftThumb));
237- DrawThumbstick (dl, L, L.P (240 , 170 ), gs.rightStickX , gs.rightStickY ,
367+ DrawThumbstick (dl, L, L.P (c. rightStickX , c. rightStickY ), gs.rightStickX , gs.rightStickY ,
238368 gs.IsPressed (Button::RightThumb));
239369
240- DrawBumper (dl, L, L.P (70 , 30 ), L.P (160 , 48 ), gs.IsPressed (Button::LeftBumper));
241- DrawBumper (dl, L, L.P (240 , 30 ), L.P (330 , 48 ), gs.IsPressed (Button::RightBumper));
370+ DrawBumper (dl, L, L.P (c. lbTlX , c. lbTlY ), L.P (c. lbBrX , c. lbBrY ), gs.IsPressed (Button::LeftBumper));
371+ DrawBumper (dl, L, L.P (c. rbTlX , c. rbTlY ), L.P (c. rbBrX , c. rbBrY ), gs.IsPressed (Button::RightBumper));
242372
243- DrawTrigger (dl, L, L.P (50 , 2 ), L.P (80 , 28 ), gs.leftTrigger );
244- DrawTrigger (dl, L, L.P (320 , 2 ), L.P (350 , 28 ), gs.rightTrigger );
373+ DrawTrigger (dl, L, L.P (c. ltTlX , c. ltTlY ), L.P (c. ltBrX , c. ltBrY ), gs.leftTrigger );
374+ DrawTrigger (dl, L, L.P (c. rtTlX , c. rtTlY ), L.P (c. rtBrX , c. rtBrY ), gs.rightTrigger );
245375
246- DrawSmallButton (dl, L, L.P (175 , 100 ), L.S (28 ), L.S (12 ), " Back " ,
376+ DrawSmallButton (dl, L, L.P (c. backX , c. backY ), L.S (28 ), L.S (12 ), c. backLabel ,
247377 gs.IsPressed (Button::Back));
248- DrawSmallButton (dl, L, L.P (200 , 118 ), L.S (22 ), L.S (12 ), " Guide " ,
378+ DrawSmallButton (dl, L, L.P (c. guideX , c. guideY ), L.S (22 ), L.S (12 ), c. guideLabel ,
249379 gs.IsPressed (Button::Guide));
250- DrawSmallButton (dl, L, L.P (225 , 100 ), L.S (28 ), L.S (12 ), " Start " ,
380+ DrawSmallButton (dl, L, L.P (c. startX , c. startY ), L.S (28 ), L.S (12 ), c. startLabel ,
251381 gs.IsPressed (Button::Start));
252382
253383 {
0 commit comments