PageRenderTime 59ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/jni/GLSoundParticle.h

https://gitlab.com/madamovic-bg/FPlayAndroid
C Header | 475 lines | 338 code | 39 blank | 98 comment | 43 complexity | dae897963eb2534f3b596948e618e6c6 MD5 | raw file
  1. //
  2. // FPlayAndroid is distributed under the FreeBSD License
  3. //
  4. // Copyright (c) 2013-2014, Carlos Rafael Gimenes das Neves
  5. // All rights reserved.
  6. //
  7. // Redistribution and use in source and binary forms, with or without
  8. // modification, are permitted provided that the following conditions are met:
  9. //
  10. // 1. Redistributions of source code must retain the above copyright notice, this
  11. // list of conditions and the following disclaimer.
  12. // 2. Redistributions in binary form must reproduce the above copyright notice,
  13. // this list of conditions and the following disclaimer in the documentation
  14. // and/or other materials provided with the distribution.
  15. //
  16. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  17. // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  20. // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. //
  27. // The views and conclusions contained in the software and documentation are those
  28. // of the authors and should not be interpreted as representing official policies,
  29. // either expressed or implied, of the FreeBSD Project.
  30. //
  31. // https://github.com/carlosrafaelgn/FPlayAndroid
  32. //
  33. #define BG_COLUMNS 31
  34. #define BG_PARTICLES_BY_COLUMN 16
  35. #define BG_COUNT (BG_COLUMNS * BG_PARTICLES_BY_COLUMN)
  36. class GLSoundParticle {
  37. private:
  38. unsigned int lastTime;
  39. float timeCoef;
  40. float COLORS[16 * 3];
  41. float bgPos[BG_COUNT * 2], bgSpeedY[BG_COUNT], bgTheta[BG_COUNT];
  42. unsigned char bgColor[BG_COUNT];
  43. unsigned int sensorData, lastSensorTime, nextDiffusion, rotation;
  44. float matrix[16], accelData[3], magneticData[3], oldAccelData[3], oldMagneticData[3], screenLargestSize, xScale, yScale;
  45. SimpleMutex mutex;
  46. void fillBgParticle(int index, float y) {
  47. bgPos[(index << 1)] = 0.0078125f * (float)(((int)rand() & 7) - 4);
  48. bgPos[(index << 1) + 1] = y;
  49. bgTheta[index] = 0.03125f * (float)(rand() & 63);
  50. bgSpeedY[index] = 0.125f + (0.00390625f * (float)(rand() & 15));
  51. bgColor[index] = rand() & 15;
  52. }
  53. public:
  54. GLSoundParticle() {
  55. lastTime = commonTime;
  56. timeCoef = ((glType == TYPE_IMMERSIVE_PARTICLE) ? 0.0003f : 0.001f);
  57. sensorData = 0;
  58. lastSensorTime = 0;
  59. nextDiffusion = 1;
  60. rotation = 0;
  61. yScale = 0.0f;
  62. xScale = 0.0f;
  63. memset(matrix, 0, sizeof(float) * 16);
  64. memset(accelData, 0, sizeof(float) * 3);
  65. memset(magneticData, 0, sizeof(float) * 3);
  66. memset(oldAccelData, 0, sizeof(float) * 3);
  67. memset(oldMagneticData, 0, sizeof(float) * 3);
  68. #define zNear 1.0f
  69. #define zFar 50.0f
  70. #define fovCoefA (zFar / (zNear - zFar))
  71. #define fovCoefB ((zNear * zFar) / (zNear - zFar))
  72. matrix[14] = fovCoefB; //-1 //for the fov matrix
  73. #define FULL 0.75f
  74. #define HALF 0.325f
  75. #define ZERO 0.0f
  76. #define COLORS_R(A, B) COLORS[(3 * A)] = B
  77. #define COLORS_G(A, B) COLORS[(3 * A) + 1] = B
  78. #define COLORS_B(A, B) COLORS[(3 * A) + 2] = B
  79. COLORS_R( 0, FULL); COLORS_G( 0, ZERO); COLORS_B( 0, ZERO);
  80. COLORS_R( 1, ZERO); COLORS_G( 1, FULL); COLORS_B( 1, ZERO);
  81. COLORS_R( 2, ZERO); COLORS_G( 2, ZERO); COLORS_B( 2, FULL);
  82. COLORS_R( 3, FULL); COLORS_G( 3, ZERO); COLORS_B( 3, FULL);
  83. COLORS_R( 4, FULL); COLORS_G( 4, FULL); COLORS_B( 4, ZERO);
  84. COLORS_R( 5, ZERO); COLORS_G( 5, FULL); COLORS_B( 5, FULL);
  85. COLORS_R( 6, FULL); COLORS_G( 6, FULL); COLORS_B( 6, FULL);
  86. COLORS_R( 7, FULL); COLORS_G( 7, HALF); COLORS_B( 7, ZERO);
  87. COLORS_R( 8, FULL); COLORS_G( 8, ZERO); COLORS_B( 8, HALF);
  88. COLORS_R( 9, HALF); COLORS_G( 9, FULL); COLORS_B( 9, ZERO);
  89. COLORS_R(10, ZERO); COLORS_G(10, FULL); COLORS_B(10, HALF);
  90. COLORS_R(11, ZERO); COLORS_G(11, HALF); COLORS_B(11, FULL);
  91. COLORS_R(12, HALF); COLORS_G(12, ZERO); COLORS_B(12, FULL);
  92. //the colors I like most appear twice ;)
  93. COLORS_R(13, ZERO); COLORS_G(13, ZERO); COLORS_B(13, FULL);
  94. COLORS_R(14, FULL); COLORS_G(14, HALF); COLORS_B(14, ZERO);
  95. COLORS_R(15, ZERO); COLORS_G(15, HALF); COLORS_B(15, FULL);
  96. #undef FULL
  97. #undef HALF
  98. #undef ZERO
  99. #undef COLORS_R
  100. #undef COLORS_G
  101. #undef COLORS_B
  102. int i = 0, c, ic;
  103. for (c = 0; c < BG_COLUMNS; c++) {
  104. for (ic = 0; ic < BG_PARTICLES_BY_COLUMN; ic++, i++)
  105. fillBgParticle(i, -1.2f + (0.01953125f * (float)(rand() & 127)));
  106. }
  107. }
  108. static void fillTexture() {
  109. #define TEXTURE_SIZE 64
  110. unsigned char *tex = new unsigned char[TEXTURE_SIZE * TEXTURE_SIZE];
  111. glActiveTexture(GL_TEXTURE0);
  112. for (int y = 0; y < TEXTURE_SIZE; y++) {
  113. float yf = (float)(y - (TEXTURE_SIZE >> 1));
  114. yf *= yf;
  115. for (int x = 0; x < TEXTURE_SIZE; x++) {
  116. float xf = (float)(x - (TEXTURE_SIZE >> 1));
  117. float d = sqrtf((xf * xf) + yf) / (float)((TEXTURE_SIZE / 2) - 2.0f);
  118. if (d >= 1.0f) d = 0.0f;
  119. else d = 1.0f - d;
  120. float d2 = d;
  121. //we increased the particle size, while decreasing the outer glow,
  122. //in order to improve the pixel usage in the center and leave a
  123. //10-pixel border around the image that is safe to be cropped
  124. /*d = d * d;
  125. d = d + (0.5f * d);
  126. if (d < 0.55f)
  127. d = 0.0f;
  128. else if (d < 1.0f)
  129. d = glSmoothStep(0.55f, 1.0f, d);*/
  130. d2 = glSmoothStep(0.2f, 1.1f, d2); //0.0f, 1.0f, d2);
  131. d2 = d2 * d2;
  132. d2 = d2 + d2;
  133. if (d2 > 1.0f) d2 = 1.0f;
  134. const unsigned int v = (unsigned int)(255.0f * d2); //(255.0f * (d + 0.5f * d2));
  135. tex[(y * TEXTURE_SIZE) + x] = ((v >= 255) ? 255 : (unsigned char)v);
  136. }
  137. }
  138. glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, TEXTURE_SIZE, TEXTURE_SIZE, 0, GL_ALPHA, GL_UNSIGNED_BYTE, tex);
  139. delete tex;
  140. #undef TEXTURE_SIZE
  141. }
  142. void setAspect(int width, int height, int rotation) {
  143. this->rotation = rotation;
  144. if (glType == TYPE_IMMERSIVE_PARTICLE) {
  145. if (width >= height) {
  146. //landscape
  147. //yScale = cot(fovY / 2) = cot(fovYInDegrees * PI / 360) //cot(x) = tan(PI/2 - x)
  148. //considering fovYInDegrees = 50 deg:
  149. yScale = 2.1445069205095586163562607910459f;
  150. } else {
  151. //portrait
  152. //in this case, we must make up for the extended height, and increase
  153. //the fov proportionally (0.43633231299858239423092269212215 = 50 * PI / 360)
  154. yScale = tanf(1.5707963267948966192313216916398f - (0.43633231299858239423092269212215f * (float)height / (float)width));
  155. }
  156. //xScale = yScale / aspect ratio
  157. xScale = yScale * (float)height / (float)width;
  158. } else {
  159. //change the time coefficient to slow down the particles when in portrait mode
  160. timeCoef = ((width >= height) ? 0.001f : (0.001f * (float)width / (float)height));
  161. }
  162. }
  163. void setImmersiveCfg(int diffusion, int riseSpeed) {
  164. if (diffusion >= 0)
  165. nextDiffusion = diffusion + 1;
  166. if (riseSpeed >= 0) {
  167. switch (riseSpeed) {
  168. case 0:
  169. timeCoef = 0.0f;
  170. break;
  171. case 2:
  172. timeCoef = 0.001f;
  173. break;
  174. case 3:
  175. timeCoef = 0.0017f;
  176. break;
  177. default:
  178. timeCoef = 0.0003f;
  179. break;
  180. }
  181. }
  182. }
  183. void draw() {
  184. float delta = (float)(commonTime - lastTime) * timeCoef;
  185. lastTime = commonTime;
  186. glClear(GL_COLOR_BUFFER_BIT);
  187. float a;
  188. int p = 0, c, ic, i = 2, last = 44, last2 = 116;
  189. unsigned char avg, *processedData = (unsigned char*)(floatBuffer + 512);
  190. if (glType == TYPE_IMMERSIVE_PARTICLE) {
  191. if (nextDiffusion) {
  192. //not perfect... but good enough ;)
  193. c = nextDiffusion;
  194. nextDiffusion = 0;
  195. switch (c) {
  196. case 1:
  197. *((float*)&c) = 0.0f;
  198. break;
  199. case 3:
  200. *((float*)&c) = 10.0f;
  201. break;
  202. case 4:
  203. *((float*)&c) = 15.0f;
  204. break;
  205. default:
  206. *((float*)&c) = 5.0f;
  207. break;
  208. }
  209. glUniform1f(glGetUniformLocation(glProgram, "diffusion"), *((float*)&c));
  210. }
  211. mutex.enter0();
  212. glUniformMatrix4fv(glMatrix, 1, 0, matrix);
  213. mutex.leave0();
  214. }
  215. for (c = 0; c < BG_COLUMNS; c++) {
  216. #define MAX(A,B) (((A) > (B)) ? (A) : (B))
  217. //instead of dividing by 255, we are dividing by 256 (* 0.00390625f)
  218. //since the difference is visually unnoticeable
  219. //increase the amplitudes as the frequency increases, in order to improve the effect
  220. if (i < 6) {
  221. a = (float)processedData[i] * 0.00390625f;
  222. i++;
  223. } else if (i < 20) {
  224. a = (float)MAX(processedData[i], processedData[i + 1]) * (1.5f * 0.00390625f);
  225. i += 2;
  226. } else if (i < 36) {
  227. avg = MAX(processedData[i], processedData[i + 1]);
  228. avg = MAX(avg, processedData[i + 2]);
  229. avg = MAX(avg, processedData[i + 3]);
  230. a = (float)avg * (1.5f * 0.00390625f);
  231. i += 4;
  232. } else if (i < 100) {
  233. avg = processedData[i++];
  234. for (; i < last; i++)
  235. avg = MAX(avg, processedData[i]);
  236. a = (float)avg * (2.0f * 0.00390625f);
  237. last += 8;
  238. } else {
  239. avg = processedData[i++];
  240. for (; i < last2; i++)
  241. avg = MAX(avg, processedData[i]);
  242. a = (float)avg * (2.5f * 0.00390625f);
  243. last2 += 16;
  244. }
  245. #undef MAX
  246. glUniform1f(glAmplitude, (a >= 1.0f) ? 1.0f : a);
  247. //the 31 columns spread from -0.9 to 0.9, and they are evenly spaced
  248. glUniform1f(glBaseX, -0.9f + (0.06206897f * (float)c));
  249. for (ic = 0; ic < BG_PARTICLES_BY_COLUMN; ic++, p++) {
  250. if (bgPos[(p << 1) + 1] > 1.2f)
  251. fillBgParticle(p, -1.2f);
  252. else
  253. bgPos[(p << 1) + 1] += bgSpeedY[p] * delta;
  254. glUniform3fv(glColor, 1, COLORS + (bgColor[p] * 3));
  255. glUniform2fv(glPos, 1, bgPos + (p << 1));
  256. glUniform1f(glTheta, bgTheta[p]);
  257. glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  258. }
  259. }
  260. }
  261. void onSensorData(int sensorType, float* values) {
  262. if (sensorType == 1) {
  263. accelData[0] = values[0];
  264. accelData[1] = values[1];
  265. accelData[2] = values[2];
  266. sensorData |= 1;
  267. } else {
  268. magneticData[0] = values[0];
  269. magneticData[1] = values[1];
  270. magneticData[2] = values[2];
  271. sensorData |= 2;
  272. }
  273. if (sensorData != 3)
  274. return;
  275. sensorData = 0;
  276. if (lastSensorTime == 0) {
  277. oldAccelData[0] = accelData[0];
  278. oldAccelData[1] = accelData[1];
  279. oldAccelData[2] = accelData[2];
  280. oldMagneticData[0] = magneticData[0];
  281. oldMagneticData[1] = magneticData[1];
  282. oldMagneticData[2] = magneticData[2];
  283. commonUptimeDeltaMillis(&lastSensorTime);
  284. return;
  285. }
  286. //data from the acceleration sensor is less noisy than the one from the magnetic sensor,
  287. //therefore we do not need to filter it so aggressively
  288. const float delta = (float)commonUptimeDeltaMillis(&lastSensorTime);
  289. float coefNew = (0.140625f / 16.0f) * delta; //0.140625f @ 60fps (~16ms)
  290. float coefOld = 1.0f - coefNew;
  291. accelData[0] = (oldAccelData[0] * coefOld) + (accelData[0] * coefNew);
  292. accelData[1] = (oldAccelData[1] * coefOld) + (accelData[1] * coefNew);
  293. accelData[2] = (oldAccelData[2] * coefOld) + (accelData[2] * coefNew);
  294. oldAccelData[0] = accelData[0];
  295. oldAccelData[1] = accelData[1];
  296. oldAccelData[2] = accelData[2];
  297. //apply an adptative filter: the larger the change, the faster the filter! :)
  298. //this technique produced better results than using other filters, such as
  299. //higher order low-pass filters (empirically tested)
  300. for (int axis = 0; axis < 3; axis++) {
  301. float absDelta = magneticData[axis] - oldMagneticData[axis];
  302. *((int*)&absDelta) &= 0x7fffffff; //abs ;)
  303. coefNew = (absDelta >= 1.5f ? 0.15f :
  304. ((0.05f * absDelta * absDelta) + (0.025f * absDelta))
  305. //this parable also works fine, but is slower than the above...
  306. //((0.065f * absDelta * absDelta) + (0.0025f * absDelta))
  307. ) * 0.0625f * delta; //0.0625 = / 16
  308. coefOld = 1.0f - coefNew;
  309. magneticData[axis] = (oldMagneticData[axis] * coefOld) + (magneticData[axis] * coefNew);
  310. oldMagneticData[axis] = magneticData[axis];
  311. }
  312. //SensorManager.getRotationMatrix(matrix, null, accelData, magneticData);
  313. //Original code -> AOSP: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.2_r1/android/hardware/SensorManager.java
  314. //(just porting from Java to C++ to improve performance)
  315. float Ax, Ay, Az, Ex, Ey, Ez;
  316. //http://developer.download.nvidia.com/tegra/docs/tegra_android_accelerometer_v5f.pdf
  317. switch (rotation) {
  318. case 1: //ROTATION_90
  319. Ax = -accelData[1];
  320. Ay = accelData[0];
  321. Ex = -magneticData[1];
  322. Ey = magneticData[0];
  323. break;
  324. case 2: //ROTATION_180
  325. Ax = -accelData[0];
  326. Ay = -accelData[1];
  327. Ex = -magneticData[0];
  328. Ey = -magneticData[1];
  329. break;
  330. case 3: //ROTATION_270
  331. Ax = accelData[1];
  332. Ay = -accelData[0];
  333. Ex = magneticData[1];
  334. Ey = -magneticData[0];
  335. break;
  336. default:
  337. Ax = accelData[0];
  338. Ay = accelData[1];
  339. Ex = magneticData[0];
  340. Ey = magneticData[1];
  341. break;
  342. }
  343. Az = accelData[2];
  344. Ez = magneticData[2];
  345. float Hx = (Ey * Az) - (Ez * Ay);
  346. float Hy = (Ez * Ax) - (Ex * Az);
  347. float Hz = (Ex * Ay) - (Ey * Ax);
  348. const float normH = (float)sqrtf((Hx * Hx) + (Hy * Hy) + (Hz * Hz));
  349. if (normH < 0.1f) {
  350. //device is close to free fall (or in space?), or close to
  351. //magnetic north pole. Typical values are > 100...
  352. //leave the matrix as-is!
  353. return;
  354. }
  355. const float invH = 1.0f / normH;
  356. Hx *= invH;
  357. Hy *= invH;
  358. Hz *= invH;
  359. const float invA = 1.0f / (float)sqrtf((Ax * Ax) + (Ay * Ay) + (Az * Az));
  360. Ax *= invA;
  361. Ay *= invA;
  362. Az *= invA;
  363. Ex = (Ay * Hz) - (Az * Hy);
  364. Ey = (Az * Hx) - (Ax * Hz);
  365. Ez = (Ax * Hy) - (Ay * Hx);
  366. //SensorManager.getRotationMatrix() returns the matrix in row-major order and
  367. //OpenGL needs the matrices in column-major order... nevertheless we must not
  368. //transpose this matrix, as it will be used as the camera/view matrix, and the
  369. //view matrix is the inverse of the world matrix (luckly, the inverse of a pure
  370. //rotation matrix is also its transpose!)
  371. //row-major means array indices are distributed by rows, not by columns!
  372. //for example, the matrix returned by SensorManager.getRotationMatrix()
  373. //is distributed like this:
  374. // array index value
  375. // | 0 1 2 3 | | Hx Hy Hz 0 |
  376. // | 4 5 6 7 | | Mx My Mz 0 |
  377. // | 8 9 10 11 | | Ax Ay Az 0 |
  378. // | 12 13 14 15 | | 0 0 0 1 |
  379. //if it were column-major, the indices would be like this:
  380. // array index value
  381. // | 0 4 8 12 | | Hx Hy Hz 0 |
  382. // | 1 5 9 13 | | Mx My Mz 0 |
  383. // | 2 6 10 14 | | Ax Ay Az 0 |
  384. // | 3 7 11 15 | | 0 0 0 1 |
  385. //now, we apply a projection matrix with a fov of 50 deg
  386. //based on D3DXMatrixPerspectiveFovRH
  387. //http://msdn.microsoft.com/en-us/library/windows/desktop/bb205351(v=vs.85).aspx
  388. //...as a matter of fact, this is a port from my original JavaScript implementation:
  389. //http://carlosrafaelgn.com.br/WebGL/Matrix4.js
  390. //matrix = fov * view
  391. //optimizations:
  392. //- assume m3, m7, m11, m12, m13, m14 are 0 and m15 is 1 before applying the fov matrix
  393. //- use macros intead of storing everything in the actual matrix, just to read everything
  394. //back again to apply the fov
  395. #define m0 Hx
  396. #define m1 Hy
  397. #define m2 Hz
  398. #define m4 Ex
  399. #define m5 Ey
  400. #define m6 Ez
  401. #define m8 Ax
  402. #define m9 Ay
  403. #define m10 Az
  404. mutex.enter1();
  405. matrix[0] = m0 * xScale;
  406. matrix[4] = m4 * xScale;
  407. matrix[8] = m8 * xScale;
  408. matrix[1] = m1 * yScale;
  409. matrix[5] = m5 * yScale;
  410. matrix[9] = m9 * yScale;
  411. matrix[2] = m2 * fovCoefA;
  412. matrix[6] = m6 * fovCoefA;
  413. matrix[10] = m10 * fovCoefA;
  414. matrix[3] = -m2; //m2 * fovCoefB;
  415. matrix[7] = -m6; //m6 * fovCoefB;
  416. matrix[11] = -m10; //m10 * fovCoefB;
  417. mutex.leave1();
  418. #undef m0
  419. #undef m1
  420. #undef m2
  421. #undef m4
  422. #undef m5
  423. #undef m6
  424. #undef m8
  425. #undef m9
  426. #undef m10
  427. #undef zNear //defined inside the constructor
  428. #undef zFar
  429. #undef fovCoefA
  430. #undef fovCoefB
  431. }
  432. };
  433. static GLSoundParticle* glSoundParticle;
  434. #undef BG_COLUMNS
  435. #undef BG_PARTICLES_BY_COLUMN
  436. #undef BG_COUNT