comparison examples/tank_wars/render.cpp @ 300:dbeed520b014 prerelease

Renamed projects to examples
author Giulio Moro <giuliomoro@yahoo.it>
date Fri, 27 May 2016 13:58:20 +0100
parents projects/tank_wars/render.cpp@8d80eda512cd
children e4392164b458
comparison
equal deleted inserted replaced
297:a3d83ebdf49b 300:dbeed520b014
1 /*
2 * render.cpp
3 *
4 * Created on: Oct 24, 2014
5 * Author: parallels
6 */
7
8
9 #include <BeagleRT.h>
10 #include <Utilities.h>
11 #include "game.h"
12 #include <rtdk.h>
13 #include <cmath>
14 #include <cstdlib>
15 #include <time.h>
16
17 int gAudioFramesPerMatrixFrame = 2; // Ratio in audio to matrix sample rate
18
19 int gInputTank1Angle = 0; // Inputs for the cannon angles
20 int gInputTank2Angle = 1;
21 int gInputLauncher = 2; // Input for launcher FSR
22
23 int gOutputX = 0; // Outputs for the scope
24 int gOutputY = 1;
25 int gOutputPlayer1LED = 2;
26 int gOutputPlayer2LED = 3;
27
28 int gGameFramesPerSecond = 60; // How often the physics are updated
29 int gGameFrameInterval; // ...and in frames
30 int gSamplesUntilNextFrame; // Counter until next update
31 int gSamplesSinceFinish = 0; // How long since somebody won?
32 bool gGameShouldRestart = false;// Whether we need to reinitiliase the game
33
34 // Counter for overall number of samples that have elapsed
35 unsigned int gSampleCounter = 0;
36
37 // 1st-order filter and peak detector for launcher input
38 float gLauncherLastSample = 0;
39 float gLauncherFilterPole = 0.8;
40 float gLauncherPeakValue = 0;
41 float gLauncherPeakFilterPole = 0.999;
42 float gLauncherNoiseThreshold = 0.01;
43 float gLauncherMinimumPeak = 0.1;
44 bool gLauncherTriggered = false;
45
46 // Screen update rate; affects buffer size. Actual contents of buffer
47 // may be smaller than this
48 int gScreenWidth = 512;
49 int gScreenHeight = 512;
50 int gScreenFramesPerSecond = 25;
51
52 // Double buffer for rendering screen. Each is an interleaved buffer
53 // of XY data.
54 float *gScreenBuffer1, *gScreenBuffer2;
55 float *gScreenBufferWrite, *gScreenBufferRead;
56 int gScreenBufferMaxLength; // What is the total buffer allocated?
57 int gScreenBufferReadLength; // How long is the read buffer?
58 int gScreenBufferWriteLength; // How long is the write (next) buffer?
59 int gScreenBufferReadPointer; // Where are we in the read buffer now?
60 int gScreenBufferNextUpdateLocation; // When should we render the next buffer?
61 bool gScreenNextBufferReady; // Is the next buffer ready to go?
62
63 // Auxiliary (low-priority) task for updating the screen
64 AuxiliaryTask gScreenUpdateTask;
65
66 // Buffers for music and sound effects
67 extern float *gMusicBuffer;
68 extern int gMusicBufferLength;
69 extern float *gSoundBoomBuffer;
70 extern int gSoundBoomBufferLength;
71 extern float *gSoundHitBuffer;
72 extern int gSoundHitBufferLength;
73
74 // Current state for sound and music
75 int gMusicBufferPointer = 0; // 0 means start of buffer...
76 int gSoundBoomBufferPointer = -1; // -1 means don't play...
77 int gSoundHitBufferPointer = -1;
78 float gSoundProjectileOscillatorPhase = 0;
79 float gSoundProjectileOscillatorGain = 0.2;
80 float gOscillatorPhaseScaler = 0;
81
82 void screen_update();
83
84 // setup() is called once before the audio rendering starts.
85 // Use it to perform any initialisation and allocation which is dependent
86 // on the period size or sample rate.
87 //
88 // userData holds an opaque pointer to a data structure that was passed
89 // in from the call to initAudio().
90 //
91 // Return true on success; returning false halts the program.
92
93 bool setup(BeagleRTContext *context, void *userData)
94 {
95 srandom(time(NULL));
96
97 // Verify we are running with matrix enabled
98 if(context->analogFrames == 0 || context->analogChannels < 4) {
99 rt_printf("Error: this example needs the matrix enabled with at least 4 channels\n");
100 return false;
101 }
102
103 // Initialise audio variables
104 gAudioFramesPerMatrixFrame = context->audioFrames / context->analogFrames;
105 gOscillatorPhaseScaler = 2.0 * M_PI / context->audioSampleRate;
106
107 // Initialise the screen buffers
108 gScreenBufferMaxLength = 2 * context->analogSampleRate / gScreenFramesPerSecond;
109 gScreenBuffer1 = new float[gScreenBufferMaxLength];
110 gScreenBuffer2 = new float[gScreenBufferMaxLength];
111 if(gScreenBuffer1 == 0 || gScreenBuffer2 == 0) {
112 rt_printf("Error initialising screen buffers\n");
113 return false;
114 }
115
116 gScreenBufferRead = gScreenBuffer1;
117 gScreenBufferWrite = gScreenBuffer2;
118 gScreenBufferReadLength = gScreenBufferWriteLength = 0;
119 gScreenBufferReadPointer = 0;
120 gScreenBufferNextUpdateLocation = 0;
121 gScreenNextBufferReady = false;
122
123 // Initialise the game
124 setupGame(gScreenWidth, gScreenHeight);
125 gGameFrameInterval = context->analogSampleRate / gGameFramesPerSecond;
126 gSamplesUntilNextFrame = gGameFrameInterval;
127
128 // Initialise auxiliary tasks
129 if((gScreenUpdateTask = BeagleRT_createAuxiliaryTask(&screen_update, 90,
130 "beaglert-screen-update")) == 0)
131 return false;
132
133 return true;
134 }
135
136 // Swap buffers on the screen
137 void swap_buffers()
138 {
139 if(gScreenBufferRead == gScreenBuffer1) {
140 gScreenBufferRead = gScreenBuffer2;
141 gScreenBufferWrite = gScreenBuffer1;
142 }
143 else {
144 gScreenBufferRead = gScreenBuffer1;
145 gScreenBufferWrite = gScreenBuffer2;
146 }
147
148 gScreenBufferReadLength = gScreenBufferWriteLength;
149 gScreenBufferReadPointer = 0;
150
151 // Schedule next update for 3/4 of the way through the buffer
152 gScreenBufferNextUpdateLocation = gScreenBufferReadLength * 0.75;
153 gScreenNextBufferReady = false;
154 }
155
156 // render() is called regularly at the highest priority by the audio engine.
157 // Input and output are given from the audio hardware and the other
158 // ADCs and DACs (if available). If only audio is available, numMatrixFrames
159 // will be 0.
160
161 void render(BeagleRTContext *context, void *userData)
162 {
163 int audioIndex = 0;
164
165 for(unsigned int n = 0; n < context->analogFrames; n++) {
166 for(int k = 0; k < gAudioFramesPerMatrixFrame; k++) {
167 // Render music and sound
168 float audioSample = 0;
169
170 // Music plays in a loop
171 if(gMusicBuffer != 0 && gMusicBufferPointer >= 0) {
172 audioSample += gMusicBuffer[gMusicBufferPointer++];
173 if(gMusicBufferPointer >= gMusicBufferLength)
174 gMusicBufferPointer = 0;
175 }
176
177 // Sound effect plays until finished, then stops
178 if(gSoundBoomBuffer != 0 && gSoundBoomBufferPointer >= 0) {
179 audioSample += gSoundBoomBuffer[gSoundBoomBufferPointer++];
180 if(gSoundBoomBufferPointer >= gSoundBoomBufferLength)
181 gSoundBoomBufferPointer = -1;
182 }
183
184 if(gSoundHitBuffer != 0 && gSoundHitBufferPointer >= 0) {
185 audioSample += gSoundHitBuffer[gSoundHitBufferPointer++];
186 if(gSoundHitBufferPointer >= gSoundHitBufferLength)
187 gSoundHitBufferPointer = -1;
188 }
189
190 // Oscillator plays to indicate projectile height
191 if(gameStatusProjectileInMotion()) {
192 audioSample += gSoundProjectileOscillatorGain * sinf(gSoundProjectileOscillatorPhase);
193
194 gSoundProjectileOscillatorPhase += gOscillatorPhaseScaler * constrain(map(gameStatusProjectileHeight(),
195 1.0, 0, 300, 2000), 200, 6000);
196 if(gSoundProjectileOscillatorPhase > 2.0 * M_PI)
197 gSoundProjectileOscillatorPhase -= 2.0 * M_PI;
198 }
199
200 context->audioOut[2*audioIndex] = context->audioOut[2*audioIndex + 1] = audioSample;
201 audioIndex++;
202 }
203
204 // First-order lowpass filter to remove noise on launch FSR
205 float rawSample = analogReadFrame(context, n, gInputLauncher);
206 float launchSample = gLauncherFilterPole * gLauncherLastSample +
207 (1.0f - gLauncherFilterPole) * rawSample;
208 gLauncherLastSample = launchSample;
209
210 // Peak-detect on launch signal
211 if(launchSample >= gLauncherPeakValue) {
212 gLauncherPeakValue = launchSample;
213 gLauncherTriggered = false;
214 }
215 else {
216 if(gLauncherPeakValue - launchSample > gLauncherNoiseThreshold && !gLauncherTriggered) {
217 // Detected a peak; is it big enough overall?
218 if(gLauncherPeakValue >= gLauncherMinimumPeak) {
219 gLauncherTriggered = true;
220 // Peak detected-- fire!!
221 // Set both cannon strengths but only one will
222 // fire depending on whose turn it is
223 float strength = map(gLauncherPeakValue,
224 gLauncherMinimumPeak, 1.0,
225 0.5f, 10.0f);
226 setTank1CannonStrength(strength);
227 setTank2CannonStrength(strength);
228 fireProjectile();
229 }
230 }
231
232 gLauncherPeakValue *= gLauncherPeakFilterPole;
233 }
234
235 if(--gSamplesUntilNextFrame <= 0) {
236 // Update game physics and cannon angles
237 gSamplesUntilNextFrame = gGameFrameInterval;
238
239 setTank1CannonAngle(map(analogReadFrame(context, n, gInputTank1Angle),
240 0, 1.0, M_PI, 0));
241 setTank2CannonAngle(map(analogReadFrame(context, n, gInputTank2Angle),
242 0, 1.0, M_PI, 0));
243 nextGameFrame();
244
245 // Check for collision and start sound accordingly
246 if(gameStatusCollisionOccurred()) {
247 gSoundBoomBufferPointer = 0;
248 }
249
250 if(gameStatusTankHitOccurred()) {
251 gSoundHitBufferPointer = 0;
252 }
253 }
254
255 if(gScreenBufferReadPointer >= gScreenBufferReadLength - 1
256 && gScreenNextBufferReady) {
257 // Got to the end; swap buffers
258 swap_buffers();
259 }
260
261 // Push current screen buffer to the matrix output
262 if(gScreenBufferReadPointer < gScreenBufferReadLength - 1) {
263 float x = gScreenBufferRead[gScreenBufferReadPointer++];
264 float y = gScreenBufferRead[gScreenBufferReadPointer++];
265
266 // Rescale screen coordinates to matrix ranges; invert the Y
267 // coordinate to go from normal screen coordinates to scope coordinates
268 analogWriteFrameOnce(context, n, gOutputX, constrain(map(x, 0, gScreenWidth, 0, 1.0), 0, 1.0));
269 analogWriteFrameOnce(context, n, gOutputY, constrain(map(y, 0, gScreenHeight, 1.0, 0), 0, 1.0));
270 }
271 else {
272 // Still not ready! Write 0 until something happens
273 analogWriteFrameOnce(context, n, gOutputX, 0);
274 analogWriteFrameOnce(context, n, gOutputY, 0);
275 }
276
277 if(gameStatusWinner() != 0) {
278 // Blink one LED to show who won
279 // Blink both LEDs when projectile is in motion
280 float val = (gSampleCounter % 4000 > 2000) ? 1.0 : 0;
281 analogWriteFrameOnce(context, n, gOutputPlayer1LED, gameStatusWinner() == 1 ? val : 0);
282 analogWriteFrameOnce(context, n, gOutputPlayer2LED, gameStatusWinner() == 2 ? val : 0);
283
284 // After 5 seconds, restart the game
285 gSamplesSinceFinish++;
286 if(gSamplesSinceFinish > 22050*5)
287 gGameShouldRestart = true;
288 }
289 else if(gameStatusProjectileInMotion()) {
290 // Blink both LEDs when projectile is in motion
291 float val = (gSampleCounter % 2000 > 1000) ? 1.0 : 0;
292 analogWriteFrameOnce(context, n, gOutputPlayer1LED, val);
293 analogWriteFrameOnce(context, n, gOutputPlayer2LED, val);
294 }
295 else if(gameStatusPlayer1Turn()) {
296 analogWriteFrameOnce(context, n, gOutputPlayer1LED, 1.0);
297 analogWriteFrameOnce(context, n, gOutputPlayer2LED, 0);
298 }
299 else {
300 analogWriteFrameOnce(context, n, gOutputPlayer2LED, 1.0);
301 analogWriteFrameOnce(context, n, gOutputPlayer1LED, 0);
302 }
303
304 // Check if we have reached the point where we should next update
305 if(gScreenBufferReadPointer >= gScreenBufferNextUpdateLocation &&
306 !gScreenNextBufferReady) {
307 // Update the screen at lower priority than the audio thread
308 BeagleRT_scheduleAuxiliaryTask(gScreenUpdateTask);
309 }
310
311 gSampleCounter++;
312 }
313 }
314
315 void screen_update()
316 {
317 // If we should restart, reinitialise the game
318 if(gGameShouldRestart) {
319 restartGame();
320 gGameShouldRestart = false;
321 gSamplesSinceFinish = 0;
322 }
323
324 // Render the game based on the current state
325 gScreenBufferWriteLength = drawGame(gScreenBufferWrite, gScreenBufferMaxLength);
326
327 // Flag it as ready to go
328 gScreenNextBufferReady = true;
329 }
330
331 // cleanup() is called once at the end, after the audio has stopped.
332 // Release any resources that were allocated in setup().
333
334 void cleanup(BeagleRTContext *context, void *userData)
335 {
336 // Clean up the game state
337 cleanupGame();
338 }