diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 65 | ||||
-rw-r--r-- | src/adult-node.c | 32 | ||||
-rw-r--r-- | src/adult-node.h | 25 | ||||
-rw-r--r-- | src/baby-node.c | 32 | ||||
-rw-r--r-- | src/baby-node.h | 25 | ||||
-rw-r--r-- | src/bb2f.h | 38 | ||||
-rw-r--r-- | src/bb3f.h | 38 | ||||
-rw-r--r-- | src/digit-node.c | 60 | ||||
-rw-r--r-- | src/digit-node.h | 25 | ||||
-rw-r--r-- | src/game.c | 660 | ||||
-rw-r--r-- | src/hungrycat-node.c | 27 | ||||
-rw-r--r-- | src/hungrycat-node.h | 25 | ||||
-rw-r--r-- | src/hungrycat.c | 157 | ||||
-rw-r--r-- | src/m4f-3dx.h | 122 | ||||
-rw-r--r-- | src/m4f-bbx.h | 89 | ||||
-rw-r--r-- | src/plasma-node.c | 93 | ||||
-rw-r--r-- | src/plasma-node.h | 25 | ||||
-rw-r--r-- | src/sfx.c | 37 | ||||
-rw-r--r-- | src/sfx.h | 34 | ||||
-rw-r--r-- | src/shader-node.c | 174 | ||||
-rw-r--r-- | src/shader-node.h | 30 | ||||
-rw-r--r-- | src/shader.c | 144 | ||||
-rw-r--r-- | src/shader.h | 28 | ||||
-rw-r--r-- | src/tex-node.c | 100 | ||||
-rw-r--r-- | src/tex-node.h | 27 | ||||
-rw-r--r-- | src/tex.c | 189 | ||||
-rw-r--r-- | src/tex.h | 30 | ||||
-rw-r--r-- | src/tv-node.c | 32 | ||||
-rw-r--r-- | src/tv-node.h | 25 | ||||
-rw-r--r-- | src/virus-node.c | 32 | ||||
-rw-r--r-- | src/virus-node.h | 25 |
31 files changed, 2442 insertions, 3 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 9adbab6..7dce89e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,63 @@ bin_PROGRAMS = sars -sars_SOURCES = KRH/khrplatform.h clear-node.h clear-node.c macros.h main.c sars.c sars.h glad.c glad.h -sars_CPPFLAGS = -I@top_srcdir@/libstage/src -I@top_srcdir@/libplay/src -ffast-math -sars_LDADD = @top_builddir@/libstage/src/libstage.a @top_builddir@/libplay/src/libplay.a -lm -ldl +sars_SOURCES = \ + adult-node.c \ + adult-node.h \ + baby-node.c \ + baby-node.h \ + bb2f.h \ + bb3f.h \ + clear-node.c \ + clear-node.h \ + digit-node.c \ + digit-node.h \ + game.c \ + gfx/gfx-adult.h \ + gfx/gfx-baby.h \ + gfx/gfx-eight.h \ + gfx/gfx-five.h \ + gfx/gfx-four.h \ + gfx/gfx-hungrycat.h \ + gfx/gfx-nine.h \ + gfx/gfx-one.h \ + gfx/gfx-seven.h \ + gfx/gfx-six.h \ + gfx/gfx-three.h \ + gfx/gfx-tv.h \ + gfx/gfx-two.h \ + gfx/gfx-virus.h \ + gfx/gfx-zero.h \ + glad.c \ + glad.h \ + hungrycat.c \ + hungrycat-node.c \ + hungrycat-node.h \ + KHR/khrplatform.h \ + m4f-3dx.h \ + m4f-bbx.h \ + m4f.h \ + macros.h \ + main.c \ + plasma-node.c \ + plasma-node.h \ + sars.c \ + sars.h \ + sfx.c \ + sfx.h \ + shader.c \ + shader.h \ + shader-node.c \ + shader-node.h \ + tex.c \ + tex.h \ + tex-node.c \ + tex-node.h \ + tv-node.c \ + tv-node.h \ + v2f.h \ + v3f.h \ + v4f.h \ + virus-node.c \ + virus-node.h + +sars_CPPFLAGS = -I@top_srcdir@/libix2/src -I@top_srcdir@/libix2/libpad/src -I@top_srcdir@/libstage/src -I@top_srcdir@/libplay/src -ffast-math +sars_LDADD = @top_builddir@/libix2/src/libix2.a @top_builddir@/libix2/libpad/src/libpad.a @top_builddir@/libstage/src/libstage.a @top_builddir@/libplay/src/libplay.a -lm -ldl diff --git a/src/adult-node.c b/src/adult-node.c new file mode 100644 index 0000000..f314715 --- /dev/null +++ b/src/adult-node.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stage.h> + +#include "adult-node.h" +#include "gfx/gfx-adult.h" +#include "tex.h" +#include "tex-node.h" + +static tex_t *adult_tex; + +stage_t * adult_node_new(stage_conf_t *conf, m4f_t *model_x) +{ + if (!adult_tex) + adult_tex = tex_new(gfx_adult.width, gfx_adult.height, gfx_adult.pixel_data); + + return tex_node_new_tex(conf, adult_tex, model_x); +} diff --git a/src/adult-node.h b/src/adult-node.h new file mode 100644 index 0000000..151014f --- /dev/null +++ b/src/adult-node.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _ADULT_NODE_H +#define _ADULT_NODE_H + +typedef struct stage_conf_t stage_conf_t; +typedef struct m4f_t m4f_t; + +stage_t * adult_node_new(stage_conf_t *conf, m4f_t *model_x); + +#endif diff --git a/src/baby-node.c b/src/baby-node.c new file mode 100644 index 0000000..8f49670 --- /dev/null +++ b/src/baby-node.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stage.h> + +#include "baby-node.h" +#include "gfx/gfx-baby.h" +#include "tex.h" +#include "tex-node.h" + +static tex_t *baby_tex; + +stage_t * baby_node_new(stage_conf_t *conf, m4f_t *model_x) +{ + if (!baby_tex) + baby_tex = tex_new(gfx_baby.width, gfx_baby.height, gfx_baby.pixel_data); + + return tex_node_new_tex(conf, baby_tex, model_x); +} diff --git a/src/baby-node.h b/src/baby-node.h new file mode 100644 index 0000000..f6cc02e --- /dev/null +++ b/src/baby-node.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _BABY_NODE_H +#define _BABY_NODE_H + +typedef struct stage_conf_t stage_conf_t; +typedef struct m4f_t m4f_t; + +stage_t * baby_node_new(stage_conf_t *conf, m4f_t *model_x); + +#endif diff --git a/src/bb2f.h b/src/bb2f.h new file mode 100644 index 0000000..dc0a765 --- /dev/null +++ b/src/bb2f.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2018-2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _BB2F_H +#define _BB2F_H + +#include "v2f.h" + +typedef struct bb2f_t { + v2f_t min, max; +} bb2f_t; + + +/* linearly interpolate between a and b by t */ +static inline bb2f_t bb2f_lerp(const bb2f_t *a, const bb2f_t *b, float t) +{ + bb2f_t bb2f; + + bb2f.min = v2f_lerp(&a->min, &b->min, t); + bb2f.max = v2f_lerp(&a->max, &b->max, t); + + return bb2f; +} + +#endif diff --git a/src/bb3f.h b/src/bb3f.h new file mode 100644 index 0000000..8bd4009 --- /dev/null +++ b/src/bb3f.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2018-2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _BB3F_H +#define _BB3F_H + +#include "v3f.h" + +typedef struct bb3f_t { + v3f_t min, max; +} bb3f_t; + + +/* linearly interpolate between a and b by t */ +static inline bb3f_t bb3f_lerp(const bb3f_t *a, const bb3f_t *b, float t) +{ + bb3f_t bb3f; + + bb3f.min = v3f_lerp(&a->min, &b->min, t); + bb3f.max = v3f_lerp(&a->max, &b->max, t); + + return bb3f; +} + +#endif diff --git a/src/digit-node.c b/src/digit-node.c new file mode 100644 index 0000000..a5e2795 --- /dev/null +++ b/src/digit-node.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stage.h> + +#include "tex.h" +#include "tex-node.h" + +#include "gfx/gfx-zero.h" +#include "gfx/gfx-one.h" +#include "gfx/gfx-two.h" +#include "gfx/gfx-three.h" +#include "gfx/gfx-four.h" +#include "gfx/gfx-five.h" +#include "gfx/gfx-six.h" +#include "gfx/gfx-seven.h" +#include "gfx/gfx-eight.h" +#include "gfx/gfx-nine.h" + +static const unsigned char *digits_pixels[10] = { + gfx_zero.pixel_data, + gfx_one.pixel_data, + gfx_two.pixel_data, + gfx_three.pixel_data, + gfx_four.pixel_data, + gfx_five.pixel_data, + gfx_six.pixel_data, + gfx_seven.pixel_data, + gfx_eight.pixel_data, + gfx_nine.pixel_data, +}; + +static tex_t *digits_tex[10]; + +#define DIGIT_WIDTH 184 +#define DIGIT_HEIGHT 288 + +stage_t * digit_node_new(stage_conf_t *conf, unsigned digit, m4f_t *model_x) +{ + assert(digit < 10); + + if (!digits_tex[digit]) + digits_tex[digit] = tex_new(DIGIT_WIDTH, DIGIT_HEIGHT, digits_pixels[digit]); + + return tex_node_new_tex(conf, digits_tex[digit], model_x); +} diff --git a/src/digit-node.h b/src/digit-node.h new file mode 100644 index 0000000..9e686de --- /dev/null +++ b/src/digit-node.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _DIGIT_NODE_H +#define _DIGIT_NODE_H + +typedef struct stage_conf_t stage_conf_t; +typedef struct m4f_t m4f_t; + +stage_t * digit_node_new(stage_conf_t *conf, unsigned digit, m4f_t *model_x); + +#endif diff --git a/src/game.c b/src/game.c new file mode 100644 index 0000000..552471b --- /dev/null +++ b/src/game.c @@ -0,0 +1,660 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <SDL.h> +#include <assert.h> + +#include <ix2.h> +#include <pad.h> +#include <play.h> +#include <stage.h> + +#include "adult-node.h" +#include "baby-node.h" +#include "bb2f.h" +#include "bb3f.h" +#include "digit-node.h" +#include "glad.h" +#include "m4f.h" +#include "m4f-3dx.h" +#include "m4f-bbx.h" +#include "macros.h" +#include "plasma-node.h" +#include "sars.h" +#include "sfx.h" +#include "tv-node.h" +#include "v2f.h" +#include "virus-node.h" + +#define GAME_NUM_VIRUSES 8 +#define GAME_NUM_BABIES 10 + +#define GAME_VIRUS_SPEED .01f +#define GAME_ADULT_SPEED .04f + +#define GAME_VIRUS_DELAY_MS 20 +#define GAME_VIRUS_TIMER PLAY_TICKS_TIMER1 + +#define GAME_TV_DELAY_MS 3000 +#define GAME_TV_TIMER PLAY_TICKS_TIMER3 + +#define GAME_KBD_DELAY_MS 20 +#define GAME_KBD_TIMER PLAY_TICKS_TIMER4 + +#define GAME_OVER_DELAY_MS 1000 +#define GAME_OVER_TIMER PLAY_TICKS_TIMER2 + +#define GAME_BABY_SCALE (v3f_t){.05f, .05f, .05f} +#define GAME_ADULT_SCALE (v3f_t){.07f, .07f, .07f} +#define GAME_TV_SCALE (v3f_t){.15f, .15f, .15f} +#define GAME_VIRUS_SCALE (v3f_t){.05f, .05f, .05f} +#define GAME_DIGITS_SCALE (v3f_t){.05f, .05f, .05f} + +/* every entity just starts with a unit cube AABB and is transformed with a matrix into its position, + * so here's a convenient aabb to feed into those transformations as needed. + */ +static const bb3f_t any_aabb = { .min = { -1.f -1.f, -1.f}, .max = { 1.f, 1.f, 1.f } }; + +typedef enum game_state_t { + GAME_STATE_PLAYING, + GAME_STATE_OVER, + GAME_STATE_OVER_DELAY, + GAME_STATE_OVER_WAITING +} game_state_t; + +typedef enum entity_type_t { + ENTITY_TYPE_BABY, + ENTITY_TYPE_ADULT, + ENTITY_TYPE_VIRUS, + ENTITY_TYPE_TV, +} entity_type_t; + +typedef union entity_t entity_t; + +typedef struct entity_any_t { + entity_type_t type; + stage_t *node; + ix2_object_t *ix2_object; + v2f_t position; + v3f_t scale; + m4f_t model_x; + bb2f_t aabb_x; +} entity_any_t; + +typedef struct baby_t { + entity_any_t entity; +} baby_t; + +typedef struct virus_t { + entity_any_t entity; +} virus_t; + +typedef struct adult_t { + entity_any_t entity; + unsigned rescues; + unsigned frozen:1; + entity_t *holding; +} adult_t; + +typedef struct tv_t { + entity_any_t entity; +} tv_t; + +union entity_t { + entity_any_t any; + baby_t baby; + virus_t virus; + adult_t adult; + tv_t tv; +}; + + +typedef struct game_t { + game_state_t state; + + stage_t *stage; + stage_t *game_node; + stage_t *babies_node; + stage_t *viruses_node; + stage_t *plasma_node; + stage_t *score_node; + ix2_t *ix2; + pad_t *pad; + + adult_t *adult; + tv_t *tv; + virus_t *viruses[GAME_NUM_VIRUSES]; + m4f_t score_digits_x[10]; +} game_t; + + +static inline float randf(void) +{ + return 2.f / RAND_MAX * rand() - 1.f; +} + + +/* update the entity's transformation and position in the index */ +static void entity_update_x(game_t *game, entity_any_t *entity) +{ + + entity->model_x = m4f_identity(); + entity->model_x = m4f_translate(&entity->model_x, &(v3f_t){entity->position.x, entity->position.y, 0.f}); + entity->model_x = m4f_scale(&entity->model_x, &entity->scale); + + /* apply the entities transform to any_aabb to get the current transformed aabb, cache it in the + * entity in case a search needs to be done... */ + m4f_mult_bb3f_bb2f(&entity->model_x, &any_aabb, &entity->aabb_x); + + if (!(entity->ix2_object)) { + entity->ix2_object = ix2_object_new(game->ix2, NULL, NULL, &entity->aabb_x, entity); + } else { + entity->ix2_object = ix2_object_move(game->ix2, entity->ix2_object, NULL, NULL, &entity->aabb_x); + } + + fatal_if(!entity->ix2_object, "Unable to update ix2 object"); +} + + +/* this is unnecessary copy and paste junk, but I'm really falling asleep here + * and need to get shit working before I pass out. + */ + +static adult_t * adult_new(game_t *game, stage_t *parent) +{ + adult_t *adult; + + adult = pad_get(game->pad, sizeof(entity_t)); + fatal_if(!adult, "unale to allocate adult_t"); + + adult->entity.type = ENTITY_TYPE_ADULT; + adult->entity.node = adult_node_new(&(stage_conf_t){ .parent = parent, .name = "adult", .layer = 3, .alpha = 1.f }, &adult->entity.model_x); + adult->entity.scale = GAME_ADULT_SCALE; + entity_update_x(game, &adult->entity); + + return adult; +} + + + +static baby_t * baby_new(game_t *game, stage_t *parent) +{ + baby_t *baby; + + baby = pad_get(game->pad, sizeof(entity_t)); + fatal_if(!baby, "unale to allocate baby_t"); + + baby->entity.type = ENTITY_TYPE_BABY; + baby->entity.node = baby_node_new(&(stage_conf_t){ .parent = parent, .name = "baby", .active = 1, .alpha = 1.f }, &baby->entity.model_x); + baby->entity.scale = GAME_BABY_SCALE; + baby->entity.position.x = randf(); + baby->entity.position.y = randf(); + entity_update_x(game, &baby->entity); + + return baby; +} + + +static tv_t * tv_new(game_t *game, stage_t *parent) +{ + tv_t *tv; + + tv = pad_get(game->pad, sizeof(entity_t)); + fatal_if(!tv, "unale to allocate tv_t"); + + tv->entity.type = ENTITY_TYPE_TV; + tv->entity.node = tv_node_new(&(stage_conf_t){ .parent = parent, .name = "tv", .layer = 1, .alpha = 1.f }, &tv->entity.model_x); + tv->entity.scale = GAME_TV_SCALE; + entity_update_x(game, &tv->entity); + + return tv; +} + + +static void randomize_virus(virus_t *virus) +{ + virus->entity.position.y = randf(); + virus->entity.position.x = randf(); +} + + +static virus_t * virus_new(game_t *game, stage_t *parent) +{ + virus_t *virus; + + virus = pad_get(game->pad, sizeof(entity_t)); + fatal_if(!virus, "unale to allocate virus_t"); + + virus->entity.type = ENTITY_TYPE_VIRUS; + virus->entity.node = virus_node_new(&(stage_conf_t){ .parent = parent, .name = "virus", .alpha = 1.f }, &virus->entity.model_x); + virus->entity.scale = GAME_VIRUS_SCALE; + randomize_virus(virus); + entity_update_x(game, &virus->entity); + + return virus; +} + + +static void reset_virus(virus_t *virus) +{ + stage_set_active(virus->entity.node, 0); + randomize_virus(virus); +} + + +typedef struct virus_search_t { + game_t *game; + virus_t *virus; +} virus_search_t; + +static ix2_search_status_t virus_search(void *cb_context, ix2_object_t *ix2_object, v2f_t *ix2_object_position, bb2f_t *ix2_object_aabb, void *object) +{ + virus_search_t *search = cb_context; + entity_t *entity = object; + + switch (entity->any.type) { + case ENTITY_TYPE_BABY: + /* convert baby into inanimate virus (off the viruses array) */ + stage_free(entity->any.node); + entity->any.node = virus_node_new(&(stage_conf_t){ .parent = search->game->viruses_node, .name = "baby-virus", .active = 1, .alpha = 1.f }, &entity->any.model_x); + sfx_play(sfx.baby_infected); + entity->any.type = ENTITY_TYPE_VIRUS; + + /* add new baby */ + (void) baby_new(search->game, search->game->babies_node); + + /* reset virus */ + reset_virus(search->virus); + + /* stop searching */ + return IX2_SEARCH_STOP_HIT; + + case ENTITY_TYPE_ADULT: + /* convert adult into inanimate virus (of the viruses array) */ + stage_free(entity->any.node); + entity->any.node = virus_node_new(&(stage_conf_t){ .parent = search->game->viruses_node, .name = "adult-virus", .active = 1, .alpha = 1.f }, &entity->any.model_x); + sfx_play(sfx.adult_infected); + search->game->state = GAME_STATE_OVER; + return IX2_SEARCH_STOP_HIT; + + case ENTITY_TYPE_VIRUS: + case ENTITY_TYPE_TV: + return IX2_SEARCH_MORE_MISS; + + default: + assert(0); + } + + /* XXX: I'm not really caring about the return value for now */ +} + + +/* animate the viruses: + * - anything newly infected becomes an inanimate virus (change their node) + * and the virus respawns somewhere + * - if the newly infected thing is the adult, the game ends + */ +static void update_viruses(play_t *play, game_t *game) +{ + virus_search_t search = { .game = game }; + + assert(play); + assert(game); + + if (randf() > .95f && !stage_get_active(game->tv->entity.node)) { + /* sometimes turn on the TV at a random location, we + * get stuck to it */ + play_ticks_reset(play, GAME_TV_TIMER); + game->tv->entity.position.x = randf(); + game->tv->entity.position.y = randf(); + entity_update_x(game, &game->tv->entity); + stage_set_active(game->tv->entity.node, 1); + } + + for (int i = 0; i < NELEMS(game->viruses); i++) { + virus_t *virus = game->viruses[i]; + + /* are they off-screen? */ + if (virus->entity.position.y > 1.2f) { + + if (stage_get_active(virus->entity.node)) { + /* active and off-screen gets randomize and inactivated */ + reset_virus(virus); + } else { + /* inactive and off-screen gets activated and moved to the + * top */ + stage_set_active(virus->entity.node, 1); + virus->entity.position.y = -1.2f; + } + } + + virus->entity.position.y += GAME_VIRUS_SPEED; + entity_update_x(game, &virus->entity); + + if (stage_get_active(virus->entity.node)) { + search.virus = virus; + + /* search ix2 for collisions */ + ix2_search_by_aabb(game->ix2, NULL, NULL, &virus->entity.aabb_x, virus_search, &search); + } + } +} + + +static ix2_search_status_t adult_search(void *cb_context, ix2_object_t *ix2_object, v2f_t *ix2_object_position, bb2f_t *ix2_object_aabb, void *object) +{ + game_t *game = cb_context; + entity_t *entity = object; + + switch (entity->any.type) { + case ENTITY_TYPE_BABY: + if (!game->adult->holding) { + sfx_play(sfx.baby_held); + game->adult->holding = entity; + } + + /* we should probably keep looking because there could be a virus too, + * but fuck it, these types of bugs are fun in silly games. + */ + return IX2_SEARCH_STOP_HIT; + + case ENTITY_TYPE_ADULT: + /* ignore ourselves */ + return IX2_SEARCH_MORE_MISS; + + case ENTITY_TYPE_VIRUS: + if (!stage_get_active(entity->any.node)) + return IX2_SEARCH_MORE_MISS; + + /* convert adult into inanimate adult (of the adultes array) */ + stage_free(game->adult->entity.node); + game->adult->entity.node = virus_node_new(&(stage_conf_t){ .parent = game->viruses_node, .name = "adult-virus", .active = 1, .alpha = 1.f }, &game->adult->entity.model_x); + sfx_play(sfx.adult_infected); + + if (game->adult->holding) { + stage_free(game->adult->holding->any.node); + game->adult->holding->any.node = virus_node_new(&(stage_conf_t){ .parent = game->viruses_node, .name = "baby-virus", .active = 1, .alpha = 1.f }, &game->adult->holding->any.model_x); + sfx_play(sfx.baby_infected); + } + game->state = GAME_STATE_OVER; + return IX2_SEARCH_STOP_HIT; + + case ENTITY_TYPE_TV: + if (!stage_get_active(entity->any.node)) + return IX2_SEARCH_MORE_MISS; + + game->adult->frozen = 1; + return IX2_SEARCH_STOP_HIT; + + default: + assert(0); + } + + /* XXX: I'm not really caring about the return value for now */ +} + + +static void game_move_adult(game_t *game, v2f_t *dir) +{ + assert(game); + assert(dir); + + if (game->adult->frozen) + return; + + game->adult->entity.position.x += dir->x; + game->adult->entity.position.y += dir->y; + + /* prevent the player from going too far off the reservation */ + if (game->adult->entity.position.x > 1.1f) + game->adult->entity.position.x = 1.1f; + + if (game->adult->entity.position.x < -1.1f) + game->adult->entity.position.x = -1.1f; + + if (game->adult->entity.position.y > 1.1f) + game->adult->entity.position.y = 1.1f; + + if (game->adult->entity.position.y < -1.1f) + game->adult->entity.position.y = -1.1f; + + entity_update_x(game, &game->adult->entity); + + if (game->adult->holding) { + game->adult->holding->any.position = game->adult->entity.position; + entity_update_x(game, &game->adult->holding->any); + + if (game->adult->entity.position.x > 1.05f || + game->adult->entity.position.x < -1.05f || + game->adult->entity.position.y > 1.05f || + game->adult->entity.position.y < -1.05f) { + + /* rescued baby */ + sfx_play(sfx.baby_rescued); + + game->adult->holding->any.position.x = randf(); + game->adult->holding->any.position.y = randf(); + entity_update_x(game, &game->adult->holding->any); + + game->adult->holding = NULL; + game->adult->rescues++; + } + } + + /* search ix2 for collisions */ + ix2_search_by_aabb(game->ix2, NULL, NULL, &game->adult->entity.aabb_x, adult_search, game); +} + + +static void reset_game(game_t *game) +{ + ix2_reset(game->ix2); + stage_free(game->game_node); + + if (game->pad) /* XXX FIXME: this is a stupidty in libpad */ + pad_free(game->pad); + + game->game_node = stage_new(&(stage_conf_t){ .parent = game->stage, .name = "game", .active = 1, .alpha = 1.f }, NULL, NULL); + + game->babies_node = stage_new(&(stage_conf_t){ .parent = game->game_node, .name = "babies", .layer = 4, .alpha = 1.f }, NULL, NULL); + game->viruses_node = stage_new(&(stage_conf_t){ .parent = game->game_node, .name = "viruses", .layer = 5, .alpha = 1.f }, NULL, NULL); + game->score_node = stage_new(&(stage_conf_t){ .parent = game->game_node, .name = "score", .layer = 7, .alpha = 1 }, NULL, NULL); + + game->pad = pad_new(sizeof(entity_t) * 32, PAD_FLAGS_ZERO); + + game->tv = tv_new(game, game->game_node); + game->adult = adult_new(game, game->game_node); + for (int i = 0; i < NELEMS(game->viruses); i++) + game->viruses[i] = virus_new(game, game->viruses_node); + + for (int i = 0; i < GAME_NUM_BABIES; i++) + (void) baby_new(game, game->babies_node); + + stage_set_active(game->adult->entity.node, 1); + stage_set_active(game->babies_node, 1); + stage_set_active(game->viruses_node, 1); + + game->state = GAME_STATE_PLAYING; +} + + +static void show_score(game_t *game) +{ + unsigned score = game->adult->rescues * 420; + + for (unsigned i = 1000000000, pos = 0; i > 0; score %= i, i /= 10, pos++) { + unsigned v = score / i; + + digit_node_new(&(stage_conf_t){ .parent = game->score_node, .name = "score-digit", .active = 1, .alpha = 1.f }, v, &game->score_digits_x[pos]); + } + + stage_set_active(game->score_node, 1); +} + + +static void * game_init(play_t *play, int argc, char *argv[]) +{ + sars_t *sars = play_context(play, SARS_CONTEXT_SARS); + game_t *game; + + assert(sars); + + game = calloc(1, sizeof(game_t)); + fatal_if(!game, "Unable to allocate game_t"); + + game->stage = sars->stage; + game->plasma_node = plasma_node_new(&(stage_conf_t){ .parent = sars->stage, .name = "plasma", .alpha = 1 }); + + game->ix2 = ix2_new(NULL, 4, 4, 1 /* increase for nested searches */); + + /* setup transformation matrices for the score digits, this is really fast and nasty hack because + * I am completely delerious and ready to fall asleep. + */ + for (int i = 0; i < NELEMS(game->score_digits_x); i++) { + game->score_digits_x[i] = m4f_identity(); + game->score_digits_x[i] = m4f_translate(&game->score_digits_x[i], &(v3f_t){ 1.f / NELEMS(game->score_digits_x) * i - .5f, 0.f, 0.f }); + game->score_digits_x[i] = m4f_scale(&game->score_digits_x[i], &GAME_DIGITS_SCALE); + } + + sfx_init(); + + return game; +} + + +static void game_enter(play_t *play, void *context) +{ + game_t *game = context; + + assert(game); + + play_music_set(play, PLAY_MUSIC_FLAG_LOOP|PLAY_MUSIC_FLAG_IDEMPOTENT, "assets/game.ogg"); + play_ticks_reset(play, GAME_VIRUS_TIMER); + stage_set_active(game->plasma_node, 1); + reset_game(game); +} + + +static void game_update(play_t *play, void *context) +{ + sars_t *sars = play_context(play, SARS_CONTEXT_SARS); + game_t *game = context; + + assert(game); + assert(sars); + + switch (game->state) { + case GAME_STATE_PLAYING: { + if (play_ticks_elapsed(play, GAME_VIRUS_TIMER, GAME_VIRUS_DELAY_MS)) + update_viruses(play, game); + + if (play_ticks_elapsed(play, GAME_KBD_TIMER, GAME_KBD_DELAY_MS)) { + const Uint8 *key_state = SDL_GetKeyboardState(NULL); + v2f_t dir = {}, *move = NULL; + + if (key_state[SDL_SCANCODE_LEFT] || key_state[SDL_SCANCODE_A]) { + dir.x += -GAME_ADULT_SPEED; + move = &dir; + } + + if (key_state[SDL_SCANCODE_RIGHT] || key_state[SDL_SCANCODE_D]) { + dir.x += GAME_ADULT_SPEED; + move = &dir; + } + + if (key_state[SDL_SCANCODE_UP] || key_state[SDL_SCANCODE_W]) { + dir.y += GAME_ADULT_SPEED; + move = &dir; + } + + if (key_state[SDL_SCANCODE_DOWN] || key_state[SDL_SCANCODE_S]) { + dir.y += -GAME_ADULT_SPEED; + move = &dir; + } + + if (move) + game_move_adult(game, move); + } + + if (play_ticks_elapsed(play, GAME_TV_TIMER, GAME_TV_DELAY_MS)) { + stage_set_active(game->tv->entity.node, 0); + game->adult->frozen = 0; + } + + break; + } + + case GAME_STATE_OVER: + show_score(game); + play_ticks_reset(play, GAME_OVER_TIMER); + game->state = GAME_STATE_OVER_DELAY; + break; + + case GAME_STATE_OVER_DELAY: + if (!play_ticks_elapsed(play, GAME_OVER_TIMER, GAME_OVER_DELAY_MS)) + break; + + /* maybe throw something on-screen? */ + game->state = GAME_STATE_OVER_WAITING; + break; + + case GAME_STATE_OVER_WAITING: + /* just do nothing and wait for a keypress of some kind */ + break; + + default: + assert(0); + } + + /* just always dirty the stage in the game context */ + stage_dirty(sars->stage); +} + + +static void game_dispatch(play_t *play, void *context, SDL_Event *event) +{ + game_t *game = context; + + /* global handlers */ + sars_dispatch(play, context, event); + + /* anything more to do here? */ + switch (event->type) { + case SDL_KEYDOWN: + if (event->key.keysym.sym == SDLK_ESCAPE) + exit(0); + + if (game->state == GAME_STATE_OVER_WAITING) { + reset_game(game); + break; + } + + break; + + default: + break; + } +} + + +const play_ops_t game_ops = { + .init = game_init, +// .shutdown = game_shutdown, + .update = game_update, + .render = sars_render, + .dispatch = game_dispatch, + .enter = game_enter, +}; diff --git a/src/hungrycat-node.c b/src/hungrycat-node.c new file mode 100644 index 0000000..da78ed9 --- /dev/null +++ b/src/hungrycat-node.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stage.h> + +#include "gfx/gfx-hungrycat.h" +#include "hungrycat-node.h" +#include "tex-node.h" + + +stage_t * hungrycat_node_new(stage_conf_t *conf, m4f_t *model_x) +{ + return tex_node_new_mem(conf, gfx_hungrycat.width, gfx_hungrycat.height, gfx_hungrycat.pixel_data, model_x); +} diff --git a/src/hungrycat-node.h b/src/hungrycat-node.h new file mode 100644 index 0000000..8c5839f --- /dev/null +++ b/src/hungrycat-node.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _HUNGRYCAT_NODE_H +#define _HUNGRYCAT_NODE_H + +typedef struct stage_conf_t stage_conf_t; +typedef struct m4f_t m4f_t; + +stage_t * hungrycat_node_new(stage_conf_t *conf, m4f_t *model_x); + +#endif diff --git a/src/hungrycat.c b/src/hungrycat.c new file mode 100644 index 0000000..bae9c10 --- /dev/null +++ b/src/hungrycat.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <SDL.h> +#include <assert.h> + +#include <play.h> +#include <stage.h> + +#include "glad.h" +#include "hungrycat-node.h" +#include "m4f.h" +#include "m4f-3dx.h" +#include "macros.h" +#include "sars.h" + +#define HUNGRYCAT_FADE_MS 3333 + +#define HUNGRYCAT_FADEIN_MS HUNGRYCAT_FADE_MS +#define HUNGRYCAT_SHOW_MS HUNGRYCAT_FADE_MS +#define HUNGRYCAT_FADEOUT_MS HUNGRYCAT_FADE_MS + +#define HUNGRYCAT_FADE_TIMER PLAY_TICKS_TIMER0 + +typedef enum hungrycat_state_t { + HUNGRYCAT_STATE_FADEIN, + HUNGRYCAT_STATE_SHOW, + HUNGRYCAT_STATE_FADEOUT, + HUNGRYCAT_STATE_LEAVE, +} hungrycat_state_t; + +typedef struct hungrycat_t { + hungrycat_state_t state; + stage_t *node; + m4f_t model_x; +} hungrycat_t; + + +static void * hungrycat_init(play_t *play, int argc, char *argv[]) +{ + sars_t *sars = play_context(play, SARS_CONTEXT_SARS); + hungrycat_t *hungrycat; + + hungrycat = calloc(1, sizeof(hungrycat_t)); + fatal_if(!hungrycat, "Unable to allocate hungrycat_t"); + + hungrycat->node = hungrycat_node_new(&(stage_conf_t){ .parent = sars->stage, .name = "hungrycat", .active = 1 }, &hungrycat->model_x); + fatal_if(!hungrycat->node, "Unable to create hungrycat->node"); + + hungrycat->model_x = m4f_identity(); + hungrycat->model_x = m4f_scale(&hungrycat->model_x, &(v3f_t){1.f, .25f, .5f}); + + return hungrycat; +} + + +static void hungrycat_enter(play_t *play, void *context) +{ + hungrycat_t *hungrycat = context; + + assert(hungrycat); + + play_music_set(play, 0, "assets/hungrycat.ogg"); + play_ticks_reset(play, HUNGRYCAT_FADE_TIMER); +} + + +static float fade_t(play_t *play) +{ + return (1.f / (float)HUNGRYCAT_FADE_MS) * (float)play_ticks(play, HUNGRYCAT_FADE_TIMER); +} + + +static void hungrycat_update(play_t *play, void *context) +{ + hungrycat_t *hungrycat = context; + + assert(hungrycat); + + switch (hungrycat->state) { + case HUNGRYCAT_STATE_FADEIN: + stage_dirty(hungrycat->node); + if (!play_ticks_elapsed(play, HUNGRYCAT_FADE_TIMER, HUNGRYCAT_FADE_MS)) { + stage_set_alpha(hungrycat->node, fade_t(play)); + break; + } + + stage_set_alpha(hungrycat->node, 1.f); + hungrycat->state = HUNGRYCAT_STATE_SHOW; + break; + + case HUNGRYCAT_STATE_SHOW: + if (!play_ticks_elapsed(play, HUNGRYCAT_FADE_TIMER, HUNGRYCAT_FADE_MS)) + break; + + hungrycat->state = HUNGRYCAT_STATE_FADEOUT; + break; + + case HUNGRYCAT_STATE_FADEOUT: + stage_dirty(hungrycat->node); + if (!play_ticks_elapsed(play, HUNGRYCAT_FADE_TIMER, HUNGRYCAT_FADE_MS)) { + stage_set_alpha(hungrycat->node, 1.f - fade_t(play)); + break; + } + + stage_set_alpha(hungrycat->node, 0.f); + hungrycat->state = HUNGRYCAT_STATE_LEAVE; + break; + + case HUNGRYCAT_STATE_LEAVE: + play_context_enter(play, SARS_CONTEXT_GAME); + break; + + default: + assert(0); + } +} + + +static void hungrycat_leave(play_t *play, void *context) +{ + hungrycat_t *hungrycat = context; + + assert(hungrycat); + + /* we never reenter this context since it's just a splash, so + * the context leave is effectively the shutdown, cleanup. + */ + stage_free(hungrycat->node); + free(hungrycat); + /* XXX: this is icky though, play_context(SARS_CONTEXT_HUNGRYCAT) will + * return a dangling pointer! Not that it occurs anywhere though. + */ +} + + +const play_ops_t hungrycat_ops = { + .init = hungrycat_init, + .update = hungrycat_update, + .render = sars_render, + .dispatch = sars_dispatch, + .enter = hungrycat_enter, + .leave = hungrycat_leave, +}; diff --git a/src/m4f-3dx.h b/src/m4f-3dx.h new file mode 100644 index 0000000..384641b --- /dev/null +++ b/src/m4f-3dx.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2018-2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _M4F_3DX_H +#define _M4F_3DX_H + +#include <math.h> + +#include "m4f.h" +#include "v3f.h" + +/* helpers for manipulating 3D transformation matrices */ + +/* XXX: note this is column-major */ + +/* adjust the matrix m to translate by v, returning the resulting matrix */ +/* if m is NULL the identity vector is assumed */ +static inline m4f_t m4f_translate(const m4f_t *m, const v3f_t *v) +{ + m4f_t identity = m4f_identity(); + m4f_t translate = m4f_identity(); + + if (!m) + m = &identity; + + translate.m[3][0] = v->x; + translate.m[3][1] = v->y; + translate.m[3][2] = v->z; + + return m4f_mult(m, &translate); +} + + +/* adjust the matrix m to scale by v, returning the resulting matrix */ +/* if m is NULL the identity vector is assumed */ +static inline m4f_t m4f_scale(const m4f_t *m, const v3f_t *v) +{ + m4f_t identity = m4f_identity(); + m4f_t scale = {}; + + if (!m) + m = &identity; + + scale.m[0][0] = v->x; + scale.m[1][1] = v->y; + scale.m[2][2] = v->z; + scale.m[3][3] = 1.f; + + return m4f_mult(m, &scale); +} + + +/* adjust the matrix m to rotate around the specified axis by radians, returning the resulting matrix */ +/* axis is expected to be a unit vector */ +/* if m is NULL the identity vector is assumed */ +static inline m4f_t m4f_rotate(const m4f_t *m, const v3f_t *axis, float radians) +{ + m4f_t identity = m4f_identity(); + float cos_r = cosf(radians); + float sin_r = sinf(radians); + m4f_t rotate; + + if (!m) + m = &identity; + + rotate.m[0][0] = cos_r + axis->x * axis->x * (1.f - cos_r); + rotate.m[0][1] = axis->y * axis->x * (1.f - cos_r) + axis->z * sin_r; + rotate.m[0][2] = axis->z * axis->x * (1.f - cos_r) - axis->y * sin_r; + rotate.m[0][3] = 0.f; + + rotate.m[1][0] = axis->x * axis->y * (1.f - cos_r) - axis->z * sin_r; + rotate.m[1][1] = cos_r + axis->y * axis->y * (1.f - cos_r); + rotate.m[1][2] = axis->z * axis->y * (1.f - cos_r) + axis->x * sin_r; + rotate.m[1][3] = 0.f; + + rotate.m[2][0] = axis->x * axis->z * (1.f - cos_r) + axis->y * sin_r; + rotate.m[2][1] = axis->y * axis->z * (1.f - cos_r) - axis->x * sin_r; + rotate.m[2][2] = cos_r + axis->z * axis->z * (1.f - cos_r); + rotate.m[2][3] = 0.f; + + rotate.m[3][0] = 0.f; + rotate.m[3][1] = 0.f; + rotate.m[3][2] = 0.f; + rotate.m[3][3] = 1.f; + + return m4f_mult(m, &rotate); +} + + +/* this is a simple perpsective projection matrix taken from an opengl tutorial */ +static inline m4f_t m4f_frustum(float bot, float top, float left, float right, float nnear, float ffar) +{ + m4f_t m = {}; + + m.m[0][0] = 2 * nnear / (right - left); + + m.m[1][1] = 2 * nnear / (top - bot); + + m.m[2][0] = (right + left) / (right - left);; + m.m[2][1] = (top + bot) / (top - bot); + m.m[2][2] = -(ffar + nnear) / (ffar - nnear); + m.m[2][3] = -1; + + m.m[3][2] = -2 * ffar * nnear / (ffar - nnear); + + return m; +} + +#endif diff --git a/src/m4f-bbx.h b/src/m4f-bbx.h new file mode 100644 index 0000000..bcae848 --- /dev/null +++ b/src/m4f-bbx.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _M4F_BBX_H +#define _M4F_BBX_H + +#include <float.h> + +#include "bb2f.h" +#include "bb3f.h" +#include "m4f.h" +#include "macros.h" + + +/* multiply a matrix and bb3f */ +static bb3f_t * m4f_mult_bb3f(const m4f_t *transform, const bb3f_t *aabb, bb3f_t *res_aabb) +{ + v3f_t box[8] = { + { aabb->min.x, aabb->min.y, aabb->min.z }, + { aabb->min.x, aabb->min.y, aabb->max.z }, + { aabb->min.x, aabb->max.y, aabb->max.z }, + { aabb->min.x, aabb->max.y, aabb->min.z }, + { aabb->max.x, aabb->min.y, aabb->min.z }, + { aabb->max.x, aabb->min.y, aabb->max.z }, + { aabb->max.x, aabb->max.y, aabb->max.z }, + { aabb->max.x, aabb->max.y, aabb->min.z }, + }; + + bb3f_t _aabb = { + .min = { FLT_MAX, FLT_MAX, FLT_MAX }, + .max = { -FLT_MAX, -FLT_MAX, -FLT_MAX}, + }; + + for (int i = 0; i < NELEMS(box); i++) { + v3f_t X = m4f_mult_v3f(transform, &box[i]); + + if (_aabb.min.x > X.x) + _aabb.min.x = X.x; + if (_aabb.max.x < X.x) + _aabb.max.x = X.x; + + if (_aabb.min.y > X.y) + _aabb.min.y = X.y; + if (_aabb.max.y < X.y) + _aabb.max.y = X.y; + + if (_aabb.min.z > X.z) + _aabb.min.z = X.z; + if (_aabb.max.z < X.z) + _aabb.max.z = X.z; + } + + *res_aabb = _aabb; + + return res_aabb; +} + + +/* multiply a matrix and a bb3f but return only the x,y components in a bb2f */ +/* Caller must supply the storage in res_aabb, for convenience this is also returned. */ +static bb2f_t * m4f_mult_bb3f_bb2f(const m4f_t *transform, const bb3f_t *aabb, bb2f_t *res_aabb) +{ + bb3f_t _aabb; + + m4f_mult_bb3f(transform, aabb, &_aabb); + + res_aabb->min.x = _aabb.min.x; + res_aabb->min.y = _aabb.min.y; + res_aabb->max.x = _aabb.max.x; + res_aabb->max.y = _aabb.max.y; + + return res_aabb; +} + + +#endif diff --git a/src/plasma-node.c b/src/plasma-node.c new file mode 100644 index 0000000..1bc0e40 --- /dev/null +++ b/src/plasma-node.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2018-2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <SDL.h> + +#include <play.h> +#include <stage.h> + +#include "glad.h" +#include "plasma-node.h" +#include "shader-node.h" + + +static const char *plasma_vs = "" + "#version 120\n" + + "attribute vec3 vertex;" + "attribute vec2 texcoord;" + + "void main()" + "{" + " gl_TexCoord[0].xy = texcoord;" + //" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;" + " gl_Position = vec4(vertex, 1.f);" + "}" +""; + + +// derived from https://www.bidouille.org/prog/plasma +static const char *plasma_fs = "" + "#version 120\n" + + "#define PI 3.1415926535897932384626433832795\n" + + "uniform float alpha;" + "uniform float time;" + + "void main() {" + " float v;" + " float stime = sin(time * .01f) * 100.f;" + + " vec2 c = gl_TexCoord[0].st;" + + // this zooms the texture coords in and out a bit with time + " c *= (sin(stime * .01f) *.5f + .5f) * 3.f + 1.f;" + + // plasma calculations, stime instead of time directly to vary directions and speed + " v = sin((c.x + stime));" + " v += sin((c.y + stime) * .5f);" + " v += sin((c.x + c.y +stime) * .5f);" + + " c += vec2(sin(stime * .33f), cos(stime * .5f)) * 3.f;" + + " v += sin(sqrt(c.x * c.x + c.y * c.y + 1.f) + stime);" + + " vec3 col = vec3(cos(PI * v + sin(time)), sin(PI * v + cos(time * .33f)), cos(PI * v + sin(time * .66f)));" + " gl_FragColor = vec4(col * .5f + .5f, alpha);" + "}" +""; + + +static void plasma_uniforms(void *uniforms_ctxt, void *render_ctxt, unsigned n_uniforms, const int *uniforms, const m4f_t *model_x, float alpha) +{ + play_t *play = render_ctxt; + + glUniform1f(uniforms[0], alpha); + glUniform1f(uniforms[1], play_ticks(play, PLAY_TICKS_TIMER0) * .001f); // FIXME KLUDGE ALERT +} + + +/* create plasma rendering stage */ +stage_t * plasma_node_new(const stage_conf_t *conf) +{ + return shader_node_new_src(conf, plasma_vs, plasma_fs, NULL, plasma_uniforms, NULL, 2, + (const char *[]){ + "alpha", + "time", + } + ); +} diff --git a/src/plasma-node.h b/src/plasma-node.h new file mode 100644 index 0000000..1607ece --- /dev/null +++ b/src/plasma-node.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018-2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _PLASMA_NODE_H +#define _PLASMA_NODE_H + +typedef struct stage_t stage_t; +typedef struct stage_conf_t stage_conf_t; + +stage_t * plasma_node_new(const stage_conf_t *conf); + +#endif diff --git a/src/sfx.c b/src/sfx.c new file mode 100644 index 0000000..54a8bce --- /dev/null +++ b/src/sfx.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <SDL_mixer.h> + +#include "sfx.h" + +sfx_t sfx; + + +void sfx_init(void) +{ + sfx.baby_infected = Mix_LoadWAV("assets/baby-infected.wav"); + sfx.baby_held = Mix_LoadWAV("assets/baby-held.wav"); + sfx.baby_rescued = Mix_LoadWAV("assets/baby-rescued.wav"); + sfx.adult_infected = Mix_LoadWAV("assets/adult-infected.wav"); +} + + +void sfx_play(Mix_Chunk *chunk) +{ + if (chunk) + Mix_PlayChannel(-1, chunk, 0); +} diff --git a/src/sfx.h b/src/sfx.h new file mode 100644 index 0000000..a2f3214 --- /dev/null +++ b/src/sfx.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _SFX_H +#define _SFX_H + +#include <SDL_mixer.h> + +typedef struct sfx_t { + Mix_Chunk *baby_infected; + Mix_Chunk *baby_held; + Mix_Chunk *baby_rescued; + Mix_Chunk *adult_infected; +} sfx_t; + +extern sfx_t sfx; + +void sfx_init(void); +void sfx_play(Mix_Chunk *chunk); + +#endif diff --git a/src/shader-node.c b/src/shader-node.c new file mode 100644 index 0000000..5a21c89 --- /dev/null +++ b/src/shader-node.c @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2018-2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdlib.h> + +#include <stage.h> + +#include "glad.h" +#include "m4f.h" +#include "macros.h" +#include "shader.h" +#include "shader-node.h" +#include "v2f.h" + +typedef struct shader_node_t { + shader_t *shader; + shader_node_uniforms_func_t *uniforms_func; + void *uniforms_ctxt; + const m4f_t *transform; +} shader_node_t; + +static unsigned vbo, tcbo; + +static const float vertices[] = { + +1.f, +1.f, 0.f, + +1.f, -1.f, 0.f, + -1.f, +1.f, 0.f, + +1.f, -1.f, 0.f, + -1.f, -1.f, 0.f, + -1.f, +1.f, 0.f, +}; + + +/* TODO: verify that this is OK, I recall tutorials stating these are always + * in the range 0-1, but it seems perfectly OK to use -1..+1 which is more + * convenient here where these shader-textured quads appreciate being fed + * unit square coordinates with 0,0 @ the center. + */ +static const float texcoords[] = { + 1.f, 1.f, + 1.f, -1.f, + -1.f, 1.f, + 1.f, -1.f, + -1.f, -1.f, + -1.f, 1.f, +}; + + +static void shader_node_render(const stage_t *stage, void *object, float alpha, void *render_ctxt) +{ + shader_node_t *shader_node = object; + unsigned n_uniforms; + int *uniforms, *attributes; + + assert(stage); + assert(shader_node); + + shader_use(shader_node->shader, &n_uniforms, &uniforms, NULL, &attributes); + + if (shader_node->uniforms_func) + shader_node->uniforms_func(shader_node->uniforms_ctxt, render_ctxt, n_uniforms, uniforms, shader_node->transform, alpha); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glVertexAttribPointer(attributes[0], 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glEnableVertexAttribArray(attributes[0]); + + glBindBuffer(GL_ARRAY_BUFFER, tcbo); + glVertexAttribPointer(attributes[1], 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0); + glEnableVertexAttribArray(attributes[1]); + + /* XXX: this could be made optional, but since alpha is a constant throughout the stage + * integration I'm just always turning it on so stage_set_alpha() always works. There + * are definitely full-screen full-opaque shader node situations where the pointless + * performance hit sucks though, especially on older hardware. + */ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glDrawArrays(GL_TRIANGLES, 0, 6); + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + + +static void shader_node_free(const stage_t *stage, void *object) +{ + shader_node_t *shader_node = object; + + assert(stage); + assert(shader_node); + + /* XXX FIXME: hmm, maybe the caller should supply a shader_t ** instead */ + (void) shader_free(shader_node->shader); + free(shader_node); +} + + +static const stage_ops_t shader_node_ops = { + .render_func = shader_node_render, + .free_func = shader_node_free, +}; + + +/* return a new shader stage node from an already compiled and linked shader program */ +stage_t * shader_node_new_shader(const stage_conf_t *conf, shader_t *shader, const m4f_t *transform, shader_node_uniforms_func_t *uniforms_func, void *uniforms_ctxt) +{ + shader_node_t *shader_node; + stage_t *stage; + + assert(conf); + assert(shader); + + if (!vbo) { + /* common to all shader nodes */ + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glGenBuffers(1, &tcbo); + glBindBuffer(GL_ARRAY_BUFFER, tcbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(texcoords), texcoords, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + shader_node = calloc(1, sizeof(shader_node_t)); + fatal_if(!shader_node, "Unable to allocate shader_node"); + + shader_ref(shader); + shader_node->shader = shader; + shader_node->uniforms_func = uniforms_func; + shader_node->uniforms_ctxt = uniforms_ctxt; + shader_node->transform = transform; + + stage = stage_new(conf, &shader_node_ops, shader_node); + fatal_if(!stage, "Unable to create stage \"%s\"", conf->name); + + return stage; +} + + +/* return a new shader stage node from source */ +stage_t * shader_node_new_src(const stage_conf_t *conf, const char *vs_src, const char *fs_src, const m4f_t *transform, shader_node_uniforms_func_t *uniforms_func, void *uniforms_ctxt, unsigned n_uniforms, const char **uniforms) +{ + stage_t *stage; + shader_t *shader; + + assert(vs_src); + assert(fs_src); + + shader = shader_pair_new(vs_src, fs_src, n_uniforms, uniforms, + 2, + (const char *[]) { + "vertex", + "texcoord", + }); + stage = shader_node_new_shader(conf, shader, transform, uniforms_func, uniforms_ctxt); + shader_free(shader); + + return stage; +} diff --git a/src/shader-node.h b/src/shader-node.h new file mode 100644 index 0000000..34fccc2 --- /dev/null +++ b/src/shader-node.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018-2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _SHADER_NODE_H +#define _SHADER_NODE_H + +typedef struct shader_t shader_t; +typedef struct stage_t stage_t; +typedef struct stage_conf_t stage_conf_t; +typedef struct m4f_t m4f_t; + +typedef void (shader_node_uniforms_func_t)(void *uniforms_ctxt, void *render_ctxt, unsigned n_uniforms, const int *uniforms, const m4f_t *transform, float alpha); + +stage_t * shader_node_new_shader(const stage_conf_t *conf, shader_t *shader, const m4f_t *transform, shader_node_uniforms_func_t *uniforms_func, void *uniforms_ctxt); +stage_t * shader_node_new_src(const stage_conf_t *conf, const char *vs_src, const char *fs_src, const m4f_t *transform, shader_node_uniforms_func_t *uniforms_func, void *uniforms_ctxt, unsigned n_uniforms, const char **uniforms); + +#endif diff --git a/src/shader.c b/src/shader.c new file mode 100644 index 0000000..e54f9d5 --- /dev/null +++ b/src/shader.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2018-2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdlib.h> + +#include "glad.h" +#include "macros.h" +#include "shader.h" + + +typedef struct shader_t { + unsigned program, refcnt; + unsigned n_uniforms, n_attributes; + int *uniforms, *attributes; + + int locations[]; +} shader_t; + + +unsigned int shader_pair_new_bare(const char *vs_src, const char *fs_src) +{ + unsigned int vertex_shader, fragment_shader, shader; + int shader_success; + char shader_info[4096]; + + vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, &vs_src, NULL); + glCompileShader(vertex_shader); + glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &shader_success); + if (!shader_success) { + glGetShaderInfoLog(vertex_shader, sizeof(shader_info), NULL, shader_info); + fatal_if(1, "Error compiling vertex shader: \"%s\"", shader_info); + } + + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &fs_src, NULL); + glCompileShader(fragment_shader); + glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &shader_success); + if (!shader_success) { + glGetShaderInfoLog(fragment_shader, sizeof(shader_info), NULL, shader_info); + fatal_if(1, "Error compiling fragment shader: \"%s\"", shader_info); + } + + shader = glCreateProgram(); + glAttachShader(shader, vertex_shader); + glAttachShader(shader, fragment_shader); + glLinkProgram(shader); + glGetProgramiv(shader, GL_LINK_STATUS, &shader_success); + if (!shader_success) { + glGetProgramInfoLog(shader, sizeof(shader_info), NULL, shader_info); + fatal_if(1, "Error linking shader program: \"%s\"", shader_info); + } + + glDeleteShader(vertex_shader); + glDeleteShader(fragment_shader); + + return shader; +} + + +shader_t * shader_pair_new(const char *vs_src, const char *fs_src, unsigned n_uniforms, const char **uniforms, unsigned n_attributes, const char **attributes) +{ + shader_t *shader; + + assert(vs_src); + assert(fs_src); + assert(uniforms || !n_uniforms); + assert(attributes || !n_attributes); + + shader = calloc(1, sizeof(shader_t) + (n_uniforms + n_attributes) * sizeof(int)); + fatal_if(!shader, "Unable to allocate shader"); + + shader->program = shader_pair_new_bare(vs_src, fs_src); + shader->refcnt++; + shader->n_uniforms = n_uniforms; + shader->n_attributes = n_attributes; + shader->uniforms = shader->locations; + shader->attributes = &shader->locations[n_uniforms]; + + for (unsigned i = 0; i < n_uniforms; i++) + shader->uniforms[i] = glGetUniformLocation(shader->program, uniforms[i]); + + for (unsigned i = 0; i < n_attributes; i++) + shader->attributes[i] = glGetAttribLocation(shader->program, attributes[i]); + + return shader; +} + + +void shader_ref(shader_t *shader) +{ + assert(shader); + + shader->refcnt++; +} + + +shader_t * shader_free(shader_t *shader) +{ + assert(shader); + + shader->refcnt--; + if (shader->refcnt > 0) + return shader; + + glDeleteProgram(shader->program); + free(shader); + + return NULL; +} + + +void shader_use(shader_t *shader, unsigned *res_n_uniforms, int **res_uniforms, unsigned *res_n_attributes, int **res_attributes) +{ + assert(shader); + + if (res_n_uniforms) + *res_n_uniforms = shader->n_uniforms; + + if (res_uniforms) + *res_uniforms = shader->uniforms; + + if (res_n_attributes) + *res_n_attributes = shader->n_attributes; + + if (res_attributes) + *res_attributes = shader->attributes; + + glUseProgram(shader->program); +} diff --git a/src/shader.h b/src/shader.h new file mode 100644 index 0000000..c0c5be5 --- /dev/null +++ b/src/shader.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018-2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _SHADER_H +#define _SHADER_H + +typedef struct shader_t shader_t; + +unsigned int shader_pair_new_bare(const char *vs_src, const char *fs_src); +shader_t * shader_pair_new(const char *vs_src, const char *fs_src, unsigned n_uniforms, const char **uniforms, unsigned n_attributes, const char **attributes); +void shader_ref(shader_t *shader); +shader_t * shader_free(shader_t *shader); +void shader_use(shader_t *shader, unsigned *res_n_uniforms, int **res_uniforms, unsigned *res_n_attributes, int **res_attributes); + +#endif diff --git a/src/tex-node.c b/src/tex-node.c new file mode 100644 index 0000000..6d049ca --- /dev/null +++ b/src/tex-node.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2018-2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* TODO: this could totally be built upon shader-node.[ch], it would just supply + * shaders that do texturing and a uniform func that binds in the texture... + */ + +#include <assert.h> +#include <stdlib.h> + +#include <stage.h> + +#include "glad.h" +#include "m4f.h" +#include "macros.h" +#include "shader.h" +#include "tex-node.h" +#include "tex.h" + +typedef struct tex_node_t { + tex_t *tex; + m4f_t *model_x; +} tex_node_t; + + +/* Render simply renders a texd texture onto the screen */ +static void tex_node_render(const stage_t *stage, void *object, float alpha, void *render_ctxt) +{ + tex_node_t *tex_node = object; + + assert(stage); + assert(tex_node); + + tex_render(tex_node->tex, alpha, tex_node->model_x); +} + + +static void tex_node_free(const stage_t *stage, void *object) +{ + tex_node_t *tex_node = object; + + assert(stage); + assert(tex_node); + + tex_free(tex_node->tex); +} + +static const stage_ops_t tex_node_ops = { + .render_func = tex_node_render, + .free_func = tex_node_free, +}; + + +/* retun a tex node from a reusable refcounted tex instance */ +stage_t * tex_node_new_tex(stage_conf_t *conf, tex_t *tex, m4f_t *model_x) +{ + tex_node_t *tex_node; + stage_t *s; + + assert(conf); + + tex_node = calloc(1, sizeof(tex_node_t)); + fatal_if(!tex_node, "Unable to allocate tex_node \"%s\"", conf->name); + + s = stage_new(conf, &tex_node_ops, tex_node); + fatal_if(!s, "Unable to create stage \"%s\"", conf->name); + + tex_node->tex = tex_ref(tex); + tex_node->model_x = model_x; + + return s; + +} + + +/* return a tex node from a pix array + * the pixels are used in-place and no duplicate is made. + */ +stage_t * tex_node_new_mem(stage_conf_t *conf, int width, int height, const unsigned char *buf, m4f_t *model_x) +{ + tex_t *tex = tex_new(width, height, buf); + stage_t *stage = tex_node_new_tex(conf, tex_new(width, height, buf), model_x); + + tex_free(tex); + + return stage; +} diff --git a/src/tex-node.h b/src/tex-node.h new file mode 100644 index 0000000..59c731b --- /dev/null +++ b/src/tex-node.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _TEX_NODE_H +#define _TEX_NODE_H + +typedef struct m4f_t m4f_t; +typedef struct stage_t stage_t; +typedef struct tex_t tex_t; + +stage_t * tex_node_new_tex(stage_conf_t *conf, tex_t *tex, m4f_t *model_x); +stage_t * tex_node_new_mem(stage_conf_t *conf, int width, int height, const unsigned char *buf, m4f_t *model_x); + +#endif diff --git a/src/tex.c b/src/tex.c new file mode 100644 index 0000000..b8254e1 --- /dev/null +++ b/src/tex.c @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdlib.h> + +#include "glad.h" +#include "m4f.h" +#include "macros.h" +#include "shader.h" +#include "tex.h" + +typedef struct tex_t { + unsigned tex; + unsigned refcnt; +} tex_t; + +static unsigned vbo, tcbo; +static shader_t *tex_shader; + +static const float vertices[] = { + +1.f, +1.f, 0.f, + +1.f, -1.f, 0.f, + -1.f, +1.f, 0.f, + +1.f, -1.f, 0.f, + -1.f, -1.f, 0.f, + -1.f, +1.f, 0.f, +}; + +static const float texcoords[] = { + 1.f, 0.f, + 1.f, 1.f, + 0.f, 0.f, + 1.f, 1.f, + 0.f, 1.f, + 0.f, 0.f, +}; + + +static const char *tex_vs = "" + "#version 120\n" + + "uniform mat4 model_x;" + + "attribute vec3 vertex;" + "attribute vec2 texcoord;" + + "void main()" + "{" + " gl_TexCoord[0].xy = texcoord;" + " gl_Position = model_x * vec4(vertex, 1.f);" + "}" +""; + + +static const char *tex_fs = "" + "#version 120\n" + + "uniform sampler2D tex0;" + "uniform float alpha;" + + "void main()" + "{" + " gl_FragColor = texture2D(tex0, gl_TexCoord[0].st);" + " gl_FragColor.a *= alpha;" + "}" +""; + + +/* Render simply renders a texd texture onto the screen */ +void tex_render(tex_t *tex, float alpha, m4f_t *model_x) +{ + int *uniforms, *attributes; + + assert(tex); + assert(model_x); + + shader_use(tex_shader, NULL, &uniforms, NULL, &attributes); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glVertexAttribPointer(attributes[0], 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glEnableVertexAttribArray(attributes[0]); + + glBindBuffer(GL_ARRAY_BUFFER, tcbo); + glVertexAttribPointer(attributes[1], 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0); + glEnableVertexAttribArray(attributes[1]); + + glBindTexture(GL_TEXTURE_2D, tex->tex); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glUniform1f(uniforms[0], alpha); + glUniformMatrix4fv(uniforms[1], 1, GL_FALSE, &model_x->m[0][0]); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + glBindTexture(GL_TEXTURE_2D, 0); + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + + +tex_t * tex_new(int width, int height, const unsigned char *buf) +{ + tex_t *tex; + + assert(buf); + + if (!vbo) { + /* common to all tex instances */ + tex_shader = shader_pair_new(tex_vs, tex_fs, + 2, + (const char *[]) { + "alpha", + "model_x", + }, + 2, + (const char *[]) { + "vertex", + "texcoord", + }); + + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glGenBuffers(1, &tcbo); + glBindBuffer(GL_ARRAY_BUFFER, tcbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(texcoords), texcoords, GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + tex = calloc(1, sizeof(tex_t)); + fatal_if(!tex, "Unable to allocate tex_t"); + + glGenTextures(1, &tex->tex); + glBindTexture(GL_TEXTURE_2D, tex->tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); + glBindTexture(GL_TEXTURE_2D, 0); + + tex->refcnt = 1; + + return tex; +} + + +tex_t * tex_ref(tex_t *tex) +{ + assert(tex); + + tex->refcnt++; + + return tex; +} + + +tex_t * tex_free(tex_t *tex) +{ + if (!tex) + return NULL; + + assert(tex->refcnt > 0); + + tex->refcnt--; + if (!tex->refcnt) { + glDeleteTextures(1, &tex->tex); + free(tex); + } + + return NULL; +} diff --git a/src/tex.h b/src/tex.h new file mode 100644 index 0000000..00e5759 --- /dev/null +++ b/src/tex.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _TEX_H +#define _TEX_H + +#include <stdint.h> + +typedef struct tex_t tex_t; +typedef struct m4f_t m4f_t; + +void tex_render(tex_t *tex, float alpha, m4f_t *model_x); +tex_t * tex_new(int width, int height, const unsigned char *buf); +tex_t * tex_ref(tex_t *tex); +tex_t * tex_free(tex_t *tex); + +#endif diff --git a/src/tv-node.c b/src/tv-node.c new file mode 100644 index 0000000..267e2e2 --- /dev/null +++ b/src/tv-node.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stage.h> + +#include "tv-node.h" +#include "gfx/gfx-tv.h" +#include "tex.h" +#include "tex-node.h" + +static tex_t *tv_tex; + +stage_t * tv_node_new(stage_conf_t *conf, m4f_t *model_x) +{ + if (!tv_tex) + tv_tex = tex_new(gfx_tv.width, gfx_tv.height, gfx_tv.pixel_data); + + return tex_node_new_tex(conf, tv_tex, model_x); +} diff --git a/src/tv-node.h b/src/tv-node.h new file mode 100644 index 0000000..3974994 --- /dev/null +++ b/src/tv-node.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _TV_NODE_H +#define _TV_NODE_H + +typedef struct stage_conf_t stage_conf_t; +typedef struct m4f_t m4f_t; + +stage_t * tv_node_new(stage_conf_t *conf, m4f_t *model_x); + +#endif diff --git a/src/virus-node.c b/src/virus-node.c new file mode 100644 index 0000000..bf52385 --- /dev/null +++ b/src/virus-node.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stage.h> + +#include "virus-node.h" +#include "gfx/gfx-virus.h" +#include "tex.h" +#include "tex-node.h" + +static tex_t *virus_tex; + +stage_t * virus_node_new(stage_conf_t *conf, m4f_t *model_x) +{ + if (!virus_tex) + virus_tex = tex_new(gfx_virus.width, gfx_virus.height, gfx_virus.pixel_data); + + return tex_node_new_tex(conf, virus_tex, model_x); +} diff --git a/src/virus-node.h b/src/virus-node.h new file mode 100644 index 0000000..3c437ce --- /dev/null +++ b/src/virus-node.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _VIRUS_NODE_H +#define _VIRUS_NODE_H + +typedef struct stage_conf_t stage_conf_t; +typedef struct m4f_t m4f_t; + +stage_t * virus_node_new(stage_conf_t *conf, m4f_t *model_x); + +#endif |