1 #pragma version(1) 2 #pragma stateVertex(PV) 3 #pragma stateFragment(PFTexNearest) 4 #pragma stateStore(PSIcons) 5 6 #define PI 3.14159f 7 8 int g_SpecialHWWar; 9 10 // Attraction to center values from page edge to page center. 11 float g_AttractionTable[9]; 12 float g_FrictionTable[9]; 13 float g_PhysicsTableSize; 14 15 float g_PosPage; 16 float g_PosVelocity; 17 float g_LastPositionX; 18 int g_LastTouchDown; 19 float g_DT; 20 int g_LastTime; 21 int g_PosMax; 22 float g_Zoom; 23 float g_Animation; 24 float g_OldPosPage; 25 float g_OldPosVelocity; 26 float g_OldZoom; 27 float g_MoveToTotalTime; 28 float g_MoveToTime; 29 float g_MoveToOldPos; 30 31 int g_Cols; 32 int g_Rows; 33 34 // Drawing constants, should be parameters ====== 35 #define VIEW_ANGLE 1.28700222f 36 37 int g_DrawLastFrame; 38 int lastFrame(int draw) { 39 // We draw one extra frame to work around the last frame post bug. 40 // We also need to track if we drew the last frame to deal with large DT 41 // in the physics. 42 int ret = g_DrawLastFrame | draw; 43 g_DrawLastFrame = draw; 44 return ret; // should return draw instead. 45 } 46 47 void updateReadback() { 48 if ((g_OldPosPage != g_PosPage) || 49 (g_OldPosVelocity != g_PosVelocity) || 50 (g_OldZoom != g_Zoom)) { 51 52 g_OldPosPage = g_PosPage; 53 g_OldPosVelocity = g_PosVelocity; 54 g_OldZoom = g_Zoom; 55 56 int i[3]; 57 i[0] = g_PosPage * (1 << 16); 58 i[1] = g_PosVelocity * (1 << 16); 59 i[2] = g_OldZoom * (1 << 16); 60 sendToClient(&i[0], 1, 12, 1); 61 } 62 } 63 64 void setColor(float r, float g, float b, float a) { 65 if (g_SpecialHWWar) { 66 color(0, 0, 0, 0.001f); 67 } else { 68 color(r, g, b, a); 69 } 70 } 71 72 void init() { 73 g_AttractionTable[0] = 20.0f; 74 g_AttractionTable[1] = 20.0f; 75 g_AttractionTable[2] = 20.0f; 76 g_AttractionTable[3] = 10.0f; 77 g_AttractionTable[4] = -10.0f; 78 g_AttractionTable[5] = -20.0f; 79 g_AttractionTable[6] = -20.0f; 80 g_AttractionTable[7] = -20.0f; 81 g_AttractionTable[8] = -20.0f; // dup 7 to avoid a clamp later 82 g_FrictionTable[0] = 10.0f; 83 g_FrictionTable[1] = 10.0f; 84 g_FrictionTable[2] = 11.0f; 85 g_FrictionTable[3] = 15.0f; 86 g_FrictionTable[4] = 15.0f; 87 g_FrictionTable[5] = 11.0f; 88 g_FrictionTable[6] = 10.0f; 89 g_FrictionTable[7] = 10.0f; 90 g_FrictionTable[8] = 10.0f; // dup 7 to avoid a clamp later 91 g_PhysicsTableSize = 7; 92 93 g_PosVelocity = 0; 94 g_PosPage = 0; 95 g_LastTouchDown = 0; 96 g_LastPositionX = 0; 97 g_Zoom = 0; 98 g_Animation = 1.f; 99 g_SpecialHWWar = 1; 100 g_MoveToTime = 0; 101 g_MoveToOldPos = 0; 102 g_MoveToTotalTime = 0.2f; // Duration of scrolling 1 line 103 } 104 105 void resetHWWar() { 106 g_SpecialHWWar = 1; 107 } 108 109 void move() { 110 if (g_LastTouchDown) { 111 float dx = -(state->newPositionX - g_LastPositionX); 112 g_PosVelocity = 0; 113 g_PosPage += dx * 5.2f; 114 115 float pmin = -0.49f; 116 float pmax = g_PosMax + 0.49f; 117 g_PosPage = clampf(g_PosPage, pmin, pmax); 118 } 119 g_LastTouchDown = state->newTouchDown; 120 g_LastPositionX = state->newPositionX; 121 g_MoveToTime = 0; 122 //debugF("Move P", g_PosPage); 123 } 124 125 void moveTo() { 126 g_MoveToTime = g_MoveToTotalTime; 127 g_PosVelocity = 0; 128 g_MoveToOldPos = g_PosPage; 129 130 // debugF("======= moveTo", state->targetPos); 131 } 132 133 void setZoom() { 134 g_Zoom = state->zoomTarget; 135 g_DrawLastFrame = 1; 136 updateReadback(); 137 } 138 139 void fling() { 140 g_LastTouchDown = 0; 141 g_PosVelocity = -state->flingVelocity * 4; 142 float av = fabsf(g_PosVelocity); 143 float minVel = 3.5f; 144 145 minVel *= 1.f - (fabsf(fracf(g_PosPage + 0.5f) - 0.5f) * 0.45f); 146 147 if (av < minVel && av > 0.2f) { 148 if (g_PosVelocity > 0) { 149 g_PosVelocity = minVel; 150 } else { 151 g_PosVelocity = -minVel; 152 } 153 } 154 155 if (g_PosPage <= 0) { 156 g_PosVelocity = maxf(0, g_PosVelocity); 157 } 158 if (g_PosPage > g_PosMax) { 159 g_PosVelocity = minf(0, g_PosVelocity); 160 } 161 } 162 163 float 164 modf(float x, float y) 165 { 166 return x-(y*floorf(x/y)); 167 } 168 169 170 /* 171 * Interpolates values in the range 0..1 to a curve that eases in 172 * and out. 173 */ 174 float 175 getInterpolation(float input) { 176 return (cosf((input + 1) * PI) / 2.0f) + 0.5f; 177 } 178 179 180 void updatePos() { 181 if (g_LastTouchDown) { 182 return; 183 } 184 185 float tablePosNorm = fracf(g_PosPage + 0.5f); 186 float tablePosF = tablePosNorm * g_PhysicsTableSize; 187 int tablePosI = tablePosF; 188 float tablePosFrac = tablePosF - tablePosI; 189 float accel = lerpf(g_AttractionTable[tablePosI], 190 g_AttractionTable[tablePosI + 1], 191 tablePosFrac) * g_DT; 192 float friction = lerpf(g_FrictionTable[tablePosI], 193 g_FrictionTable[tablePosI + 1], 194 tablePosFrac) * g_DT; 195 196 if (g_MoveToTime) { 197 // New position is old posiition + (total distance) * (interpolated time) 198 g_PosPage = g_MoveToOldPos + (state->targetPos - g_MoveToOldPos) * getInterpolation((g_MoveToTotalTime - g_MoveToTime) / g_MoveToTotalTime); 199 g_MoveToTime -= g_DT; 200 if (g_MoveToTime <= 0) { 201 g_MoveToTime = 0; 202 g_PosPage = state->targetPos; 203 } 204 return; 205 } 206 207 // If our velocity is low OR acceleration is opposing it, apply it. 208 if (fabsf(g_PosVelocity) < 4.0f || (g_PosVelocity * accel) < 0) { 209 g_PosVelocity += accel; 210 } 211 //debugF("g_PosPage", g_PosPage); 212 //debugF(" g_PosVelocity", g_PosVelocity); 213 //debugF(" friction", friction); 214 //debugF(" accel", accel); 215 216 // Normal physics 217 if (g_PosVelocity > 0) { 218 g_PosVelocity -= friction; 219 g_PosVelocity = maxf(g_PosVelocity, 0); 220 } else { 221 g_PosVelocity += friction; 222 g_PosVelocity = minf(g_PosVelocity, 0); 223 } 224 225 if ((friction > fabsf(g_PosVelocity)) && (friction > fabsf(accel))) { 226 // Special get back to center and overcome friction physics. 227 float t = tablePosNorm - 0.5f; 228 if (fabsf(t) < (friction * g_DT)) { 229 // really close, just snap 230 g_PosPage = roundf(g_PosPage); 231 g_PosVelocity = 0; 232 } else { 233 if (t > 0) { 234 g_PosVelocity = -friction; 235 } else { 236 g_PosVelocity = friction; 237 } 238 } 239 } 240 241 // Check for out of boundry conditions. 242 if (g_PosPage < 0 && g_PosVelocity < 0) { 243 float damp = 1.0 + (g_PosPage * 4); 244 damp = clampf(damp, 0.f, 0.9f); 245 g_PosVelocity *= damp; 246 } 247 if (g_PosPage > g_PosMax && g_PosVelocity > 0) { 248 float damp = 1.0 - ((g_PosPage - g_PosMax) * 4); 249 damp = clampf(damp, 0.f, 0.9f); 250 g_PosVelocity *= damp; 251 } 252 253 g_PosPage += g_PosVelocity * g_DT; 254 g_PosPage = clampf(g_PosPage, -0.49, g_PosMax + 0.49); 255 } 256 257 258 void 259 draw_home_button() 260 { 261 setColor(1.0f, 1.0f, 1.0f, 1.0f); 262 bindTexture(NAMED_PFTexNearest, 0, state->homeButtonId); 263 264 float w = getWidth(); 265 float h = getHeight(); 266 267 float x; 268 float y; 269 if (getWidth() > getHeight()) { 270 x = w - (params->homeButtonTextureWidth * (1 - g_Animation)) + 20; 271 y = (h - params->homeButtonTextureHeight) * 0.5f; 272 } else { 273 x = (w - params->homeButtonTextureWidth) / 2; 274 y = -g_Animation * params->homeButtonTextureHeight; 275 y -= 30; // move the house to the edge of the screen as it doesn't fill the texture. 276 } 277 278 drawSpriteScreenspace(x, y, 0, params->homeButtonTextureWidth, params->homeButtonTextureHeight); 279 } 280 281 void drawFrontGrid(float rowOffset, float p) 282 { 283 float h = getHeight(); 284 float w = getWidth(); 285 286 int intRowOffset = rowOffset; 287 float rowFrac = rowOffset - intRowOffset; 288 float colWidth = 120.f;//getWidth() / 4; 289 float rowHeight = colWidth + 25.f; 290 float yoff = 0.5f * h + 1.5f * rowHeight; 291 292 int row, col; 293 int colCount = 4; 294 if (w > h) { 295 colCount = 6; 296 rowHeight -= 12.f; 297 yoff = 0.47f * h + 1.0f * rowHeight; 298 } 299 300 int iconNum = (intRowOffset - 5) * colCount; 301 302 303 bindProgramVertex(NAMED_PVCurve); 304 305 vpConstants->Position.z = p; 306 307 setColor(1.0f, 1.0f, 1.0f, 1.0f); 308 for (row = -5; row < 15; row++) { 309 float y = yoff - ((-rowFrac + row) * rowHeight); 310 311 for (col=0; col < colCount; col++) { 312 if (iconNum >= state->iconCount) { 313 return; 314 } 315 316 if (iconNum >= 0) { 317 float x = colWidth * col + (colWidth / 2); 318 vpConstants->Position.x = x + 0.2f; 319 320 if (state->selectedIconIndex == iconNum && !p) { 321 bindProgramFragment(NAMED_PFTexNearest); 322 bindTexture(NAMED_PFTexNearest, 0, state->selectedIconTexture); 323 vpConstants->ImgSize.x = SELECTION_TEXTURE_WIDTH_PX; 324 vpConstants->ImgSize.y = SELECTION_TEXTURE_HEIGHT_PX; 325 vpConstants->Position.y = y - (SELECTION_TEXTURE_HEIGHT_PX - ICON_TEXTURE_HEIGHT_PX) * 0.5f; 326 drawSimpleMesh(NAMED_SMCell); 327 } 328 329 bindProgramFragment(NAMED_PFTexMip); 330 vpConstants->ImgSize.x = ICON_TEXTURE_WIDTH_PX; 331 vpConstants->ImgSize.y = ICON_TEXTURE_HEIGHT_PX; 332 vpConstants->Position.y = y - 0.2f; 333 bindTexture(NAMED_PFTexMip, 0, loadI32(ALLOC_ICON_IDS, iconNum)); 334 drawSimpleMesh(NAMED_SMCell); 335 336 bindProgramFragment(NAMED_PFTexMipAlpha); 337 vpConstants->ImgSize.x = 120.f; 338 vpConstants->ImgSize.y = 64.f; 339 vpConstants->Position.y = y - 64.f - 0.2f; 340 bindTexture(NAMED_PFTexMipAlpha, 0, loadI32(ALLOC_LABEL_IDS, iconNum)); 341 drawSimpleMesh(NAMED_SMCell); 342 } 343 iconNum++; 344 } 345 } 346 } 347 348 349 int 350 main(int launchID) 351 { 352 // Compute dt in seconds. 353 int newTime = uptimeMillis(); 354 g_DT = (newTime - g_LastTime) / 1000.f; 355 g_LastTime = newTime; 356 357 if (!g_DrawLastFrame) { 358 // If we stopped rendering we cannot use DT. 359 // assume 30fps in this case. 360 g_DT = 0.033f; 361 } 362 // physics may break if DT is large. 363 g_DT = minf(g_DT, 0.2f); 364 365 if (g_Zoom != state->zoomTarget) { 366 float dz = g_DT * 1.7f; 367 if (state->zoomTarget < 0.5f) { 368 dz = -dz; 369 } 370 if (fabsf(g_Zoom - state->zoomTarget) < fabsf(dz)) { 371 g_Zoom = state->zoomTarget; 372 } else { 373 g_Zoom += dz; 374 } 375 updateReadback(); 376 } 377 g_Animation = powf(1-g_Zoom, 3); 378 379 // Set clear value to dim the background based on the zoom position. 380 if ((g_Zoom < 0.001f) && (state->zoomTarget < 0.001f) && !g_SpecialHWWar) { 381 pfClearColor(0.0f, 0.0f, 0.0f, 0.0f); 382 // When we're zoomed out and not tracking motion events, reset the pos to 0. 383 if (!g_LastTouchDown) { 384 g_PosPage = 0; 385 } 386 return lastFrame(0); 387 } else { 388 pfClearColor(0.0f, 0.0f, 0.0f, g_Zoom); 389 } 390 391 // icons & labels 392 int iconCount = state->iconCount; 393 if (getWidth() > getHeight()) { 394 g_Cols = 6; 395 g_Rows = 3; 396 } else { 397 g_Cols = 4; 398 g_Rows = 4; 399 } 400 g_PosMax = ((iconCount + (g_Cols-1)) / g_Cols) - g_Rows; 401 if (g_PosMax < 0) g_PosMax = 0; 402 403 updatePos(); 404 updateReadback(); 405 406 //debugF(" draw g_PosPage", g_PosPage); 407 408 // Draw the icons ======================================== 409 drawFrontGrid(g_PosPage, g_Animation); 410 411 bindProgramFragment(NAMED_PFTexNearest); 412 draw_home_button(); 413 414 // This is a WAR to do a rendering pass without drawing during init to 415 // force the driver to preload and compile its shaders. 416 // Without this the first animation does not appear due to the time it 417 // takes to init the driver state. 418 if (g_SpecialHWWar) { 419 g_SpecialHWWar = 0; 420 return 1; 421 } 422 423 // Bug workaround where the last frame is not always displayed 424 // So we keep rendering until the bug is fixed. 425 return lastFrame((g_PosVelocity != 0) || fracf(g_PosPage) || g_Zoom != state->zoomTarget || (g_MoveToTime != 0)); 426 } 427 428