├── Common.mk ├── Module.mk ├── Rpi.mk ├── SDL.mk ├── audio.c ├── audio.h ├── audio_rpi.c ├── audio_sdl.c ├── audio_test.c ├── common.scm ├── evolution.scm ├── gambitmain.c ├── game.c ├── game.h ├── joystick.c ├── joystick.h ├── link.scm ├── list_modes.c ├── listlib.c ├── listlib.h ├── math.scm ├── memory.c ├── memory.h ├── monster ├── .dropbox.attr ├── Example.SCML ├── desktop.ini ├── mon_arms │ ├── forearm_0.png │ ├── forearm_a.png │ ├── hand_0_0.png │ ├── hand_0_1.png │ ├── hand_0_2.png │ ├── hand_0_3.png │ ├── hand_a_0.png │ ├── hand_a_1.png │ ├── hand_a_2.png │ ├── hand_a_3.png │ ├── shoulder_0.png │ └── shoulder_a.png ├── mon_head │ ├── head_0.png │ ├── head_1.png │ ├── head_2.png │ └── head_3.png ├── mon_legs │ ├── foot_0.png │ ├── foot_a.png │ ├── pelvis_0.png │ ├── thigh_0.png │ └── thigh_a.png ├── mon_shadows │ └── shadow_idle.png └── mon_torso │ └── torso_0.png ├── object.scm ├── particle-main.scm ├── realmain.c ├── rect.scm ├── sampler.c ├── sampler.h ├── scmlib.scm ├── spacer ├── enemy-bullet.png ├── hero-engines.png ├── hero.png ├── images_default.png ├── images_default.xml ├── night-sky-stars.jpg ├── plasma.png ├── ship-right.png └── smoke.png ├── sparrow.scm ├── spatial.scm ├── spriter-main.scm ├── spriter-util.scm ├── spriter.scm ├── stb_image.c ├── stb_image.h ├── test.png ├── testcase.h ├── testlib.c ├── testlib.h ├── testlib_gl.c ├── testlib_internal.h ├── testlib_rpi.c ├── testlib_sdl.c ├── testlib_test.c ├── threadlib.c ├── threadlib.h ├── vector.c ├── vector.h └── xml2.scm /Common.mk: -------------------------------------------------------------------------------- 1 | C_SRC+= \ 2 | threadlib.c memory.c listlib.c testlib.c \ 3 | sampler.c audio.c game.c vector.c \ 4 | gambitmain.c realmain.c stb_image.c 5 | 6 | SCM_LIB_SRC=link.scm 7 | 8 | GAMBIT_ROOT?=/usr/local/Gambit-C 9 | GSC=$(GAMBIT_ROOT)/bin/gsc 10 | XML_INCLUDE:=-I/usr/include/libxml2 11 | CFLAGS+=-I$(GAMBIT_ROOT)/include $(XML_INCLUDE) 12 | LDFLAGS+=-L$(GAMBIT_ROOT)/lib -lpthread 13 | 14 | MKMOD=make -f Module.mk 15 | MAKE_XML2=$(MKMOD) SCM_SRC=xml2.scm OUTPUT=xml2 CFLAGS="$(CFLAGS)" LDFLAGS="-lxml2" 16 | 17 | 18 | SCM_LIB_C=$(patsubst %.scm,%.c,$(SCM_LIB_SRC)) \ 19 | link_.c 20 | 21 | SCM_OBJ=$(patsubst %.c,%.o,$(SCM_LIB_C)) 22 | C_OBJS=$(patsubst %.c,%.o,$(C_SRC)) 23 | SCM_GAMBIT_OBJ=$(patsubst %.scm,%.o1,$(SCM_GAMBIT_SRC)) 24 | SCM_R5_OBJ=$(patsubst %.scm,%.o1,$(SCM_R5_SRC)) 25 | 26 | all: $(BIN) $(SCM_GAMBIT_OBJ) $(SCM_R5_OBJ) xml2.o1.o 27 | 28 | SCM_FILES=math.scm common.scm scmlib.scm rect.scm spatial.scm sparrow.scm 29 | 30 | scmlib: 31 | $(GSC) -track-scheme -keep-c $(SCM_FILES) 32 | 33 | $(SCM_LIB_C): $(SCM_LIB_SRC) 34 | $(GSC) -f -link -track-scheme $(SCM_LIB_SRC) 35 | 36 | $(SCM_OBJ): $(SCM_LIB_C) 37 | $(CC) -D___DYNAMIC $(CFLAGS) -c $(SCM_LIB_C) 38 | 39 | $(SCM_R5_OBJ): $(SCM_R5_SRC) 40 | $(GSC) -:s -o $@ $< 41 | 42 | $(SCM_GAMBIT_OBJ): $(SCM_GAMBIT_SRC) 43 | $(GSC) -o $@ $< 44 | 45 | $(BIN): $(SCM_OBJ) $(C_OBJS) 46 | $(CC) $(CFLAGS) -o $@ $(C_OBJS) $(SCM_OBJ) $(LDFLAGS) -lgambc 47 | 48 | clean: 49 | rm -rf *.o* $(SCM_LIB_C) $(BIN) 50 | $(MAKE_XML2) clean $(patsubst %.scm,%.c,$(SCM_FILES)) 51 | 52 | test_bin: memory.o testlib_test.o 53 | $(CC) $(CFLAGS) -o $@ memory.o testlib_test.o $(LDFLAGS) 54 | 55 | test: test_bin 56 | ./test_bin 57 | 58 | xml2.o1.o: xml2.scm 59 | $(MAKE_XML2) 60 | 61 | xml2: xml2.o1.o 62 | 63 | .phony: all 64 | -------------------------------------------------------------------------------- /Module.mk: -------------------------------------------------------------------------------- 1 | GAMBIT_ROOT?=/usr/local/Gambit-C 2 | GSC=$(GAMBIT_ROOT)/bin/gsc 3 | 4 | SCM_VERSION?=1 5 | SCMLIB=$(OUTPUT).o$(SCM_VERSION) 6 | SCM_C=$(patsubst %.scm,%.c,$(SCM_SRC)) $(SCMLIB).c 7 | SCM_O=$(patsubst %.c,%.o,$(SCM_C)) 8 | 9 | all: $(SCMLIB) 10 | 11 | $(SCMLIB): $(SCM_SRC) 12 | $(GSC) -keep-c -cc-options "-D___DYNAMIC $(CFLAGS)" -ld-options "$(LDFLAGS)" -o $(SCMLIB) $(SCM_SRC) 13 | 14 | clean: 15 | rm -rf $(SCMLIB) $(SCM_C) $(SCM_O) 16 | -------------------------------------------------------------------------------- /Rpi.mk: -------------------------------------------------------------------------------- 1 | C_SRC=testlib_rpi.c audio_rpi.c joystick.c 2 | BIN=pimain 3 | 4 | # from https://github.com/raspberrypi/firmware/blob/master/opt/vc/src/hello_pi/Makefile.include 5 | CFLAGS+=-I$(SDKSTAGE)/opt/vc/include/ -I$(SDKSTAGE)/opt/vc/include/interface/vcos/pthreads -I/opt/vc/src/hello_pi/libs/ilclient/ 6 | 7 | LDFLAGS+=-L$(SDKSTAGE)/opt/vc/lib/ -lGLESv2 -lEGL -lbcm_host -lvcos -lutil -L/opt/vc/src/hello_pi/libs/ilclient/ -lilclient -lopenmaxil 8 | 9 | include Common.mk 10 | 11 | %.o: %.c 12 | @rm -f $@ 13 | $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ -Wno-deprecated-declarations 14 | 15 | # testlib needs c99 enabled for the FOREACH macro to be implementable 16 | testlib.o: testlib.c 17 | @rm -f $@ 18 | $(CC) -std=c99 $(CFLAGS) $(INCLUDES) -c $< -o $@ -Wno-deprecated-declarations 19 | -------------------------------------------------------------------------------- /SDL.mk: -------------------------------------------------------------------------------- 1 | C_SRC=testlib_sdl.c audio_sdl.c 2 | BIN=sdlmain 3 | 4 | SDL_LIBS:=`sdl-config --libs` 5 | 6 | PLATFORM:=$(shell uname) 7 | ifeq ($(PLATFORM), Darwin) 8 | LDFLAGS+= -framework OpenGL 9 | else 10 | LDFLAGS+= -lGL -lm -ldl -lutil 11 | endif 12 | 13 | CFLAGS+=-std=c99 `sdl-config --cflags` 14 | LDFLAGS+=$(SDL_LIBS) $(OPENGL) 15 | 16 | include Common.mk 17 | 18 | # force include of SDL header so that it can do it's main redirection 19 | # magic 20 | gambitmain.o: gambitmain.c 21 | $(CC) $(CFLAGS) -c $< -include "SDL/SDL.h" 22 | 23 | audio_test: audio_test.c sampler.c 24 | gcc -g -o audio_test sampler.c audio_test.c `sdl-config --libs` `sdl-config --cflags` 25 | -------------------------------------------------------------------------------- /audio.c: -------------------------------------------------------------------------------- 1 | #include "threadlib.h" 2 | #include "audio.h" 3 | #include "memory.h" 4 | 5 | PlayList playlist; 6 | Queue audio_queue; 7 | Filter global_filter; 8 | FixedAllocator pls_allocator; 9 | 10 | PlayListSample playlistsample_make(Sampler sampler) { 11 | PlayListSample pl = (PlayListSample)fixed_allocator_alloc(pls_allocator); 12 | pl->sampler = sampler; 13 | return pl; 14 | } 15 | 16 | void playlistsample_free(PlayListSample pls) { 17 | RELEASE_SAMPLER(pls->sampler); 18 | fixed_allocator_free(pls_allocator, pls); 19 | } 20 | 21 | PlayList playlist_make() { 22 | PlayList pl = malloc(sizeof(struct PlayList_)); 23 | pl->head = NULL; 24 | pl->next_sample = 0; 25 | return pl; 26 | } 27 | 28 | void playlist_insert_sampler(PlayList list, PlayListSample sample) { 29 | if(list->head == NULL) { 30 | list->head = sample; 31 | sample->node.next = NULL; 32 | return; 33 | } 34 | 35 | if(START(sample->sampler) < START(list->head->sampler)) { 36 | sample->node.next = (DLLNode)list->head; 37 | list->head = sample; 38 | return; 39 | } 40 | 41 | PlayListSample last_node = list->head; 42 | PlayListSample current_node = (PlayListSample)list->head->node.next; 43 | 44 | while(current_node != NULL) { 45 | if(START(sample->sampler) < START(current_node->sampler)) { 46 | // insert before this node 47 | sample->node.next = (DLLNode)current_node; 48 | last_node->node.next = (DLLNode)sample; 49 | return; // done 50 | } 51 | 52 | last_node = current_node; 53 | current_node = (PlayListSample)current_node->node.next; 54 | } 55 | 56 | // must be after the end 57 | sample->node.next = NULL; 58 | last_node->node.next = (DLLNode)sample; 59 | } 60 | 61 | void playlist_fill_buffer(PlayList list, int16_t* buffer, int nsamples) { 62 | int ii; 63 | long next_sample = list->next_sample; 64 | list->next_sample += nsamples; 65 | 66 | for(ii = 0; ii < nsamples; ii+=2) { 67 | long sample = next_sample + ii; 68 | PlayListSample node; 69 | 70 | /* mixing strategy outlined at: 71 | * http://www.vttoth.com/CMS/index.php/technical-notes/68 72 | */ 73 | float value = 0; 74 | for(node = list->head; node != NULL; 75 | node = (PlayListSample)node->node.next) { 76 | if(START(node->sampler) > sample) break; 77 | int16_t sampled = SAMPLE(node->sampler, sample); 78 | float normalized = (float)sampled / INT16_MAX; 79 | value = value + normalized - (value * normalized); 80 | } 81 | 82 | buffer[ii] = INT16_MAX * value; //filter_value(global_filter, value); 83 | buffer[ii+1] = buffer[ii]; 84 | } 85 | 86 | /* remove any nodes that are no longer playable */ 87 | while(list->head != NULL && 88 | END(list->head->sampler) < list->next_sample) { 89 | PlayListSample node = list->head; 90 | list->head = (PlayListSample)node->node.next; 91 | playlistsample_free(node); 92 | } 93 | } 94 | 95 | void audio_init() { 96 | sampler_init(); 97 | pls_allocator = fixed_allocator_make(sizeof(struct PlayListSample_), 98 | NUM_SAMPLERS, 99 | "pls_allocator"); 100 | 101 | playlist = playlist_make(); 102 | audio_queue = queue_make(); 103 | global_filter = lowpass_make(0, 0); 104 | 105 | native_audio_init(); 106 | } 107 | 108 | void audio_enqueue(Sampler sampler) { 109 | enqueue(audio_queue, (DLLNode)playlistsample_make(sampler)); 110 | } 111 | 112 | long audio_current_sample() { 113 | /* race condition but we don't care */ 114 | return playlist->next_sample; 115 | } 116 | 117 | void audio_fill_buffer(int16_t* buffer, int nsamples) { 118 | PlayListSample sample; 119 | while((sample = (PlayListSample)dequeue_noblock(audio_queue)) != NULL) { 120 | playlist_insert_sampler(playlist, sample); 121 | } 122 | 123 | playlist_fill_buffer(playlist, buffer, nsamples); 124 | } 125 | -------------------------------------------------------------------------------- /audio.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIO_H 2 | #define AUDIO_H 3 | 4 | #include "sampler.h" 5 | #include "listlib.h" 6 | 7 | typedef struct PlayListSample_ { 8 | struct DLLNode_ node; 9 | Sampler sampler; 10 | } *PlayListSample; 11 | 12 | typedef struct PlayList_ { 13 | PlayListSample head; 14 | long next_sample; 15 | } *PlayList; 16 | 17 | PlayListSample playlistsample_make(Sampler sampler); 18 | void playlistsample_free(PlayListSample pls); 19 | void playlist_insert_sample(PlayList list, PlayListSample sample); 20 | void playlist_fill_buffer(PlayList list, int16_t* buffer, int nsamples); 21 | 22 | /* high level api */ 23 | void audio_init(); 24 | void audio_enqueue(Sampler sampler); 25 | long audio_current_sample(); 26 | 27 | void audio_fill_buffer(int16_t* buffer, int nsamples); 28 | 29 | /* provided by the system specific library */ 30 | void native_audio_init(); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /audio_rpi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "audio.h" 4 | #include "bcm_host.h" 5 | #include "ilclient.h" 6 | #include "threadlib.h" 7 | 8 | #define NUM_SAMPLES 1024 9 | 10 | ILCLIENT_T *client; 11 | COMPONENT_T *audio_render; 12 | pthread_t audio_thread; 13 | 14 | int scaled_buffer_size(int samples) { 15 | return (samples * 16 * 2) >> 3; 16 | } 17 | 18 | uint32_t audio_get_latency() { 19 | OMX_PARAM_U32TYPE param; 20 | OMX_ERRORTYPE error; 21 | 22 | memset(¶m, 0, sizeof(OMX_PARAM_U32TYPE)); 23 | param.nSize = sizeof(OMX_PARAM_U32TYPE); 24 | param.nVersion.nVersion = OMX_VERSION; 25 | param.nPortIndex = 100; 26 | 27 | error = OMX_GetConfig(ILC_GET_HANDLE(audio_render), OMX_IndexConfigAudioRenderingLatency, ¶m); 28 | assert(error == OMX_ErrorNone); 29 | 30 | return param.nU32; 31 | } 32 | 33 | void* audio_exec(void* udata) { 34 | float buffer_time_us = (float)(1e6 * NUM_SAMPLES) / SAMPLE_FREQ; 35 | while(1) { 36 | /* get a buffer */ 37 | OMX_BUFFERHEADERTYPE *hdr; 38 | while((hdr = ilclient_get_input_buffer(audio_render, 100, 0)) == NULL) { 39 | usleep(buffer_time_us / 4); // wait 1/4 of the time to drain a buffer 40 | } 41 | 42 | // fill the buffer 43 | audio_fill_buffer((int16_t*)hdr->pBuffer, NUM_SAMPLES * 2); 44 | hdr->nOffset = 0; 45 | hdr->nFilledLen = scaled_buffer_size(NUM_SAMPLES); 46 | 47 | // submit the buffer 48 | OMX_ERRORTYPE error; 49 | error = OMX_EmptyThisBuffer(ILC_GET_HANDLE(audio_render), hdr); 50 | assert(error == OMX_ErrorNone); 51 | 52 | // drive down the latency to a buffer's length or less 53 | uint32_t latency; 54 | while(audio_get_latency() > NUM_SAMPLES) { 55 | usleep(buffer_time_us / 2); 56 | } 57 | } 58 | } 59 | 60 | void native_audio_init() { 61 | OMX_ERRORTYPE error; 62 | OMX_PARAM_PORTDEFINITIONTYPE param; 63 | OMX_AUDIO_PARAM_PCMMODETYPE pcm; 64 | int32_t s; 65 | 66 | // create and start up everything 67 | client = ilclient_init(); 68 | assert(client != NULL); 69 | 70 | error = OMX_Init(); 71 | assert(error == OMX_ErrorNone); 72 | 73 | ilclient_create_component(client, &audio_render, "audio_render", ILCLIENT_ENABLE_INPUT_BUFFERS | ILCLIENT_DISABLE_ALL_PORTS); 74 | assert(audio_render != NULL); 75 | 76 | // set up the number/size of buffers 77 | memset(¶m, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); 78 | param.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); 79 | param.nVersion.nVersion = OMX_VERSION; 80 | param.nPortIndex = 100; 81 | 82 | error = OMX_GetParameter(ILC_GET_HANDLE(audio_render), 83 | OMX_IndexParamPortDefinition, ¶m); 84 | assert(error == OMX_ErrorNone); 85 | 86 | int size = scaled_buffer_size(NUM_SAMPLES); 87 | size = (size + 15) & ~15; 88 | param.nBufferSize = size; 89 | param.nBufferCountActual = 2; 90 | 91 | error = OMX_SetParameter(ILC_GET_HANDLE(audio_render), 92 | OMX_IndexParamPortDefinition, ¶m); 93 | assert(error == OMX_ErrorNone); 94 | 95 | // set the pcm parameters 96 | memset(&pcm, 0, sizeof(OMX_AUDIO_PARAM_PCMMODETYPE)); 97 | pcm.nSize = sizeof(OMX_AUDIO_PARAM_PCMMODETYPE); 98 | pcm.nVersion.nVersion = OMX_VERSION; 99 | pcm.nPortIndex = 100; 100 | pcm.nChannels = 2; 101 | pcm.eNumData = OMX_NumericalDataSigned; 102 | pcm.eEndian = OMX_EndianLittle; 103 | pcm.nSamplingRate = SAMPLE_FREQ; 104 | pcm.bInterleaved = OMX_TRUE; 105 | pcm.nBitPerSample = 16; 106 | pcm.ePCMMode = OMX_AUDIO_PCMModeLinear; 107 | pcm.eChannelMapping[0] = OMX_AUDIO_ChannelLF; 108 | pcm.eChannelMapping[1] = OMX_AUDIO_ChannelRF; 109 | 110 | error = OMX_SetParameter(ILC_GET_HANDLE(audio_render), OMX_IndexParamAudioPcm, &pcm); 111 | assert(error == OMX_ErrorNone); 112 | 113 | assert(ilclient_change_component_state(audio_render, OMX_StateIdle) == 0); 114 | if(ilclient_enable_port_buffers(audio_render, 100, NULL, NULL, NULL) < 0) { 115 | // error 116 | ilclient_change_component_state(audio_render, OMX_StateLoaded); 117 | 118 | COMPONENT_T* list[2]; 119 | list[0] = audio_render; 120 | list[1] = 0; 121 | ilclient_cleanup_components(list); 122 | 123 | error = OMX_Deinit(); 124 | assert(error == OMX_ErrorNone); 125 | 126 | ilclient_destroy(client); 127 | exit(-1); 128 | } 129 | 130 | assert(ilclient_change_component_state(audio_render, OMX_StateExecuting) == 0); 131 | 132 | // set the destination 133 | OMX_CONFIG_BRCMAUDIODESTINATIONTYPE ar_dest; 134 | 135 | const char* name = "hdmi"; 136 | memset(&ar_dest, 0, sizeof(ar_dest)); 137 | ar_dest.nSize = sizeof(OMX_CONFIG_BRCMAUDIODESTINATIONTYPE); 138 | ar_dest.nVersion.nVersion = OMX_VERSION; 139 | strcpy((char *)ar_dest.sName, name); 140 | 141 | error = OMX_SetConfig(ILC_GET_HANDLE(audio_render), OMX_IndexConfigBrcmAudioDestination, &ar_dest); 142 | assert(error == OMX_ErrorNone); 143 | 144 | // get the buffer flow going 145 | pthread_create(&audio_thread, NULL, audio_exec, NULL); 146 | } 147 | -------------------------------------------------------------------------------- /audio_sdl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "audio.h" 6 | #include "memory.h" 7 | 8 | #define NUM_SAMPLES 2048 9 | 10 | char * audio_pre_buffer; 11 | CircularBuffer audio_buffer; 12 | pthread_t audio_thread; 13 | pthread_mutex_t audio_mutex; 14 | 15 | int buffer_samples_can_write() { 16 | int result; 17 | pthread_mutex_lock(&audio_mutex); 18 | result = circularbuffer_bytes_writable(audio_buffer); 19 | pthread_mutex_unlock(&audio_mutex); 20 | return result / 2; 21 | } 22 | 23 | void* audio_exec(void* udata) { 24 | float buffer_time_us = (float)(1e6 * NUM_SAMPLES) / SAMPLE_FREQ; 25 | int sample_thresh = NUM_SAMPLES / 2; 26 | while(1) { 27 | // wait till we can write a good chunk 28 | int nsamples; 29 | while((nsamples = buffer_samples_can_write()) < sample_thresh) { 30 | usleep(buffer_time_us / 4); 31 | } 32 | 33 | // compute the audio that we know we need 34 | audio_fill_buffer((int16_t*)audio_pre_buffer, nsamples); 35 | 36 | // now lock the circular buffer and copy 37 | pthread_mutex_lock(&audio_mutex); 38 | 39 | int s1, s2; 40 | char *b1, *b2; 41 | int nbytes = nsamples * 2; 42 | circularbuffer_write_buffers(audio_buffer, &b1, &s1, &b2, &s2, 43 | nbytes); 44 | 45 | int to_write = MIN(nbytes, s1); 46 | memcpy(b1, audio_pre_buffer, to_write); 47 | nbytes -= to_write; 48 | 49 | if(nbytes > 0) { 50 | to_write = MIN(nbytes, s2); 51 | memcpy(b2, &audio_pre_buffer[s1], to_write); 52 | } 53 | 54 | pthread_mutex_unlock(&audio_mutex); 55 | 56 | // don't immediately bang on the lock 57 | usleep(buffer_time_us / 4); 58 | } 59 | } 60 | 61 | void fill_audio(void *udata, Uint8 *stream, int len) { 62 | int nwords = len / 2; 63 | int s1, s2; 64 | char *b1, *b2; 65 | 66 | pthread_mutex_lock(&audio_mutex); 67 | circularbuffer_read_buffers(audio_buffer, &b1, &s1, &b2, &s2, 68 | len); 69 | 70 | int tocopy = MIN(len, s1); 71 | memcpy(stream, b1, tocopy); 72 | len -= tocopy; 73 | 74 | if(len > 0) { 75 | tocopy = MIN(len, s2); 76 | memcpy(stream, b2, tocopy); 77 | len -= tocopy; 78 | } 79 | 80 | pthread_mutex_unlock(&audio_mutex); 81 | } 82 | 83 | /* 84 | void fill_audio(void *udata, Uint8 *stream, int len) { 85 | audio_fill_buffer((int16_t*)stream, len / 2); 86 | } 87 | */ 88 | 89 | void native_audio_init() { 90 | // twice the number of samples in the sdl buffer (2 bytes per 91 | // channel per sample) 92 | int buffer_size = NUM_SAMPLES * 2 * 2 * 2; 93 | audio_buffer = circularbuffer_make(buffer_size); 94 | audio_pre_buffer = malloc(buffer_size); 95 | 96 | pthread_mutex_init(&audio_mutex, NULL); 97 | pthread_create(&audio_thread, NULL, audio_exec, NULL); 98 | 99 | SDL_AudioSpec wanted; 100 | 101 | // Set the audio format 102 | wanted.freq = SAMPLE_FREQ; 103 | wanted.format = AUDIO_S16SYS; 104 | wanted.channels = 2; // 1 = mono, 2 = stereo 105 | wanted.samples = NUM_SAMPLES; // Good low-latency value for callback 106 | wanted.padding = 0; 107 | wanted.callback = fill_audio; 108 | wanted.userdata = NULL; 109 | 110 | // Open the audio device, forcing the desired format 111 | if ( SDL_OpenAudio(&wanted, NULL) < 0 ) { 112 | fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError()); 113 | exit(-1); 114 | } 115 | 116 | SDL_PauseAudio(0); 117 | } 118 | -------------------------------------------------------------------------------- /audio_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "sampler.h" 3 | 4 | long last_count = 0; 5 | float gfreq = 5000; 6 | //long gsample_freq = 44100 / 2; //22050; 7 | long gamp = 32767; 8 | 9 | 10 | FiniteSequence gsequence; 11 | Filter gfilter; 12 | 13 | /* The audio function callback takes the following parameters: 14 | stream: A pointer to the audio buffer to be filled 15 | len: The length (in bytes) of the audio buffer 16 | */ 17 | void fill_audio(void *udata, Uint8 *stream, int len) 18 | { 19 | int nwords = len / 2; 20 | int ii; 21 | Sint16 *words = (Sint16*)stream; 22 | 23 | for(ii = 0; ii < nwords; ++ii) { 24 | words[ii] = SAMPLE(gfilter, ii + last_count); 25 | } 26 | 27 | last_count += nwords; 28 | } 29 | 30 | void sample_to_file(Sampler sampler, float sample_freq, float max, FILE* out) { 31 | int ii = 0; 32 | long samples = max * SAMPLE_FREQ; 33 | Filter filter = lowpass_make(sampler, 2000, sample_freq); 34 | for (ii = 0; ii < samples; ++ii) { 35 | float t = (float)ii / SAMPLE_FREQ; 36 | fprintf(out, "%f, %d, %d\n", t, 37 | SAMPLE(sampler, ii), 38 | SAMPLE(filter, ii)); 39 | } 40 | } 41 | 42 | int main(int argc, char ** argv) { 43 | float cutoff = 2000; 44 | 45 | if(argc > 1) { 46 | cutoff = atoi(argv[1]); 47 | } 48 | 49 | if(argc > 2) { 50 | gamp = atoi(argv[2]); 51 | } 52 | 53 | float freqs[] = {C_(1), D_(1), E_(1), E_(1), 54 | C_(1), D_(1), E_(1), 55 | C_(1), D_(1), E_(1), D_(1), C_(1), D_(1), C_(1)}; 56 | 57 | gsequence = make_sequence(freqs, array_size(freqs), gamp, 0.13); 58 | gfilter = lowpass_make((Sampler)gsequence, cutoff, SAMPLE_FREQ); 59 | 60 | FILE * seqfile = fopen("seq.csv", "w"); 61 | sample_to_file((Sampler)gsequence, SAMPLE_FREQ, 1.0, seqfile); 62 | fclose(seqfile); 63 | 64 | SDL_AudioSpec wanted; 65 | 66 | // Set the audio format 67 | wanted.freq = SAMPLE_FREQ; 68 | wanted.format = AUDIO_S16SYS; 69 | wanted.channels = 1; // 1 = mono, 2 = stereo 70 | wanted.samples = 1024; // Good low-latency value for callback 71 | //wanted.size = 4096; 72 | wanted.padding = 0; 73 | wanted.callback = fill_audio; 74 | wanted.userdata = NULL; 75 | 76 | // Open the audio device, forcing the desired format 77 | if ( SDL_OpenAudio(&wanted, NULL) < 0 ) { 78 | fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError()); 79 | return(-1); 80 | } 81 | 82 | SDL_PauseAudio(0); 83 | SDL_Delay(2500); 84 | 85 | RELEASE_SAMPLER(gfilter); 86 | 87 | SDL_CloseAudio(); 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /common.scm: -------------------------------------------------------------------------------- 1 | 2 | (define (reduce fn init lst) 3 | (let loop ((result init) 4 | (lst lst)) 5 | (if (null? lst) result 6 | (loop (fn result (car lst)) 7 | (cdr lst))))) 8 | 9 | (define (filter-reverse pred lst) 10 | (reduce (lambda (result item) 11 | (if (pred item) 12 | (cons item result) 13 | result)) 14 | '() 15 | lst)) 16 | 17 | (define (filter pred lst) 18 | (reverse (filter-reverse pred lst))) 19 | 20 | (define (remove-if pred lst) 21 | (filter (lambda (x) (not (pred x))) lst)) 22 | 23 | (define (some? pred lst) 24 | (let loop ((lst lst)) 25 | (cond 26 | ((null? lst) #f) 27 | ((pred (car lst)) #t) 28 | (#t (loop (cdr lst)))))) 29 | 30 | (define (repeatedly fn n) 31 | (let loop ((result '()) 32 | (n n)) 33 | (if (> n 0) 34 | (loop (cons (fn) result) 35 | (- n 1)) 36 | (reverse result)))) 37 | 38 | (define (identity o) o) 39 | 40 | (define (concat lsts) 41 | (let ((result '())) 42 | (let loop-lsts ((lsts lsts)) 43 | (if (null? lsts) 44 | (reverse result) 45 | (let loop ((lst (car lsts))) 46 | (if (null? lst) 47 | (loop-lsts (cdr lsts)) 48 | (begin 49 | (set! result (cons (car lst) result)) 50 | (loop (cdr lst))))))))) 51 | 52 | (define (mapcat fn lst) 53 | (concat (map fn lst))) 54 | 55 | 56 | (define (delete-reverse item lst) 57 | (filter-reverse (lambda (lst-item) 58 | (not (eq? item lst-item))) 59 | lst)) 60 | 61 | (define (delete item lst) 62 | (reverse (delete-reverse item lst))) 63 | 64 | (define (not-null? p) 65 | (not (null? p))) 66 | 67 | (define (index-of item lst) 68 | (let loop ((lst lst) 69 | (idx 0)) 70 | (cond 71 | ((null? lst) #f) 72 | ((eq? item (car lst)) idx) 73 | (#t (loop (cdr lst) (+ idx 1)))))) 74 | -------------------------------------------------------------------------------- /evolution.scm: -------------------------------------------------------------------------------- 1 | (load "object") 2 | (load "math") 3 | (load "common") 4 | 5 | (define (consume! predator resources resource? range-rect amount) 6 | (let loop ((objs (spatial-rect table range-rect)) 7 | (amt amt)) 8 | (cond 9 | ((null? objs) #f) 10 | ((resource? (car objs)) 11 | ))) 12 | (let* ((start (table-ref table resource #f)) 13 | (end (and start (- start amt)))) 14 | (if (and end (> end 0)) 15 | (begin 16 | (table-set! table resource end) 17 | #t) 18 | #f))) 19 | 20 | (define (consume-each! table range-rect resources) 21 | (let loop ((resources resources) 22 | (success #t)) 23 | (if (null? resources) 24 | success 25 | (loop (cdr resources) 26 | (consume! table range-rect (caar resources) (cdar resources)))))) 27 | 28 | (define (update obj resources) 29 | (obj-invoke obj 'update resources)) 30 | 31 | (define (spawn obj) 32 | (obj-invoke obj 'spawn)) 33 | 34 | (define (within? this radius) 35 | (let ((tx (obj-field this 'x)) 36 | (ty (obj-field this 'y))) 37 | (lambda (other) 38 | (let ((ox (obj-field other 'x)) 39 | (oy (obj-field other 'y))) 40 | (> radius (dist tx ty ox oy)))))) 41 | 42 | (define (range-rect this) 43 | (let ((range (obj-field this 'range)) 44 | (px (obj-field this 'x)) 45 | (py (obj-field this 'y))) 46 | (rect-make-radius px py range))) 47 | 48 | (define (resources-in-range this resources pred) 49 | (let* ((neighbors (spatial-rect resources (range-rect this))) 50 | (range? (within? this (obj-field this 'range)))) 51 | (filter (lambda (o) (and (range? o) (pred o))) neighbors))) 52 | 53 | ;;; markov chain AI with decaying self-transition 54 | (define (random-symbol symbols) 55 | (let loop ((value (random-real)) 56 | (symbols symbols)) 57 | (cond 58 | ((null? symbols) (error "symbols didn't cover [0,1]")) 59 | ((< value (cdar symbols)) (caar symbols)) 60 | (#t (loop (- value (cdar symbols)) 61 | (cdr symbols)))))) 62 | 63 | (define-structure timeline value) 64 | 65 | (define timeline-make make-timeline) 66 | 67 | (define (timeline-update timeline dt) 68 | (let* ((last (timeline-value timeline)) 69 | (next (+ last dt))) 70 | (timeline-value-set! timeline next) 71 | next)) 72 | 73 | (define (markov-transition symbols state) 74 | (random-symbol (cdr (assoc state symbols)))) 75 | 76 | (define (exponential-decay time time-constant) 77 | (exp (/ time (- time-constant)))) 78 | 79 | (define idle-markov 80 | '((initial . ((hunting . 0.5) 81 | (sleeping . 0.5))) 82 | (hunting . ((sleeping . 0.5) 83 | (hunting . 0.1) 84 | (mating . 0.4))) 85 | (sleeping . ((sleeping . 0.6) 86 | (hunting . 0.2) 87 | (mating . 0.2))) 88 | (mating . ((sleeping . 0.8) 89 | (hunting . 0.1) 90 | (mating . 0.1))))) 91 | 92 | (define (predator-update this resources) 93 | (let* ((prey? (obj-field this 'prey?))) 94 | (let () 95 | (if (apply consume-each! resources prey) 96 | (list this (spawn this)) 97 | '())))) 98 | 99 | (define predator (obj-make fields: (list 'update predator-update 100 | 'range 1 101 | 'x 0 102 | 'y 0))) 103 | 104 | (define (fox-make chickens-per-cycle) 105 | (obj-make parent: predator 106 | fields: (list 'spawn (lambda (this) (fox-make chickens-per-cycle)) 107 | 'prey (list 'chicken chickens-per-cycle)))) 108 | 109 | (define (chicken-make grain-per-cycle) 110 | (obj-make parent: predator 111 | fields: (list 'spawn (lambda (this) (chicken-make grain-per-cycle)) 112 | 'prey (list 'grain grain-per-cycle)))) 113 | 114 | (define world (spatial-make 100)) 115 | 116 | (define (resources-seed resources) 117 | (define (random-point-rect) 118 | (rect-make-point (random-integer 100) (random-integer 100))) 119 | 120 | (repeatedly 121 | (lambda () 122 | (spatial-update! resources 123 | (fox-make (random-integer 3)) 124 | (random-point-rect))) 125 | (random-integer 50)) 126 | 127 | (repeatedly 128 | (lambda () 129 | (spatial-update! resources 130 | (chicken-make (random-integer 3)) 131 | (random-point-rect))) 132 | (random-integer 50))) 133 | -------------------------------------------------------------------------------- /gambitmain.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define ___VERSION 406006 4 | #include 5 | 6 | #define SCHEME_LIBRARY_LINKER ____20_link__ 7 | ___BEGIN_C_LINKAGE 8 | extern ___mod_or_lnk SCHEME_LIBRARY_LINKER (___global_state_struct*); 9 | ___END_C_LINKAGE 10 | 11 | extern int real_main(int argc, char ** argv); 12 | extern void at_exit(); 13 | 14 | void gambit_cleanup() { 15 | ___cleanup(); 16 | } 17 | 18 | int main(int argc, char ** argv) { 19 | ___setup_params_struct setup_params; 20 | ___setup_params_reset(&setup_params); 21 | setup_params.version = ___VERSION; 22 | setup_params.linker = SCHEME_LIBRARY_LINKER; 23 | setup_params.min_heap = 5000000; 24 | 25 | ___setup(&setup_params); 26 | 27 | int result = real_main(argc, argv); 28 | 29 | gambit_cleanup(); 30 | 31 | return result; 32 | } 33 | -------------------------------------------------------------------------------- /game.c: -------------------------------------------------------------------------------- 1 | #include "game.h" 2 | #include "testlib.h" 3 | #include "vector.h" 4 | #include "listlib.h" 5 | #include "memory.h" 6 | 7 | #include 8 | #include 9 | 10 | #define NUM_GAME_PARTICLES 30 11 | 12 | float player_speed = 600; 13 | float enemy_speed = 50; 14 | 15 | typedef struct GameParticle_ { 16 | struct DLLNode_ node; 17 | Vector pos; 18 | Vector vel; 19 | ImageResource image; 20 | float scale; 21 | float angle; 22 | } *GameParticle; 23 | 24 | FixedAllocator particle_allocator; 25 | 26 | GameParticle player; 27 | struct DLL_ enemies; 28 | 29 | ImageResource stars; 30 | ImageResource image_enemy; 31 | 32 | Clock main_clock; 33 | 34 | int rand_in_range(int lower, int upper) { 35 | int range = upper - lower; 36 | return lower + (rand() % range); 37 | } 38 | 39 | Sprite frame_resource_sprite(ImageResource resource) { 40 | Sprite sprite = frame_make_sprite(); 41 | sprite->resource = resource; 42 | sprite->w = resource->w; 43 | sprite->h = resource->h; 44 | return sprite; 45 | } 46 | 47 | GameParticle gameparticle_make() { 48 | GameParticle particle = fixed_allocator_alloc(particle_allocator); 49 | particle->scale = 1.0; 50 | particle->angle = 0; 51 | return particle; 52 | } 53 | 54 | void gameparticle_free(GameParticle particle) { 55 | fixed_allocator_free(particle_allocator, particle); 56 | } 57 | 58 | float gameparticle_width(GameParticle particle) { 59 | return particle->image->w; 60 | } 61 | 62 | float gameparticle_height(GameParticle particle) { 63 | return particle->image->h; 64 | } 65 | 66 | Sprite gameparticle_sprite(GameParticle particle) { 67 | Sprite sprite = frame_make_sprite(); 68 | sprite->resource = particle->image; 69 | sprite->w = gameparticle_width(particle); 70 | sprite->h = gameparticle_height(particle); 71 | sprite->displayX = particle->pos.x; 72 | sprite->displayY = particle->pos.y; 73 | sprite->originX = 0.5; 74 | sprite->originY = 0.5; 75 | sprite->angle = particle->angle; 76 | return sprite; 77 | } 78 | 79 | void gameparticle_integrate(GameParticle particle, float dt) { 80 | vector_integrate(&particle->pos, &particle->pos, &particle->vel, dt); 81 | } 82 | 83 | SpriteList gameparticles_spritelist(DLL list) { 84 | SpriteList result = NULL; 85 | GameParticle p = (GameParticle)list->head; 86 | while(p) { 87 | result = frame_spritelist_append(result, gameparticle_sprite(p)); 88 | p = (GameParticle)p->node.next; 89 | } 90 | return result; 91 | } 92 | 93 | GameParticle spawn_enemy() { 94 | GameParticle enemy = gameparticle_make(); 95 | enemy->image = image_enemy; 96 | enemy->pos.x = screen_width + (image_enemy->w / 2); 97 | 98 | int nrows = ceil(screen_height / image_enemy->h); 99 | enemy->pos.y = 100 | image_enemy->h * rand_in_range(0, nrows) 101 | - (image_enemy->h / 2); 102 | enemy->vel.x = -rand_in_range(enemy_speed, 2*enemy_speed); 103 | enemy->vel.y = 0; 104 | enemy->angle = 180.0; 105 | dll_add_head(&enemies, (DLLNode)enemy); 106 | return enemy; 107 | } 108 | 109 | void enemies_update(float dt) { 110 | GameParticle p = (GameParticle)enemies.head; 111 | 112 | // step all enemies forward and remove those that are offscreen 113 | while(p != NULL) { 114 | gameparticle_integrate(p, dt); 115 | GameParticle next = (GameParticle)p->node.next; 116 | 117 | if(p->pos.x < -(gameparticle_width(p) / 2)) { 118 | dll_remove(&enemies, (DLLNode)p); 119 | gameparticle_free(p); 120 | } 121 | 122 | p = next; 123 | } 124 | 125 | if(dll_count(&enemies) < 10) { 126 | spawn_enemy(); 127 | } 128 | } 129 | 130 | void sprite_submit(Sprite sprite) { 131 | SpriteList sl = frame_spritelist_append(NULL, sprite); 132 | spritelist_enqueue_for_screen(sl); 133 | } 134 | 135 | void game_init() { 136 | particle_allocator = fixed_allocator_make(sizeof(struct GameParticle_), 137 | NUM_GAME_PARTICLES, 138 | "particle_allocator"); 139 | main_clock = clock_make(); 140 | 141 | stars = image_load("spacer/night-sky-stars.jpg"); 142 | image_enemy = image_load("spacer/ship-right.png"); 143 | 144 | enemies.head = NULL; 145 | enemies.tail = NULL; 146 | 147 | player = gameparticle_make(); 148 | player->image = image_load("spacer/hero.png"); 149 | player->pos.x = player->image->w; 150 | player->pos.y = screen_height / 2; 151 | player->vel.x = 0; 152 | player->vel.y = 0; 153 | } 154 | 155 | void game_step(long delta, InputState state) { 156 | float dt = clock_update(main_clock, delta / 1000.0); 157 | 158 | Sprite background = frame_resource_sprite(stars); 159 | background->displayX = 0; 160 | background->displayY = 0; 161 | sprite_submit(background); 162 | 163 | // read player input 164 | player->vel.x = state->leftright * player_speed; 165 | player->vel.y = state->updown * player_speed; 166 | 167 | // update player position 168 | gameparticle_integrate(player, dt); 169 | 170 | // update enemies 171 | enemies_update(dt); 172 | 173 | // draw the enemies 174 | spritelist_enqueue_for_screen(gameparticles_spritelist(&enemies)); 175 | 176 | // draw the player 177 | sprite_submit(gameparticle_sprite(player)); 178 | } 179 | 180 | void game_shutdown() { 181 | 182 | } 183 | -------------------------------------------------------------------------------- /game.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_H 2 | #define GAME_H 3 | 4 | struct InputState_; 5 | 6 | void game_init(); 7 | void game_step(long delta, struct InputState_* state); 8 | void game_shutdown(); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /joystick.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "joystick.h" 7 | 8 | const char* type2str(__u8 type) { 9 | type &= ~JS_EVENT_INIT; 10 | switch(type) { 11 | case JS_EVENT_BUTTON: 12 | return "JS_EVENT_BUTTON"; 13 | break; 14 | case JS_EVENT_AXIS: 15 | return "JS_EVENT_AXIS"; 16 | break; 17 | case JS_EVENT_INIT: 18 | return "JS_EVENT_INIT"; 19 | break; 20 | default: 21 | return "UNKNOWN"; 22 | } 23 | } 24 | 25 | void js_event_print(struct js_event* e) { 26 | printf("%s: time=%d value=%d number=%d\n", type2str(e->type), 27 | e->time, e->value, e->number); 28 | } 29 | 30 | int joystick_maybe_read(int fileno, struct js_event* e) { 31 | if(read(fileno, e, sizeof(struct js_event)) <= 0) { 32 | if(errno != EAGAIN) { 33 | fprintf(stderr, "error reading from joystick\n"); 34 | exit(-1); 35 | } 36 | return 0; 37 | } else { 38 | e->type &= ~JS_EVENT_INIT; 39 | return 1; 40 | } 41 | } 42 | 43 | js_state joystick_open(const char* device) { 44 | int fd = open ("/dev/input/js0", O_RDONLY|O_NONBLOCK); 45 | 46 | int capacity = 32; 47 | js_state state = malloc(sizeof(struct js_state_)); 48 | state->num_values = 0; 49 | state->capacity = capacity; 50 | state->fileno = fd; 51 | state->values = malloc(capacity * sizeof(struct js_event)); 52 | memset(state->values, 0, capacity * sizeof(struct js_event)); 53 | 54 | joystick_update_state(state); 55 | return state; 56 | } 57 | 58 | void joystick_close(js_state state) { 59 | close(state->fileno); 60 | free(state->values); 61 | free(state); 62 | } 63 | 64 | void joystick_update_state(js_state state) { 65 | struct js_event e; 66 | 67 | while(joystick_maybe_read(state->fileno, &e)) { 68 | int slot = e.number * 2 + e.type - 1; 69 | if(slot >= state->num_values) { 70 | if(slot >= state->capacity) { 71 | int new_cap = slot + 1; 72 | state->values = realloc(state->values, new_cap * sizeof(struct js_event)); 73 | state->capacity = new_cap; 74 | } 75 | 76 | state->num_values = slot + 1; 77 | } 78 | 79 | state->values[slot] = e; 80 | } 81 | } 82 | 83 | void joystick_print_state(js_state state) { 84 | int ii; 85 | for(ii = 0; ii < state->num_values; ++ii) { 86 | js_event_print(state->values + ii); 87 | } 88 | } 89 | 90 | /* 91 | int main(int argc, char** argv) { 92 | int ii; 93 | js_state state = joystick_open("/dev/input/js0"); 94 | 95 | joystick_print_state(state); 96 | 97 | joystick_close(state); 98 | 99 | return 1; 100 | } 101 | */ 102 | -------------------------------------------------------------------------------- /joystick.h: -------------------------------------------------------------------------------- 1 | #ifndef JOYSTICK_H 2 | #define JOYSTICK_H 3 | 4 | #include 5 | #include "testlib.h" 6 | 7 | #define JS_EVENT_BUTTON 0x01 /* button pressed/released */ 8 | #define JS_EVENT_AXIS 0x02 /* joystick moved */ 9 | #define JS_EVENT_INIT 0x80 /* initial state of device */ 10 | 11 | struct js_event { 12 | __u32 time; /* event timestamp in milliseconds */ 13 | __s16 value; /* value */ 14 | __u8 type; /* event type */ 15 | __u8 number; /* axis/button number */ 16 | }; 17 | 18 | typedef struct js_state_ { 19 | int fileno; 20 | unsigned int num_values; 21 | unsigned int capacity; 22 | struct js_event* values; 23 | } *js_state; 24 | 25 | js_state joystick_open(const char* device); 26 | void joystick_close(js_state state); 27 | void joystick_update_state(js_state state); 28 | void joystick_print_state(js_state state); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /link.scm: -------------------------------------------------------------------------------- 1 | (c-declare #<fixnum x) 21 | (cond 22 | ((fixnum? x) x) 23 | ((flonum? x) (##flonum->fixnum x)) 24 | (else (inexact->exact x)))) 25 | 26 | (define ->flonum exact->inexact) 27 | 28 | (define image-load-internal 29 | (c-lambda (nonnull-char-string) 30 | ImageResource 31 | "image_load")) 32 | 33 | (define image-width 34 | (c-lambda (ImageResource) 35 | int 36 | "image_width")) 37 | 38 | (define image-height 39 | (c-lambda (ImageResource) 40 | int 41 | "image_height")) 42 | 43 | (define images-free 44 | (c-lambda () 45 | void 46 | "images_free")) 47 | 48 | (define clock-free 49 | (c-lambda (Clock) 50 | void 51 | "clock_free")) 52 | 53 | (define clock-make 54 | (c-lambda () 55 | Clock 56 | "clock_make")) 57 | 58 | (define clock-time-scale 59 | (c-lambda (Clock) 60 | float 61 | "___result = ___arg1->time_scale;")) 62 | 63 | (define clock-time-scale-set! 64 | (c-lambda (Clock float) 65 | void 66 | "___arg1->time_scale = ___arg2;")) 67 | 68 | (define (comp2 f g) 69 | (lambda (item x) (f item (g x)))) 70 | 71 | (define (compose f g) 72 | (lambda (x) (f (g x)))) 73 | 74 | (define %clock-update 75 | (c-lambda (Clock float) 76 | float 77 | "clock_update")) 78 | 79 | (define clock-update (comp2 %clock-update ->flonum)) 80 | 81 | (define clock-time 82 | (c-lambda (Clock) 83 | long 84 | "clock_time")) 85 | 86 | (define %cycles->seconds 87 | (c-lambda (long) 88 | float 89 | "clock_cycles_to_seconds")) 90 | 91 | (define %seconds->cycles 92 | (c-lambda (float) 93 | long 94 | "clock_seconds_to_cycles")) 95 | 96 | (define input-leftright 97 | (c-lambda (InputState) 98 | float 99 | "___result = ___arg1->leftright;")) 100 | 101 | (define input-updown 102 | (c-lambda (InputState) 103 | float 104 | "___result = ___arg1->updown;")) 105 | 106 | (define %input-action1 107 | (c-lambda (InputState) 108 | int 109 | "___result = ___arg1->action1;")) 110 | 111 | (define (input-action1 input) 112 | (= 1 (%input-action1 input))) 113 | 114 | (define cycles->seconds (compose %cycles->seconds ->fixnum)) 115 | (define seconds->cycles (compose %seconds->cycles ->flonum)) 116 | 117 | (define *screen-width* #f) 118 | 119 | (define *screen-height* #f) 120 | 121 | (define frame/make-sprite 122 | (c-lambda () 123 | Sprite 124 | "frame_make_sprite")) 125 | 126 | (define sprite-resource-set! 127 | (c-lambda (Sprite ImageResource) 128 | void 129 | "___arg1->resource = ___arg2;")) 130 | 131 | (define %sprite-x-set! 132 | (c-lambda (Sprite float) 133 | void 134 | "___arg1->displayX = ___arg2;")) 135 | 136 | (define %sprite-y-set! 137 | (c-lambda (Sprite float) 138 | void 139 | "___arg1->displayY = ___arg2;")) 140 | 141 | (define %sprite-origin-x-set! 142 | (c-lambda (Sprite float) 143 | void 144 | "___arg1->originX = ___arg2;")) 145 | 146 | (define %sprite-origin-y-set! 147 | (c-lambda (Sprite float) 148 | void 149 | "___arg1->originY = ___arg2;")) 150 | 151 | (define %sprite-angle-set! 152 | (c-lambda (Sprite float) 153 | void 154 | "___arg1->angle = ___arg2;")) 155 | 156 | (define %sprite-width-set! 157 | (c-lambda (Sprite float) 158 | void 159 | "___arg1->w = ___arg2;")) 160 | 161 | (define %sprite-height-set! 162 | (c-lambda (Sprite float) 163 | void 164 | "___arg1->h = ___arg2;")) 165 | 166 | 167 | 168 | (define sprite-x-set! (comp2 %sprite-x-set! ->flonum)) 169 | (define sprite-y-set! (comp2 %sprite-y-set! ->flonum)) 170 | (define sprite-origin-x-set! (comp2 %sprite-origin-x-set! ->flonum)) 171 | (define sprite-origin-y-set! (comp2 %sprite-origin-y-set! ->flonum)) 172 | (define sprite-angle-set! (comp2 %sprite-angle-set! ->flonum)) 173 | (define sprite-width-set! (comp2 %sprite-width-set! ->flonum)) 174 | (define sprite-height-set! (comp2 %sprite-height-set! ->flonum)) 175 | 176 | (define %sprite-parms-set! 177 | ;; sprite image x y cx cy angle width height 178 | (c-lambda (Sprite ImageResource float float float float float float float) 179 | void 180 | " 181 | ___arg1->resource = ___arg2; 182 | ___arg1->displayX = ___arg3; 183 | ___arg1->displayY = ___arg4; 184 | ___arg1->originX = ___arg5; 185 | ___arg1->originY = ___arg6; 186 | ___arg1->angle = ___arg7; 187 | ___arg1->w = ___arg8; 188 | ___arg1->h = ___arg9; 189 | ")) 190 | 191 | (define (sprite-parms-set! sprite img x y cx cy angle width height) 192 | (%sprite-parms-set! sprite img 193 | (->flonum x) (->flonum y) 194 | (->flonum cx) (->flonum cy) 195 | (->flonum angle) 196 | (->flonum width) (->flonum height))) 197 | 198 | (define %sprite-coords-set! 199 | ;; u0, v0, u1, v1 200 | (c-lambda (Sprite float float float float) 201 | void 202 | " 203 | ___arg1->u0 = ___arg2; 204 | ___arg1->v0 = ___arg3; 205 | ___arg1->u1 = ___arg4; 206 | ___arg1->v1 = ___arg5;")) 207 | 208 | (define (sprite-coords-set! sprite u0 v0 u1 v1) 209 | (%sprite-coords-set! sprite 210 | (->flonum u0) (->flonum v0) 211 | (->flonum u1) (->flonum v1))) 212 | 213 | (define frame/spritelist-append 214 | (c-lambda (SpriteList Sprite) 215 | SpriteList 216 | "frame_spritelist_append")) 217 | 218 | (define spritelist-enqueue-for-screen! 219 | (c-lambda (SpriteList) 220 | void 221 | "spritelist_enqueue_for_screen")) 222 | 223 | ;;; audio 224 | (c-define-type Sampler (pointer (struct "Sampler_"))) 225 | 226 | (define %sinsampler-make 227 | (c-lambda (long long float float float) 228 | Sampler 229 | "sinsampler_make")) 230 | 231 | (define (sinsampler-make start duration freq amp phase) 232 | (%sinsampler-make (->fixnum start) 233 | (->fixnum duration) 234 | (->flonum freq) 235 | (->flonum amp) 236 | (->flonum phase))) 237 | 238 | (define %sawsampler-make 239 | (c-lambda (long long float float float) 240 | Sampler 241 | "sawsampler_make")) 242 | 243 | (define (sawsampler-make start duration freq amp phase) 244 | (%sawsampler-make (->fixnum start) 245 | (->fixnum duration) 246 | (->flonum freq) 247 | (->flonum amp) 248 | (->flonum phase))) 249 | 250 | (define *sample-freq* ((c-lambda () long "___result = SAMPLE_FREQ;"))) 251 | (define *num-channels* 2) 252 | 253 | (define (seconds->samples seconds) 254 | (* *num-channels* *sample-freq* seconds)) 255 | 256 | (define audio-current-sample 257 | (c-lambda () 258 | long 259 | "audio_current_sample")) 260 | 261 | (define audio-enqueue 262 | (c-lambda (Sampler) 263 | void 264 | "audio_enqueue")) 265 | 266 | ;;; game lifecycle 267 | (define *game-clock* #f) 268 | 269 | ;; wild hack to keep gambit from trying to kill our process before 270 | ;; we're done. We use a continuation to send the gambit exit system 271 | ;; off into space after we've told the C side that we need to be torn 272 | ;; down when it's ready. Seems to only work on non-arm. Call exit 273 | ;; instead of giving the repl a ,q 274 | 275 | (define ##exit-cc-hack #f) 276 | (call/cc (lambda (cc) (set! ##exit-cc-hack cc))) 277 | 278 | (define (exit) 279 | (terminate)) 280 | 281 | (define quit exit) 282 | 283 | (c-define (scm-init) () void "scm_init" "" 284 | (set! *game-clock* (clock-make)) 285 | (##add-exit-job! 286 | (lambda () 287 | (exit) 288 | (##exit-cc-hack))) 289 | 290 | (set! *screen-width* 291 | ((c-lambda () int "___result = screen_width;"))) 292 | (set! *screen-height* 293 | ((c-lambda () int "___result = screen_height;"))) 294 | 295 | ;(clock-time-scale-set! *game-clock* 0.2) 296 | (display "initializing") (newline) 297 | (ensure-resources) 298 | (thread-start! 299 | (make-thread 300 | (lambda () 301 | (##repl-debug-main))))) 302 | 303 | ;;; resource lifecycle 304 | (define *resources* (make-table)) 305 | 306 | (define (image-load path) 307 | (let ((resource (table-ref *resources* path #f))) 308 | (if resource resource 309 | (begin 310 | (let ((new-resource (image-load-internal path))) 311 | (table-set! *resources* path new-resource) 312 | new-resource))))) 313 | 314 | (c-define (resources-released) () void "resources_released" "" 315 | (set! *resources* (make-table))) 316 | 317 | ;;; gameloop 318 | (c-define (step msecs input) (int InputState) void "step" "" 319 | (update-view (clock-update *game-clock* (/ msecs 1000.0)) input)) 320 | 321 | 322 | ;;; termination 323 | 324 | ;; c tells us to terminate 325 | (c-define (terminate) () void "terminate" "" 326 | (display "terminating") (newline)) 327 | -------------------------------------------------------------------------------- /list_modes.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char ** argv) { 4 | SDL_Rect** modes; 5 | const SDL_VideoInfo* info; 6 | 7 | int i; 8 | 9 | if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) { 10 | fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError()); 11 | exit(1); 12 | } 13 | 14 | /* Get available fullscreen/hardware modes */ 15 | modes = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_OPENGL); 16 | 17 | /* Check if there are any modes available */ 18 | if (modes == (SDL_Rect**)0) { 19 | printf("No modes available!\n"); 20 | exit(-1); 21 | } 22 | 23 | /* Check if our resolution is restricted */ 24 | if (modes == (SDL_Rect**)-1) { 25 | printf("All resolutions available.\n"); 26 | } else{ 27 | /* Print valid modes */ 28 | printf("Available Modes\n"); 29 | for (i=0; modes[i]; ++i) { 30 | printf(" %d x %d\n", modes[i]->w, modes[i]->h); 31 | } 32 | } 33 | 34 | info = SDL_GetVideoInfo(); 35 | printf("suggested %d bits per pixel\n", info->vfmt->BitsPerPixel); 36 | if(info->hw_available) { 37 | printf("hardware acceleration available\n"); 38 | } 39 | if(info->wm_available) { 40 | printf("window manager available\n"); 41 | } 42 | printf("video memory: %d KB\n", info->video_mem); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /listlib.c: -------------------------------------------------------------------------------- 1 | #include "listlib.h" 2 | 3 | void llnode_insert_after(DLLNode target, DLLNode addition) { 4 | DLLNode after = target->next; 5 | target->next = addition; 6 | addition->prev = target; 7 | addition->next = after; 8 | if(after) { 9 | after->prev = addition; 10 | } 11 | } 12 | 13 | void llnode_insert_before(DLLNode target, DLLNode addition) { 14 | DLLNode before = target->prev; 15 | target->prev = addition; 16 | addition->next = target; 17 | addition->prev = before; 18 | if(before) { 19 | before->next = addition; 20 | } 21 | } 22 | 23 | void llnode_remove(DLLNode node) { 24 | DLLNode after = node->next; 25 | DLLNode before = node->prev; 26 | 27 | if(after) { 28 | after->prev = before; 29 | } 30 | 31 | if(before) { 32 | before->next = after; 33 | } 34 | } 35 | 36 | #define NULL 0 37 | 38 | void dll_add_head(DLL list, DLLNode addition) { 39 | if(list->head == NULL) { 40 | addition->next = NULL; 41 | addition->prev = NULL; 42 | list->head = addition; 43 | list->tail = addition; 44 | } else { 45 | INSERT_BEFORE(list->head, addition); 46 | list->head = addition; 47 | } 48 | } 49 | 50 | DLLNode dll_remove_tail(DLL list) { 51 | if (list->tail == NULL) return NULL; 52 | 53 | DLLNode result = list->tail; 54 | DLLNode before = result->prev; 55 | if(before) { 56 | before->next = NULL; 57 | list->tail = before; 58 | } else { 59 | list->head = NULL; 60 | list->tail = NULL; 61 | } 62 | return result; 63 | } 64 | 65 | void dll_remove(DLL list, DLLNode node) { 66 | if(list->head == node) { 67 | DLLNode next = node->next; 68 | if(next) { 69 | next->prev = NULL; 70 | list->head = node->next; 71 | } else { 72 | list->head = NULL; 73 | list->tail = NULL; 74 | } 75 | return; 76 | } 77 | 78 | if(list->tail == node) { 79 | dll_remove_tail(list); 80 | } 81 | 82 | REMOVE(node); 83 | } 84 | 85 | int dll_count(DLL list) { 86 | int count = 0; 87 | DLLNode node = list->head; 88 | while(node) { 89 | ++count; 90 | node = node->next; 91 | } 92 | return count; 93 | } 94 | -------------------------------------------------------------------------------- /listlib.h: -------------------------------------------------------------------------------- 1 | #ifndef LISTLIB_H 2 | #define LISTLIB_H 3 | 4 | typedef struct LLNode_* LLNode; 5 | 6 | struct LLNode_ { 7 | LLNode next; 8 | }; 9 | 10 | typedef struct DLLNode_ *DLLNode; 11 | 12 | struct DLLNode_ { 13 | DLLNode next; 14 | DLLNode prev; 15 | }; 16 | 17 | typedef struct DLL_ { 18 | DLLNode head; 19 | DLLNode tail; 20 | } *DLL; 21 | 22 | void llnode_insert_after(DLLNode target, DLLNode addition); 23 | void llnode_insert_before(DLLNode target, DLLNode addition); 24 | void llnode_remove(DLLNode node); 25 | 26 | void dll_add_head(DLL list, DLLNode addition); 27 | DLLNode dll_remove_tail(DLL list); 28 | void dll_remove(DLL list, DLLNode node); 29 | int dll_count(DLL list); 30 | 31 | #define INSERT_AFTER(target, addition) \ 32 | llnode_insert_after((DLLNode)target, (DLLNode)addition) 33 | 34 | #define INSERT_BEFORE(target, addition) \ 35 | llnode_insert_before((DLLNode)target, (DLLNode)addition) 36 | 37 | #define REMOVE(node) \ 38 | llnode_remove((DLLNode)node) 39 | 40 | 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /math.scm: -------------------------------------------------------------------------------- 1 | (declare 2 | (standard-bindings) 3 | (extended-bindings) 4 | (not safe) 5 | (flonum)) 6 | 7 | (define-structure vect x y) 8 | 9 | (define (vect-make x y) 10 | (make-vect (exact->inexact x) (exact->inexact y))) 11 | 12 | (define (vect-add-into! r a b) 13 | (let ((xa (vect-x a)) 14 | (xb (vect-x b)) 15 | (ya (vect-y a)) 16 | (yb (vect-y b))) 17 | (vect-x-set! r (fl+ xa xb)) 18 | (vect-y-set! r (fl+ ya yb)) 19 | r)) 20 | 21 | (define (vect-sub-into! r a b) 22 | (let ((xa (vect-x a)) 23 | (xb (vect-x b)) 24 | (ya (vect-y a)) 25 | (yb (vect-y b))) 26 | (vect-x-set! r (fl- xa xb)) 27 | (vect-y-set! r (fl- ya yb)) 28 | r)) 29 | 30 | (define (vect-scale-into! r a s) 31 | (let ((xa (vect-x a)) 32 | (ya (vect-y a))) 33 | (vect-x-set! r (fl* xa (exact->inexact s))) 34 | (vect-y-set! r (fl* ya (exact->inexact s))) 35 | r)) 36 | 37 | (define (vect-scale-both-into! r a sx sy) 38 | (let ((xa (vect-x a)) 39 | (ya (vect-y a))) 40 | (vect-x-set! r (fl* xa (exact->inexact sx))) 41 | (vect-y-set! r (fl* ya (exact->inexact sy))) 42 | r)) 43 | 44 | (define (vect-scale a s) 45 | (vect-scale-into! (vect-make 0 0) a s)) 46 | 47 | (define (vect-copy v) 48 | (make-vect (vect-x v) (vect-y v))) 49 | 50 | (define (dist x1 y1 x2 y2) 51 | (let ((dx (- x1 x2)) 52 | (dy (- y1 y2))) 53 | (sqrt (fl+ (fl* dx dx) (fl* dy dy))))) 54 | 55 | (define-structure particle r dr t dt s ds) 56 | 57 | (define (particle-make r dr t dt s ds) 58 | (make-particle r dr 59 | (exact->inexact t) (exact->inexact dt) 60 | (exact->inexact s) (exact->inexact ds))) 61 | 62 | (define (particle-x p) 63 | (vect-x (particle-r p))) 64 | 65 | (define (particle-y p) 66 | (vect-y (particle-r p))) 67 | 68 | (define (particle-integrate p dt) 69 | (let ((dt (exact->inexact dt))) 70 | (vect-add-into! (particle-r p) 71 | (particle-r p) 72 | (vect-scale (particle-dr p) dt)) 73 | (particle-t-set! p (fl+ (particle-t p) 74 | (fl* (particle-dt p) dt))) 75 | (particle-s-set! p (flmax 0. (fl+ (particle-s p) 76 | (fl* (particle-ds p) dt)))))) 77 | 78 | (define (random-vector maxx maxy) 79 | (let ((maxx (exact->inexact maxx)) 80 | (maxy (exact->inexact maxy))) 81 | (vect-make (rand-in-range (fl- maxx) maxx) 82 | (rand-in-range (fl- maxy) maxy)))) 83 | 84 | (define (rand-in-range min max) 85 | (let ((min (->fixnum min)) 86 | (max (->fixnum max))) 87 | (fx+ min (random-integer (fx- max min))))) 88 | 89 | (define pi 3.141592654) 90 | 91 | (define (degrees->radians deg) 92 | (/ (* pi deg) 180.0)) 93 | 94 | (define (radians->degrees rad) 95 | (/ (* rad 180.0) pi)) 96 | 97 | 98 | -------------------------------------------------------------------------------- /memory.c: -------------------------------------------------------------------------------- 1 | #include "memory.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define SAFETY(x) x 8 | #define OFFSET(idx, obj_size, ptr) ((void*)(((char*)ptr) + (idx * obj_size))) 9 | #define NEXT_ALIGNED_SIZE(x) ((x + 8 - 1) & ~(8 - 1)) 10 | 11 | void* fail_exit(const char * message, ...) { 12 | fprintf(stderr, "FAIL_EXIT: "); 13 | 14 | va_list args; 15 | va_start(args, message); 16 | vfprintf(stderr, message, args); 17 | va_end(args); 18 | 19 | fprintf(stderr, "\n"); 20 | fflush(stderr); 21 | exit(1); 22 | return NULL; 23 | } 24 | 25 | /** 26 | * FixedAllocator's are used to quickly allocate and free objects of 27 | * fixed size. They operrate in constant time but cannot allocate more 28 | * objects than they were initially designed to hold. This makes them 29 | * appropriate for holding things like resource handles (since the 30 | * number of resources in the system is finite), timelines, and other 31 | * finite arity and long duration objects. 32 | */ 33 | FixedAllocator fixed_allocator_make(size_t obj_size, unsigned int n, 34 | const char* name) { 35 | int ii; 36 | 37 | /* next 8 byte aligned size */ 38 | obj_size = NEXT_ALIGNED_SIZE(obj_size); 39 | 40 | FixedAllocator allocator = (FixedAllocator)malloc(obj_size * n + 2*sizeof(struct FixedAllocator_)); 41 | 42 | #ifdef DEBUG_MEMORY 43 | allocator->name = name; 44 | allocator->inflight = 0; 45 | allocator->max_inflight = 0; 46 | #endif 47 | 48 | pthread_mutex_init(&allocator->mutex, NULL); 49 | allocator->allocation_size = obj_size; 50 | 51 | void* mem = &allocator[1]; 52 | allocator->first_free = NULL; 53 | for(ii = 0; ii < n; ++ii) { 54 | *(void**)mem = allocator->first_free; 55 | allocator->first_free = mem; 56 | mem += obj_size; 57 | } 58 | 59 | return allocator; 60 | } 61 | 62 | void* fixed_allocator_alloc(FixedAllocator allocator) { 63 | SAFETY(if(!allocator->first_free) return fail_exit("fixed_allocator %s failed", allocator->name)); 64 | 65 | pthread_mutex_lock(&allocator->mutex); 66 | void * mem = allocator->first_free; 67 | allocator->first_free = *(void**)allocator->first_free; 68 | 69 | #ifdef DEBUG_MEMORY 70 | allocator->inflight += 1; 71 | if(allocator->inflight > allocator->max_inflight) { 72 | allocator->max_inflight = allocator->inflight; 73 | } 74 | #endif 75 | pthread_mutex_unlock(&allocator->mutex); 76 | 77 | return mem; 78 | } 79 | 80 | void fixed_allocator_free(FixedAllocator allocator, void *obj) { 81 | pthread_mutex_lock(&allocator->mutex); 82 | *(void**)obj = allocator->first_free; 83 | allocator->first_free = obj; 84 | 85 | #ifdef DEBUG_MEMORY 86 | allocator->inflight -= 1; 87 | #endif 88 | pthread_mutex_unlock(&allocator->mutex); 89 | } 90 | 91 | StackAllocator stack_allocator_make(size_t stack_size, const char* name) { 92 | size_t size = sizeof(struct StackAllocator_) * 2 + stack_size; 93 | size = NEXT_ALIGNED_SIZE(size); 94 | 95 | StackAllocator allocator = (StackAllocator)malloc(size); 96 | #ifdef DEBUG_MEMORY 97 | allocator->name = name; 98 | #endif 99 | pthread_mutex_init(&allocator->mutex, NULL); 100 | allocator->stack_bottom = &allocator[1]; 101 | allocator->stack_top = allocator->stack_bottom; 102 | allocator->stack_max = (char*)allocator->stack_top + stack_size; 103 | return allocator; 104 | } 105 | 106 | void* stack_allocator_alloc(StackAllocator allocator, size_t size) { 107 | pthread_mutex_lock(&allocator->mutex); 108 | size = NEXT_ALIGNED_SIZE(size); 109 | SAFETY(if((char*)allocator->stack_top + size > (char*)allocator->stack_max) return fail_exit("stack_allocator %s failed", allocator->name)); 110 | void* mem = allocator->stack_top; 111 | allocator->stack_top = (char*)allocator->stack_top + size; 112 | pthread_mutex_unlock(&allocator->mutex); 113 | 114 | return mem; 115 | } 116 | 117 | void stack_allocator_freeall(StackAllocator allocator) { 118 | allocator->stack_top = allocator->stack_bottom; 119 | } 120 | 121 | CircularBuffer circularbuffer_make(size_t bytes) { 122 | CircularBuffer buffer = malloc(sizeof(struct CircularBuffer_)); 123 | buffer->read_index = 0; 124 | buffer->write_index = 0; 125 | buffer->size = bytes; 126 | buffer->filled = 0; 127 | buffer->data = malloc(bytes); 128 | return buffer; 129 | } 130 | 131 | void circularbuffer_free(CircularBuffer buffer) { 132 | free(buffer->data); 133 | free(buffer); 134 | } 135 | 136 | int circularbuffer_distance(CircularBuffer buffer, int i1, int i2) { 137 | if(i2 > i1) { 138 | return i2 - i1; 139 | } else { 140 | return i2 + buffer->size - i1; 141 | } 142 | } 143 | 144 | int circularbuffer_bytes_writable(CircularBuffer buffer) { 145 | if(!buffer->filled && (buffer->write_index == buffer->read_index)) { 146 | return buffer->size; 147 | } else if(buffer->filled) { 148 | return 0; 149 | } else { 150 | return circularbuffer_distance(buffer, buffer->write_index, 151 | buffer->read_index); 152 | } 153 | } 154 | 155 | int circularbuffer_bytes_readable(CircularBuffer buffer) { 156 | if(buffer->filled) { 157 | return buffer->size; 158 | } else if(buffer->read_index == buffer->write_index) { 159 | return 0; 160 | } else { 161 | return circularbuffer_distance(buffer, buffer->read_index, 162 | buffer->write_index); 163 | } 164 | } 165 | 166 | int circularbuffer_add(CircularBuffer buffer, int a, int b) { 167 | return (a + b) % buffer->size; 168 | } 169 | 170 | void circularbuffer_read_buffers(CircularBuffer buffer, 171 | char ** buffer1, int * size1, 172 | char ** buffer2, int * size2, 173 | int bytes_to_read) { 174 | // easy case, 1 buffer 175 | if(buffer->read_index < buffer->write_index) { 176 | *buffer1 = &buffer->data[buffer->read_index]; 177 | *size1 = buffer->write_index - buffer->read_index; 178 | *buffer2 = NULL; 179 | *size2 = 0; 180 | } else if(!buffer->filled && buffer->read_index == buffer->write_index) { 181 | *buffer1 = NULL; 182 | *size1 = 0; 183 | *buffer2 = NULL; 184 | *size2 = 0; 185 | } else { 186 | // 2 buffers 187 | *buffer1 = &buffer->data[buffer->read_index]; 188 | *size1 = buffer->size - buffer->read_index; 189 | *buffer2 = buffer->data; 190 | *size2 = buffer->write_index; 191 | } 192 | 193 | bytes_to_read = MIN(bytes_to_read, *size1 + *size2); 194 | buffer->read_index = circularbuffer_add(buffer, buffer->read_index, 195 | bytes_to_read); 196 | 197 | if(bytes_to_read > 0) buffer->filled = 0; 198 | } 199 | 200 | void circularbuffer_write_buffers(CircularBuffer buffer, 201 | char ** buffer1, int * size1, 202 | char ** buffer2, int * size2, 203 | int bytes_to_write) { 204 | // 1 buffer 205 | if(buffer->write_index < buffer->read_index) { 206 | *buffer1 = &buffer->data[buffer->write_index]; 207 | *size1 = buffer->read_index - buffer->write_index; 208 | *buffer2 = NULL; 209 | *size2 = 0; 210 | } else if (buffer->filled) { 211 | *buffer1 = NULL; 212 | *size1 = 0; 213 | *buffer2 = NULL; 214 | *size2 = 0; 215 | } else { 216 | // the corner case and the 2 buffer case are the same for writing 217 | *buffer1 = &buffer->data[buffer->write_index]; 218 | *size1 = buffer->size - buffer->write_index; 219 | *buffer2 = buffer->data; 220 | *size2 = buffer->read_index; 221 | } 222 | 223 | bytes_to_write = MIN(bytes_to_write, *size1 + *size2); 224 | buffer->write_index = circularbuffer_add(buffer, buffer->write_index, 225 | bytes_to_write); 226 | 227 | if(bytes_to_write > 0 && buffer->read_index == buffer->write_index) { 228 | buffer->filled = 1; 229 | } 230 | } 231 | 232 | int circularbuffer_insert(CircularBuffer buffer, char * bytes, int length) { 233 | int s1, s2; 234 | char *b1, *b2; 235 | circularbuffer_write_buffers(buffer, &b1, &s1, &b2, &s2, length); 236 | 237 | int to_write = MIN(length, s1); 238 | int written = 0; 239 | 240 | memcpy(b1, bytes, to_write); 241 | length -= to_write; 242 | written += to_write; 243 | 244 | if(length > 0) { 245 | to_write = MIN(length, s2); 246 | memcpy(b2, &bytes[s1], to_write); 247 | length -= to_write; 248 | written += to_write; 249 | } 250 | 251 | return length == 0; 252 | } 253 | 254 | int circularbuffer_read(CircularBuffer buffer, char * target, int length) { 255 | int s1, s2; 256 | char *b1, *b2; 257 | circularbuffer_read_buffers(buffer, &b1, &s1, &b2, &s2, length); 258 | 259 | int to_read = MIN(length, s1); 260 | int read = 0; 261 | 262 | memcpy(target, b1, to_read); 263 | length -= to_read; 264 | read += to_read; 265 | 266 | if(length > 0) { 267 | to_read = MIN(length, s2); 268 | memcpy(&target[s1], b2, to_read); 269 | length -= to_read; 270 | read += to_read; 271 | } 272 | 273 | return length == 0; 274 | } 275 | -------------------------------------------------------------------------------- /memory.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_H 2 | #define MEMORY_H 3 | 4 | #include 5 | #include 6 | 7 | #define MIN(x, y) ((x) < (y) ? (x) : (y)) 8 | 9 | #define DEBUG_MEMORY 10 | 11 | typedef struct FixedAllocator_ { 12 | #ifdef DEBUG_MEMORY 13 | const char* name; 14 | long inflight; 15 | long max_inflight; 16 | #endif 17 | pthread_mutex_t mutex; 18 | size_t allocation_size; 19 | void* first_free; 20 | } *FixedAllocator; 21 | 22 | typedef struct StackAllocator_ { 23 | #ifdef DEBUG_MEMORY 24 | const char* name; 25 | #endif 26 | pthread_mutex_t mutex; 27 | void* stack_top; 28 | void* stack_bottom; 29 | void* stack_max; 30 | } *StackAllocator; 31 | 32 | FixedAllocator fixed_allocator_make(size_t obj_size, unsigned int n, 33 | const char* name); 34 | void* fixed_allocator_alloc(FixedAllocator allocator); 35 | void fixed_allocator_free(FixedAllocator allocator, void *obj); 36 | 37 | StackAllocator stack_allocator_make(size_t stack_size, 38 | const char* name); 39 | void* stack_allocator_alloc(StackAllocator allocator, size_t size); 40 | void stack_allocator_freeall(StackAllocator allocator); 41 | 42 | typedef struct CircularBuffer_ { 43 | int read_index; 44 | int write_index; 45 | int size; 46 | int filled; 47 | char* data; 48 | } *CircularBuffer; 49 | 50 | CircularBuffer circularbuffer_make(size_t bytes); 51 | void circularbuffer_free(CircularBuffer buffer); 52 | int circularbuffer_bytes_writable(CircularBuffer buffer); 53 | int circularbuffer_bytes_readable(CircularBuffer buffer); 54 | 55 | void circularbuffer_read_buffers(CircularBuffer buffer, 56 | char ** buffer1, int * size1, 57 | char ** buffer2, int * size2, 58 | int bytes_to_read); 59 | 60 | void circularbuffer_write_buffers(CircularBuffer buffer, 61 | char ** buffer1, int * size1, 62 | char ** buffer2, int * size2, 63 | int bytes_to_write); 64 | 65 | int circularbuffer_insert(CircularBuffer buffer, char * bytes, int length); 66 | 67 | int circularbuffer_read(CircularBuffer buffer, char * target, int length); 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /monster/.dropbox.attr: -------------------------------------------------------------------------------- 1 | {"mac": {"com.apple.FinderInfo": {"data": "AAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="}}} -------------------------------------------------------------------------------- /monster/desktop.ini: -------------------------------------------------------------------------------- 1 | [.ShellClassInfo] 2 | IconFile=X:\Users\lucid\AppData\Roaming\Dropbox\bin\Dropbox.exe 3 | IconIndex=-2301 4 | InfoTip=A securely backed up place to put your important files. 5 | -------------------------------------------------------------------------------- /monster/mon_arms/forearm_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_arms/forearm_0.png -------------------------------------------------------------------------------- /monster/mon_arms/forearm_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_arms/forearm_a.png -------------------------------------------------------------------------------- /monster/mon_arms/hand_0_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_arms/hand_0_0.png -------------------------------------------------------------------------------- /monster/mon_arms/hand_0_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_arms/hand_0_1.png -------------------------------------------------------------------------------- /monster/mon_arms/hand_0_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_arms/hand_0_2.png -------------------------------------------------------------------------------- /monster/mon_arms/hand_0_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_arms/hand_0_3.png -------------------------------------------------------------------------------- /monster/mon_arms/hand_a_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_arms/hand_a_0.png -------------------------------------------------------------------------------- /monster/mon_arms/hand_a_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_arms/hand_a_1.png -------------------------------------------------------------------------------- /monster/mon_arms/hand_a_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_arms/hand_a_2.png -------------------------------------------------------------------------------- /monster/mon_arms/hand_a_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_arms/hand_a_3.png -------------------------------------------------------------------------------- /monster/mon_arms/shoulder_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_arms/shoulder_0.png -------------------------------------------------------------------------------- /monster/mon_arms/shoulder_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_arms/shoulder_a.png -------------------------------------------------------------------------------- /monster/mon_head/head_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_head/head_0.png -------------------------------------------------------------------------------- /monster/mon_head/head_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_head/head_1.png -------------------------------------------------------------------------------- /monster/mon_head/head_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_head/head_2.png -------------------------------------------------------------------------------- /monster/mon_head/head_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_head/head_3.png -------------------------------------------------------------------------------- /monster/mon_legs/foot_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_legs/foot_0.png -------------------------------------------------------------------------------- /monster/mon_legs/foot_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_legs/foot_a.png -------------------------------------------------------------------------------- /monster/mon_legs/pelvis_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_legs/pelvis_0.png -------------------------------------------------------------------------------- /monster/mon_legs/thigh_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_legs/thigh_0.png -------------------------------------------------------------------------------- /monster/mon_legs/thigh_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_legs/thigh_a.png -------------------------------------------------------------------------------- /monster/mon_shadows/shadow_idle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_shadows/shadow_idle.png -------------------------------------------------------------------------------- /monster/mon_torso/torso_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/monster/mon_torso/torso_0.png -------------------------------------------------------------------------------- /object.scm: -------------------------------------------------------------------------------- 1 | ;;; simple single-dispatch polymorphic message passing style object 2 | ;;; system 3 | 4 | (define (plist->alist functions) 5 | (let loop ((result '()) 6 | (functions functions)) 7 | (if (null? functions) 8 | (reverse result) 9 | (loop (cons (cons (car functions) (cadr functions)) result) 10 | (cddr functions))))) 11 | 12 | (define-structure %obj parent fields) 13 | 14 | (define (%obj-make parent . fields) 15 | (make-%obj parent (plist->alist fields))) 16 | 17 | (define (%obj-field obj field-name #!optional (default #f)) 18 | (let ((field (assoc field-name (%obj-fields obj)))) 19 | (if field (cdr field) 20 | (if (%obj-parent obj) 21 | (%obj-field (%obj-parent obj) field-name default) 22 | (if default 23 | default 24 | (error "field does not exist: " field-name)))))) 25 | 26 | (define (%obj-field-set! obj field-name value) 27 | (let ((field (assoc field-name (%obj-fields obj)))) 28 | (if field (set-cdr! field value) 29 | (%obj-fields-set! obj (cons (cons field-name value) 30 | (%obj-fields obj)))))) 31 | 32 | (define (%obj-invoke obj field . args) 33 | (let ((method (%obj-field obj field))) 34 | (apply method obj args))) 35 | 36 | (define %obj-root (%obj-make #f)) 37 | 38 | (define (obj-make #!key (parent %obj-root) (fields '())) 39 | (apply %obj-make parent fields)) 40 | 41 | (define obj-field %obj-field) 42 | (define obj-field-set! %obj-field-set!) 43 | (define obj-invoke %obj-invoke) 44 | -------------------------------------------------------------------------------- /particle-main.scm: -------------------------------------------------------------------------------- 1 | (define (update-particle sprite-list p dt w h) 2 | (let* ((x (particle-x p)) 3 | (y (particle-y p)) 4 | (dr (particle-dr p)) 5 | (sprite (frame/make-sprite))) 6 | 7 | (if (or (>= x w) (<= x 0)) 8 | (vect-scale-both-into! dr dr -1 1)) 9 | (if (or (>= y h) (<= y 0)) 10 | (vect-scale-both-into! dr dr 1 -1)) 11 | 12 | (particle-integrate p dt) 13 | (sprite-resource-set! sprite (*test-image*)) 14 | (sprite-x-set! sprite (particle-x p)) 15 | (sprite-y-set! sprite (particle-y p)) 16 | (frame/spritelist-append sprite-list sprite))) 17 | 18 | (define (update-view-old dt) 19 | (call-with-resources 20 | (lambda () 21 | (let ((w (- 640 (image-width (*test-image*)))) 22 | (h (- 480 (image-height (*test-image*))))) 23 | (let loop ((ps *ps*) 24 | (sprite-list #f)) 25 | (if (null? ps) 26 | (if sprite-list (spritelist-enqueue-for-screen! sprite-list)) 27 | (loop (cdr ps) 28 | (update-particle sprite-list 29 | (car ps) dt w h)))))))) 30 | 31 | -------------------------------------------------------------------------------- /realmain.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "testlib.h" 3 | #include "game.h" 4 | 5 | #define min_time 18 6 | #define max_time 100 7 | 8 | int last_time; 9 | 10 | int loop_once() { 11 | int new_time; 12 | 13 | InputState state = frame_inputstate(); 14 | if(state->quit_requested) { 15 | lib_shutdown(); 16 | return 0; 17 | } 18 | 19 | /* check the time */ 20 | new_time = time_millis(); 21 | long old_delta = new_time - last_time; 22 | if(old_delta < min_time) { 23 | sleep_millis(min_time - old_delta); 24 | new_time = time_millis(); 25 | } 26 | 27 | long delta = new_time - last_time; 28 | if(delta > max_time) { 29 | delta = max_time; 30 | } 31 | 32 | begin_frame(); 33 | game_step(delta, state); 34 | end_frame(); 35 | 36 | last_time = new_time; 37 | 38 | return 1; 39 | } 40 | 41 | int real_main(int argc, char ** argv) { 42 | int last_time = time_millis(); 43 | lib_init(); 44 | game_init(); 45 | 46 | while(loop_once()) {} 47 | 48 | game_shutdown(); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /rect.scm: -------------------------------------------------------------------------------- 1 | (declare 2 | (standard-bindings) 3 | (extended-bindings) 4 | (not safe) 5 | (flonum)) 6 | 7 | (define-structure rect minx miny maxx maxy) 8 | 9 | (define (rect-make minx miny maxx maxy) 10 | (make-rect (exact->inexact minx) (exact->inexact miny) 11 | (exact->inexact maxx) (exact->inexact maxy))) 12 | 13 | (define (rect-width rect) 14 | (fl- (rect-maxx rect) (rect-minx rect))) 15 | 16 | (define (rect-height rect) 17 | (fl- (rect-maxy rect) (rect-miny rect))) 18 | 19 | (define (rect-make-point x y) 20 | (let* ((minx (floor x)) 21 | (maxx (ceiling x)) 22 | (miny (floor y)) 23 | (maxy (ceiling y)) 24 | 25 | (maxx (if (fl= minx maxx) 26 | (fl+ maxx 1) 27 | maxx)) 28 | (maxy (if (fl= miny maxy) 29 | (fl+ maxy 1) 30 | maxy))) 31 | (rect-make minx miny maxx maxy))) 32 | 33 | (define (rect-make-radius cx cy radius) 34 | (rect-make (max 0. (fl- cx radius)) 35 | (max 0. (fl- cy radius)) 36 | (fl+ cx radius) 37 | (fl+ cy radius))) 38 | 39 | (define (rect-intersect a b) 40 | (let ((aminx (rect-minx a)) 41 | (aminy (rect-miny a)) 42 | (amaxx (rect-maxx a)) 43 | (amaxy (rect-maxy a)) 44 | 45 | (bminx (rect-minx b)) 46 | (bminy (rect-miny b)) 47 | (bmaxx (rect-maxx b)) 48 | (bmaxy (rect-maxy b))) 49 | 50 | (cond 51 | ((fl< amaxx bminx) #f) 52 | ((fl> aminx bmaxx) #f) 53 | ((fl< amaxy bminy) #f) 54 | ((fl> aminy bmaxy) #f) 55 | (#t #t)))) 56 | -------------------------------------------------------------------------------- /sampler.c: -------------------------------------------------------------------------------- 1 | #include "sampler.h" 2 | #include "memory.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define MAX(x,y) ((x)>(y) ? (x) : (y)) 9 | 10 | FixedAllocator sampler_allocator; 11 | 12 | #define SIN_TABLE 13 | 14 | #ifdef SIN_TABLE 15 | #define TABLE_SIZE 1024 16 | float *sin_table; 17 | #define SIN(x) sin_table[((int)((x * TABLE_SIZE) / (2*M_PI))) % TABLE_SIZE] 18 | #else 19 | #define SIN(x) sinf(x) 20 | #endif 21 | 22 | void sampler_init() { 23 | #ifdef SIN_TABLE 24 | int ii; 25 | sin_table = malloc(sizeof(float) * TABLE_SIZE); 26 | 27 | for(ii = 0; ii < TABLE_SIZE; ++ii) { 28 | float factor = (float)ii/TABLE_SIZE; 29 | sin_table[ii] = sin(2 * M_PI * factor); 30 | } 31 | #endif 32 | 33 | size_t max_sampler_size 34 | = MAX(sizeof(struct SinSampler_), 35 | MAX(sizeof(struct SawSampler_), 36 | sizeof(struct Filter_))); 37 | 38 | sampler_allocator = fixed_allocator_make(max_sampler_size, 39 | NUM_SAMPLERS, 40 | "sampler_allocator"); 41 | } 42 | 43 | void sampler_free(void* obj) { 44 | fixed_allocator_free(sampler_allocator, obj); 45 | } 46 | 47 | long sampler_offset_(Sampler sampler, long sample) { 48 | return sample - START(sampler); 49 | } 50 | 51 | #define sampler_offset(sampler, sample) \ 52 | sampler_offset_((Sampler)sampler, sample) 53 | 54 | int16_t sin_sample(SinSampler sampler, long sample) { 55 | sample = sampler_offset(sampler, sample); 56 | return sampler->amp * 57 | SIN(sampler->phase + sampler->radians_per_sample * sample); 58 | } 59 | 60 | /** all phases are normalized phase values ranging from [0, 1) 61 | */ 62 | 63 | Sampler sinsampler_make(long start, long duration, 64 | float freq, float amp, float phase) { 65 | SinSampler sampler = (SinSampler)fixed_allocator_alloc(sampler_allocator); 66 | sampler->sampler.function = (SamplerFunction)sin_sample; 67 | sampler->sampler.release = sampler_free; 68 | sampler->sampler.start_sample = start; 69 | sampler->sampler.duration_samples = duration; 70 | 71 | float cycles_per_sample = freq / SAMPLE_FREQ; 72 | sampler->radians_per_sample = cycles_per_sample * 2 * M_PI; 73 | sampler->amp = amp; 74 | sampler->phase = phase * 2 * M_PI; 75 | 76 | return (Sampler)sampler; 77 | } 78 | 79 | int16_t saw_sample(SawSampler sampler, long sample) { 80 | sample = sampler_offset(sampler, sample); 81 | long pos = (sampler->phase_samples + sample) % sampler->samples_per_period; 82 | return -sampler->amp + pos * sampler->slope; 83 | } 84 | 85 | Sampler sawsampler_make(long start, long duration, 86 | float freq, float amp, float phase) { 87 | SawSampler sampler = (SawSampler)fixed_allocator_alloc(sampler_allocator); 88 | sampler->sampler.function = (SamplerFunction)saw_sample; 89 | sampler->sampler.release = sampler_free; 90 | sampler->sampler.start_sample = start; 91 | sampler->sampler.duration_samples = duration; 92 | 93 | long samples_per_period = SAMPLE_FREQ / freq; 94 | float slope = 2 * amp / samples_per_period; 95 | 96 | sampler->samples_per_period = samples_per_period; 97 | sampler->slope = slope; 98 | sampler->phase_samples = phase * samples_per_period; 99 | sampler->amp = amp; 100 | 101 | return (Sampler)sampler; 102 | } 103 | 104 | int16_t filter_value(Filter filter, int16_t value) { 105 | int ii; 106 | int xi = filter->xi; 107 | int yi = filter->yi; 108 | 109 | filter->xi = (xi + 1) % filter->na; 110 | filter->yi = (yi + 1) % filter->nb; 111 | 112 | filter->xs[xi] = value; 113 | 114 | int16_t result = 0; 115 | for(ii = 0; ii < filter->na; ++ii) { 116 | int idx = (xi + ii + 1) % filter->na; 117 | result += filter->xs[idx] * filter->as[filter->na - ii - 1]; 118 | } 119 | 120 | for(ii = 0; ii < filter->nb; ++ii) { 121 | int idx = (yi + ii + 1) % filter->nb; 122 | result += filter->ys[idx] * filter->bs[filter->nb - ii - 1]; 123 | } 124 | 125 | filter->ys[yi] = result; 126 | return result; 127 | } 128 | 129 | void filter_release(Filter filter) { 130 | free(filter->as); 131 | free(filter->bs); 132 | free(filter->xs); 133 | free(filter->ys); 134 | 135 | sampler_free(filter); 136 | } 137 | 138 | Filter filter_make(float* as, int na, float* bs, int nb) { 139 | Filter filter = (Filter)fixed_allocator_alloc(sampler_allocator); 140 | filter->na = na; 141 | filter->nb = nb; 142 | filter->xi = 0; 143 | filter->yi = 0; 144 | filter->as = malloc(na * sizeof(float)); 145 | filter->xs = malloc(na * sizeof(int16_t)); 146 | filter->bs = malloc(nb * sizeof(float)); 147 | filter->ys = malloc(nb * sizeof(int16_t)); 148 | 149 | memcpy(filter->as, as, na * sizeof(float)); 150 | memcpy(filter->bs, bs, nb * sizeof(float)); 151 | memset(filter->xs, 0, na * sizeof(int16_t)); 152 | memset(filter->ys, 0, nb * sizeof(int16_t)); 153 | return filter; 154 | } 155 | 156 | Filter lowpass_make(float cutoff, float sample_freq) { 157 | /* 158 | float omega_c = tan(cutoff / sample_freq * M_PI); 159 | float a = (1 - omega_c) / 2; 160 | float as[] = {a, a}; 161 | float bs[] = {omega_c}; 162 | */ 163 | float as[] = {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}; 164 | float bs[] = {0.0}; 165 | return filter_make(as, array_size(as), bs, array_size(bs)); 166 | } 167 | -------------------------------------------------------------------------------- /sampler.h: -------------------------------------------------------------------------------- 1 | #ifndef SAMPLER_H 2 | #define SAMPLER 3 | 4 | #include 5 | 6 | #define N_(n,b) (n*b) 7 | 8 | #define C_(n) N_(n, 261.6) 9 | #define D_(n) N_(n, 293.7) 10 | #define E_(n) N_(n, 329.6) 11 | #define F_(n) N_(n, 349.2) 12 | #define G_(n) N_(n, 392.0) 13 | #define A_(n) N_(n, 440.0) 14 | #define B_(n) N_(n, 493.9) 15 | 16 | #define SAMPLE_FREQ 22050 17 | #define NUM_SAMPLERS 128 18 | #define SAMPLE(f, x) (((Sampler)(f))->function(f, x)) 19 | #define RELEASE_SAMPLER(f) (((Sampler)(f))->release(f)) 20 | 21 | #define array_size(a) (sizeof(a)/sizeof(a[0])) 22 | 23 | typedef int16_t (*SamplerFunction)(void*, long); 24 | typedef void (*ReleaseSampler)(void*); 25 | 26 | void sampler_init(); 27 | 28 | typedef struct Sampler_ { 29 | SamplerFunction function; 30 | ReleaseSampler release; 31 | long start_sample; 32 | long duration_samples; 33 | } *Sampler; 34 | 35 | typedef struct SinSampler_ { 36 | struct Sampler_ sampler; 37 | float phase; /* radians */ 38 | float radians_per_sample; 39 | float amp; 40 | } *SinSampler; 41 | 42 | Sampler sinsampler_make(long start, long duration, 43 | float freq, float amp, float phase); 44 | 45 | typedef struct SawSampler_ { 46 | struct Sampler_ sampler; 47 | long samples_per_period; 48 | long phase_samples; 49 | float slope; 50 | float amp; 51 | } *SawSampler; 52 | 53 | Sampler sawsampler_make(long start, long duration, 54 | float freq, float amp, float phase); 55 | 56 | #define DURATION(f) (((Sampler)f)->duration_samples) 57 | #define START(f) (((Sampler)f)->start_sample) 58 | #define END(f) (START(f) + DURATION(f)) 59 | 60 | typedef struct Filter_ { 61 | int na; 62 | int nb; 63 | int xi; 64 | float yi; 65 | 66 | float *as; 67 | float *bs; 68 | int16_t *xs; 69 | int16_t *ys; 70 | } *Filter; 71 | 72 | Filter filter_make(float* as, int na, float* bs, int nb); 73 | 74 | int16_t filter_value(Filter filter, int16_t value); 75 | 76 | Filter lowpass_make(float cutoff, float sample_freq); 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /scmlib.scm: -------------------------------------------------------------------------------- 1 | (load "math") 2 | (load "common") 3 | (load "rect") 4 | (load "spatial") 5 | (load "sparrow") 6 | 7 | (declare 8 | (standard-bindings) 9 | (extended-bindings)) 10 | 11 | (define-structure game-particle particle atlas img-name extra) 12 | 13 | (define (game-particle-make particle atlas img-name #!optional (extra #f)) 14 | (let* ((gp (make-game-particle particle atlas img-name extra))) 15 | 16 | gp)) 17 | 18 | (define (game-particle-w particle) 19 | (sparrow-width (game-particle-atlas particle) (game-particle-img-name particle))) 20 | 21 | (define (game-particle-h particle) 22 | (sparrow-height (game-particle-atlas particle) (game-particle-img-name particle))) 23 | 24 | (define (game-particle-width gp) 25 | (* (particle-s (game-particle-particle gp)) (game-particle-w gp))) 26 | 27 | (define (game-particle-height gp) 28 | (* (particle-s (game-particle-particle gp)) (game-particle-h gp))) 29 | 30 | (define (game-particle-r gp) 31 | (particle-r (game-particle-particle gp))) 32 | 33 | (define (game-particle-dr gp) 34 | (particle-dr (game-particle-particle gp))) 35 | 36 | (define (game-particle-x gp) 37 | (particle-x (game-particle-particle gp))) 38 | 39 | (define (game-particle-y gp) 40 | (particle-y (game-particle-particle gp))) 41 | 42 | (define (game-particle-cx gp) 43 | (/ (game-particle-w gp) 2.)) 44 | 45 | (define (game-particle-cy gp) 46 | (/ (game-particle-h gp) 2.)) 47 | 48 | (define (game-particle-t gp) 49 | (particle-t (game-particle-particle gp))) 50 | 51 | (define (game-particle-dr-set! gp dr) 52 | (particle-dr-set! (game-particle-particle gp) dr)) 53 | 54 | (define (game-particle-s gp) 55 | (particle-s (game-particle-particle gp))) 56 | 57 | (define (game-particle-rect-scaled gp s) 58 | (let* ((hw (* s (/ (game-particle-width gp) 2.))) 59 | (hh (* s (/ (game-particle-width gp) 2.))) 60 | (cx (* s (game-particle-x gp))) 61 | (cy (* s (game-particle-y gp)))) 62 | (rect-make (- cx hw) (- cy hh) (+ cx hw) (+ cy hh)))) 63 | 64 | (define (game-particle-rect gp) 65 | (game-particle-rect-scaled gp 1)) 66 | 67 | (define (game-particle->sprite gp ocx ocy) 68 | (let ((sprite (frame/make-sprite)) 69 | (w (game-particle-width gp)) 70 | (h (game-particle-height gp))) 71 | (sprite-parms-set! sprite (sparrow-image (game-particle-atlas gp)) 72 | (game-particle-x gp) (game-particle-y gp) 73 | (+ ocx 0.5) 74 | (+ ocy 0.5) 75 | (game-particle-t gp) 76 | (* w (game-particle-s gp)) 77 | (* h (game-particle-s gp))) 78 | (sprite-sparrow-coords-set! sprite (game-particle-atlas gp) 79 | (game-particle-img-name gp)) 80 | sprite)) 81 | 82 | (define (game-particles->sprite-list gps #!optional (ocx 0) (ocy 0)) 83 | (reduce (lambda (sprite-list gp) 84 | (frame/spritelist-append 85 | sprite-list 86 | (game-particle->sprite gp ocx ocy))) 87 | #f 88 | gps)) 89 | 90 | (define (game-particle-integrate gp dt) 91 | (particle-integrate (game-particle-particle gp) dt) 92 | gp) 93 | 94 | (define *base-volume* 1000) 95 | (define *player* '()) 96 | (define *player-bullets* '()) 97 | (define *enemy-bullets* '()) 98 | (define *enemies* '()) 99 | (define *particles* '()) 100 | 101 | (define *enemy-speed* 50) 102 | (define *player-speed* 600) 103 | (define *player-bullet-speed* 1200) 104 | (define *enemy-bullet-speed* 400) 105 | (define *initial-enemies* 1) 106 | (define *max-enemies* 10) 107 | 108 | (define *texture-atlas* #f) 109 | 110 | (define (step-pretty-particles dt) 111 | (set! *particles* 112 | (mapcat (lambda (p) 113 | (filter not-null? (integrate-game-particle p dt))) 114 | *particles*))) 115 | 116 | (define (spawn-smoke-particle source) 117 | (let* ((r (vect-copy (game-particle-r source))) 118 | (dr (vect-copy (game-particle-dr source))) 119 | (dr (vect-add-into! dr dr (random-vector 50 100))) 120 | (death-time (+ (seconds->cycles (/ (rand-in-range 500 3500) 1000.)) 121 | (clock-time *game-clock*)))) 122 | (game-particle-make 123 | (particle-make 124 | r dr 125 | (rand-in-range 0 360) (rand-in-range -20 20) 126 | 0.01 (* 0.5 (rand-in-range 1 4))) 127 | *texture-atlas* 128 | "smoke.png" 129 | (lambda (gp dt) 130 | (if (> (clock-time *game-clock*) death-time) 131 | '() 132 | (list (game-particle-integrate gp dt))))))) 133 | 134 | (define (spawn-hulk-particle source img) 135 | (let* ((r (vect-copy (game-particle-r source))) 136 | (dr (vect-copy (game-particle-dr source))) 137 | (dr (vect-add-into! dr dr (random-vector 300 600))) 138 | (death-time (+ (seconds->cycles (/ (rand-in-range 500 6500) 1000.)) 139 | (clock-time *game-clock*)))) 140 | (game-particle-make 141 | (particle-make 142 | r dr 143 | 180 (rand-in-range -360 360) 144 | 1 -1) 145 | *texture-atlas* 146 | img 147 | (lambda (gp dt) 148 | (if (> (clock-time *game-clock*) death-time) 149 | '() 150 | (list (game-particle-integrate gp dt))))))) 151 | 152 | (define (add-pretty-particles! p) 153 | (if (list? p) 154 | (set! *particles* (append p *particles*)) 155 | (set! *particles* (cons p *particles*)))) 156 | 157 | (define (spawn-enemy) 158 | (let* ((img-name "ship-right.png") 159 | (img-height (sparrow-height *texture-atlas* img-name)) 160 | (img-width (sparrow-width *texture-atlas* img-name)) 161 | (nrows (ceiling (/ *screen-height* img-height))) 162 | (shot-period (seconds->cycles (rand-in-range 1 5))) 163 | (next-shot (+ shot-period (clock-time *game-clock*)))) 164 | 165 | (game-particle-make 166 | (particle-make (vect-make 167 | (+ *screen-width* (/ (sparrow-width *texture-atlas* img-name) 2.)) 168 | (+ (/ img-height 2.) 169 | (* img-height (rand-in-range 0 nrows)))) 170 | (vect-make (- (rand-in-range *enemy-speed* 171 | (* 2 *enemy-speed*))) 172 | 0) 173 | 180 0 1 0) 174 | *texture-atlas* 175 | img-name 176 | (lambda (gp dt) 177 | (if (> (clock-time *game-clock*) next-shot) 178 | (begin 179 | (set! *enemy-bullets* 180 | (cons (spawn-bullet gp "enemy-bullet.png" 181 | *enemy-bullet-speed*) 182 | *enemy-bullets*)) 183 | (set! next-shot (+ (clock-time *game-clock*) shot-period)) 184 | (audio-enqueue (tone-make 600 (/ *base-volume* 2.) 0.05)))) 185 | (game-particle-integrate gp dt))))) 186 | 187 | (define (spawn-enemies n) 188 | (repeatedly spawn-enemy n)) 189 | 190 | (define (spawn-player) 191 | (let* ((img-name "hero.png")) 192 | (game-particle-make 193 | (particle-make (vect-make (sparrow-width *texture-atlas* img-name) 194 | (/ *screen-height* 2.)) 195 | (vect-make 0 0) 196 | 0 0 1 0) 197 | *texture-atlas* 198 | img-name))) 199 | 200 | (define (spawn-bullet shooter img speed) 201 | (let* ((sx (game-particle-x shooter)) 202 | (sy (game-particle-y shooter)) 203 | (ang (game-particle-t shooter)) 204 | 205 | (dx (cos (degrees->radians ang))) 206 | (dy (sin (degrees->radians ang)))) 207 | 208 | (game-particle-make 209 | (particle-make (vect-make sx sy) 210 | (vect-make (* dx speed) 211 | (* dy speed)) 212 | (rand-in-range 0 360) 500 213 | 0.25 0.5) 214 | *texture-atlas* 215 | img))) 216 | 217 | (define (player-fire) 218 | (set! *player-bullets* (cons (spawn-bullet *player* "plasma.png" 219 | *player-bullet-speed*) 220 | *player-bullets*))) 221 | 222 | (define-structure repeating-latch period latch-value last-state last-time) 223 | 224 | (define (repeating-latch-make period latch-value) 225 | (make-repeating-latch period latch-value 0 0)) 226 | 227 | (define (repeating-latch-state latch input-state) 228 | (if (not (eq? (repeating-latch-last-state latch) input-state)) 229 | (begin 230 | (repeating-latch-last-state-set! latch input-state) 231 | (repeating-latch-last-time-set! latch (clock-time *game-clock*)) 232 | input-state) 233 | 234 | (if (> (cycles->seconds (- (clock-time *game-clock*) 235 | (repeating-latch-last-time latch))) 236 | (repeating-latch-period latch)) 237 | (begin 238 | (repeating-latch-last-time-set! latch (clock-time *game-clock*)) 239 | input-state) 240 | (repeating-latch-latch-value latch)))) 241 | 242 | (define *font-table* (make-table)) 243 | 244 | (define (build-font-table atlas) 245 | (let ((index 1)) 246 | (for-each 247 | (lambda (char) 248 | (let* ((name (string-append (number->string index) ".png")) 249 | (rec (sparrow-record atlas name))) 250 | (table-set! *font-table* char rec) 251 | (set! index (+ index 1)))) 252 | (string->list "abcdefghijklmnopqrstuvwxyz")))) 253 | 254 | (define (char-rec char) 255 | (table-ref *font-table* (char-downcase char))) 256 | 257 | (define (char->sprite char) 258 | (let ((sprite (frame/make-sprite)) 259 | (rec (char-rec char))) 260 | (sprite-sparrow-record-set! sprite *texture-atlas* rec) 261 | sprite)) 262 | 263 | (define (char-width char) 264 | (%sparrow-entry-width (char-rec char))) 265 | 266 | (define (char-height char) 267 | (%sparrow-entry-height (char-rec char))) 268 | 269 | (define (string->spritelist string x y) 270 | (reduce 271 | (lambda (spritelist char) 272 | (if (eq? char #\space) 273 | (begin 274 | (set! x (+ x (char-width #\m))) 275 | spritelist) 276 | (let ((sprite (char->sprite char))) 277 | (sprite-x-set! sprite x) 278 | (sprite-y-set! sprite y) 279 | (set! x (+ x (char-width char))) 280 | (frame/spritelist-append spritelist sprite)))) 281 | #f 282 | (string->list string))) 283 | 284 | (define (ensure-resources) 285 | (set! *texture-atlas* (sparrow-load "spacer/images_default")) 286 | (build-font-table *texture-atlas*) 287 | 288 | (set! *player* (spawn-player)) 289 | (set! *enemies* (spawn-enemies *initial-enemies*)) 290 | (thread-start! (make-thread music))) 291 | 292 | (define (integrate-game-particle gp dt) 293 | (let ((extra (game-particle-extra gp))) 294 | (if extra 295 | (extra gp dt) 296 | (game-particle-integrate gp dt)))) 297 | 298 | (define (integrate-objects dt) 299 | (let ((integrate (lambda (item) (integrate-game-particle item dt)))) 300 | (step-pretty-particles dt) 301 | (for-each integrate (list *player*)) 302 | (for-each integrate *player-bullets*) 303 | (for-each integrate *enemy-bullets*) 304 | (for-each integrate *enemies*) 305 | (for-each integrate *enemy-bullets*))) 306 | 307 | (define (with-collisions as bs fn) 308 | (for-each (lambda (a) 309 | (for-each (lambda (b) 310 | (if (rect-intersect (game-particle-rect a) 311 | (game-particle-rect b)) 312 | (fn a b))) 313 | bs)) 314 | as)) 315 | 316 | (define *spatial-scale-factor* (/ 256.)) 317 | 318 | (define (game-particle-spatial-rect g) 319 | (game-particle-rect-scaled g *spatial-scale-factor*)) 320 | 321 | (define (with-spatial-collisions spatial bs fn) 322 | (for-each (lambda (b) 323 | (let* ((rect (game-particle-spatial-rect b)) 324 | (as (spatial-rect spatial rect))) 325 | (if (not (null? as)) 326 | (with-collisions as (list b) fn)))) 327 | bs)) 328 | 329 | (define (game-objects->spatial gs) 330 | (reduce (lambda (spatial g) 331 | (spatial-update! spatial g (game-particle-spatial-rect g)) 332 | spatial) 333 | (spatial-make (* *screen-width* *spatial-scale-factor*)) 334 | gs)) 335 | 336 | (define (tone-make freq amp duration) 337 | (sinsampler-make (audio-current-sample) (seconds->samples duration) 338 | freq amp 0)) 339 | 340 | (define (explosion-sound duration) 341 | (saw-samplers *base-volume* '(100 40) duration)) 342 | 343 | (define (handle-collisions) 344 | (if (or (null? *player-bullets*) 345 | (null? *enemies*)) 346 | '() 347 | (with-spatial-collisions 348 | (game-objects->spatial *player-bullets*) 349 | *enemies* 350 | 351 | (lambda (bullet enemy) 352 | (set! *player-bullets* (delete bullet *player-bullets*)) 353 | (set! *enemies* (delete enemy *enemies*)) 354 | (add-pretty-particles! (spawn-smoke-particle enemy)) 355 | (add-pretty-particles! (spawn-hulk-particle 356 | bullet 357 | "ship-right.png"))))) 358 | 359 | (if (null? *enemy-bullets*) 360 | '() 361 | (with-collisions 362 | (list *player*) *enemy-bullets* 363 | 364 | (lambda (player bullet) 365 | (set! *enemy-bullets* (delete bullet *enemy-bullets*)) 366 | (enqueue-all (explosion-sound 0.15)) 367 | (add-pretty-particles! (spawn-hulk-particle 368 | player 369 | "hero.png")))))) 370 | 371 | (define (random-spawn? num max-num prob) 372 | (let ((rand (rand-in-range 0 max-num)) 373 | (thresh (* prob (- max-num num)))) 374 | (< rand thresh))) 375 | 376 | (define (spawn-and-terminate dt) 377 | ;; remove bullets that go off the right 378 | (for-each 379 | (lambda (bullet) 380 | (if (> (game-particle-x bullet) (+ (/ (game-particle-width bullet) 2.) 381 | *screen-width*)) 382 | (set! *player-bullets* (delete bullet *player-bullets*)))) 383 | *player-bullets*) 384 | 385 | ;; remove bullets that go off the left 386 | (for-each 387 | (lambda (bullet) 388 | (if (< (game-particle-x bullet) (- (/ (game-particle-width bullet) 2.))) 389 | (set! *enemy-bullets* (delete bullet *enemy-bullets*)))) 390 | *enemy-bullets*) 391 | 392 | ;; remove enemies that go off the left 393 | (for-each 394 | (lambda (enemy) 395 | (if (< (game-particle-x enemy) (- (/ (game-particle-width enemy) 2.))) 396 | (set! *enemies* (delete enemy *enemies*)))) 397 | *enemies*) 398 | 399 | ;; spawn new enemies 400 | (if (random-spawn? (length *enemies*) *max-enemies* 0.1) 401 | (set! *enemies* (cons (spawn-enemy) *enemies*)))) 402 | 403 | (define (stars-spritelist) 404 | (let ((stars (image-load "spacer/night-sky-stars.jpg")) 405 | (sprite (frame/make-sprite))) 406 | (sprite-resource-set! sprite stars) 407 | (sprite-x-set! sprite 0) 408 | (sprite-y-set! sprite 0) 409 | (sprite-width-set! sprite *screen-width*) 410 | (sprite-height-set! sprite *screen-height*) 411 | (frame/spritelist-append #f sprite))) 412 | 413 | (define (render input) 414 | (spritelist-enqueue-for-screen! 415 | (stars-spritelist)) 416 | 417 | (spritelist-enqueue-for-screen! 418 | (game-particles->sprite-list *particles*)) 419 | 420 | (spritelist-enqueue-for-screen! 421 | (game-particles->sprite-list *enemies*)) 422 | 423 | (spritelist-enqueue-for-screen! 424 | (game-particles->sprite-list *enemy-bullets*)) 425 | 426 | (if (and (= 0 (input-leftright input)) 427 | (= 0 (input-updown input))) 428 | (game-particle-img-name-set! *player* "hero.png") 429 | (game-particle-img-name-set! *player* "hero-engines.png")) 430 | (spritelist-enqueue-for-screen! 431 | (game-particles->sprite-list (cons *player* *player-bullets*))) 432 | 433 | ;; draw a letter 434 | (spritelist-enqueue-for-screen! (string->spritelist "Hello World" 100 100))) 435 | 436 | (define *player-fire-repeater* (repeating-latch-make 0.2 #f)) 437 | 438 | (define (update-view dt input) 439 | '()) 440 | 441 | ;;; tools for playing with audio 442 | 443 | (define (disable-game) 444 | (set! update-view (lambda (dt input) '())) 445 | (set! music (lambda () '()))) 446 | 447 | (define (enqueue-all samplers) 448 | (for-each audio-enqueue samplers)) 449 | 450 | (define (samplers constructor amp freqs duration mix start-sample) 451 | (let* ((duration (if mix duration (/ duration (length freqs))))) 452 | (map 453 | (lambda (f) 454 | (let ((sampler (constructor start-sample 455 | (seconds->samples duration) 456 | f amp 0))) 457 | (if (not mix) 458 | (set! start-sample (+ start-sample (seconds->samples duration)))) 459 | sampler)) 460 | freqs))) 461 | 462 | (define (sin-samplers amp freqs duration #!key (mix #t) (start (audio-current-sample))) 463 | (samplers sinsampler-make amp freqs duration mix start)) 464 | 465 | (define (saw-samplers amp freqs duration #!key (mix #t) (start (audio-current-sample))) 466 | (samplers sawsampler-make amp freqs duration mix start)) 467 | 468 | (define +c+ 261.6) 469 | (define +c#+ 277.2) 470 | (define +d+ 293.7) 471 | (define +eb+ 311.1) 472 | (define +e+ 329.6) 473 | (define +f+ 349.2) 474 | (define +f#+ 370.0) 475 | (define +g+ 392.0) 476 | (define +g#+ 415.3) 477 | (define +a+ 440.0) 478 | (define +bb+ 466.2) 479 | (define +b+ 493.9) 480 | 481 | (define +notes+ (list +c+ +c#+ +d+ +eb+ +e+ +f+ +f#+ +g+ +g#+ +a+ +bb+ +b+)) 482 | 483 | ;;; scales (derived from notes) 484 | (define +ionian+ (list 2 2 1 2 2 2 1)) 485 | (define +locrian+ (list 1 2 2 1 2 2 2)) 486 | (define +aeolian+ (list 2 1 2 2 1 2 2)) 487 | 488 | (define (index-sequentially offsets lst) 489 | (let ((idx 0) 490 | (lst-len (length lst))) 491 | (map 492 | (lambda (offset) 493 | (set! idx (+ idx offset)) 494 | (* (ceiling (/ (+ 1 idx) lst-len)) 495 | (list-ref lst (modulo idx lst-len)))) 496 | offsets))) 497 | 498 | 499 | (define (scale type center) 500 | (let ((idx (index-of center +notes+)) 501 | (notes (length +notes+))) 502 | (index-sequentially (cons idx type) 503 | +notes+))) 504 | 505 | (define +c-major-scale+ (scale +ionian+ +c+)) 506 | 507 | ;;; chord forms (derived from scales) 508 | (define +triad+ (list 0 2 2)) 509 | (define +diminished-triad+ (list 0 1 2)) 510 | (define +7th+ (list 0 2 2 2)) 511 | (define +6th+ (list 0 2 2 1)) 512 | (define +sus2+ (list 0 1 3)) 513 | (define +sus4+ (list 0 3 1)) 514 | 515 | (define (chord scale-type chord-type center) 516 | (let ((scale-notes (scale scale-type center))) 517 | (index-sequentially chord-type scale-notes))) 518 | 519 | (define +c-major+ (chord +ionian+ +triad+ +c+)) 520 | 521 | (define (enqueue-chord-sequence scale-type chord-type amp freqs duration 522 | #!optional (sampler sin-samplers)) 523 | (thread-start! 524 | (make-thread 525 | (lambda () 526 | (for-each 527 | (lambda (note) 528 | (enqueue-all 529 | (sampler amp (chord scale-type chord-type note) duration)) 530 | (thread-sleep! duration)) 531 | freqs))))) 532 | 533 | (define (enqueue-arpeggio-sequence scale-type chord-type amp freqs duration 534 | #!optional (sampler sin-samplers)) 535 | (thread-start! 536 | (make-thread 537 | (lambda () 538 | (for-each 539 | (lambda (note) 540 | (enqueue-all 541 | (sampler amp (chord scale-type chord-type note) duration mix: #f)) 542 | (thread-sleep! duration)) 543 | freqs))))) 544 | 545 | (define +last-silent-sample+ #f) 546 | (define (play freqs duration) 547 | (if (not +last-silent-sample+) (set! +last-silent-sample+ (audio-current-sample))) 548 | (if (and (> +last-silent-sample+ 0) 549 | (< (audio-current-sample) 0)) 550 | ;; this is an integer wraparound situation. we'll get an audio mishap 551 | (set! +last-silent-sample+ (audio-current-sample))) 552 | 553 | ;; wait till the next silence is two durations 554 | (let loop ((delta (- +last-silent-sample+ (audio-current-sample)))) 555 | (if (> delta (seconds->samples 10)) 556 | (begin 557 | (thread-sleep! 0.1) 558 | (loop (- +last-silent-sample+ (audio-current-sample)))))) 559 | 560 | (enqueue-all (sin-samplers 1000 freqs duration start: +last-silent-sample+)) 561 | (set! +last-silent-sample+ (+ +last-silent-sample+ (seconds->samples duration)))) 562 | 563 | (define (bpm->seconds n notes-per-beat) 564 | (/ n (* notes-per-beat 60.))) 565 | 566 | (define (progression base) 567 | (let ((duration (bpm->seconds 120 4))) 568 | 569 | (play (list (* base 4/3) base) duration) 570 | (play (list (* base 3/3) (* 2 base)) duration) 571 | (play (list (* base 4/3) (* 2 base)) (/ duration 2.)) 572 | (play (list (* base 4/3) base) (/ duration 2.)) 573 | 574 | (play (list (* base 1/3) (* 1 base)) duration) 575 | 576 | (play (list (* 1/3 base) (* 1 base)) (/ duration 2.)) 577 | (play (list (* 1/3 base) (* 2 base)) (/ duration 2.)) 578 | 579 | (play (list (* base 1/3) base) duration) 580 | 581 | (play (list (* 1/3 base) (* 2 base)) (/ duration 3.)) 582 | (play (list (* 2/3 base) (* 1 base)) (/ duration 3.)) 583 | (play (list (* 2/3 base) (* 2 base)) (/ duration 3.)) 584 | 585 | (play (list (* base 2/3) base) duration) 586 | 587 | (play (list (* 1/3 base) (* 2 base)) (/ duration 3.)) 588 | (play (list (* 1/3 base) (* 1 base)) (/ duration 3.)) 589 | (play (list (* 2/3 base) (* 1 base)) (/ duration 3.)) 590 | 591 | (play (list (* base 4/3) (* 2 base)) duration) 592 | 593 | (play (list (* 1/3 base) (* 2 base)) (/ duration 2.)) 594 | (play (list (* 1/3 base) (* 1 base)) (/ duration 2.)))) 595 | 596 | (define (music) 597 | (progression 100.) 598 | (progression 150.) 599 | (progression 75.) 600 | (progression 50.) 601 | (music)) 602 | 603 | #| 604 | (define (music) '()) 605 | (thread-start! (make-thread music)) 606 | |# 607 | 608 | ;;; examples 609 | #| 610 | (enqueue-all (saw-samplers 6000 '(200 50) 2)) 611 | (enqueue-all (saw-samplers 6000 '(180 50) 2)) 612 | (enqueue-all (saw-samplers 6000 '(200 150) 2)) 613 | (enqueue-all (saw-samplers 10000 '(200 150) 1 mix: #f)) 614 | (enqueue-all (sin-samplers 10000 615 | (list +c+ +d+ +e+ 616 | +c+ +d+ +e+ 617 | +c+ +d+ +e+ +d+ +c+ +d+ +c+) 618 | 2 mix: #f)) 619 | 620 | (enqueue-arpeggio-sequence +ionian+ +sus2+ 10000 621 | (list +c+ +d+ +e+ 622 | +c+ +d+ +e+ 623 | +c+ +d+ +e+ +d+ +c+ +d+ +c+) 624 | 0.6) 625 | |# 626 | -------------------------------------------------------------------------------- /spacer/enemy-bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/spacer/enemy-bullet.png -------------------------------------------------------------------------------- /spacer/hero-engines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/spacer/hero-engines.png -------------------------------------------------------------------------------- /spacer/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/spacer/hero.png -------------------------------------------------------------------------------- /spacer/images_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/spacer/images_default.png -------------------------------------------------------------------------------- /spacer/images_default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /spacer/night-sky-stars.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/spacer/night-sky-stars.jpg -------------------------------------------------------------------------------- /spacer/plasma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/spacer/plasma.png -------------------------------------------------------------------------------- /spacer/ship-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/spacer/ship-right.png -------------------------------------------------------------------------------- /spacer/smoke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/spacer/smoke.png -------------------------------------------------------------------------------- /sparrow.scm: -------------------------------------------------------------------------------- 1 | ;;; read xml files in the sparrow format 2 | (load "xml2") 3 | 4 | (define-structure %sparrow table image-file w h) 5 | (define-structure %sparrow-entry x y width height) 6 | 7 | (define (subtexture-markup sml) 8 | (filter (sml:node-named? "SubTexture") (sml:children sml))) 9 | 10 | (define (sparrow-load base) 11 | (let* ((xml (string-append base ".xml")) 12 | (image-file (string-append base ".png")) 13 | (sparrow (sml:parse-file xml)) 14 | (table (make-table))) 15 | 16 | (for-each 17 | (lambda (subtex) 18 | (let ((x (string->number (sml:attr subtex "x"))) 19 | (y (string->number (sml:attr subtex "y"))) 20 | (w (string->number (sml:attr subtex "width"))) 21 | (h (string->number (sml:attr subtex "height"))) 22 | (name (sml:attr subtex "name"))) 23 | (table-set! table name (make-%sparrow-entry x y w h)))) 24 | (subtexture-markup sparrow)) 25 | 26 | (let* ((sparrow (make-%sparrow table image-file #f #f)) 27 | (img (sparrow-image sparrow)) 28 | (w (image-width img)) 29 | (h (image-height img))) 30 | 31 | (%sparrow-w-set! sparrow w) 32 | (%sparrow-h-set! sparrow h) 33 | 34 | sparrow))) 35 | 36 | (define (sparrow-record sparrow name) 37 | (table-ref (%sparrow-table sparrow) name)) 38 | 39 | ;;; now a link.scm specific approach 40 | (define (sparrow-image sparrow) 41 | (image-load (%sparrow-image-file sparrow))) 42 | 43 | (define (sparrow-width sparrow name) 44 | (%sparrow-entry-width (sparrow-record sparrow name))) 45 | 46 | (define (sparrow-height sparrow name) 47 | (%sparrow-entry-height (sparrow-record sparrow name))) 48 | 49 | (define (sparrow-x sparrow name) 50 | (%sparrow-entry-x (sparrow-record sparrow name))) 51 | 52 | (define (sparrow-y sparrow name) 53 | (%sparrow-entry-y (sparrow-record sparrow name))) 54 | 55 | (define (sparrow-texcoords sparrow name) 56 | (let* ((rec (sparrow-record sparrow name)) 57 | (w (%sparrow-w sparrow)) 58 | (h (%sparrow-h sparrow)) 59 | (sp-x (%sparrow-entry-x rec)) 60 | (sp-y (%sparrow-entry-y rec)) 61 | (sp-w (%sparrow-entry-width rec)) 62 | (sp-h (%sparrow-entry-height rec)) 63 | (u0 (/ sp-x w)) 64 | (u1 (/ (+ sp-x sp-w) w)) 65 | (v0 (/ sp-y h)) 66 | (v1 (/ (+ sp-y sp-h) h))) 67 | (rect-make (exact->inexact u0) (exact->inexact v0) 68 | (exact->inexact u1) (exact->inexact v1)))) 69 | 70 | (define (sprite-sparrow-record-coords-set! sprite sparrow rec) 71 | (let* ((w (%sparrow-w sparrow)) 72 | (h (%sparrow-h sparrow)) 73 | (sp-x (%sparrow-entry-x rec)) 74 | (sp-y (%sparrow-entry-y rec)) 75 | (sp-w (%sparrow-entry-width rec)) 76 | (sp-h (%sparrow-entry-height rec)) 77 | (u0 (/ sp-x w)) 78 | (u1 (/ (+ sp-x sp-w) w)) 79 | (v0 (/ (+ sp-y sp-h) h)) 80 | (v1 (/ sp-y h))) 81 | 82 | (sprite-coords-set! sprite u0 v0 u1 v1))) 83 | 84 | (define (sprite-sparrow-record-set! sprite sparrow rec) 85 | (sprite-sparrow-record-coords-set! sprite sparrow rec) 86 | (sprite-width-set! sprite (%sparrow-entry-width rec)) 87 | (sprite-height-set! sprite (%sparrow-entry-height rec)) 88 | (sprite-resource-set! sprite (sparrow-image sparrow))) 89 | 90 | (define (sprite-sparrow-coords-set! sprite sparrow name) 91 | (let* ((rec (sparrow-record sparrow name))) 92 | (sprite-sparrow-record-coords-set! sprite sparrow rec))) 93 | 94 | -------------------------------------------------------------------------------- /spatial.scm: -------------------------------------------------------------------------------- 1 | (declare 2 | (standard-bindings) 3 | (extended-bindings) 4 | (fixnum) 5 | (not safe)) 6 | 7 | (define-structure spatial obj->idxs idx->objs width) 8 | 9 | (define (spatial-calc-idx width x y) 10 | (fx+ x (fx* y width))) 11 | 12 | (define (spatial-idx spatial x y) 13 | (spatial-calc-idx (spatial-width spatial) x y)) 14 | 15 | (define (spatial-rect->idxs spatial rect) 16 | (let ((width (spatial-width spatial)) 17 | (minx (->fixnum (floor (rect-minx rect)))) 18 | (maxx (->fixnum (ceiling (rect-maxx rect)))) 19 | (miny (->fixnum (floor (rect-miny rect)))) 20 | (maxy (->fixnum (ceiling (rect-maxy rect)))) 21 | (result '())) 22 | (let loopy ((y miny)) 23 | (if (< y maxy) 24 | (let loopx ((x minx)) 25 | (if (< x maxx) 26 | (begin 27 | (set! result (cons (spatial-calc-idx width x y) result)) 28 | (loopx (fx+ x 1))) 29 | (loopy (fx+ y 1)))) 30 | result)))) 31 | 32 | (define (spatial-append-unique-pred pred new old) 33 | (let loop ((new new) 34 | (old old)) 35 | (if (null? new) 36 | old 37 | (if (some? (lambda (x) (pred x (car new))) old) 38 | (loop (cdr new) old) 39 | (loop (cdr new) (cons (car new) old)))))) 40 | 41 | (define (spatial-rect spatial rect) 42 | (let ((idxs (spatial-rect->idxs spatial rect)) 43 | (idx->objs (spatial-idx->objs spatial))) 44 | 45 | (let loop ((idxs idxs) 46 | (result '())) 47 | (if (null? idxs) 48 | result 49 | (loop (cdr idxs) 50 | (spatial-append-unique-pred eq? 51 | (table-ref idx->objs (car idxs) '()) 52 | result)))))) 53 | 54 | (define (spatial-delete! spatial value) 55 | (let* ((obj->idxs (spatial-obj->idxs spatial)) 56 | (idx->objs (spatial-idx->objs spatial)) 57 | (idxs (table-ref obj->idxs value '()))) 58 | (let loop ((idxs idxs)) 59 | (if (not (null? idxs)) 60 | ;; remove this object from the index table 61 | (let* ((objs (table-ref idx->objs (car idxs))) 62 | (objs (remove-if (lambda (o) (eq? o value)) objs))) 63 | (if (null? objs) 64 | (table-set! idx->objs (car idxs)) 65 | (table-set! idx->objs (car idxs) objs)) 66 | (loop (cdr idxs))))) 67 | ;; remove this object from the object table 68 | (table-set! obj->idxs value))) 69 | 70 | (define (spatial-update! spatial value rect) 71 | (spatial-delete! spatial value) 72 | (let ((idxs (spatial-rect->idxs spatial rect)) 73 | (obj->idxs (spatial-obj->idxs spatial)) 74 | (idx->objs (spatial-idx->objs spatial))) 75 | (table-set! obj->idxs value idxs) 76 | (let loop ((idxs idxs)) 77 | (if (not (null? idxs)) 78 | (let ((values (table-ref idx->objs (car idxs) '()))) 79 | (table-set! idx->objs (car idxs) (cons value values)) 80 | (loop (cdr idxs)))))) 81 | value) 82 | 83 | (define (spatial-objects spatial) 84 | (map car (table->list (spatial-obj->idxs spatial)))) 85 | 86 | (define (spatial-make width) 87 | (make-spatial (make-table test: eq?) (make-table) (->fixnum (ceiling width)))) 88 | -------------------------------------------------------------------------------- /spriter-main.scm: -------------------------------------------------------------------------------- 1 | (define (ensure-resources) 2 | (lambda () 3 | (set! *ps* (repeatedly random-particle 100)) 4 | (set! *scml* (scml-load "monster/Example.SCML")) 5 | (set! *anim* (animation (entity *scml* "0") "Idle")))) 6 | 7 | (define *speed* 50) 8 | (define *pos* (make-vect 320 100)) 9 | 10 | (define (update-view dt input) 11 | (let* ((cycles-for-anim (seconds->cycles (animation-length *anim*))) 12 | (anim-cycle (modulo (clock-time *game-clock*) cycles-for-anim)) 13 | (anim-time (cycles->seconds anim-cycle)) 14 | (lr (scale-input (input-leftright input) dt)) 15 | (ud (scale-input (input-updown input) dt)) 16 | (temp (vect-add-into! *pos* *pos* (make-vect lr ud))) 17 | (sprite-list (add-animation #f *anim* anim-time 18 | (vect-x *pos*) (vect-y *pos*)))) 19 | 20 | ;(update-view-old dt) 21 | (spritelist-enqueue-for-screen! sprite-list))) 22 | -------------------------------------------------------------------------------- /spriter-util.scm: -------------------------------------------------------------------------------- 1 | (define (tkey->sprite tkey ox oy) 2 | (let* ((image (image-load (tkey-name tkey))) 3 | (sprite (frame/make-sprite)) 4 | (h (image-height image)) 5 | (w (image-width image)) 6 | (px-img-offset (* (tkey-cx tkey) w)) 7 | (py-img-offset (* (tkey-cy tkey) h)) 8 | (piv-x (+ ox (tkey-x tkey))) 9 | (piv-y (+ oy (tkey-y tkey)))) 10 | 11 | (sprite-resource-set! sprite image) 12 | (sprite-x-set! sprite piv-x) 13 | (sprite-y-set! sprite piv-y) 14 | (sprite-origin-x-set! sprite px-img-offset) 15 | (sprite-origin-y-set! sprite py-img-offset) 16 | 17 | (sprite-angle-set! sprite (tkey-angle tkey)) 18 | sprite)) 19 | 20 | (define (add-animation sprite-list anim time ox oy) 21 | (reduce (lambda (sprite-list tkey) 22 | (frame/spritelist-append sprite-list 23 | (tkey->sprite tkey ox oy))) 24 | #f 25 | (reverse (interp-anim anim time)))) 26 | 27 | -------------------------------------------------------------------------------- /spriter.scm: -------------------------------------------------------------------------------- 1 | (load "xml2") 2 | 3 | (define (resource-id-make folder-id file-id) 4 | (cons folder-id file-id)) 5 | 6 | (define (folders-markup scml) 7 | (filter (sml:node-named? "folder") (sml:children scml))) 8 | 9 | (define (entities-markup scml) 10 | (filter (sml:node-named? "entity") (sml:children scml))) 11 | 12 | (define (entity->animations-markup entity) 13 | (filter (sml:node-named? "animation") (sml:children entity))) 14 | 15 | (define (animation->mainline-markup animation) 16 | (car (filter (sml:node-named? "mainline") (sml:children animation)))) 17 | 18 | (define (animation->timelines-markup animation) 19 | (filter (sml:node-named? "timeline") (sml:children animation))) 20 | 21 | (define (timeline->keys-markup timeline) 22 | (filter (sml:node-named? "key") (sml:children timeline))) 23 | 24 | (define mainline->keys-markup timeline->keys-markup) 25 | 26 | (define (resources scml base) 27 | (mapcat (lambda (folder) 28 | (map (lambda (file) 29 | (cons (resource-id-make 30 | (sml:attr folder "id") 31 | (sml:attr file "id")) 32 | (path-expand (sml:attr file "name") base))) 33 | (sml:children folder))) 34 | (folders-markup scml))) 35 | 36 | (define-structure tkey time name x y cx cy angle spin) 37 | 38 | (define (tkey-make time name x y cx cy angle spin) 39 | (make-tkey (string->number (or time "0")) 40 | name 41 | (string->number (or x "0")) 42 | (string->number (or y "0")) 43 | (string->number (or cx "0")) 44 | (string->number (or cy "1")) 45 | (string->number (or angle "0")) 46 | (string->number (or spin "1")))) 47 | 48 | (define (timeline-parse timeline-markup resources) 49 | (map (lambda (key) 50 | (let* ((obj (car (filter (sml:node-named? "object") 51 | (sml:children key)))) 52 | (name (cdr (assoc (resource-id-make 53 | (sml:attr obj "folder") 54 | (sml:attr obj "file")) 55 | resources)))) 56 | (cons (sml:attr key "id") 57 | (tkey-make 58 | (sml:attr key "time") 59 | name 60 | (sml:attr obj "x") 61 | (sml:attr obj "y") 62 | (sml:attr obj "pivot_x") 63 | (sml:attr obj "pivot_y") 64 | (sml:attr obj "angle") 65 | (sml:attr key "spin"))))) 66 | (timeline->keys-markup timeline-markup))) 67 | 68 | (define (timelines-parse timelines resources) 69 | (map (lambda (timeline) 70 | (cons (sml:attr timeline "id") 71 | (timeline-parse timeline resources))) 72 | timelines)) 73 | 74 | 75 | (define (mainline-parse mainline-markup timelines) 76 | (map (lambda (key) 77 | (cons (string->number (or (sml:attr key "time") "0")) 78 | (map (lambda (obj) 79 | (cons 80 | (sml:attr obj "id") 81 | (cond 82 | ((equal? (sml:name obj) "object_ref") 83 | (cdr (assoc (sml:attr obj "key") 84 | (cdr (assoc (sml:attr obj "timeline") 85 | timelines))))) 86 | (#t (error "unrecognized tag " (sml:name obj)))))) 87 | (sml:children key)))) 88 | (mainline->keys-markup mainline-markup))) 89 | 90 | (define-structure animation length mainline) 91 | 92 | (define (animation-make length mainline) 93 | (make-animation (/ (string->number length) 1000.0) mainline)) 94 | 95 | (define (animations entity-markup resources) 96 | (map (lambda (animation) 97 | (let* ((timelines (timelines-parse 98 | (animation->timelines-markup animation) 99 | resources)) 100 | (mainline (mainline-parse 101 | (animation->mainline-markup animation) 102 | timelines))) 103 | (cons (sml:attr animation "name") 104 | (animation-make 105 | (sml:attr animation "length") 106 | mainline)))) 107 | (entity->animations-markup entity-markup))) 108 | 109 | (define (scml-load filename) 110 | (let* ((scml (sml:parse-file filename)) 111 | (res (resources scml (path-directory filename)))) 112 | 113 | (map (lambda (entity) 114 | (cons (sml:attr entity "id") 115 | (animations entity res))) 116 | (entities-markup scml)))) 117 | 118 | (define (entity data entity) 119 | (cdr (assoc entity data))) 120 | 121 | (define (animation entity animation) 122 | (cdr (assoc animation entity))) 123 | 124 | (define (find-frame animation time) 125 | (let ((mainline (animation-mainline animation))) 126 | (let loop((keys mainline) 127 | (nkeys (if (null? mainline) '() 128 | (cdr mainline)))) 129 | (cond 130 | ((null? nkeys) keys) 131 | ((and (>= time (caar keys)) (<= time (caar nkeys))) keys) 132 | (#t (loop (cdr keys) 133 | (cddr keys))))))) 134 | 135 | (define (lerp-internal a s d) 136 | (+ a (* s d))) 137 | 138 | (define (lerp a ta b tb t) 139 | (let ((s (/ (- t ta) 140 | (- tb ta))) 141 | (d (- b a))) 142 | (lerp-internal a s d))) 143 | 144 | (define (clerp a ta b tb d t) 145 | (if (= d 1) 146 | (if (< (- b a) 0) 147 | (lerp a ta (+ 360 b) tb t) 148 | (lerp a ta b tb t)) 149 | (if (> (- b a) 0) 150 | (lerp a ta (- b 360) tb t) 151 | (lerp a ta b tb t)))) 152 | 153 | (define (interp-objects obj1 obj2 max-time t) 154 | (let* ((t1 (tkey-time obj1)) 155 | (t2 (tkey-time obj2)) 156 | (t2 (if (> t2 t1) t2 157 | max-time))) 158 | (make-tkey t 159 | (tkey-name obj1) 160 | (lerp (tkey-x obj1) t1 (tkey-x obj2) t2 t) 161 | (lerp (tkey-y obj1) t1 (tkey-y obj2) t2 t) 162 | (lerp (tkey-cx obj1) t1 (tkey-cx obj2) t2 t) 163 | (lerp (tkey-cy obj1) t1 (tkey-cy obj2) t2 t) 164 | (clerp (tkey-angle obj1) t1 (tkey-angle obj2) t2 165 | (tkey-spin obj1) t) 166 | (tkey-spin obj1)))) 167 | 168 | (define (interp-anim anim t) 169 | (let* ((t (* 1000 t)) 170 | (l (* 1000 (animation-length anim))) 171 | (frames (find-frame anim t)) 172 | (f1 (cdar frames)) 173 | ;; always assume looping 174 | (f2 (if (null? (cdr frames)) 175 | (cdar (find-frame anim 0)) 176 | (cdadr frames)))) 177 | 178 | (map (lambda (f) 179 | (let* ((id (car f)) 180 | (obj1 (cdr f)) 181 | (obj2 (cdr (assoc id f2)))) 182 | (interp-objects obj1 obj2 l t))) 183 | f1))) 184 | 185 | ;(define timeline (car (animation->timelines-markup (car (entity->animations-markup (car (entities-markup sml))))))) 186 | 187 | 188 | -------------------------------------------------------------------------------- /stb_image.h: -------------------------------------------------------------------------------- 1 | // cute hack! 2 | 3 | #define STBI_HEADER_FILE_ONLY 4 | #include "stb_image.c" 5 | -------------------------------------------------------------------------------- /test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguy204/gambit-game-lib/f977be496d0a72e15144259f5a75b509b9be59c8/test.png -------------------------------------------------------------------------------- /testcase.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int any_fail = 0; 4 | 5 | #define ASSERT(x) do { \ 6 | if(!(x)) { \ 7 | fprintf(stderr, "FAILED: "#x"\n"); \ 8 | any_fail = 1; \ 9 | } \ 10 | } while(0); 11 | 12 | #define END_MAIN() return any_fail 13 | 14 | 15 | -------------------------------------------------------------------------------- /testlib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "testlib.h" 5 | #include "testlib_internal.h" 6 | #include "stb_image.h" 7 | 8 | 9 | ThreadBarrier render_barrier; 10 | FixedAllocator clock_allocator; 11 | FixedAllocator image_resource_allocator; 12 | StackAllocator frame_allocator; 13 | FixedAllocator command_allocator; 14 | Queue render_queue; 15 | 16 | uint32_t screen_width; 17 | uint32_t screen_height; 18 | 19 | static pthread_t renderer_thread; 20 | 21 | void process_render_command() { 22 | Command command = command_dequeue(render_queue); 23 | command->function(command->data); 24 | command_free(command); 25 | } 26 | 27 | static int renderer_running = 0; 28 | void* renderer_exec(void* empty) { 29 | while(renderer_running) { 30 | process_render_command(); 31 | } 32 | return NULL; 33 | } 34 | 35 | void renderer_await_startup(void* empty) { 36 | threadbarrier_wait(render_barrier); 37 | } 38 | 39 | void lib_init() { 40 | clock_allocator = fixed_allocator_make(sizeof(struct Clock_), MAX_NUM_CLOCKS, "clock_allocator"); 41 | image_resource_allocator = fixed_allocator_make(sizeof(struct ImageResource_), MAX_NUM_IMAGES, "image_resource_allocator"); 42 | frame_allocator = stack_allocator_make(1024 * 1024, "frame_allocator"); 43 | command_allocator = fixed_allocator_make(sizeof(struct Command_), MAX_NUM_COMMANDS, "command_allocator"); 44 | render_queue = queue_make(); 45 | render_barrier = threadbarrier_make(2); 46 | 47 | native_init(); 48 | 49 | renderer_running = 1; 50 | pthread_create(&renderer_thread, NULL, renderer_exec, NULL); 51 | 52 | // let the renderer finish init 53 | renderer_enqueue_sync(renderer_init, NULL); 54 | 55 | // kick off the audio system 56 | audio_init(); 57 | 58 | scm_init(); 59 | } 60 | 61 | void render_loop_exit(void* empty) { 62 | renderer_running = 0; 63 | threadbarrier_wait(render_barrier); 64 | } 65 | 66 | void lib_shutdown() { 67 | images_free(); 68 | renderer_enqueue(renderer_shutdown, NULL); 69 | renderer_enqueue_sync(render_loop_exit, NULL); 70 | at_exit(); 71 | } 72 | 73 | void begin_frame() { 74 | stack_allocator_freeall(frame_allocator); 75 | renderer_enqueue(renderer_begin_frame, NULL); 76 | } 77 | 78 | void end_frame() { 79 | renderer_enqueue_sync(signal_render_complete, NULL); 80 | } 81 | 82 | static LLNode last_resource = NULL; 83 | 84 | int image_width(ImageResource resource) { 85 | return resource->w; 86 | } 87 | 88 | int image_height(ImageResource resource) { 89 | return resource->h; 90 | } 91 | 92 | ImageResource image_load(char * file) { 93 | int w, h, channels; 94 | unsigned char *data = stbi_load(file, &w, &h, &channels, 0); 95 | 96 | if(data == NULL) { 97 | fprintf(stderr, "failed to load %s\n", file); 98 | return NULL; 99 | } 100 | 101 | ImageResource resource = (ImageResource)fixed_allocator_alloc(image_resource_allocator); 102 | resource->w = w; 103 | resource->h = h; 104 | resource->channels = channels; 105 | resource->node.next = last_resource; 106 | resource->data = data; 107 | last_resource = (LLNode)resource; 108 | 109 | renderer_enqueue(renderer_finish_image_load, resource); 110 | 111 | return resource; 112 | } 113 | 114 | void images_free() { 115 | LLNode head = last_resource; 116 | LLNode next; 117 | while(head) { 118 | ImageResource resource = (ImageResource)head; 119 | renderer_enqueue(renderer_finish_image_free, 120 | resource->texture); 121 | 122 | next = head->next; 123 | fixed_allocator_free(image_resource_allocator, resource); 124 | head = next; 125 | } 126 | last_resource = NULL; 127 | } 128 | 129 | /* portable implementation */ 130 | Clock clock_make() { 131 | Clock clock = (Clock)fixed_allocator_alloc(clock_allocator); 132 | clock->cycles = 0; 133 | clock->time_scale = 1.0f; 134 | clock->paused = 0; 135 | return clock; 136 | } 137 | 138 | void clock_free(Clock clock) { 139 | fixed_allocator_free(clock_allocator, clock); 140 | } 141 | 142 | float clock_update(Clock clock, float delta) { 143 | if(!clock->paused) { 144 | float scaled = delta * clock->time_scale; 145 | clock->cycles += clock_seconds_to_cycles(scaled); 146 | return scaled; 147 | } else { 148 | return 0.0f; 149 | } 150 | } 151 | 152 | long clock_time(Clock clock) { 153 | return clock->cycles; 154 | } 155 | 156 | float clock_cycles_to_seconds(long cycles) { 157 | return cycles / 1000.0f; 158 | } 159 | 160 | long clock_seconds_to_cycles(float seconds) { 161 | return roundf(seconds * 1000.0f); 162 | } 163 | 164 | Sprite frame_make_sprite() { 165 | Sprite sprite = stack_allocator_alloc(frame_allocator, sizeof(struct Sprite_)); 166 | sprite->angle = 0.0f; 167 | sprite->originX = 0.0f; 168 | sprite->originY = 0.0f; 169 | 170 | sprite->u0 = 0.0f; 171 | sprite->v0 = 1.0f; 172 | sprite->u1 = 1.0f; 173 | sprite->v1 = 0.0f; 174 | 175 | sprite->w = 100; 176 | sprite->h = 100; 177 | return sprite; 178 | } 179 | 180 | SpriteList frame_spritelist_append(SpriteList rest, Sprite sprite) { 181 | SpriteList list = stack_allocator_alloc(frame_allocator, sizeof(struct SpriteList_)); 182 | list->node.next = (LLNode)rest; 183 | list->sprite = sprite; 184 | return list; 185 | } 186 | 187 | void spritelist_render_to_screen(SpriteList list) { 188 | for(SpriteList element = list; element != NULL; 189 | element = (SpriteList)element->node.next) { 190 | Sprite sprite = element->sprite; 191 | sprite_render_to_screen(sprite); 192 | } 193 | } 194 | 195 | void spritelist_enqueue_for_screen(SpriteList list) { 196 | renderer_enqueue(spritelist_render_to_screen, list); 197 | } 198 | 199 | Command command_make(CommandFunction function, void* data) { 200 | Command command = (Command)fixed_allocator_alloc(command_allocator); 201 | command->node.next = NULL; 202 | command->node.prev = NULL; 203 | command->function = function; 204 | command->data = data; 205 | return command; 206 | } 207 | 208 | void command_free(Command command) { 209 | fixed_allocator_free(command_allocator, command); 210 | } 211 | 212 | void command_async(Queue queue, CommandFunction function, void* data) { 213 | Command command = command_make(function, data); 214 | enqueue(queue, (DLLNode)command); 215 | } 216 | 217 | static void command_sync_function(ThreadBarrier b) { 218 | threadbarrier_wait(b); 219 | } 220 | 221 | void command_sync(Queue queue, ThreadBarrier b, 222 | CommandFunction function, void* data) { 223 | command_async(queue, function, data); 224 | command_async(queue, (CommandFunction)command_sync_function, b); 225 | threadbarrier_wait(b); 226 | } 227 | -------------------------------------------------------------------------------- /testlib.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_H 2 | #define TEST_H 3 | 4 | /** conventions: 5 | * 6 | * Things that happen on the renderer thread are prefixed with 7 | * renderer_ 8 | * 9 | * Things that allocate from the per frame memory allocator are 10 | * prefixed with frame_ 11 | * 12 | * Functions that deal with objects are prefixed with objectname_ 13 | */ 14 | 15 | #define MAX_NUM_CLOCKS 20 16 | #define MAX_NUM_IMAGES 40 17 | #define MAX_NUM_COMMANDS 60 18 | 19 | #include 20 | #include 21 | 22 | #include "threadlib.h" 23 | #include "memory.h" 24 | #include "listlib.h" 25 | #include "audio.h" 26 | 27 | /* allocators */ 28 | extern ThreadBarrier render_barrier; 29 | extern FixedAllocator clock_allocator; 30 | extern FixedAllocator image_resource_allocator; 31 | extern StackAllocator frame_allocator; 32 | extern FixedAllocator command_allocator; 33 | extern Queue render_queue; 34 | 35 | extern uint32_t screen_width; 36 | extern uint32_t screen_height; 37 | 38 | /* initialize the internal allocators for the library. Must be called 39 | before other functions */ 40 | void lib_init(); 41 | 42 | /* shuts down the renderer and frees memory. Must be called before 43 | termination */ 44 | void lib_shutdown(); 45 | 46 | void begin_frame(); 47 | void end_frame(); 48 | 49 | typedef struct InputState_ { 50 | int quit_requested; 51 | float updown; 52 | float leftright; 53 | int action1; 54 | } *InputState; 55 | 56 | /* exported by test.scm */ 57 | void scm_init(); 58 | void step(int, InputState); 59 | void terminate(); 60 | void resources_released(); 61 | 62 | /* provided by system specific lib */ 63 | void native_init(); 64 | InputState frame_inputstate(); 65 | long time_millis(); 66 | void sleep_millis(long millis); 67 | 68 | /* exported by testlib.c */ 69 | typedef struct Clock_ { 70 | long cycles; /* msecs */ 71 | float time_scale; 72 | int paused; 73 | } *Clock; 74 | 75 | Clock clock_make(); 76 | void clock_free(Clock clock); 77 | float clock_update(Clock clock, float delta); /* time in seconds */ 78 | long clock_time(Clock clock); /* time in cycles */ 79 | 80 | float clock_cycles_to_seconds(long cycles); 81 | long clock_seconds_to_cycles(float seconds); 82 | 83 | typedef struct ImageResource_ { 84 | struct LLNode_ node; 85 | int w, h; 86 | unsigned int texture; 87 | int channels; 88 | unsigned char* data; /* shortlived, internal */ 89 | } *ImageResource; 90 | 91 | ImageResource image_load(char * file); 92 | int image_width(ImageResource resource); 93 | int image_height(ImageResource resource); 94 | void images_free(); 95 | 96 | typedef struct Sprite_ { 97 | ImageResource resource; 98 | float angle; 99 | float originX; 100 | float originY; 101 | float displayX; 102 | float displayY; 103 | float w, h; 104 | float u0, u1, v0, v1; 105 | } *Sprite; 106 | 107 | Sprite frame_make_sprite(); 108 | 109 | typedef struct SpriteList_ { 110 | struct LLNode_ node; 111 | Sprite sprite; 112 | } *SpriteList; 113 | 114 | SpriteList frame_spritelist_append(SpriteList list, Sprite sprite); 115 | 116 | void spritelist_enqueue_for_screen(SpriteList list); 117 | 118 | typedef void (*CommandFunction)(void*); 119 | 120 | typedef struct Command_ { 121 | struct DLLNode_ node; 122 | CommandFunction function; 123 | void *data; 124 | } *Command; 125 | 126 | Command command_make(CommandFunction function, void* data); 127 | void command_free(Command command); 128 | 129 | #define command_dequeue(queue) (Command)dequeue(queue) 130 | 131 | void command_async(Queue queue, CommandFunction function, void* data); 132 | void command_sync(Queue queue, ThreadBarrier b, 133 | CommandFunction function, void* data); 134 | 135 | #define renderer_enqueue(function, data) \ 136 | command_async(render_queue, (CommandFunction)function, (void*)data) 137 | 138 | #define renderer_enqueue_sync(function, data) \ 139 | command_sync(render_queue, render_barrier, \ 140 | (CommandFunction)function, (void*)data) 141 | 142 | #endif 143 | -------------------------------------------------------------------------------- /testlib_gl.c: -------------------------------------------------------------------------------- 1 | 2 | static const GLfloat quadCoords[4 * 3] = { 3 | 0.0f, 0.0f, 0.0f, 4 | 1.0f, 0.0f, 0.0f, 5 | 1.0f, 1.0f, 0.0f, 6 | 0.0f, 1.0f, 0.0f, 7 | }; 8 | 9 | void gl_check_(const char * msg) { 10 | GLenum error = glGetError(); 11 | if(error == GL_NO_ERROR) return; 12 | 13 | char* e_msg; 14 | switch(error) { 15 | case GL_INVALID_ENUM: 16 | e_msg = "GL_INVALID_ENUM"; 17 | break; 18 | case GL_INVALID_VALUE: 19 | e_msg = "GL_INVALID_VALUE"; 20 | break; 21 | case GL_INVALID_OPERATION: 22 | e_msg = "GL_INVALID_OPERATION"; 23 | break; 24 | case GL_STACK_OVERFLOW: 25 | e_msg = "GL_STACK_OVERFLOW"; 26 | break; 27 | case GL_STACK_UNDERFLOW: 28 | e_msg = "GL_STACK_UNDERFLOW"; 29 | break; 30 | case GL_OUT_OF_MEMORY: 31 | e_msg = "GL_OUT_OF_MEMORY"; 32 | break; 33 | default: 34 | e_msg = "unknown"; 35 | } 36 | 37 | fprintf(stderr, "GL_ERROR: %s => %s\n", msg, e_msg); 38 | } 39 | 40 | #ifdef GL_CHECK_ERRORS 41 | #define gl_check(command); gl_check_(#command) 42 | #else 43 | #define gl_check(command) command 44 | #endif 45 | 46 | void renderer_gl_init() { 47 | glEnable(GL_TEXTURE_2D); 48 | glEnable(GL_BLEND); 49 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 50 | 51 | glClearColor(0.8f, 0.8f, 0.8f, 1.0f); 52 | glViewport(0, 0, screen_width, screen_height); 53 | glMatrixMode(GL_PROJECTION); 54 | glLoadIdentity(); 55 | glOrthof(0.0f, screen_width, 0.0f, screen_height, -1.0f, 1.0f); 56 | glMatrixMode(GL_MODELVIEW); 57 | glLoadIdentity(); 58 | 59 | // set up for drawing just quads 60 | glEnableClientState(GL_VERTEX_ARRAY); 61 | glVertexPointer(3, GL_FLOAT, 0, quadCoords); 62 | 63 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); 64 | gl_check_("setup"); 65 | } 66 | 67 | void renderer_gl_shutdown() { 68 | glClear(GL_COLOR_BUFFER_BIT); 69 | } 70 | 71 | void renderer_begin_frame(void* empty) { 72 | glClear(GL_COLOR_BUFFER_BIT); 73 | } 74 | 75 | void renderer_finish_image_load(ImageResource resource) { 76 | GLuint texture; 77 | GLenum texture_format; 78 | GLint num_colors; 79 | 80 | num_colors = resource->channels; 81 | if(num_colors == 4) { 82 | texture_format = GL_RGBA; 83 | } else { 84 | texture_format = GL_RGB; 85 | } 86 | 87 | gl_check(glGenTextures(1, &texture)); 88 | gl_check(glBindTexture(GL_TEXTURE_2D, texture)); 89 | gl_check(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); 90 | gl_check(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); 91 | gl_check(glTexImage2D(GL_TEXTURE_2D, 0, texture_format, 92 | resource->w, resource->h, 0, 93 | texture_format, GL_UNSIGNED_BYTE, resource->data)); 94 | 95 | resource->texture = texture; 96 | 97 | free(resource->data); 98 | } 99 | 100 | void renderer_finish_image_free(void* texturep) { 101 | GLuint texture = (GLuint)texturep; 102 | glDeleteTextures(1, &texture); 103 | } 104 | 105 | GLuint last_texture = -1; 106 | 107 | void sprite_render_to_screen(Sprite sprite) { 108 | if(sprite->resource->texture != last_texture) { 109 | glBindTexture(GL_TEXTURE_2D, sprite->resource->texture); 110 | last_texture = sprite->resource->texture; 111 | } 112 | 113 | glPushMatrix(); 114 | 115 | const GLfloat texCoords[4 * 2] = { 116 | sprite->u0, sprite->v0, 117 | sprite->u1, sprite->v0, 118 | sprite->u1, sprite->v1, 119 | sprite->u0, sprite->v1, 120 | }; 121 | glTexCoordPointer(2, GL_FLOAT, 0, texCoords); 122 | 123 | glTranslatef(sprite->displayX, sprite->displayY, 0.0f); 124 | glRotatef(sprite->angle, 0.0f, 0.0f, 1.0f); 125 | glScalef(sprite->w, sprite->h, 1.0f); 126 | glTranslatef(-sprite->originX, -sprite->originY, 0.0f); 127 | 128 | glDrawArrays(GL_TRIANGLE_FAN, 0, 4); 129 | 130 | glPopMatrix(); 131 | } 132 | -------------------------------------------------------------------------------- /testlib_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef TESTLIB_INTERNAL_H 2 | #define TESTLIB_INTERNAL_H 3 | 4 | #include "threadlib.h" 5 | 6 | /* happens on the renderer thread. */ 7 | void renderer_init(void* empty); 8 | void renderer_shutdown(void* empty); // barrier 9 | void renderer_begin_frame(void* empty); 10 | void signal_render_complete(void* empty); // barrier 11 | void renderer_finish_image_load(ImageResource resource); 12 | void renderer_finish_image_free(void* texturep); 13 | void sprite_render_to_screen(Sprite sprite); 14 | 15 | void at_exit(); 16 | 17 | /* internal data structures */ 18 | ThreadBarrier render_barrier; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /testlib_rpi.c: -------------------------------------------------------------------------------- 1 | /* EGL implementation of testlib targeting the raspberry pi */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "testlib.h" 12 | #include "testlib_internal.h" 13 | #include "joystick.h" 14 | 15 | #include "bcm_host.h" 16 | 17 | #include "GLES/gl.h" 18 | #include "EGL/egl.h" 19 | #include "EGL/eglext.h" 20 | 21 | EGLDisplay display; 22 | EGLSurface surface; 23 | EGLContext context; 24 | 25 | extern StackAllocator frame_allocator; 26 | 27 | // include common code that is dependant on the platform variable 28 | // location/name of the opengl headers 29 | #include "testlib_gl.c" 30 | 31 | void egl_assert_(int test, const char * string) { 32 | if(!test) { 33 | fprintf(stderr, "egl_assert: %s -> %s\n", string, 34 | eglQueryString(display, eglGetError())); 35 | exit(-1); 36 | } 37 | } 38 | 39 | #define egl_assert(test) egl_assert_(test, "" #test) 40 | 41 | /* GLES2 42 | static GLbyte vShaderStr[] = 43 | "attribute vec4 vPosition; \n" 44 | "void main() { \n" 45 | " gl_Position = vPosition; \n" 46 | "}\n"; 47 | 48 | static GLbyte fShaderStr[] = 49 | "precision mediump float;\n" 50 | "void main() { \n" 51 | " gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); \n" 52 | "}\n"; 53 | 54 | GLuint load_shader(GLenum type, const char * src) { 55 | GLuint shader; 56 | GLint compiled; 57 | 58 | shader = glCreateShader(type); 59 | if(shader == 0) return 0; 60 | 61 | glShaderSource(shader, 1, &src, NULL); 62 | glCompilerShader(shader); 63 | glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); 64 | if(!compiled) { 65 | GLint info_len = 0; 66 | glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_len); 67 | if(info_len > 0) { 68 | char* log = malloc(info_len); 69 | glGetShaderInfoLog(shader, info_len, NULL, log); 70 | fprintf(stderr, "Error compiling shader:\n%s\n", log); 71 | free(log); 72 | } 73 | 74 | glDeleteShader(shader); 75 | return 0; 76 | } 77 | return shader; 78 | } 79 | */ 80 | 81 | struct timeval start_time; 82 | js_state joystick_state; 83 | 84 | void native_init() { 85 | gettimeofday(&start_time, NULL); 86 | joystick_state = joystick_open("/dev/input/js0"); 87 | } 88 | 89 | long time_millis() { 90 | struct timeval now_time; 91 | gettimeofday(&now_time); 92 | 93 | long delta_secs = now_time.tv_sec - start_time.tv_sec; 94 | long delta_usecs = now_time.tv_usec - start_time.tv_usec; 95 | return (delta_secs * 1000) + (delta_usecs / 1000); 96 | } 97 | 98 | void sleep_millis(long millis) { 99 | usleep(millis * 1000); 100 | } 101 | 102 | int sign(int val) { 103 | if(val > 0) return 1; 104 | if(val < 0) return -1; 105 | return 0; 106 | } 107 | 108 | InputState frame_inputstate() { 109 | InputState state = stack_allocator_alloc(frame_allocator, sizeof(struct InputState_)); 110 | memset(state, 0, sizeof(struct InputState_)); 111 | 112 | joystick_update_state(joystick_state); 113 | 114 | /* 115 | if(joystick_state->values[4].value != state->leftright 116 | || joystick_state->values[5].value != state->updown) { 117 | joystick_print_state(joystick_state); 118 | } 119 | joystick_print_state(joystick_state); 120 | */ 121 | 122 | state->leftright = ((float)joystick_state->values[1].value) / 32767.0; 123 | state->updown = -((float)joystick_state->values[3].value) / 32767.0; 124 | state->action1 = joystick_state->values[0].value; 125 | 126 | return state; 127 | } 128 | 129 | void renderer_init(void* empty) { 130 | int32_t success = 0; 131 | EGLBoolean result; 132 | EGLint num_config; 133 | 134 | static EGL_DISPMANX_WINDOW_T nativewindow; 135 | 136 | DISPMANX_ELEMENT_HANDLE_T dispman_element; 137 | DISPMANX_DISPLAY_HANDLE_T dispman_display; 138 | DISPMANX_UPDATE_HANDLE_T dispman_update; 139 | VC_RECT_T dst_rect; 140 | VC_RECT_T src_rect; 141 | 142 | static const EGLint attribute_list[] = 143 | { 144 | EGL_RED_SIZE, 8, 145 | EGL_GREEN_SIZE, 8, 146 | EGL_BLUE_SIZE, 8, 147 | EGL_ALPHA_SIZE, 8, 148 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 149 | EGL_NONE 150 | }; 151 | 152 | EGLConfig config; 153 | 154 | bcm_host_init(); 155 | 156 | // get an EGL display connection 157 | display = eglGetDisplay(EGL_DEFAULT_DISPLAY); 158 | egl_assert(display!=EGL_NO_DISPLAY); 159 | 160 | // initialize the EGL display connection 161 | int major, minor; 162 | result = eglInitialize(display, &major, &minor); 163 | egl_assert(EGL_FALSE != result); 164 | fprintf(stderr, "EGL initialzed version %d %d\n", major, minor); 165 | 166 | // get an appropriate EGL frame buffer configuration 167 | result = eglChooseConfig(display, attribute_list, &config, 1, &num_config); 168 | egl_assert(EGL_FALSE != result); 169 | 170 | // create an EGL rendering context 171 | context = eglCreateContext(display, config, EGL_NO_CONTEXT, NULL); 172 | egl_assert(context!=EGL_NO_CONTEXT); 173 | 174 | // create an EGL window surface 175 | success = graphics_get_display_size(0 /* LCD */, &screen_width, &screen_height); 176 | fprintf(stderr, "success = %d, screen_width = %d, screen_height = %d\n", success, screen_width, screen_height); 177 | egl_assert( success >= 0 ); 178 | 179 | dst_rect.x = 0; 180 | dst_rect.y = 0; 181 | dst_rect.width = screen_width; 182 | dst_rect.height = screen_height; 183 | 184 | src_rect.x = 0; 185 | src_rect.y = 0; 186 | src_rect.width = screen_width << 16; 187 | src_rect.height = screen_height << 16; 188 | 189 | dispman_display = vc_dispmanx_display_open( 0 /* LCD */); 190 | dispman_update = vc_dispmanx_update_start( 0 ); 191 | 192 | dispman_element = 193 | vc_dispmanx_element_add ( dispman_update, dispman_display, 194 | 0/*layer*/, &dst_rect, 0/*src*/, 195 | &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); 196 | 197 | nativewindow.element = dispman_element; 198 | nativewindow.width = screen_width; 199 | nativewindow.height = screen_height; 200 | vc_dispmanx_update_submit_sync( dispman_update ); 201 | 202 | surface = eglCreateWindowSurface( display, config, &nativewindow, NULL ); 203 | egl_assert(surface != EGL_NO_SURFACE); 204 | 205 | // connect the context to the surface 206 | result = eglMakeCurrent(display, surface, surface, context); 207 | egl_assert(EGL_FALSE != result); 208 | 209 | renderer_gl_init(); 210 | } 211 | 212 | void renderer_shutdown(void* empty) { 213 | renderer_gl_shutdown(); 214 | 215 | // Release OpenGL resources 216 | eglMakeCurrent( display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); 217 | eglDestroySurface( display, surface ); 218 | eglDestroyContext( display, context ); 219 | eglTerminate( display ); 220 | } 221 | 222 | void at_exit() { 223 | } 224 | 225 | void signal_render_complete(void* empty) { 226 | eglSwapBuffers(display, surface); 227 | gl_check_("endframe"); 228 | } 229 | 230 | -------------------------------------------------------------------------------- /testlib_sdl.c: -------------------------------------------------------------------------------- 1 | /* SDL/OpenGL implementation of testlib suitable for desktops */ 2 | 3 | #include 4 | #include 5 | 6 | #include "testlib.h" 7 | #include "testlib_internal.h" 8 | 9 | // include common code that is dependant on the platform variable 10 | // location/name of the opengl headers 11 | #define glOrthof glOrtho 12 | #include "testlib_gl.c" 13 | 14 | extern StackAllocator frame_allocator; 15 | 16 | static struct InputState_ pstate; 17 | 18 | void native_init() { 19 | memset(&pstate, 0, sizeof(struct InputState_)); 20 | screen_width = 1360; 21 | screen_height = 768; 22 | } 23 | 24 | 25 | InputState frame_inputstate() { 26 | SDL_Event event; 27 | 28 | /* pump the events */ 29 | while(SDL_PollEvent(&event)) { 30 | int keydown = 0; 31 | 32 | switch(event.type) { 33 | case SDL_QUIT: 34 | pstate.quit_requested = 1; 35 | break; 36 | case SDL_KEYDOWN: 37 | keydown = 1; 38 | } 39 | 40 | if(event.type == SDL_KEYDOWN || 41 | event.type == SDL_KEYUP) { 42 | switch(event.key.keysym.sym) { 43 | case SDLK_LEFT: 44 | pstate.leftright = keydown * -1; 45 | break; 46 | case SDLK_RIGHT: 47 | pstate.leftright = keydown * 1; 48 | break; 49 | case SDLK_DOWN: 50 | pstate.updown = keydown * -1; 51 | break; 52 | case SDLK_UP: 53 | pstate.updown = keydown * 1; 54 | break; 55 | case SDLK_SPACE: 56 | pstate.action1 = keydown * 1; 57 | default: 58 | break; 59 | } 60 | } 61 | } 62 | 63 | InputState state = stack_allocator_alloc(frame_allocator, sizeof(struct InputState_)); 64 | memcpy(state, &pstate, sizeof(struct InputState_)); 65 | 66 | state->leftright = pstate.leftright; 67 | state->updown = pstate.updown; 68 | 69 | return state; 70 | } 71 | 72 | long time_millis() { 73 | return SDL_GetTicks(); 74 | } 75 | 76 | void sleep_millis(long millis) { 77 | SDL_Delay(millis); 78 | } 79 | 80 | void renderer_init(void* empty) { 81 | if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) { 82 | fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError()); 83 | exit(1); 84 | } 85 | 86 | SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 87 | SDL_Surface* screen = SDL_SetVideoMode(screen_width, screen_height, 88 | 16, SDL_OPENGL); 89 | if(screen == NULL) { 90 | fprintf(stderr, "Unable to set %dx%d video: %s\n", 91 | screen_width, screen_height, SDL_GetError()); 92 | exit(1); 93 | } 94 | 95 | renderer_gl_init(); 96 | } 97 | 98 | void renderer_shutdown(void* empty) { 99 | renderer_gl_shutdown(); 100 | } 101 | 102 | void at_exit() { 103 | SDL_Quit(); 104 | } 105 | 106 | void signal_render_complete(void* empty) { 107 | SDL_GL_SwapBuffers(); 108 | } 109 | 110 | -------------------------------------------------------------------------------- /testlib_test.c: -------------------------------------------------------------------------------- 1 | #include "memory.h" 2 | #include "testcase.h" 3 | 4 | int main(int argc, char ** argv) { 5 | int ii; 6 | 7 | FixedAllocator fa = fixed_allocator_make(sizeof(long), 100, "fa"); 8 | void* last; 9 | for(ii=0; ii < 100; ++ii) { 10 | ASSERT((last = fixed_allocator_alloc(fa)) != NULL); 11 | } 12 | 13 | //ASSERT(fixed_allocator_alloc(fa) == NULL); 14 | fixed_allocator_free(fa, last); 15 | ASSERT(fixed_allocator_alloc(fa) != NULL); 16 | 17 | StackAllocator sa = stack_allocator_make(sizeof(long) * 100, "sa"); 18 | for(ii=0; ii < 100; ++ii) { 19 | ASSERT((last = stack_allocator_alloc(sa, sizeof(long))) != NULL); 20 | } 21 | 22 | //ASSERT(stack_allocator_alloc(sa, sizeof(long)) == NULL); 23 | stack_allocator_freeall(sa); 24 | ASSERT(stack_allocator_alloc(sa, sizeof(long) * 5) != NULL); 25 | 26 | CircularBuffer buffer = circularbuffer_make(100); 27 | ASSERT(circularbuffer_bytes_writable(buffer) == 100); 28 | ASSERT(circularbuffer_bytes_readable(buffer) == 0); 29 | 30 | char bytes[100]; 31 | for(ii = 0; ii < 100; ++ii) { 32 | bytes[ii] = ii; 33 | } 34 | 35 | circularbuffer_insert(buffer, bytes, 100); 36 | ASSERT(circularbuffer_bytes_writable(buffer) == 0); 37 | ASSERT(circularbuffer_bytes_readable(buffer) == 100); 38 | 39 | char morebytes[100]; 40 | circularbuffer_read(buffer, morebytes, 50); 41 | ASSERT(circularbuffer_bytes_readable(buffer) == 50); 42 | ASSERT(circularbuffer_bytes_writable(buffer) == 50); 43 | for(int ii = 0; ii < 50; ++ii) { 44 | ASSERT(morebytes[ii] == ii); 45 | } 46 | 47 | circularbuffer_insert(buffer, bytes, 50); 48 | ASSERT(circularbuffer_bytes_writable(buffer) == 0); 49 | ASSERT(circularbuffer_bytes_readable(buffer) == 100); 50 | 51 | circularbuffer_read(buffer, morebytes, 100); 52 | ASSERT(circularbuffer_bytes_writable(buffer) == 100); 53 | ASSERT(circularbuffer_bytes_readable(buffer) == 0); 54 | for(int ii = 0; ii < 100; ++ii) { 55 | if(ii < 50) { 56 | ASSERT(morebytes[ii] == ii + 50); 57 | } else { 58 | ASSERT(morebytes[ii] == ii - 50); 59 | } 60 | } 61 | 62 | END_MAIN(); 63 | } 64 | -------------------------------------------------------------------------------- /threadlib.c: -------------------------------------------------------------------------------- 1 | #include "threadlib.h" 2 | 3 | Queue queue_make() { 4 | Queue queue = (Queue)malloc(sizeof(struct Queue_)); 5 | queue->list.head = NULL; 6 | queue->list.tail = NULL; 7 | pthread_mutex_init(&queue->mutex, NULL); 8 | pthread_cond_init(&queue->cond, NULL); 9 | return queue; 10 | } 11 | 12 | void queue_free(Queue queue) { 13 | free(queue); 14 | } 15 | 16 | void enqueue(Queue queue, DLLNode item) { 17 | pthread_mutex_lock(&queue->mutex); 18 | dll_add_head((DLL)queue, item); 19 | pthread_mutex_unlock(&queue->mutex); 20 | pthread_cond_signal(&queue->cond); 21 | } 22 | 23 | DLLNode dequeue(Queue queue) { 24 | pthread_mutex_lock(&queue->mutex); 25 | while(1) { 26 | if(queue->list.tail) { 27 | DLLNode result = dll_remove_tail((DLL)queue); 28 | pthread_mutex_unlock(&queue->mutex); 29 | return result; 30 | } else { 31 | /* need to wait for something to be put in the queue */ 32 | pthread_cond_wait(&queue->cond, &queue->mutex); 33 | } 34 | } 35 | } 36 | 37 | DLLNode dequeue_noblock(Queue queue) { 38 | pthread_mutex_lock(&queue->mutex); 39 | DLLNode result = NULL; 40 | if(queue->list.tail) { 41 | result = dll_remove_tail((DLL)queue); 42 | } 43 | pthread_mutex_unlock(&queue->mutex); 44 | return result; 45 | } 46 | 47 | ThreadBarrier threadbarrier_make(int nthreads) { 48 | ThreadBarrier barrier = (ThreadBarrier)malloc(sizeof(struct ThreadBarrier_)); 49 | pthread_mutex_init(&barrier->mutex, NULL); 50 | pthread_cond_init(&barrier->cond, NULL); 51 | barrier->nthreads = nthreads; 52 | barrier->threads_waiting = 0; 53 | return barrier; 54 | } 55 | 56 | void threadbarrier_free(ThreadBarrier barrier) { 57 | free(barrier); 58 | } 59 | 60 | void threadbarrier_wait(ThreadBarrier barrier) { 61 | pthread_mutex_lock(&barrier->mutex); 62 | barrier->threads_waiting += 1; 63 | 64 | if(barrier->threads_waiting == barrier->nthreads) { 65 | barrier->threads_waiting = 0; 66 | pthread_cond_broadcast(&barrier->cond); 67 | } else { 68 | while(barrier->threads_waiting != 0) { 69 | pthread_cond_wait(&barrier->cond, &barrier->mutex); 70 | } 71 | } 72 | 73 | pthread_mutex_unlock(&barrier->mutex); 74 | } 75 | -------------------------------------------------------------------------------- /threadlib.h: -------------------------------------------------------------------------------- 1 | #ifndef THREADLIB_H 2 | #define THREADLIB_H 3 | 4 | #include 5 | #include 6 | #include "listlib.h" 7 | 8 | typedef struct Queue_ { 9 | struct DLL_ list; 10 | pthread_mutex_t mutex; 11 | pthread_cond_t cond; 12 | } *Queue; 13 | 14 | Queue queue_make(); 15 | void queue_free(Queue queue); 16 | 17 | void enqueue(Queue queue, DLLNode item); 18 | DLLNode dequeue(Queue queue); 19 | DLLNode dequeue_noblock(Queue queue); 20 | 21 | typedef struct ThreadBarrier_ { 22 | pthread_mutex_t mutex; 23 | pthread_cond_t cond; 24 | int nthreads; 25 | int threads_waiting; 26 | } *ThreadBarrier; 27 | 28 | ThreadBarrier threadbarrier_make(); 29 | void threadbarrier_free(ThreadBarrier barrier); 30 | 31 | void threadbarrier_wait(ThreadBarrier barrier); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /vector.c: -------------------------------------------------------------------------------- 1 | #include "vector.h" 2 | 3 | void vector_add(Vector* dst, Vector* a, Vector* b) { 4 | dst->x = a->x + b->x; 5 | dst->y = a->y + b->y; 6 | } 7 | 8 | void vector_sub(Vector* dst, Vector* a, Vector* b) { 9 | dst->x = a->x - b->x; 10 | dst->y = a->y - b->y; 11 | } 12 | 13 | void vector_scale(Vector* dst, Vector* a, float s) { 14 | dst->x = a->x * s; 15 | dst->y = a->y * s; 16 | } 17 | 18 | void vector_integrate(Vector* dst, Vector* r, Vector* dr, float dt) { 19 | dst->x = r->x + (dr->x * dt); 20 | dst->y = r->y + (dr->y * dt); 21 | } 22 | -------------------------------------------------------------------------------- /vector.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTOR_H 2 | #define VECTOR_H 3 | 4 | typedef struct { 5 | float x; 6 | float y; 7 | } Vector; 8 | 9 | void vector_add(Vector* dst, Vector* a, Vector* b); 10 | void vector_sub(Vector* dst, Vector* a, Vector* b); 11 | void vector_scale(Vector* dst, Vector* a, float s); 12 | 13 | void vector_integrate(Vector* dst, Vector* r, Vector* dr, float dt); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /xml2.scm: -------------------------------------------------------------------------------- 1 | (c-declare #< 4 | #include 5 | 6 | c-declare-end 7 | ) 8 | 9 | (c-define-type xmlDoc (pointer "xmlDoc")) 10 | (c-define-type xmlNode (pointer "xmlNode")) 11 | (c-define-type xmlAttribute (pointer "xmlAttribute")) 12 | 13 | (define xml:parse-file 14 | (c-lambda (nonnull-char-string) 15 | xmlDoc 16 | "xmlParseFile")) 17 | 18 | (define xml:root-element 19 | (c-lambda (xmlDoc) 20 | xmlNode 21 | "xmlDocGetRootElement")) 22 | 23 | (define xml:free-doc 24 | (c-lambda (xmlDoc) 25 | void 26 | "xmlFreeDoc")) 27 | 28 | (define xml:node-children 29 | (c-lambda (xmlNode) 30 | xmlNode 31 | "___result_voidstar = ___arg1->children;")) 32 | 33 | (define xml:node-next 34 | (c-lambda (xmlNode) 35 | xmlNode 36 | "___result_voidstar = ___arg1->next;")) 37 | 38 | (define xml:node-name 39 | (c-lambda (xmlNode) 40 | nonnull-char-string 41 | "___result = ___arg1->name;")) 42 | 43 | (define xml:node-content 44 | (c-lambda (xmlNode) 45 | nonnull-char-string 46 | "___result = ___arg1->content;")) 47 | 48 | (define xml:node-properties 49 | (c-lambda (xmlNode) 50 | xmlAttribute 51 | "___result_voidstar = ___arg1->properties;")) 52 | 53 | (define xml:node-type 54 | (c-lambda (xmlNode) 55 | int 56 | "___result = ___arg1->type;")) 57 | 58 | (define xml:node-doc 59 | (c-lambda (xmlNode) 60 | xmlDoc 61 | "___result_voidstar = ___arg1->doc;")) 62 | 63 | (define xml:node-list-string 64 | (c-lambda (xmlDoc xmlNode int) 65 | nonnull-char-string 66 | "xmlNodeListGetString")) 67 | 68 | (define xml:ELEMENT-NODE 69 | ((c-lambda () int "___result = XML_ELEMENT_NODE;"))) 70 | 71 | (define xml:attr-name 72 | (c-lambda (xmlAttribute) 73 | nonnull-char-string 74 | "___result = ___arg1->name;")) 75 | 76 | (define xml:attr-children 77 | (c-lambda (xmlAttribute) 78 | xmlNode 79 | "___result_voidstar = ___arg1->children;")) 80 | 81 | (define xml:attr-next 82 | (c-lambda (xmlAttribute) 83 | xmlAttribute 84 | "___result_voidstar = ___arg1->next;")) 85 | 86 | (define (xml:node->attr-alist node) 87 | (let loop ((result '()) 88 | (attr (xml:node-properties node))) 89 | (if attr 90 | (loop (cons (cons (xml:attr-name attr) 91 | (xml:node-list-string (xml:node-doc node) 92 | (xml:attr-children attr) 93 | 1)) 94 | result) 95 | (xml:attr-next attr)) 96 | result))) 97 | 98 | (define (call-with-links head next-fn call-fn) 99 | (let loop ((head head)) 100 | (if head 101 | (begin 102 | (call-fn head) 103 | (loop (next-fn head)))))) 104 | 105 | (define (xml:node->list node) 106 | (let ((result '())) 107 | (call-with-links node xml:node-next 108 | (lambda (node) 109 | (set! result (cons node result)))) 110 | (reverse result))) 111 | 112 | (define (xml:element? node) 113 | (= (xml:node-type node) xml:ELEMENT-NODE)) 114 | 115 | (define (sml:make node-name node-attr-alist children) 116 | (cons node-name (cons node-attr-alist children))) 117 | 118 | (define (sml:name exp) (car exp)) 119 | 120 | (define (sml:attrs exp) (cadr exp)) 121 | 122 | (define (sml:attr exp attr) 123 | (let ((a (assoc attr (sml:attrs exp)))) 124 | (if a (cdr a) #f))) 125 | 126 | (define (sml:children exp) (cddr exp)) 127 | 128 | (define (xml->sml-internal node) 129 | (map (lambda (node) 130 | (sml:make (xml:node-name node) 131 | (xml:node->attr-alist node) 132 | (xml->sml-internal (xml:node-children node)))) 133 | (filter xml:element? (xml:node->list node)))) 134 | 135 | (define (xml->sml node) 136 | (car (xml->sml-internal node))) 137 | 138 | (define (sml:node-named? name) 139 | (lambda (node) 140 | (equal? (sml:name node) name))) 141 | 142 | (define (sml:parse-file filename) 143 | (let* ((doc (xml:parse-file filename)) 144 | (node (if doc 145 | (xml:root-element doc) 146 | (error "failed to load " filename))) 147 | (scml (xml->sml node))) 148 | (xml:free-doc doc) 149 | 150 | scml)) 151 | --------------------------------------------------------------------------------