mirror of
https://github.com/GayPizzaSpecifications/padlab.git
synced 2025-08-06 14:41:34 +00:00
src dir for reasons
This commit is contained in:
260
src/analogue.c
Normal file
260
src/analogue.c
Normal file
@ -0,0 +1,260 @@
|
||||
#include "maths.h"
|
||||
#include "draw.h"
|
||||
#include "stick.h"
|
||||
#include <SDL.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define CAPTION "PadLab"
|
||||
#define WINDOW_WIDTH 512
|
||||
#define WINDOW_HEIGHT 288
|
||||
|
||||
static SDL_Window* window = NULL;
|
||||
static SDL_JoystickID joyid = -1;
|
||||
static SDL_GameController* pad = NULL;
|
||||
|
||||
static bool UseGamepad(int aJoyid)
|
||||
{
|
||||
pad = SDL_GameControllerOpen(aJoyid);
|
||||
joyid = SDL_JoystickGetDeviceInstanceID(aJoyid);
|
||||
if (pad != NULL)
|
||||
{
|
||||
printf("using gamepad #%d, \"%s\"\n", aJoyid, SDL_GameControllerName(pad));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#define FATAL(CONDITION, RETURN) if (CONDITION) { res = (RETURN); goto error; }
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER);
|
||||
if (res < 0)
|
||||
goto error;
|
||||
|
||||
const int winpos = SDL_WINDOWPOS_CENTERED;
|
||||
#ifdef USE_OPENGL
|
||||
const int winflg = SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
#else
|
||||
const int winflg = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
#endif
|
||||
int winw = WINDOW_WIDTH;
|
||||
int winh = WINDOW_HEIGHT;
|
||||
DrawWindowHints();
|
||||
window = SDL_CreateWindow(CAPTION, winpos, winpos, winw, winh, winflg);
|
||||
FATAL(window == NULL, -1)
|
||||
|
||||
FATAL(InitDraw(window), -1)
|
||||
size rendSize = GetDrawSizeInPixels();
|
||||
|
||||
if ((res = SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt")) != -1)
|
||||
printf("read %d mappings from gamecontrollerdb.txt\n", res);
|
||||
for (int i = 0; i < SDL_NumJoysticks(); ++i)
|
||||
{
|
||||
if (SDL_IsGameController(i))
|
||||
{
|
||||
if (UseGamepad(i))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vector plrpos = {10.0, 10.0};
|
||||
StickState stickl, stickr;
|
||||
InitDefaults(&stickl);
|
||||
InitDefaults(&stickr);
|
||||
|
||||
bool running = true;
|
||||
bool repaint = true;
|
||||
bool showavatar = false;
|
||||
uint32_t tickslast = SDL_GetTicks();
|
||||
int side = 0;
|
||||
|
||||
while (running)
|
||||
{
|
||||
//FIXME: probably doesn't matter but this isn't very precise
|
||||
const uint32_t ticks = SDL_GetTicks();
|
||||
const double framedelta = (double)(ticks - tickslast);
|
||||
tickslast = ticks;
|
||||
|
||||
SDL_Event event;
|
||||
bool onevent = false;
|
||||
if (!showavatar || (stickl.compos.x == 0.0 && stickl.compos.y == 0.0))
|
||||
{
|
||||
onevent = SDL_WaitEvent(&event) != 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
onevent = SDL_PollEvent(&event) > 0;
|
||||
repaint = true;
|
||||
}
|
||||
if (onevent)
|
||||
{
|
||||
do
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case (SDL_KEYDOWN):
|
||||
if (event.key.keysym.sym == SDLK_ESCAPE)
|
||||
{
|
||||
running = false;
|
||||
}
|
||||
else if (event.key.keysym.sym == SDLK_e)
|
||||
{
|
||||
showavatar = !showavatar;
|
||||
repaint = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case (SDL_CONTROLLERBUTTONDOWN):
|
||||
if (event.cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
|
||||
{
|
||||
showavatar = !showavatar;
|
||||
repaint = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case (SDL_QUIT):
|
||||
running = false;
|
||||
break;
|
||||
|
||||
case (SDL_WINDOWEVENT):
|
||||
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
|
||||
{
|
||||
winw = event.window.data1;
|
||||
winh = event.window.data2;
|
||||
rendSize = GetDrawSizeInPixels();
|
||||
SetDrawViewport(rendSize);
|
||||
repaint = true;
|
||||
}
|
||||
else if (event.window.event == SDL_WINDOWEVENT_EXPOSED)
|
||||
{
|
||||
repaint = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case (SDL_CONTROLLERAXISMOTION):
|
||||
if (event.caxis.which == joyid)
|
||||
{
|
||||
if (event.caxis.axis == SDL_CONTROLLER_AXIS_LEFTX)
|
||||
{
|
||||
stickl.rawpos.x = (vec_t)event.caxis.value / (vec_t)0x7FFF;
|
||||
repaint = stickl.recalc = true;
|
||||
}
|
||||
else if (event.caxis.axis == SDL_CONTROLLER_AXIS_LEFTY)
|
||||
{
|
||||
stickl.rawpos.y = (vec_t)event.caxis.value / (vec_t)0x7FFF;
|
||||
repaint = stickl.recalc = true;
|
||||
}
|
||||
else if (event.caxis.axis == SDL_CONTROLLER_AXIS_RIGHTX)
|
||||
{
|
||||
stickr.rawpos.x = (vec_t)event.caxis.value / (vec_t)0x7FFF;
|
||||
repaint = stickr.recalc = true;
|
||||
}
|
||||
else if (event.caxis.axis == SDL_CONTROLLER_AXIS_RIGHTY)
|
||||
{
|
||||
stickr.rawpos.y = (vec_t)event.caxis.value / (vec_t)0x7FFF;
|
||||
repaint = stickr.recalc = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case (SDL_MOUSEBUTTONDOWN):
|
||||
if (event.button.state & (SDL_BUTTON_LMASK | SDL_BUTTON_RMASK ))
|
||||
{
|
||||
side = (event.button.x > winw / 2) ? 1 : 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case (SDL_MOUSEMOTION):
|
||||
if (event.motion.state & SDL_BUTTON_LMASK)
|
||||
{
|
||||
const double hwinw = winw / 2.0;
|
||||
const double dispscale = 1.0 / (((hwinw > winh) ? winh : hwinw) * DISPLAY_SCALE / 2.0);
|
||||
const vector newpos = {
|
||||
CLAMP(((double)event.motion.x - hwinw / 2.0 - hwinw * side) * dispscale, -1.0, 1.0),
|
||||
CLAMP(((double)event.motion.y - winh / 2.0) * dispscale, -1.0, 1.0) };
|
||||
|
||||
StickState* stick = side ? &stickr : &stickl;
|
||||
stick->rawpos = newpos;
|
||||
repaint = stick->recalc = true;
|
||||
}
|
||||
else if (event.motion.state & SDL_BUTTON_RMASK)
|
||||
{
|
||||
const double hwinw = winw / 2.0;
|
||||
const double valx = SATURATE(1.0 - (double)event.motion.x / (double)hwinw);
|
||||
const double valy = SATURATE(1.0 - (double)event.motion.y / (double)winh);
|
||||
|
||||
if (side == 0)
|
||||
{
|
||||
stickl.digiangle = valx;
|
||||
stickl.digideadzone = valy;
|
||||
repaint = stickl.recalc = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//p2.accelpow = valy * valy * valy * 32;
|
||||
stickr.accelpow = pow(valy * 3, 1.0 + valy * 3);
|
||||
repaint = stickr.recalc = true;
|
||||
}
|
||||
}
|
||||
|
||||
case (SDL_CONTROLLERDEVICEADDED):
|
||||
if (pad == NULL)
|
||||
UseGamepad(event.cdevice.which);
|
||||
break;
|
||||
case (SDL_CONTROLLERDEVICEREMOVED):
|
||||
if (pad != NULL && event.cdevice.which == joyid)
|
||||
{
|
||||
SDL_GameControllerClose(pad);
|
||||
pad = NULL;
|
||||
printf("active gamepad was removed\n");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (SDL_PollEvent(&event) > 0);
|
||||
}
|
||||
|
||||
if (repaint)
|
||||
{
|
||||
// background
|
||||
SetDrawColour(GREY1);
|
||||
DrawClear();
|
||||
|
||||
const int hrw = rendSize.w / 2;
|
||||
DrawDigital(&(rect){ 0, 0, hrw, rendSize.h}, &stickl);
|
||||
DrawAnalogue(&(rect){ hrw, 0, hrw, rendSize.h}, &stickr);
|
||||
|
||||
// test player thingo
|
||||
if (showavatar)
|
||||
{
|
||||
plrpos = VecAdd(plrpos, VecScale(stickl.compos, framedelta * 0.5));
|
||||
plrpos.x = pfmod(plrpos.x, rendSize.w);
|
||||
plrpos.y = pfmod(plrpos.y, rendSize.h);
|
||||
|
||||
SetDrawColour(AVATAR);
|
||||
const int plrSz = 32;
|
||||
DrawRect(
|
||||
(int)plrpos.x - plrSz / 2,
|
||||
(int)plrpos.y - plrSz / 2,
|
||||
plrSz, plrSz);
|
||||
}
|
||||
|
||||
DrawPresent();
|
||||
repaint = false;
|
||||
}
|
||||
}
|
||||
|
||||
res = 0;
|
||||
error:
|
||||
SDL_GameControllerClose(pad);
|
||||
QuitDraw();
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return res;
|
||||
}
|
122
src/draw.c
Normal file
122
src/draw.c
Normal file
@ -0,0 +1,122 @@
|
||||
#include "draw.h"
|
||||
#include "maths.h"
|
||||
#include <SDL_render.h>
|
||||
|
||||
static SDL_Renderer* rend = NULL;
|
||||
|
||||
void DrawWindowHints(void) {}
|
||||
|
||||
int InitDraw(SDL_Window* window)
|
||||
{
|
||||
const int rendflags = SDL_RENDERER_PRESENTVSYNC;
|
||||
rend = SDL_CreateRenderer(window, -1, rendflags);
|
||||
return (rend == NULL) ? -1 : 0;
|
||||
}
|
||||
|
||||
void QuitDraw(void)
|
||||
{
|
||||
SDL_DestroyRenderer(rend);
|
||||
rend = NULL;
|
||||
}
|
||||
|
||||
|
||||
size GetDrawSizeInPixels(void)
|
||||
{
|
||||
size out = {0, 0};
|
||||
SDL_GetRendererOutputSize(rend, &out.w, &out.h);
|
||||
return out;
|
||||
}
|
||||
|
||||
void SetDrawViewport(size size) {}
|
||||
|
||||
|
||||
void SetDrawColour(uint32_t c)
|
||||
{
|
||||
SDL_SetRenderDrawColor(rend,
|
||||
(c & 0xFF000000) >> 24,
|
||||
(c & 0x00FF0000) >> 16,
|
||||
(c & 0x0000FF00) >> 8,
|
||||
(c & 0x000000FF));
|
||||
}
|
||||
|
||||
void DrawClear(void)
|
||||
{
|
||||
SDL_RenderClear(rend);
|
||||
}
|
||||
|
||||
void DrawPoint(int x, int y)
|
||||
{
|
||||
SDL_RenderDrawPoint(rend, x, y);
|
||||
}
|
||||
|
||||
void DrawRect(int x, int y, int w, int h)
|
||||
{
|
||||
SDL_Rect dst = {
|
||||
.x = x, .y = y,
|
||||
.w = w, .h = h };
|
||||
SDL_RenderDrawRect(rend, &dst);
|
||||
}
|
||||
|
||||
void DrawLine(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
SDL_RenderDrawLine(rend, x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
void DrawCircle(int x, int y, int r)
|
||||
{
|
||||
DrawCircleSteps(x, y, r, (int)(sqrt((double)r) * 8.0));
|
||||
}
|
||||
|
||||
void DrawCircleSteps(int x, int y, int r, int steps)
|
||||
{
|
||||
double stepsz = (double)TAU / steps;
|
||||
int lastx = r;
|
||||
int lasty = 0;
|
||||
for (int i = 1; i <= steps; ++i)
|
||||
{
|
||||
const double mag = (double)r;
|
||||
int ofsx = (int)round(cos(stepsz * i) * mag);
|
||||
int ofsy = (int)round(sin(stepsz * i) * mag);
|
||||
|
||||
SDL_RenderDrawLine(rend,
|
||||
x + lastx, y + lasty,
|
||||
x + ofsx, y + ofsy);
|
||||
|
||||
lastx = ofsx;
|
||||
lasty = ofsy;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawArc(int x, int y, int r, int startAng, int endAng)
|
||||
{
|
||||
const int steps = (int)(sqrt((double)r) * (double)abs(endAng - startAng) / 360.0 * 8.0);
|
||||
DrawArcSteps(x, y, r, startAng, endAng, steps);
|
||||
}
|
||||
|
||||
void DrawArcSteps(int x, int y, int r, int startAng, int endAng, int steps)
|
||||
{
|
||||
const double fstart = (double)startAng * DEG2RAD;
|
||||
const double fstepSz = (double)(endAng - startAng) / abs(steps) * DEG2RAD;
|
||||
const double mag = (double)r;
|
||||
|
||||
int lastx = (int)round(cos(fstart) * mag);
|
||||
int lasty = (int)round(-sin(fstart) * mag);
|
||||
for (int i = 1; i <= steps; ++i)
|
||||
{
|
||||
const double theta = fstart + fstepSz * (double)i;
|
||||
int ofsx = (int)round(cos(theta) * mag);
|
||||
int ofsy = (int)round(-sin(theta) * mag);
|
||||
|
||||
SDL_RenderDrawLine(rend,
|
||||
x + lastx, y + lasty,
|
||||
x + ofsx, y + ofsy);
|
||||
|
||||
lastx = ofsx;
|
||||
lasty = ofsy;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawPresent(void)
|
||||
{
|
||||
SDL_RenderPresent(rend);
|
||||
}
|
79
src/draw.h
Normal file
79
src/draw.h
Normal file
@ -0,0 +1,79 @@
|
||||
#ifndef DRAW_H
|
||||
#define DRAW_H
|
||||
|
||||
#define DISPLAY_SCALE 0.8889
|
||||
|
||||
#include "util.h"
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct SDL_Window SDL_Window;
|
||||
|
||||
// Call before window creation to setup backend-specific
|
||||
// hints and attributes.
|
||||
void DrawWindowHints(void);
|
||||
|
||||
// Initialise the drawing subsystem.
|
||||
//
|
||||
// Params:
|
||||
// window - The application's SDL window.
|
||||
//
|
||||
// Returns:
|
||||
// 0 on success, -1 on failure.
|
||||
int InitDraw(SDL_Window* window);
|
||||
|
||||
// Quit the drawing subsystem.
|
||||
//
|
||||
// This is safe (but pointless) to call when the drawing
|
||||
// subsystem is uninitialised.
|
||||
void QuitDraw(void);
|
||||
|
||||
// Get the actual size of the canvas in pixels.
|
||||
//
|
||||
// Returns:
|
||||
// size struct with 'w' and 'h' set to the width &
|
||||
// height of the canvas in actual pixels.
|
||||
size GetDrawSizeInPixels(void);
|
||||
|
||||
// Call on resize for backends that need manual viewport resizing.
|
||||
void SetDrawViewport(size size);
|
||||
|
||||
// Set the current draw colour.
|
||||
//
|
||||
// Params:
|
||||
// c - Colour in the unsigned 32-bit int packed format of:
|
||||
// 0xFF000000 - red channel
|
||||
// 0x00FF0000 - blue channel
|
||||
// 0x0000FF00 - green channel
|
||||
// 0x000000FF - alpha channel
|
||||
void SetDrawColour(uint32_t c);
|
||||
|
||||
// Clear the entire screen using the current draw colour.
|
||||
void DrawClear(void);
|
||||
|
||||
// Draw a single pixel point at x, y w/ the draw colour.
|
||||
void DrawPoint(int x, int y);
|
||||
|
||||
// Draw rectangle outline.
|
||||
void DrawRect(int x, int y, int w, int h);
|
||||
|
||||
// Draw straight line between x1,y1 and x2,y2.
|
||||
void DrawLine(int x1, int y1, int x2, int y2);
|
||||
|
||||
// Draw outline circle.
|
||||
void DrawCircle(int x, int y, int r);
|
||||
|
||||
// Draw outline circle made of lines w/ a discrete number of steps.
|
||||
//
|
||||
// Can be used to draw regular convex polygons such as an octagon.
|
||||
void DrawCircleSteps(int x, int y, int r, int steps);
|
||||
|
||||
// Draw an arc.
|
||||
void DrawArc(int x, int y, int r, int startAng, int endAng);
|
||||
|
||||
// Draw an arc with a discrete number of steps.
|
||||
void DrawArcSteps(int x, int y, int r, int startAng, int endAng, int steps);
|
||||
|
||||
// Present the current buffer to the screen.
|
||||
void DrawPresent(void);
|
||||
|
||||
#endif//DRAW_H
|
213
src/draw_opengl.c
Normal file
213
src/draw_opengl.c
Normal file
@ -0,0 +1,213 @@
|
||||
#include "draw.h"
|
||||
#include "maths.h"
|
||||
#include <SDL_video.h>
|
||||
#include <SDL_opengl.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static SDL_GLContext* ctx = NULL;
|
||||
static SDL_Window* window = NULL;
|
||||
static uint32_t colour = 0x00000000;
|
||||
static uint32_t clrColour = 0x00000000;
|
||||
static double scaleWidth = 0.0;
|
||||
static double scaleHeight = 0.0;
|
||||
static bool antialias = false;
|
||||
|
||||
void DrawWindowHints(void)
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); // Enable MSAA
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 8); // 8x MSAA
|
||||
|
||||
// Legacy OpenGL profile
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
|
||||
}
|
||||
|
||||
int InitDraw(SDL_Window* w)
|
||||
{
|
||||
ctx = SDL_GL_CreateContext(w);
|
||||
window = w;
|
||||
if (ctx == NULL || window == NULL)
|
||||
return -1;
|
||||
|
||||
SDL_GL_SetSwapInterval(1); // Enable vsync
|
||||
|
||||
// Detect if MSAA is available & active
|
||||
int res;
|
||||
if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &res) == 0 && res == 1)
|
||||
if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &res) == 0 && res > 0)
|
||||
antialias = true;
|
||||
|
||||
// Disable depth test & culling, enable MSAA
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
|
||||
SetDrawViewport(GetDrawSizeInPixels()); // Fills scaleWidth & scaleHeight
|
||||
|
||||
glLineWidth(2.0f);
|
||||
|
||||
// Setup orthographic viewport
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glOrtho(0.0, 1.0f, 1.0f, 0.0, 1.0, -1.0);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void QuitDraw(void)
|
||||
{
|
||||
SDL_GL_DeleteContext(ctx);
|
||||
ctx = NULL;
|
||||
window = NULL;
|
||||
}
|
||||
|
||||
|
||||
size GetDrawSizeInPixels(void)
|
||||
{
|
||||
size out;
|
||||
SDL_GL_GetDrawableSize(SDL_GL_GetCurrentWindow(), &out.w, &out.h);
|
||||
return out;
|
||||
}
|
||||
|
||||
void SetDrawViewport(size size)
|
||||
{
|
||||
glViewport(0, 0, size.w, size.h);
|
||||
scaleWidth = 1.0 / (double)size.w;
|
||||
scaleHeight = 1.0 / (double)size.h;
|
||||
}
|
||||
|
||||
|
||||
void SetDrawColour(uint32_t c)
|
||||
{
|
||||
colour = c;
|
||||
}
|
||||
|
||||
void DrawClear(void)
|
||||
{
|
||||
if (clrColour != colour)
|
||||
{
|
||||
const float mul = 1.0f / 255.0f;
|
||||
glClearColor(
|
||||
(GLclampf)((colour & 0xFF000000) >> 24) * mul,
|
||||
(GLclampf)((colour & 0x00FF0000) >> 16) * mul,
|
||||
(GLclampf)((colour & 0x0000FF00) >> 8) * mul,
|
||||
(GLclampf)((colour & 0x000000FF)) * mul);
|
||||
clrColour = colour;
|
||||
}
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
static inline void GlColour(void)
|
||||
{
|
||||
const float mul = 1.0f / 255.0f;
|
||||
glColor4f(
|
||||
(GLclampf)((colour & 0xFF000000) >> 24) * mul,
|
||||
(GLclampf)((colour & 0x00FF0000) >> 16) * mul,
|
||||
(GLclampf)((colour & 0x0000FF00) >> 8) * mul,
|
||||
(GLclampf)((colour & 0x000000FF)) * mul);
|
||||
}
|
||||
|
||||
void DrawPoint(int x, int y)
|
||||
{
|
||||
const double fx = ((double)x) * scaleWidth;
|
||||
const double fy = ((double)y) * scaleHeight;
|
||||
|
||||
glBegin(GL_POINTS);
|
||||
GlColour();
|
||||
glVertex2d(fx, fy);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void DrawRect(int x, int y, int w, int h)
|
||||
{
|
||||
const double fx = (double)x * scaleWidth;
|
||||
const double fy = (double)y * scaleHeight;
|
||||
const double fw = (double)w * scaleWidth;
|
||||
const double fh = (double)h * scaleHeight;
|
||||
|
||||
glBegin(GL_LINE_LOOP);
|
||||
GlColour();
|
||||
glVertex2d(fx, fy);
|
||||
glVertex2d(fx + fw, fy);
|
||||
glVertex2d(fx + fw, fy + fh);
|
||||
glVertex2d(fx, fy + fh);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void DrawLine(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
const double fx1 = (double)x1 * scaleWidth;
|
||||
const double fy1 = (double)y1 * scaleHeight;
|
||||
const double fx2 = (double)x2 * scaleWidth;
|
||||
const double fy2 = (double)y2 * scaleHeight;
|
||||
|
||||
glBegin(GL_LINES);
|
||||
GlColour();
|
||||
glVertex2d(fx1, fy1);
|
||||
glVertex2d(fx2, fy2);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void DrawCircle(int x, int y, int r)
|
||||
{
|
||||
DrawCircleSteps(x, y, r, (int)(sqrt((double)r) * 8.0));
|
||||
}
|
||||
|
||||
void DrawCircleSteps(int x, int y, int r, int steps)
|
||||
{
|
||||
// Circles look better when offset negatively by half a pixel w/o MSAA
|
||||
const double fx = (antialias ? (double)x : (double)x - 0.5f) * scaleWidth;
|
||||
const double fy = (antialias ? (double)y : (double)y - 0.5f) * scaleHeight;
|
||||
|
||||
const double stepsz = (double)TAU / (double)steps;
|
||||
const double magw = (double)r * scaleWidth;
|
||||
const double magh = (double)r * scaleHeight;
|
||||
|
||||
glBegin(GL_LINE_LOOP);
|
||||
GlColour();
|
||||
glVertex2d(fx + magw, fy);
|
||||
for (int i = 1; i < steps; ++i)
|
||||
{
|
||||
const double theta = stepsz * (double)i;
|
||||
double ofsx = cos(theta) * magw;
|
||||
double ofsy = sin(theta) * magh;
|
||||
glVertex2d(fx + ofsx, fy - ofsy);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void DrawArc(int x, int y, int r, int startAng, int endAng)
|
||||
{
|
||||
const int steps = (int)(sqrt((double)r) * (double)abs(endAng - startAng) / 360.0 * 8.0);
|
||||
DrawArcSteps(x, y, r, startAng, endAng, steps);
|
||||
}
|
||||
|
||||
void DrawArcSteps(int x, int y, int r, int startAng, int endAng, int steps)
|
||||
{
|
||||
// Arcs look better when offset negatively by half a pixel w/o MSAA
|
||||
const double fx = (antialias ? (double)x : (double)x - 0.5f) * scaleWidth;
|
||||
const double fy = (antialias ? (double)y : (double)y - 0.5f) * scaleHeight;
|
||||
|
||||
const double magw = (double)r * scaleWidth;
|
||||
const double magh = (double)r * scaleHeight;
|
||||
|
||||
glBegin(GL_LINE_STRIP);
|
||||
GlColour();
|
||||
const double fstart = (double)startAng * DEG2RAD;
|
||||
const double fstepSz = (double)(endAng - startAng) / abs(steps) * DEG2RAD;
|
||||
for (int i = 0; i <= steps; ++i)
|
||||
{
|
||||
const double theta = fstart + fstepSz * (double)i;
|
||||
double ofsx = cos(theta) * magw;
|
||||
double ofsy = sin(theta) * magh;
|
||||
glVertex2d(fx + ofsx, fy - ofsy);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void DrawPresent(void)
|
||||
{
|
||||
SDL_GL_SwapWindow(window);
|
||||
}
|
30
src/maths.h
Normal file
30
src/maths.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef MATHS_H
|
||||
#define MATHS_H
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#define PI 3.141592653589793238462643383279502884L
|
||||
#define TAU 6.283185307179586476925286766559005768L
|
||||
|
||||
#define RAD2DEG 57.2957795130823208768
|
||||
#define DEG2RAD 0.01745329251994329577
|
||||
|
||||
typedef double vec_t;
|
||||
typedef struct { vec_t x, y; } vector;
|
||||
|
||||
static inline vector VecAdd(vector l, vector r)
|
||||
{
|
||||
return (vector){l.x + r.x, l.y + r.y};
|
||||
}
|
||||
|
||||
static inline vector VecScale(vector v, vec_t x)
|
||||
{
|
||||
return (vector){v.x * x, v.y * x};
|
||||
}
|
||||
|
||||
static inline double pfmod(double x, double d)
|
||||
{
|
||||
return fmod(fmod(x, d) + d, (d));
|
||||
}
|
||||
|
||||
#endif//MATHS_H
|
293
src/stick.c
Normal file
293
src/stick.c
Normal file
@ -0,0 +1,293 @@
|
||||
#include "stick.h"
|
||||
#include "draw.h"
|
||||
|
||||
extern inline void InitDefaults(StickState* p);
|
||||
|
||||
vector RadialDeadzone(vector v, double min, double max)
|
||||
{
|
||||
double mag = sqrt(v.x * v.x + v.y * v.y);
|
||||
|
||||
if (mag <= min)
|
||||
return (vector){0.0, 0.0};
|
||||
|
||||
if (mag >= max)
|
||||
return (vector){v.x / mag, v.y / mag};
|
||||
|
||||
double rescale = (mag - min) / (max - min);
|
||||
return (vector){v.x / mag * rescale, v.y / mag * rescale};
|
||||
}
|
||||
|
||||
point DigitalEight(vector v, double angle, double deadzone)
|
||||
{
|
||||
const double absx = fabs(v.x);
|
||||
const double absy = fabs(v.y);
|
||||
point p = {0, 0};
|
||||
|
||||
if (absx * angle >= absy)
|
||||
{
|
||||
if (absx > deadzone)
|
||||
p.x = signbit(v.x) ? -1 : 1;
|
||||
}
|
||||
else if (absy * angle > absx)
|
||||
{
|
||||
if (absy > deadzone)
|
||||
p.y = signbit(v.y) ? -1 : 1;
|
||||
}
|
||||
else if (absx + absy >= deadzone * (1.0 + angle))
|
||||
{
|
||||
p.x = signbit(v.x) ? -1 : 1;
|
||||
p.y = signbit(v.y) ? -1 : 1;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
vector DigitalToVector(point p)
|
||||
{
|
||||
const double dscale = (p.x && p.y) ? 1.0 / sqrt(2.0) : 1.0;
|
||||
return (vector){
|
||||
p.x ? copysign(dscale, (double)p.x) : 0.0,
|
||||
p.y ? copysign(dscale, (double)p.y) : 0.0};
|
||||
}
|
||||
|
||||
static inline double AccelCurve(double x, double y)
|
||||
{
|
||||
return (x * (x + y)) / (1.0 + y);
|
||||
}
|
||||
|
||||
vector ApplyAcceleration(vector v, double y)
|
||||
{
|
||||
double mag = sqrt(v.x * v.x + v.y * v.y);
|
||||
if (mag <= 0.0)
|
||||
return (vector){0.0, 0.0};
|
||||
|
||||
double curve = AccelCurve(mag, y);
|
||||
return (vector){v.x / mag * curve, v.y / mag * curve};
|
||||
}
|
||||
|
||||
extern inline void InitDefaults(StickState* p);
|
||||
|
||||
void DrawAnalogue(const rect* win, StickState* p)
|
||||
{
|
||||
if (p->recalc)
|
||||
{
|
||||
p->compos = RadialDeadzone(p->rawpos, p->deadzone, 0.99);
|
||||
p->preaccel = sqrt(p->compos.x * p->compos.x + p->compos.y * p->compos.y);
|
||||
p->compos = ApplyAcceleration(p->compos, p->accelpow);
|
||||
p->postacel = sqrt(p->compos.x * p->compos.x + p->compos.y * p->compos.y);
|
||||
|
||||
p->recalc = false;
|
||||
}
|
||||
|
||||
double size = (double)(win->w > win->h ? win->h : win->w) * DISPLAY_SCALE;
|
||||
|
||||
// range rect
|
||||
SetDrawColour(GREY3);
|
||||
const int rectSz = (int)round(size);
|
||||
DrawRect(
|
||||
win->x + (win->w - rectSz) / 2,
|
||||
win->y + (win->h - rectSz) / 2,
|
||||
rectSz, rectSz);
|
||||
|
||||
const int ox = win->x + win->w / 2;
|
||||
const int oy = win->y + win->h / 2;
|
||||
|
||||
// acceleration curve
|
||||
SetDrawColour(GREY5);
|
||||
const int accelsamp = (int)(sqrt(size) * 4.20);
|
||||
const double step = 1.0 / (double)accelsamp;
|
||||
double y1 = AccelCurve(0.0, p->accelpow);
|
||||
for (int i = 1; i <= accelsamp; ++i)
|
||||
{
|
||||
double y2 = AccelCurve(step * i, p->accelpow);
|
||||
DrawLine(
|
||||
win->x + (int)(step * (i - 1) * size) + (win->w - rectSz) / 2,
|
||||
win->y + (int)((1.0 - y1) * size) + (win->h - rectSz) / 2,
|
||||
win->x + (int)(step * i * size) + (win->w - rectSz) / 2,
|
||||
win->y + (int)((1.0 - y2) * size) + (win->h - rectSz) / 2);
|
||||
y1 = y2;
|
||||
}
|
||||
const int tickerx = (int)((p->preaccel - 0.5) * size);
|
||||
const int tickery = (int)((0.5 - p->postacel) * size);
|
||||
SetDrawColour(HILIGHT_PU1);
|
||||
DrawLine(
|
||||
ox + tickerx,
|
||||
win->y + (win->h - rectSz) / 2,
|
||||
ox + tickerx,
|
||||
win->y + (win->h + rectSz) / 2);
|
||||
SetDrawColour(HILIGHT_PU2);
|
||||
DrawLine(
|
||||
win->x + (win->w - rectSz) / 2,
|
||||
oy + tickery,
|
||||
win->x + (win->w + rectSz) / 2,
|
||||
oy + tickery);
|
||||
|
||||
// guide circle
|
||||
SetDrawColour(GREY5);
|
||||
DrawCircle(ox, oy, rectSz / 2);
|
||||
|
||||
SetDrawColour(GREY4);
|
||||
DrawCircle(ox, oy, (int)round(p->deadzone * size) / 2);
|
||||
|
||||
// 0,0 line axis'
|
||||
SetDrawColour(GREY2);
|
||||
DrawLine(
|
||||
win->x, oy,
|
||||
win->x + win->w, oy);
|
||||
DrawLine(
|
||||
ox, win->y,
|
||||
ox, win->y + win->h);
|
||||
|
||||
// compensated position
|
||||
SetDrawColour(HILIGHT_PU3);
|
||||
DrawCircleSteps(
|
||||
ox + (int)round(p->compos.x * size / 2.0),
|
||||
oy + (int)round(p->compos.y * size / 2.0),
|
||||
8, 16);
|
||||
DrawPoint(
|
||||
ox + (int)round(p->compos.x * size / 2.0),
|
||||
oy + (int)round(p->compos.y * size / 2.0));
|
||||
|
||||
// raw position
|
||||
SetDrawColour(WHITE);
|
||||
DrawLine(
|
||||
ox + (int)round(p->rawpos.x * size / 2.0) - 4,
|
||||
oy + (int)round(p->rawpos.y * size / 2.0),
|
||||
ox + (int)round(p->rawpos.x * size / 2.0) + 4,
|
||||
oy + (int)round(p->rawpos.y * size / 2.0));
|
||||
DrawLine(
|
||||
ox + (int)round(p->rawpos.x * size / 2.0),
|
||||
oy + (int)round(p->rawpos.y * size / 2.0) - 4,
|
||||
ox + (int)round(p->rawpos.x * size / 2.0),
|
||||
oy + (int)round(p->rawpos.y * size / 2.0) + 4);
|
||||
}
|
||||
|
||||
void DrawDigital(const rect* win, StickState* p)
|
||||
{
|
||||
if (p->recalc)
|
||||
{
|
||||
p->digixy = DigitalEight(p->rawpos, p->digiangle, p->digideadzone);
|
||||
p->compos = DigitalToVector(p->digixy);
|
||||
p->recalc = false;
|
||||
}
|
||||
|
||||
const double size = (double)(win->w > win->h ? win->h : win->w) * DISPLAY_SCALE;
|
||||
|
||||
// range rect
|
||||
SetDrawColour(GREY3);
|
||||
const int rectSz = (int)round(size);
|
||||
DrawRect(
|
||||
win->x + (win->w - rectSz) / 2,
|
||||
win->y + (win->h - rectSz) / 2,
|
||||
rectSz, rectSz);
|
||||
|
||||
// window centre
|
||||
const int ox = win->x + win->w / 2;
|
||||
const int oy = win->y + win->h / 2;
|
||||
|
||||
// guide circle
|
||||
SetDrawColour(GREY5);
|
||||
int radius = rectSz / 2;
|
||||
DrawCircle(ox, oy, radius);
|
||||
|
||||
// 0,0 line axis'
|
||||
SetDrawColour(GREY2);
|
||||
DrawLine(
|
||||
win->x, oy,
|
||||
win->x + win->w, oy);
|
||||
DrawLine(
|
||||
ox, win->y,
|
||||
ox, win->y + win->h);
|
||||
|
||||
// calcuate points for the zone previews
|
||||
const double outerinvmag = 1.0 / sqrt(1.0 + p->digiangle * p->digiangle);
|
||||
const int outh = (int)round((size * outerinvmag) / 2.0);
|
||||
const int outq = (int)round((size * outerinvmag) / 2.0 * p->digiangle);
|
||||
const int innh = (int)round(p->digideadzone * size / 2.0);
|
||||
const int innq = (int)round(p->digideadzone * size / 2.0 * p->digiangle);
|
||||
|
||||
SetDrawColour(GREY4);
|
||||
|
||||
// angles preview
|
||||
DrawLine(ox - outq, oy - outh, ox - innq, oy - innh);
|
||||
DrawLine(ox + outq, oy - outh, ox + innq, oy - innh);
|
||||
DrawLine(ox + outh, oy - outq, ox + innh, oy - innq);
|
||||
DrawLine(ox + outh, oy + outq, ox + innh, oy + innq);
|
||||
DrawLine(ox + outq, oy + outh, ox + innq, oy + innh);
|
||||
DrawLine(ox - outq, oy + outh, ox - innq, oy + innh);
|
||||
DrawLine(ox - outh, oy + outq, ox - innh, oy + innq);
|
||||
DrawLine(ox - outh, oy - outq, ox - innh, oy - innq);
|
||||
|
||||
// deadzone octagon
|
||||
DrawLine(ox - innq, oy - innh, ox + innq, oy - innh);
|
||||
DrawLine(ox + innq, oy - innh, ox + innh, oy - innq);
|
||||
DrawLine(ox + innh, oy - innq, ox + innh, oy - innq);
|
||||
DrawLine(ox + innh, oy - innq, ox + innh, oy + innq);
|
||||
DrawLine(ox + innh, oy + innq, ox + innq, oy + innh);
|
||||
DrawLine(ox + innq, oy + innh, ox - innq, oy + innh);
|
||||
DrawLine(ox - innq, oy + innh, ox - innh, oy + innq);
|
||||
DrawLine(ox - innh, oy + innq, ox - innh, oy - innq);
|
||||
DrawLine(ox - innh, oy - innq, ox - innq, oy - innh);
|
||||
|
||||
// highlight active zone
|
||||
if (p->digixy.x || p->digixy.y)
|
||||
{
|
||||
const int x = p->digixy.x;
|
||||
const int y = p->digixy.y;
|
||||
|
||||
SetDrawColour(HILIGHT_GR2);
|
||||
|
||||
if (x)
|
||||
{
|
||||
if (y <= 0) DrawLine(ox + outh * x, oy - outq, ox + innh * x, oy - innq);
|
||||
if (!y) DrawLine(ox + innh * x, oy + innq, ox + innh * x, oy - innq);
|
||||
if (y >= 0) DrawLine(ox + outh * x, oy + outq, ox + innh * x, oy + innq);
|
||||
}
|
||||
|
||||
if (y)
|
||||
{
|
||||
if (x <= 0) DrawLine(ox - outq, oy + outh * y, ox - innq, oy + innh * y);
|
||||
if (!x) DrawLine(ox + innq, oy + innh * y, ox - innq, oy + innh * y);
|
||||
if (x >= 0) DrawLine(ox + outq, oy + outh * y, ox + innq, oy + innh * y);
|
||||
}
|
||||
|
||||
if (x && y)
|
||||
{
|
||||
DrawLine(ox + innh * x, oy + innq * y, ox + innq * x, oy + innh * y);
|
||||
DrawArc(ox, oy, radius,
|
||||
-(int)round(atan2(outerinvmag * p->digiangle * y, outerinvmag * x) * RAD2DEG),
|
||||
-(int)round(atan2(outerinvmag * y, outerinvmag * p->digiangle * x) * RAD2DEG));
|
||||
}
|
||||
else
|
||||
{
|
||||
const int hemi = (int)round(atan2(outerinvmag * p->digiangle, outerinvmag) * RAD2DEG);
|
||||
if (x > 0) DrawArc(ox, oy, radius, -hemi, hemi);
|
||||
else if (y < 0) DrawArc(ox, oy, radius, -hemi + 90, hemi + 90);
|
||||
else if (x < 0) DrawArc(ox, oy, radius, -hemi + 180, hemi + 180);
|
||||
else if (y > 0) DrawArc(ox, oy, radius, -hemi + 270, hemi + 270);
|
||||
}
|
||||
}
|
||||
|
||||
// compensated position
|
||||
SetDrawColour(HILIGHT_GR3);
|
||||
DrawCircleSteps(
|
||||
ox + (int)round(p->compos.x * size / 2.0),
|
||||
oy + (int)round(p->compos.y * size / 2.0),
|
||||
8, 16);
|
||||
DrawPoint(
|
||||
ox + (int)round(p->compos.x * size / 2.0),
|
||||
oy + (int)round(p->compos.y * size / 2.0));
|
||||
|
||||
// raw position
|
||||
SetDrawColour(WHITE);
|
||||
DrawLine(
|
||||
ox + (int)round(p->rawpos.x * size / 2.0) - 4,
|
||||
oy + (int)round(p->rawpos.y * size / 2.0),
|
||||
ox + (int)round(p->rawpos.x * size / 2.0) + 4,
|
||||
oy + (int)round(p->rawpos.y * size / 2.0));
|
||||
DrawLine(
|
||||
ox + (int)round(p->rawpos.x * size / 2.0),
|
||||
oy + (int)round(p->rawpos.y * size / 2.0) - 4,
|
||||
ox + (int)round(p->rawpos.x * size / 2.0),
|
||||
oy + (int)round(p->rawpos.y * size / 2.0) + 4);
|
||||
}
|
44
src/stick.h
Normal file
44
src/stick.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef STICK_H
|
||||
#define STICK_H
|
||||
|
||||
#include "maths.h"
|
||||
#include "util.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// common
|
||||
vector rawpos, compos;
|
||||
bool recalc;
|
||||
|
||||
// analogue
|
||||
double preaccel, postacel;
|
||||
double accelpow;
|
||||
double deadzone;
|
||||
|
||||
// digital
|
||||
point digixy;
|
||||
double digiangle;
|
||||
double digideadzone;
|
||||
} StickState;
|
||||
|
||||
inline void InitDefaults(StickState* p)
|
||||
{
|
||||
p->rawpos = (vector){0.0, 0.0};
|
||||
p->compos = (vector){0.0, 0.0};
|
||||
|
||||
p->recalc = true;
|
||||
p->preaccel = 0.0;
|
||||
p->postacel = 0.0;
|
||||
p->accelpow = 1.25;
|
||||
p->deadzone = 0.125;
|
||||
|
||||
p->digixy = (point){0, 0};
|
||||
p->digiangle = sqrt(2.0) - 1.0;
|
||||
p->digideadzone = 0.5;
|
||||
}
|
||||
|
||||
void DrawAnalogue(const rect* win, StickState* p);
|
||||
void DrawDigital(const rect* win, StickState* p);
|
||||
|
||||
#endif//STICK_H
|
37
src/util.h
Normal file
37
src/util.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX(A, B) ((A) > (B) ? (A) : (B))
|
||||
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
||||
#define CLAMP(X, A, B) (MIN((B), MAX((A), (X))))
|
||||
#define SATURATE(X) (CLAMP((X), 0, 1))
|
||||
|
||||
typedef struct { int x, y; } point;
|
||||
typedef struct { int w, h; } size;
|
||||
typedef struct { int x, y, w, h; } rect;
|
||||
|
||||
#define MKGREY(L, A) (uint32_t)( \
|
||||
(((L) << 24) & 0xFF000000) | \
|
||||
(((L) << 16) & 0x00FF0000) | \
|
||||
(((L) << 8) & 0x0000FF00) | \
|
||||
((A) & 0x000000FF))
|
||||
|
||||
#define MKRGB(C) (uint32_t)(((C) << 8) | 0x000000FF)
|
||||
|
||||
#define WHITE 0xFFFFFFFF
|
||||
#define GREY1 MKGREY(0x1F, 0xFF)
|
||||
#define GREY2 MKGREY(0x37, 0xFF)
|
||||
#define GREY3 MKGREY(0x4F, 0xFF)
|
||||
#define GREY4 MKGREY(0x5F, 0xFF)
|
||||
#define GREY5 MKGREY(0x83, 0xFF)
|
||||
#define HILIGHT_GR1 MKRGB(0x2F3F1F)
|
||||
#define HILIGHT_GR2 MKRGB(0x387138)
|
||||
#define HILIGHT_GR3 MKRGB(0x1FFF1F)
|
||||
#define HILIGHT_PU1 MKRGB(0x632E63)
|
||||
#define HILIGHT_PU2 MKRGB(0x8A418A)
|
||||
#define HILIGHT_PU3 MKRGB(0xFF68FF)
|
||||
#define AVATAR MKRGB(0xFF3333)
|
||||
|
||||
#endif//UTIL_H
|
Reference in New Issue
Block a user