src dir for reasons

This commit is contained in:
2022-11-18 20:53:50 +11:00
parent 4255841a6b
commit 065fa979f3
9 changed files with 4 additions and 4 deletions

260
src/analogue.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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