Commit a0d368af authored by Nicholas Walton's avatar Nicholas Walton

Updated for most recent Kero libs

Bundled Kero libs + stretchy buffer for easier compilation
parent dfae9ec4
# Croaking Kero C Libraries
These libraries are developed by me, Nick Walton, as they're needed while developing games and tutorials. Many of them will be used as examples for my C tutorials at http://croakingkero.com/blog
Currently all libs are single header files. Just download whichever ones you want, add their include directory to your project and go.
# These libraries are not good and you shouldn't use them!
Disclaimer: I take no responsibility for this code and give no warranty. Use it at your own risk.
Libraries:
Kero Platform - Cross-platform window/input library.
Kero Sprite - Sprite loading, blitting and transformation.
Kero Math - Math stuff as I need it.
Kero Vec2 - 2D vectors.
Kero Font - Bitmap fonts. Requires Kero Sprite.
#ifndef KERO_2D_COLLISION_H
#define KERO_2D_COLLISION_H
#include <stdbool.h>
#include "kero_vec2.h"
static inline vec2_t Vec2Normal(vec2_t v, bool s) {
vec2_t n;
if(s) {
n.x = -v.y;
n.y = v.x;
}
else {
n.x = v.y;
n.y = -v.x;
}
return Vec2Normalized(n);
}
vec2_t ProjectPolygonToVectorMinMax(vec2_t *vertices, int vertices_count, vec2_t v) {
float projected = Vec2Dot(vertices[0], v), min = projected, max = projected;
int i = 0;
while(++i < vertices_count) {
projected = Vec2Dot(vertices[i], v);
if(projected < min) {
min = projected;
}
if(projected > max) {
max = projected;
}
}
return Vec2Make(min, max);;
}
bool PolygonsIntersect(vec2_t *verticesa, int verticesa_count, vec2_t *verticesb, int verticesb_count) {
if(verticesa_count > verticesb_count) {
Swap(int, verticesa_count, verticesb_count);
Swap(vec2_t *, verticesa, verticesb);
}
vec2_t axis, aminmax, bminmax;
for(int s = 0; s < verticesa_count; ++s) {
axis = Vec2Normal(Vec2AToB(verticesa[s], verticesa[(s + 1) % verticesa_count]), true);
aminmax = ProjectPolygonToVectorMinMax(verticesa, verticesa_count, axis);
bminmax = ProjectPolygonToVectorMinMax(verticesb, verticesb_count, axis);
if(!(aminmax.x >= bminmax.x && aminmax.x <= bminmax.y ||
aminmax.y >= bminmax.x && aminmax.y <= bminmax.y ||
bminmax.x >= aminmax.x && bminmax.x <= aminmax.y ||
bminmax.y >= aminmax.x && bminmax.y <= aminmax.y)) {
return false;
}
}
for(int s = 0; s < verticesb_count; ++s) {
axis = Vec2Normal(Vec2AToB(verticesb[s], verticesb[(s + 1) % verticesb_count]), true);
aminmax = ProjectPolygonToVectorMinMax(verticesa, verticesa_count, axis);
bminmax = ProjectPolygonToVectorMinMax(verticesb, verticesb_count, axis);
if(!(aminmax.x >= bminmax.x && aminmax.x <= bminmax.y ||
aminmax.y >= bminmax.x && aminmax.y <= bminmax.y ||
bminmax.x >= aminmax.x && bminmax.x <= aminmax.y ||
bminmax.y >= aminmax.x && bminmax.y <= aminmax.y)) {
return false;
}
}
return true;
}
bool PolygonsIntersectMTV(vec2_t *verticesa, int verticesa_count, vec2_t *verticesb, int verticesb_count, vec2_t *mtv) {
vec2_t axis, aminmax, bminmax;
float translation_magnitude = HUGE_VAL;
float magnitude[4];
for(int s = 0; s < verticesa_count; ++s) {
axis = Vec2Normal(Vec2AToB(verticesa[s], verticesa[(s + 1) % verticesa_count]), true);
aminmax = ProjectPolygonToVectorMinMax(verticesa, verticesa_count, axis);
bminmax = ProjectPolygonToVectorMinMax(verticesb, verticesb_count, axis);
if(aminmax.x >= bminmax.x && aminmax.x <= bminmax.y ||
aminmax.y >= bminmax.x && aminmax.y <= bminmax.y ||
bminmax.x >= aminmax.x && bminmax.x <= aminmax.y ||
bminmax.y >= aminmax.x && bminmax.y <= aminmax.y) {
magnitude[0] = bminmax.y - aminmax.x;
magnitude[1] = bminmax.x - aminmax.y;
magnitude[2] = aminmax.y - bminmax.x;
magnitude[3] = aminmax.x - bminmax.y;
for(int i = 1; i < 4; ++i) {
if(Absolute(magnitude[i]) < Absolute(magnitude[0])) {
magnitude[0] = magnitude[i];
}
}
if(Absolute(magnitude[0]) < Absolute(translation_magnitude)) {
translation_magnitude = magnitude[0];
*mtv = axis;
}
}
else {
mtv->x = mtv->y = 0;
return false;
}
}
for(int s = 0; s < verticesb_count; ++s) {
axis = Vec2Normal(Vec2AToB(verticesb[s], verticesb[(s + 1) % verticesb_count]), true);
aminmax = ProjectPolygonToVectorMinMax(verticesa, verticesa_count, axis);
bminmax = ProjectPolygonToVectorMinMax(verticesb, verticesb_count, axis);
if(aminmax.x >= bminmax.x && aminmax.x <= bminmax.y ||
aminmax.y >= bminmax.x && aminmax.y <= bminmax.y ||
bminmax.x >= aminmax.x && bminmax.x <= aminmax.y ||
bminmax.y >= aminmax.x && bminmax.y <= aminmax.y) {
magnitude[0] = bminmax.y - aminmax.x;
magnitude[1] = bminmax.x - aminmax.y;
magnitude[2] = aminmax.y - bminmax.x;
magnitude[3] = aminmax.x - bminmax.y;
for(int i = 1; i < 4; ++i) {
if(Absolute(magnitude[i]) < Absolute(magnitude[0])) {
magnitude[0] = magnitude[i];
}
}
if(Absolute(magnitude[0]) < Absolute(translation_magnitude)) {
translation_magnitude = magnitude[0];
*mtv = axis;
}
}
else {
mtv->x = mtv->y = 0;
return false;
}
}
*mtv = Vec2MulScalar(Vec2Normalized(*mtv), translation_magnitude);
return true;
}
#endif
#ifndef KERO_3D_H
#define KERO_3D_H
/*
// Standard initial code:
void InitializeView();
int main(int argc, char* argv[]) {
KPGLInit(1920, 1080, "Notemon");
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glEnable(GL_TEXTURE_2D);
InitializeView();
koglposrot_t camera = {
.position = { 0, 0, -10 },
.rotation = { 0, 0, 0 },
};
bool continue_loop = true;
while(continue_loop) {
while(KPEventsQueued()) {
kp_event_t* e = KPNextEvent();
switch(e->type) {
case KPEVENT_KEY_PRESS:{
switch(e->key) {
case KEY_ESCAPE:{
continue_loop = false;
}break;
}
}break;
case KPEVENT_MOUSE_BUTTON_PRESS:{
switch(e->button) {
case MOUSE_LEFT: {
}break;
}
}break;
case KPEVENT_RESIZE:{
InitializeView();
}break;
case KPEVENT_QUIT:{
continue_loop = false;
}break;
}
KPFreeEvent(e);
}
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(camera.rotation.pitch, 1, 0, 0);
glRotatef(camera.rotation.yaw, 0, 1, 0);
glRotatef(camera.rotation.roll, 0, 0, 1);
glTranslatef(-camera.position.x, -camera.position.y, -camera.position.z);
glPushMatrix();
{
}
glPopMatrix();
KPGLFlip();
}
}
void InitializeView() {
glViewport(0, 0, kp_window.w, kp_window.h);
gluLookAt(0, 0, 0, 0, 0, 0, 0, 1, 0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//glOrtho(0, kp_window.w, 0, kp_window.h, 0, 100);
gluPerspective(40, (float)kp_window.w / (float)kp_window.h, 1, 100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
*/
#define KERO_PLATFORM_GL
#include "kero_platform.h"
#include <stdint.h>
#include <stdbool.h>
bool K3D_LoadTexture(GLuint *texture_id, const char *const filepath);
void K3D_FreeTexture(GLuint texture_id);
#include "kero_image.h"
#include "kero_vec3.h"
#include "kero_3dmodel.h"
typedef struct {
union {
vec3_t position;
vec3_t pos;
vec3_t p;
};
union {
vec3_t rotation;
vec3_t rot;
vec3_t r;
};
} k3d_posrot_t;
typedef struct {
union {
int width;
int w;
};
union {
int height;
int h;
};
float u0, v0, u1, v1;
float originx, originy;
union {
GLuint texture;
GLuint tex;
GLuint t;
};
} k3d_sprite_t;
typedef struct {
union {
struct { uint32_t width, depth; };
struct { uint32_t w, d; };
struct { uint32_t x, z; };
};
union {
float **heights;
float **h;
float **y;
};
} k3d_heightmap_t;
bool K3D_LoadTexture(GLuint *texture_id, const char *const filepath) {
kimage_t image;
if(KILoadHardwareDefaults(&image, filepath) != KILOAD_SUCCESS) {
fprintf(stderr, "Failed to load texture: %s\n", filepath);
return false;
}
glGenTextures(1, texture_id);
glBindTexture(GL_TEXTURE_2D, *texture_id);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.w, image.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.pixels);
KIFree(&image);
return true;
}
void K3D_FreeTexture(GLuint texture_id) {
glDeleteTextures(1, &texture_id);
}
void K3D_ResetLightMaterial() {
float ambient[4] = {.2, .2, .2, 1};
//float diffuse[4] = {.8, .8, .8, 1};
float ones[4] = {1, 1, 1, 1};
float zeroes[4] = {0};
glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, ones);
glMaterialfv(GL_FRONT, GL_SPECULAR, ones);
glMaterialfv(GL_FRONT, GL_EMISSION, zeroes);
glMaterialf(GL_FRONT, GL_SHININESS, 64);
}
void K3D_DrawString(const char *const string, uint32_t length) {
glBegin(GL_TRIANGLES); {
float x = 0, y = 0;
float tx, ty;
for(unsigned int c = 0; c < length; ++c) {
switch(string[c]) {
case '\n':{
--y;
x = 0;
}break;
case ' ':{
++x;
}break;
default:
tx = (string[c] % 16) / 16.f;
ty = 1.f - (string[c] / 16) / 16.f;
glTexCoord2d(tx, ty - 1.f / 16.f);
glVertex3f(x, y - 1, 0);
glTexCoord2d(tx + 1.f / 16.f, ty - 1.f / 16.f);
glVertex3f(x + 1, y - 1, 0);
glTexCoord2d(tx + 1.f / 16.f, ty);
glVertex3f(x + 1, y, 0);
glTexCoord2d(tx, ty - 1.f / 16.f);
glVertex3f(x, y - 1, 0);
glTexCoord2d(tx + 1.f / 16.f, ty);
glVertex3f(x + 1, y, 0);
glTexCoord2d(tx, ty);
glVertex3f(x, y, 0);
++x;
}
}
} glEnd();
}
void K3D_SpriteInit(k3d_sprite_t *sprite, int width, int height, float u0, float u1, float v0, float v1, float originx, float originy, GLuint texture) {
sprite->width = width;
sprite->height = height;
sprite->u0 = u0;
sprite->u1 = u1;
sprite->v0 = v0;
sprite->v1 = v1;
sprite->originx = originx;
sprite->originy = originy;
sprite->texture = texture;
}
void K3D_DrawSprite(k3d_sprite_t *sprite, float x, float y, float z, float rotation, int framex, int framey) {
glPushMatrix(); {
glTranslatef(x, y, z);
glRotatef(rotation, 0, 0, 1);
glBindTexture(GL_TEXTURE_2D, sprite->texture);
float texw = sprite->u1 - sprite->u0;
float texh = sprite->v1 - sprite->v0;
glBegin(GL_TRIANGLE_FAN); {
glTexCoord2d(sprite->u0 + framex * texw, sprite->v0 + framey * texh);
glVertex3f(-sprite->originx, -sprite->originy, z);
glTexCoord2d(sprite->u0 + framex * texw, sprite->v1 + framey * texh);
glVertex3f(-sprite->originx, sprite->h - sprite->originy, z);
glTexCoord2d(sprite->u1 + framex * texw, sprite->v1 + framey * texh);
glVertex3f(sprite->w - sprite->originx, sprite->h - sprite->originy, z);
glTexCoord2d(sprite->u1 + framex * texw, sprite->v0 + framey * texh);
glVertex3f(sprite->w - sprite->originx, -sprite->originy, z);
} glEnd();
} glPopMatrix();
}
void K3D_DrawSpriteScale(k3d_sprite_t *sprite, float x, float y, float z, float rotation, int framex, int framey, float scalex, float scaley) {
glPushMatrix(); {
glTranslatef(x, y, z);
glRotatef(rotation, 0, 0, 1);
glScalef(scalex, scaley, 1);
glBindTexture(GL_TEXTURE_2D, sprite->texture);
float texw = sprite->u1 - sprite->u0;
float texh = sprite->v1 - sprite->v0;
glBegin(GL_TRIANGLE_FAN); {
glTexCoord2d(sprite->u0 + framex * texw, sprite->v0 + framey * texh);
glVertex3f(-sprite->originx, -sprite->originy, z);
glTexCoord2d(sprite->u0 + framex * texw, sprite->v1 + framey * texh);
glVertex3f(-sprite->originx, sprite->height - sprite->originy, z);
glTexCoord2d(sprite->u1 + framex * texw, sprite->v1 + framey * texh);
glVertex3f(sprite->width - sprite->originx, sprite->height - sprite->originy, z);
glTexCoord2d(sprite->u1 + framex * texw, sprite->v0 + framey * texh);
glVertex3f(sprite->width - sprite->originx, -sprite->originy, z);
} glEnd();
} glPopMatrix();
}
#endif
#ifndef KERO_COLOR_H
#ifdef __cplusplus
extern "C"{
#endif
#include <stdint.h>
typedef struct {
union {
struct { float r, g, b, a; };
float f[4];
};
} kero_colorf_t;
typedef struct {
union {
struct { uint8_t b, g, r, a; };
uint32_t abgr;
};
} kero_colorb_t;
uint32_t KC_RGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a){
return b + (g<<8) + (r<<16) + (a<<24);
}
uint32_t KC_Darken(uint32_t color, float brightness){
brightness = Min(1, Max(0, brightness));
int b = (float)((uint8_t)color) * brightness;
int g = (float)((uint8_t)(color>>8)) * brightness;
int r = (float)((uint8_t)(color>>16)) * brightness;
int a = (uint8_t)(color>>24);
return b + (g<<8) + (r<<16) + (a<<24);
}
void KC_HSVToRGB(float h, float s, float v, int* r, int* g, int* b) {
h = Absolute(h/60.f);
float c = v * s;
float x = c * (1.f - Absolute(FMod(h, 2) - 1.f));
float m = v - c;
int hue_prime = (int)(h)%6;
switch(hue_prime) {
case 0:{
*r = (c + m) * 255, *g = (x + m) * 255, *b = m * 255;
}break;
case 1:{
*r = (x + m) * 255, *g = (c + m) * 255, *b = m * 255;
}break;
case 2:{
*r = m * 255, *g = (c + m) * 255, *b = (x + m) * 255;
}break;
case 3:{
*r = m * 255, *g = (x + m) * 255, *b = (c + m) * 255;
}break;
case 4:{
*r = (x + m) * 255, *g = m * 255, *b = (c + m) * 255;
}break;
case 5:{
*r = (c + m) * 255, *g = m * 255, *b = (x + m) * 255;
}break;
}
}
uint32_t KC_HSVToRGBu32(float h, float s, float v) {
h = Absolute(h/60.f);
float c = v * s;
float x = c * (1.f - Absolute(FMod(h, 2) - 1.f));
float m = v - c;
int hue_prime = (int)(h)%6;
uint8_t r, g, b;
switch(hue_prime) {
case 0:{
r = (c + m) * 255, g = (x + m) * 255, b = m * 255;
}break;
case 1:{
r = (x + m) * 255, g = (c + m) * 255, b = m * 255;
}break;
case 2:{
r = m * 255, g = (c + m) * 255, b = (x + m) * 255;
}break;
case 3:{
r = m * 255, g = (x + m) * 255, b = (c + m) * 255;
}break;
case 4:{
r = (x + m) * 255, g = m * 255, b = (c + m) * 255;
}break;
case 5:{
r = (c + m) * 255, g = m * 255, b = (x + m) * 255;
}break;
}
return (b) + (g << 8) + (r << 16) + (255 << 24);
}
#ifdef __cplusplus
}
#endif
#define KERO_COLOR_H
#endif
\ No newline at end of file
#ifndef FONT_H
#define FONT_H
#ifdef __cplusplus
extern "C"{
#endif
#include "kero_sprite.h"
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
typedef struct{
ksprite_t characters[256];
} kfont_t;
bool KF_Load(kfont_t* font, char* file_path){
ksprite_t font_spr;
if(!KS_Load(&font_spr, file_path)) {
fprintf(stderr, "Failed to load font: %s\n", file_path);
return false;
}
int w16th = font_spr.w / 16;
int h16th = font_spr.h / 16;
for(int y = 0; y < 16; y++){
for(int x = 0; x < 16; x++){
KS_CreateFromSprite(&font->characters[y*16+x], &font_spr, x*w16th, y*h16th, (x+1)*w16th, (y+1)*h16th);
}
}
return true;
}
void KF_Draw(kfont_t* font, ksprite_t* target, int x, int y, char* text){
int xoffset = 0;
for(int unsigned i = 0; text[i] != '\0'; i++){
if(text[i] == '\n'){
y+=16;
xoffset = -(i+1)*16;
} else{
KS_Blit(&font->characters[text[i]], target, x+i*16+xoffset, y);
}
}
}
void KF_DrawAlpha10(kfont_t* font, ksprite_t* target, int x, int y, char* text){
int xoffset = 0;
for(int unsigned i = 0; text[i] != '\0'; i++){
if(text[i] == '\n'){
y+=16;
xoffset = -(i+1)*16;
} else{
KS_BlitAlpha10(&font->characters[text[i]], target, x+i*16+xoffset, y);
}
}
}
void KF_DrawBlend(kfont_t* font, ksprite_t* target, int x, int y, char* text){
int xoffset = 0;
for(int unsigned i = 0; text[i] != '\0'; i++){
if(text[i] == '\n'){
y+=16;
xoffset = -(i+1)*16;
} else{
KS_BlitBlend(&font->characters[text[i]], target, x+i*16+xoffset, y);
}
}
}
void KF_DrawColored(kfont_t* font, ksprite_t* target, int x, int y, char* text, uint32_t colour){
int xoffset = 0;
for(int unsigned i = 0; text[i] != '\0'; i++){
if(text[i] == '\n'){
y+=16;
xoffset = -(i+1)*16;
} else{
KS_BlitColoredAlpha10(&font->characters[text[i]], target, x+i*16+xoffset, y, 0, 0, colour);
}
}
}
#ifdef __cplusplus
}
#endif
#endif
\ No newline at end of file
#ifndef KERO_MATH_H
#define KERO_MATH_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <stdbool.h>
#define ROOT2 1.414213562f
#define PI 3.14159265359f
#define TWOPI 6.28318530718
#define TAU TWOPI
#define HALFPI 1.570796327f
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define Absolute(a) ( (a) > 0 ? (a) : ( -(a) ) )