Mercurial > hg > beaglert
view projects/tank_wars/render.cpp @ 10:49f22e1246b2
Tank wars!
author | andrewm |
---|---|
date | Thu, 13 Nov 2014 15:58:08 +0100 |
parents | |
children | 06f93bef7dd2 |
line wrap: on
line source
/* * render.cpp * * Created on: Oct 24, 2014 * Author: parallels */ #include "../../include/RTAudio.h" #include "../../include/Utilities.h" #include "game.h" #include <rtdk.h> #include <cmath> #include <cstdlib> #include <time.h> float gFrequency; float gPhase; float gInverseSampleRate; int gNumChannels; int gInputTank1Angle = 0; // Inputs for the cannon angles int gInputTank2Angle = 1; int gInputLauncher = 2; // Input for launcher FSR int gOutputX = 0; // Outputs for the scope int gOutputY = 1; int gOutputPlayer1LED = 2; int gOutputPlayer2LED = 3; int gGameFramesPerSecond = 60; // How often the physics are updated int gGameFrameInterval; // ...and in frames int gSamplesUntilNextFrame; // Counter until next update int gSamplesSinceFinish = 0; // How long since somebody won? bool gGameShouldRestart = false;// Whether we need to reinitiliase the game // Counter for overall number of samples that have elapsed unsigned int gSampleCounter = 0; // 1st-order filter and peak detector for launcher input float gLauncherLastSample = 0; float gLauncherFilterPole = 0.8; float gLauncherPeakValue = 0; float gLauncherPeakFilterPole = 0.999; float gLauncherNoiseThreshold = 0.01 * MATRIX_MAX; float gLauncherMinimumPeak = 0.1 * MATRIX_MAX; bool gLauncherTriggered = false; // Screen update rate; affects buffer size. Actual contents of buffer // may be smaller than this int gScreenWidth = 512; int gScreenHeight = 512; int gScreenFramesPerSecond = 25; // Double buffer for rendering screen. Each is an interleaved buffer // of XY data. float *gScreenBuffer1, *gScreenBuffer2; float *gScreenBufferWrite, *gScreenBufferRead; int gScreenBufferMaxLength; // What is the total buffer allocated? int gScreenBufferReadLength; // How long is the read buffer? int gScreenBufferWriteLength; // How long is the write (next) buffer? int gScreenBufferReadPointer; // Where are we in the read buffer now? int gScreenBufferNextUpdateLocation; // When should we render the next buffer? bool gScreenNextBufferReady; // Is the next buffer ready to go? // Auxiliary (low-priority) task for updating the screen AuxiliaryTask gScreenUpdateTask; void screen_update(); // initialise_render() is called once before the audio rendering starts. // Use it to perform any initialisation and allocation which is dependent // on the period size or sample rate. // // userData holds an opaque pointer to a data structure that was passed // in from the call to initAudio(). // // Return true on success; returning false halts the program. bool initialise_render(int numChannels, int numMatrixFramesPerPeriod, int numAudioFramesPerPeriod, float matrixSampleRate, float audioSampleRate, void *userData) { srandom(time(NULL)); // Verify we are running with matrix enabled if(numMatrixFramesPerPeriod*2 != numAudioFramesPerPeriod) { rt_printf("Error: this example needs the matrix enabled, running at half audio rate\n"); return false; } // Initialise the screen buffers gScreenBufferMaxLength = 2 * matrixSampleRate / gScreenFramesPerSecond; gScreenBuffer1 = new float[gScreenBufferMaxLength]; gScreenBuffer2 = new float[gScreenBufferMaxLength]; if(gScreenBuffer1 == 0 || gScreenBuffer2 == 0) { rt_printf("Error initialising screen buffers\n"); return false; } gScreenBufferRead = gScreenBuffer1; gScreenBufferWrite = gScreenBuffer2; gScreenBufferReadLength = gScreenBufferWriteLength = 0; gScreenBufferReadPointer = 0; gScreenBufferNextUpdateLocation = 0; gScreenNextBufferReady = false; // Initialise the game setupGame(gScreenWidth, gScreenHeight); gGameFrameInterval = matrixSampleRate / gGameFramesPerSecond; gSamplesUntilNextFrame = gGameFrameInterval; // Initialise auxiliary tasks if((gScreenUpdateTask = createAuxiliaryTaskLoop(&screen_update, 90, "beaglert-screen-update")) == 0) return false; return true; } // Swap buffers on the screen void swap_buffers() { if(gScreenBufferRead == gScreenBuffer1) { gScreenBufferRead = gScreenBuffer2; gScreenBufferWrite = gScreenBuffer1; } else { gScreenBufferRead = gScreenBuffer1; gScreenBufferWrite = gScreenBuffer2; } gScreenBufferReadLength = gScreenBufferWriteLength; gScreenBufferReadPointer = 0; // Schedule next update for 3/4 of the way through the buffer gScreenBufferNextUpdateLocation = gScreenBufferReadLength * 0.75; gScreenNextBufferReady = false; } // render() is called regularly at the highest priority by the audio engine. // Input and output are given from the audio hardware and the other // ADCs and DACs (if available). If only audio is available, numMatrixFrames // will be 0. void render(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut, uint16_t *matrixIn, uint16_t *matrixOut) { for(int n = 0; n < numMatrixFrames; n++) { // First-order lowpass filter to remove noise on launch FSR float rawSample = analogRead(gInputLauncher, n); float launchSample = gLauncherFilterPole * gLauncherLastSample + (1.0f - gLauncherFilterPole) * rawSample; gLauncherLastSample = launchSample; // Peak-detect on launch signal if(launchSample >= gLauncherPeakValue) { gLauncherPeakValue = launchSample; gLauncherTriggered = false; } else { if(gLauncherPeakValue - launchSample > gLauncherNoiseThreshold && !gLauncherTriggered) { // Detected a peak; is it big enough overall? if(gLauncherPeakValue >= gLauncherMinimumPeak) { gLauncherTriggered = true; // Peak detected-- fire!! // Set both cannon strengths but only one will // fire depending on whose turn it is float strength = map(gLauncherPeakValue, gLauncherMinimumPeak, MATRIX_MAX, 0.5f, 10.0f); setTank1CannonStrength(strength); setTank2CannonStrength(strength); fireProjectile(); } } gLauncherPeakValue *= gLauncherPeakFilterPole; } if(--gSamplesUntilNextFrame <= 0) { // Update game physics and cannon angles gSamplesUntilNextFrame = gGameFrameInterval; setTank1CannonAngle(map(analogRead(gInputTank1Angle, n), 0, MATRIX_MAX, M_PI, 0)); setTank2CannonAngle(map(analogRead(gInputTank2Angle, n), 0, MATRIX_MAX, M_PI, 0)); nextGameFrame(); } if(gScreenBufferReadPointer >= gScreenBufferReadLength - 1 && gScreenNextBufferReady) { // Got to the end; swap buffers swap_buffers(); } // Push current screen buffer to the matrix output if(gScreenBufferReadPointer < gScreenBufferReadLength - 1) { float x = gScreenBufferRead[gScreenBufferReadPointer++]; float y = gScreenBufferRead[gScreenBufferReadPointer++]; // Rescale screen coordinates to matrix ranges; invert the Y // coordinate to go from normal screen coordinates to scope coordinates analogWrite(gOutputX, n, constrain(map(x, 0, gScreenWidth, 0, MATRIX_MAX), 0, MATRIX_MAX)); analogWrite(gOutputY, n, constrain(map(y, 0, gScreenHeight, MATRIX_MAX, 0), 0, MATRIX_MAX)); } else { // Still not ready! Write 0 until something happens analogWrite(gOutputX, n, 0); analogWrite(gOutputY, n, 0); } if(gameStatusWinner() != 0) { // Blink one LED to show who won // Blink both LEDs when projectile is in motion uint16_t val = (gSampleCounter % 4000 > 2000) ? MATRIX_MAX : 0; analogWrite(gOutputPlayer1LED, n, gameStatusWinner() == 1 ? val : 0); analogWrite(gOutputPlayer2LED, n, gameStatusWinner() == 2 ? val : 0); // After 5 seconds, restart the game gSamplesSinceFinish++; if(gSamplesSinceFinish > 22050*5) gGameShouldRestart = true; } else if(gameStatusProjectileInMotion()) { // Blink both LEDs when projectile is in motion uint16_t val = (gSampleCounter % 2000 > 1000) ? MATRIX_MAX : 0; analogWrite(gOutputPlayer1LED, n, val); analogWrite(gOutputPlayer2LED, n, val); } else if(gameStatusPlayer1Turn()) { analogWrite(gOutputPlayer1LED, n, MATRIX_MAX); analogWrite(gOutputPlayer2LED, n, 0); } else { analogWrite(gOutputPlayer2LED, n, MATRIX_MAX); analogWrite(gOutputPlayer1LED, n, 0); } // Check if we have reached the point where we should next update if(gScreenBufferReadPointer >= gScreenBufferNextUpdateLocation && !gScreenNextBufferReady) { // Update the screen at lower priority than the audio thread scheduleAuxiliaryTask(gScreenUpdateTask); } gSampleCounter++; } } void screen_update() { // If we should restart, reinitialise the game if(gGameShouldRestart) { restartGame(); gGameShouldRestart = false; gSamplesSinceFinish = 0; } // Render the game based on the current state gScreenBufferWriteLength = drawGame(gScreenBufferWrite, gScreenBufferMaxLength); // Flag it as ready to go gScreenNextBufferReady = true; } // cleanup_render() is called once at the end, after the audio has stopped. // Release any resources that were allocated in initialise_render(). void cleanup_render() { // Clean up the game state cleanupGame(); }