Mercurial > hg > beaglert
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 } |