15ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller/* 25ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * Copyright (C) 2010 The Android Open Source Project 35ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * 45ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * Licensed under the Apache License, Version 2.0 (the "License"); 55ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * you may not use this file except in compliance with the License. 65ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * You may obtain a copy of the License at 75ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * 85ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * http://www.apache.org/licenses/LICENSE-2.0 95ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * 105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * Unless required by applicable law or agreed to in writing, software 115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * distributed under the License is distributed on an "AS IS" BASIS, 125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * See the License for the specific language governing permissions and 145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * limitations under the License. 155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller */ 165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller#pragma version(1) 185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller#pragma rs java_package_name(com.android.ex.carousel); 195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller#pragma rs set_reflect_license() 205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller#include "rs_graphics.rsh" 225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millertypedef struct __attribute__((aligned(4))) Card { 246f2cc8cf611860467315ecc542f71a225625eb1cJason Sams // *** Update initCard if you add/remove fields here. 257cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_allocation texture; // basic card texture 267cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_allocation detailTexture; // screen-aligned detail texture 277cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller float2 detailTextureOffset; // offset to add, in screen coordinates 28b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float2 detailLineOffset; // offset to add to detail line, in screen coordinates 298fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma float2 detailTexturePosition[2]; // screen coordinates of detail texture, computed at draw time 305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_mesh geometry; 317cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_matrix4x4 matrix; // custom transform for this card/geometry 327cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller int textureState; // whether or not the primary card texture is loaded. 337cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller int detailTextureState; // whether or not the detail for the card is loaded. 345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int geometryState; // whether or not geometry is loaded 358fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma int cardVisible; // not bool because of packing bug? 368fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma int detailVisible; // not bool because of packing bug? 375ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney int shouldPrefetch; // not bool because of packing bug? 383adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma int64_t textureTimeStamp; // time when this texture was last updated, in ms 393adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma int64_t detailTextureTimeStamp; // time when this texture was last updated, in ms 403adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma int64_t geometryTimeStamp; // time when the card itself was last updated, in ms 415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} Card_t; 425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millertypedef struct Ray_s { 445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 position; 455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 direction; 465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} Ray; 475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millertypedef struct Plane_s { 4943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float3 point; 5043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float3 normal; 5143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float constant; 5243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller} Plane; 5343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 5443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millertypedef struct Cylinder_s { 5543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float3 center; // center of a y-axis-aligned infinite cylinder 5643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float radius; 5743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller} Cylinder; 5843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millertypedef struct PerspectiveCamera_s { 605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 from; 615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 at; 625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 up; 635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float fov; 645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float aspect; 655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float near; 665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float far; 675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} PerspectiveCamera; 685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 69f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumatypedef struct ProgramStore_s { 70f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rs_program_store programStore; 71f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma} ProgramStore_t; 72f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma 73420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millertypedef struct FragmentShaderConstants_s { 74420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float fadeAmount; 753adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma float overallAlpha; 76420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller} FragmentShaderConstants; 77420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Request states. Used for loading 3D object properties from the Java client. 795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Typical properties: texture, geometry and matrices. 805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerenum { 815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_INVALID = 0, // item hasn't been loaded 825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_LOADING, // we've requested an item but are waiting for it to load 836af401bca5f8854524d128e9df5700035fae1160Jim Shuma STATE_STALE, // we have an old item, but should request an update 846af401bca5f8854524d128e9df5700035fae1160Jim Shuma STATE_UPDATING, // we've requested an update, and will display the old one in the meantime 855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_LOADED // item was delivered 865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 888debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry// Interpolation modes ** THIS LIST MUST MATCH THOSE IN CarouselView.java *** 898debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berryenum { 908debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry INTERPOLATION_LINEAR = 0, 918debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry INTERPOLATION_DECELERATE_QUADRATIC = 1, 928debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry INTERPOLATION_ACCELERATE_DECELERATE_CUBIC = 2, 938debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry}; 948debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 954a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney// Detail texture alignments ** THIS LIST MUST MATCH THOSE IN CarouselView.java *** 964a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinneyenum { 974a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Detail is centered vertically with respect to the card **/ 984a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney CENTER_VERTICAL = 1, 994a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Detail is aligned with the top edge of the carousel view **/ 1004a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney VIEW_TOP = 1 << 1, 1014a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Detail is aligned with the bottom edge of the carousel view (not yet implemented) **/ 1024a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney VIEW_BOTTOM = 1 << 2, 1034a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Detail is positioned above the card (not yet implemented) **/ 1044a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney ABOVE = 1 << 3, 1054a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Detail is positioned below the card **/ 1064a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney BELOW = 1 << 4, 1074a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Mask that selects those bits that control vertical alignment **/ 1084a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney VERTICAL_ALIGNMENT_MASK = 0xff, 1094a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney 1104a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** 1114a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * Detail is centered horizontally with respect to either the top or bottom 1124a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * extent of the card, depending on whether the detail is above or below the card. 1134a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney */ 1144a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney CENTER_HORIZONTAL = 1 << 8, 1154a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** 1164a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * Detail is aligned with the left edge of either the top or the bottom of 1174a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * the card, depending on whether the detail is above or below the card. 1184a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney */ 1194a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney LEFT = 1 << 9, 1204a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** 1214a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * Detail is aligned with the right edge of either the top or the bottom of 1224a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * the card, depending on whether the detail is above or below the card. 1234a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * (not yet implemented) 1244a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney */ 1254a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney RIGHT = 1 << 10, 1264a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Mask that selects those bits that control horizontal alignment **/ 1274a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney HORIZONTAL_ALIGNMENT_MASK = 0xff00, 1284a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney}; 1294a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney 1305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Client messages *** THIS LIST MUST MATCH THOSE IN CarouselRS.java. *** 1315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_CARD_SELECTED = 100; 1328fd40311898a9ec759a76f021642f43e617e38c4Jim Shumastatic const int CMD_DETAIL_SELECTED = 105; 133594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shumastatic const int CMD_CARD_LONGPRESS = 110; 1345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_REQUEST_TEXTURE = 200; 1355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_INVALIDATE_TEXTURE = 210; 1365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_REQUEST_GEOMETRY = 300; 1375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_INVALIDATE_GEOMETRY = 310; 1385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_ANIMATION_STARTED = 400; 1395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_ANIMATION_FINISHED = 500; 1407cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_REQUEST_DETAIL_TEXTURE = 600; 1417cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_INVALIDATE_DETAIL_TEXTURE = 610; 1427cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_PING = 1000; 1435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 144be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller// Drag model *** THIS LIST MUST MATCH THOSE IN CarouselRS.java. *** 145be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic const int DRAG_MODEL_SCREEN_DELTA = 0; // Drag relative to x coordinate of motion vector 146be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic const int DRAG_MODEL_PLANE = 1; // Drag relative to projected point on plane of carousel 147be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic const int DRAG_MODEL_CYLINDER_INSIDE = 2; // Drag relative to point on inside of cylinder 148be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic const int DRAG_MODEL_CYLINDER_OUTSIDE = 3; // Drag relative to point on outside of cylinder 149be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller 1505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Constants 151ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Millerstatic const int ANIMATION_DELAY_TIME = 125; // hold off scale animation until this time 152ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Millerstatic const int ANIMATION_SCALE_UP_TIME = 200; // Time it takes to animate selected card, in ms 1532cf7d5b78744e0d95951ddd631ca11904296ba7cJim Millerstatic const int ANIMATION_SCALE_DOWN_TIME = 200; // Time it takes to animate selected card, in ms 154ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Millerstatic const float3 SELECTED_SCALE_FACTOR = { 0.1f, 0.1f, 0.1f }; // increase by this % 15555b237bcd720774e27248f5fecf6c32a3f420a4cJim Millerstatic const int VELOCITY_HISTORY_MAX = 10; // # recent velocity samples used to calculate average 156b4959ac31abdaf6ab7309c17f56fceaa1baabed7Bryan Mawhinneystatic const int VISIBLE_SLOT_PADDING = 2; // # slots to draw on either side of visible slots 1575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1585dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney// Constants affecting tilt overscroll. Some of these should be parameters. 1595dac0398c2684197a4363f15cef292fee126038fBryan Mawhinneystatic const int TILT_SLOT_NUMBER = 5; 1605dac0398c2684197a4363f15cef292fee126038fBryan Mawhinneystatic const float TILT_MIN_ANGLE = M_PI / 315.0f; 1615dac0398c2684197a4363f15cef292fee126038fBryan Mawhinneystatic const float TILT_MAX_BIAS = M_PI / 8.0f; 1625dac0398c2684197a4363f15cef292fee126038fBryan Mawhinneystatic const float TILT_MAX_ANGLE = M_PI / 8.0f; 1635dac0398c2684197a4363f15cef292fee126038fBryan Mawhinneystatic const float MAX_DELTA_BIAS = 0.008f; 1645dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney 1655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Debug flags 1667cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugCamera = false; // dumps ray/camera coordinate stuff 16743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerconst bool debugSelection = false; // logs selection events 1687cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugTextureLoading = false; // for debugging texture load/unload 1697cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugGeometryLoading = false; // for debugging geometry load/unload 1707c09ccce478100d75e4427d87866ff19d758ae7aJim Shumaconst bool debugDetails = false; // for debugging detail texture geometry 171b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerconst bool debugRendering = false; // flashes display when the frame changes 17243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerconst bool debugRays = false; // shows visual depiction of hit tests, See renderWithRays(). 1735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Exported variables. These will be reflected to Java set_* variables. 1755ce730797a8a7278dfe19dac8a9460b25675fed0Jim MillerCard_t *cards; // array of cards to draw 1765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat startAngle; // position of initial card, in radians 1775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint slotCount; // number of positions where a card can be 1785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint cardCount; // number of cards in stack 179f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumaint programStoresCardCount; // number of program fragment stores 1805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint visibleSlotCount; // number of visible slots (for culling) 1817cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerint visibleDetailCount; // number of visible detail textures to show 1824fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shumaint prefetchCardCount; // how many cards to keep in memory 1834a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinneyint detailTextureAlignment; // How to align detail texture with respect to card 1847c09ccce478100d75e4427d87866ff19d758ae7aJim Shumabool drawRuler; // whether to draw a ruler from the card to the detail texture 1855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat radius; // carousel radius. Cards will be centered on a circle with this radius 1865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat cardRotation; // rotation of card in XY plane relative to Z=1 18783d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinneybool cardsFaceTangent; // whether cards are rotated to face along a tangent to the circle 188c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat swaySensitivity; // how much to rotate cards in relation to the rotation velocity 189c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat frictionCoeff; // how much to slow down the carousel over time 190c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat dragFactor; // a scale factor for how sensitive the carousel is to user dragging 191420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerint fadeInDuration; // amount of time (in ms) for smoothly switching out textures 1923adf712e636f67265da7a6ff425c87e63fc20884Jim Shumaint cardCreationFadeDuration; // amount of time (in ms) to fade while initially showing a card 193420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerfloat rezInCardCount; // this controls how rapidly distant card textures will be rez-ed in 194a9e9c4bef076e718094786edfe0290f798e1db4bJim Millerfloat detailFadeRate; // rate at which details fade as they move into the distance 19543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerfloat4 backgroundColor; 1960cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinneyint rowCount; // number of rows of cards in a given slot, default 1 1970cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinneyfloat rowSpacing; // spacing between rows of cards 1981a5b4d109397ea175b5cbaa7490ca18e78eb040fSimon Wilsonbool firstCardTop; // set true for first card on top row when multiple rows used 1999afded4d212243e554c2695c4a2f90c13628e24bBryan Mawhinneyfloat overscrollSlots; // amount of allowed overscroll (in slots) 2000cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney 201be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerint dragModel = DRAG_MODEL_SCREEN_DELTA; 20214d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shumaint fillDirection; // the order in which to lay out cards: +1 for CCW (default), -1 for CW 203f664659f79399e92025e1dfe1ffbb682ff05613cJim ShumaProgramStore_t *programStoresCard; 204f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumars_program_store programStoreBackground; 205f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumars_program_store programStoreDetail; 206a9e9c4bef076e718094786edfe0290f798e1db4bJim Millerrs_program_fragment singleTextureFragmentProgram; 2073adf712e636f67265da7a6ff425c87e63fc20884Jim Shumars_program_fragment singleTextureBlendingFragmentProgram; 208a9e9c4bef076e718094786edfe0290f798e1db4bJim Millerrs_program_fragment multiTextureFragmentProgram; 2093adf712e636f67265da7a6ff425c87e63fc20884Jim Shumars_program_fragment multiTextureBlendingFragmentProgram; 2105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_vertex vertexProgram; 2115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_raster rasterProgram; 2125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_allocation defaultTexture; // shown when no other texture is assigned 2135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_allocation loadingTexture; // progress texture (shown when app is fetching the texture) 2149afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Millerrs_allocation backgroundTexture; // drawn behind everything, if set 2157cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerrs_allocation detailLineTexture; // used to draw detail line (as a quad, of course) 216420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerrs_allocation detailLoadingTexture; // used when detail texture is loading 2175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_mesh defaultGeometry; // shown when no geometry is loaded 2185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_mesh loadingGeometry; // shown when geometry is loading 21951dd0196e4f3bd4086545f5bf30038ca9ad9ac27Bryan Mawhinneyrs_matrix4x4 defaultCardMatrix; 2205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_matrix4x4 projectionMatrix; 2215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_matrix4x4 modelviewMatrix; 222420b44b8b11ec1c309ea130e69a6876325dbfef9Jim MillerFragmentShaderConstants* shaderConstants; 223420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerrs_sampler linearClamp; 2245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Local variables 2265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float bias; // rotation bias, in radians. Used for animation and dragging. 2275dac0398c2684197a4363f15cef292fee126038fBryan Mawhinneystatic float overscrollBias; // Track overscroll bias separately for tilt effect. 2285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool updateCamera; // force a recompute of projection and lookat matrices 2295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float FLT_MAX = 1.0e37; 23043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic int animatedSelection = -1; 23143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic int currentFirstCard = -1; 232ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Millerstatic int64_t touchTime = -1; // time of first touch (see doStart()) 233ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Millerstatic int64_t releaseTime = 0L; // when touch was released 2345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float touchBias = 0.0f; // bias on first touch 23543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic float2 touchPosition; // position of first touch, as defined by last call to doStart(x,y) 236c0bb8af58ae15674178f2db240283719918c6f28Jim Shumastatic float velocity = 0.0f; // angular velocity in radians/s 237ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Millerstatic bool isOverScrolling = false; // whether we're in the overscroll animation 238ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Millerstatic bool isAutoScrolling = false; // whether we're in the autoscroll animation 23943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic bool isDragging = false; // true while the user is dragging the carousel 24043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic float selectionRadius = 50.0f; // movement greater than this will result in no selection 24143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic bool enableSelection = false; // enabled until the user drags outside of selectionRadius 2425dac0398c2684197a4363f15cef292fee126038fBryan Mawhinneystatic float tiltAngle = 0.0f; 24343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 24443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// Default plane of the carousel. Used for angular motion estimation in view. 24543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic Plane carouselPlane = { 24643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller { 0.0f, 0.0f, 0.0f }, // point 24743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller { 0.0f, 1.0f, 0.0f }, // normal 24843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 0.0f // plane constant (= -dot(P, N)) 24943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller}; 2505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 251be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic Cylinder carouselCylinder = { 252be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller {0.0f, 0.0f, 0.0f }, // center 253be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller 1.0f // radius - update with carousel radius. 254be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller}; 255be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller 2563df59346f395434454d310b070fff195089fbaf1Jim Miller// Because allocations can't have 0 dimensions, we have to track whether or not 257f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma// cards and program stores are valid separately. 2583df59346f395434454d310b070fff195089fbaf1Jim Miller// TODO: Remove this dependency once allocations can have a zero dimension. 2593df59346f395434454d310b070fff195089fbaf1Jim Millerstatic bool cardAllocationValid = false; 260f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumastatic bool programStoresAllocationValid = false; 2613df59346f395434454d310b070fff195089fbaf1Jim Miller 2625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Default geometry when card.geometry is not set. 2635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float3 cardVertices[4] = { 2645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { -1.0, -1.0, 0.0 }, 2655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { 1.0, -1.0, 0.0 }, 2665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { 1.0, 1.0, 0.0 }, 2675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {-1.0, 1.0, 0.0 } 2685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 2695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Default camera 2715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic PerspectiveCamera camera = { 2725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {2,2,2}, // from 2735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {0,0,0}, // at 2745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {0,1,0}, // up 2755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 25.0f, // field of view 2765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1.0f, // aspect 2775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 0.1f, // near 2785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 100.0f // far 2795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 2805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Forward references 2825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int intersectGeometry(Ray* ray, float *bestTime); 2838fd40311898a9ec759a76f021642f43e617e38c4Jim Shumastatic int intersectDetailTexture(float x, float y, float2 *tapCoordinates); 284b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 285b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller makeRayForPixelAt(Ray* ray, PerspectiveCamera* cam, float x, float y); 286b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 287b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller makeRayForPixelAt(Ray* ray, rs_matrix4x4* model, rs_matrix4x4* proj, float x, float y); 2885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float deltaTimeInSeconds(int64_t current); 289be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic bool rayPlaneIntersect(Ray* ray, Plane* plane, float* tout); 290be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic bool rayCylinderIntersect(Ray* ray, Cylinder* cylinder, float* tout); 2918debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berrystatic void stopAutoscroll(); 2925dac0398c2684197a4363f15cef292fee126038fBryan Mawhinneystatic bool tiltOverscroll(); 2935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid init() { 2955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // initializers currently have a problem when the variables are exported, so initialize 2965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // globals here. 2972ba04e061b52c488a154739379501dc833e39f79Jim Miller if (debugTextureLoading) rsDebug("Renderscript: init()", 0); 2985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller startAngle = 0.0f; 2995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller slotCount = 10; 3005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller visibleSlotCount = 1; 3017cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller visibleDetailCount = 3; 3025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bias = 0.0f; 3035dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney overscrollBias = 0.0f; 3045dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney tiltAngle = 0.0f; 305be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller radius = carouselCylinder.radius = 1.0f; 3065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardRotation = 0.0f; 30783d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney cardsFaceTangent = false; 3085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = true; 309b0f070636c29ad178f4e21306f301fe3d20c183bJim Miller backgroundColor = (float4) { 0.0f, 0.0f, 0.0f, 1.0f }; 3103df59346f395434454d310b070fff195089fbaf1Jim Miller cardAllocationValid = false; 311f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma programStoresAllocationValid = false; 3123df59346f395434454d310b070fff195089fbaf1Jim Miller cardCount = 0; 3130cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney rowCount = 1; 3140cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney rowSpacing = 0.0f; 3151a5b4d109397ea175b5cbaa7490ca18e78eb040fSimon Wilson firstCardTop = false; 316420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller fadeInDuration = 250; 317420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rezInCardCount = 0.0f; // alpha will ramp to 1.0f over this many cards (0.0f means disabled) 318a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller detailFadeRate = 0.5f; // fade details over this many slot positions. 31951dd0196e4f3bd4086545f5bf30038ca9ad9ac27Bryan Mawhinney rsMatrixLoadIdentity(&defaultCardMatrix); 3205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 322f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumastatic void updateAllocationVars() 3235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Cards 325ed5cdfa293ec57cb14b98cdc3fa00ac5ec1c1ed4Stephen Hines rs_allocation cardAlloc; 32655392759e4f1fd3b17799ef8bd75d959dcf3b0a7Stephen Hines cardAlloc = rsGetAllocation(cards); 327dcfb45adbcf37de68920c181322aaa9e4e4b58d8Stephen Hines cardCount = (cardAllocationValid && rsIsObject(cardAlloc)) ? rsAllocationGetDimX(cardAlloc) : 0; 328f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma 329f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma // Program stores 330f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rs_allocation psAlloc; 33155392759e4f1fd3b17799ef8bd75d959dcf3b0a7Stephen Hines psAlloc = rsGetAllocation(programStoresCard); 332f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma programStoresCardCount = (programStoresAllocationValid && rsIsObject(psAlloc) ? 333f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsAllocationGetDimX(psAlloc) : 0); 3345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 336be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millervoid setRadius(float rad) 337be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller{ 338be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller radius = carouselCylinder.radius = rad; 339be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller} 340be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller 341370b177eb74cd8a7d9a2ab06a5ee8bb3ed25f74fStephen Hinesstatic void initCard(Card_t* card) 3428b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich{ 3436f2cc8cf611860467315ecc542f71a225625eb1cJason Sams // Object refs are always initilized cleared. 3442ba04e061b52c488a154739379501dc833e39f79Jim Miller static const float2 zero = {0.0f, 0.0f}; 3452ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailTextureOffset = zero; 3462ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailLineOffset = zero; 34751dd0196e4f3bd4086545f5bf30038ca9ad9ac27Bryan Mawhinney rsMatrixLoad(&card->matrix, &defaultCardMatrix); 3482ba04e061b52c488a154739379501dc833e39f79Jim Miller card->textureState = STATE_INVALID; 3492ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailTextureState = STATE_INVALID; 3502ba04e061b52c488a154739379501dc833e39f79Jim Miller card->geometryState = STATE_INVALID; 3518fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma card->cardVisible = false; 3528fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma card->detailVisible = false; 3535ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney card->shouldPrefetch = false; 3542ba04e061b52c488a154739379501dc833e39f79Jim Miller card->textureTimeStamp = 0; 3552ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailTextureTimeStamp = 0; 3563adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma card->geometryTimeStamp = rsUptimeMillis(); 3572ba04e061b52c488a154739379501dc833e39f79Jim Miller} 3582ba04e061b52c488a154739379501dc833e39f79Jim Miller 3596f2cc8cf611860467315ecc542f71a225625eb1cJason Samsvoid createCards(int start, int total) 3602ba04e061b52c488a154739379501dc833e39f79Jim Miller{ 3615b54f405b4a1afcf57b5ccee2026a00a1004be20Bryan Mawhinney if (!cardAllocationValid) { 3625b54f405b4a1afcf57b5ccee2026a00a1004be20Bryan Mawhinney // If the allocation is invalid, it contains a single place-holder 3635b54f405b4a1afcf57b5ccee2026a00a1004be20Bryan Mawhinney // card that has not yet been initialized (see CarouselRS.createCards). 3645b54f405b4a1afcf57b5ccee2026a00a1004be20Bryan Mawhinney // Here we ensure that it is initialized when growing the total. 3655b54f405b4a1afcf57b5ccee2026a00a1004be20Bryan Mawhinney start = 0; 3665b54f405b4a1afcf57b5ccee2026a00a1004be20Bryan Mawhinney } 3676f2cc8cf611860467315ecc542f71a225625eb1cJason Sams for (int k = start; k < total; k++) { 3686f2cc8cf611860467315ecc542f71a225625eb1cJason Sams initCard(cards + k); 3692ba04e061b52c488a154739379501dc833e39f79Jim Miller } 3702ba04e061b52c488a154739379501dc833e39f79Jim Miller 3716f2cc8cf611860467315ecc542f71a225625eb1cJason Sams // Since allocations can't have 0-size, we track validity ourselves based on the call to 3726f2cc8cf611860467315ecc542f71a225625eb1cJason Sams // this method. 3736f2cc8cf611860467315ecc542f71a225625eb1cJason Sams cardAllocationValid = total > 0; 3742ba04e061b52c488a154739379501dc833e39f79Jim Miller 375ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller updateAllocationVars(); 3765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 378420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller// Computes an alpha value for a card using elapsed time and constant fadeInDuration 3793adf712e636f67265da7a6ff425c87e63fc20884Jim Shumastatic float getAnimatedAlpha(int64_t startTime, int64_t currentTime, int64_t duration) 380420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller{ 381420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller double timeElapsed = (double) (currentTime - startTime); // in ms 3823adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma double alpha = duration > 0 ? (double) timeElapsed / duration : 1.0; 383420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller return min(1.0f, (float) alpha); 384420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller} 385420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 3860cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney// Returns total angle for given number of slots 3870cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinneystatic float wedgeAngle(float slots) 3885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3890cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney return slots * 2.0f * M_PI / slotCount; 3905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3920cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney// Return angle of slot in position p. 3930cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinneystatic float slotPosition(int p) 3945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 39514d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma return startAngle + wedgeAngle(p) * fillDirection; 3965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3980cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney// Return angle for card in position p. 3990cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinneystatic float cardPosition(int p) 400f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller{ 4010cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney return bias + slotPosition(p / rowCount); 402f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller} 403f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller 40414d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma// Return the lowest possible bias value, based on the fill direction 40514d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shumastatic float minimumBias() 40614d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma{ 40714d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma const int totalSlots = (cardCount + rowCount - 1) / rowCount; 40814d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma return (fillDirection > 0) ? 40914d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma -max(0.0f, wedgeAngle(totalSlots - visibleDetailCount)) : 41014d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma wedgeAngle(0.0f); 41114d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma} 41214d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma 41314d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma// Return the highest possible bias value, based on the fill direction 41414d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shumastatic float maximumBias() 41514d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma{ 41614d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma const int totalSlots = (cardCount + rowCount - 1) / rowCount; 41714d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma return (fillDirection > 0) ? 41814d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma wedgeAngle(0.0f) : 41914d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma max(0.0f, wedgeAngle(totalSlots - visibleDetailCount)); 42014d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma} 42114d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma 42214d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma 423a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich// convert from carousel rotation angle (in card slot units) to radians. 424a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevichstatic float carouselRotationAngleToRadians(float carouselRotationAngle) 425a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich{ 426a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich return -wedgeAngle(carouselRotationAngle); 427a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich} 428a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich 429a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich// convert from radians to carousel rotation angle (in card slot units). 430a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevichstatic float radiansToCarouselRotationAngle(float angle) 431a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich{ 432a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich return -angle * slotCount / ( 2.0f * M_PI ); 433a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich} 434a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich 4355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Set basic camera properties: 4365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// from - position of the camera in x,y,z 4375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// at - target we're looking at - used to compute view direction 4385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// up - a normalized vector indicating up (typically { 0, 1, 0}) 4395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// 4405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// NOTE: the view direction and up vector cannot be parallel/antiparallel with each other 4415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid lookAt(float fromX, float fromY, float fromZ, 4425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float atX, float atY, float atZ, 4435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float upX, float upY, float upZ) 4445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.x = fromX; 4465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.y = fromY; 4475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.z = fromZ; 4485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.x = atX; 4495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.y = atY; 4505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.z = atZ; 4515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.x = upX; 4525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.y = upY; 4535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.z = upZ; 4545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = true; 4555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Load a projection matrix for the given parameters. This is equivalent to gluPerspective() 4585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void loadPerspectiveMatrix(rs_matrix4x4* matrix, float fovy, float aspect, float near, float far) 4595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadIdentity(matrix); 4615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float top = near * tan((float) (fovy * M_PI / 360.0f)); 4625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bottom = -top; 4635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float left = bottom * aspect; 4645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float right = top * aspect; 4655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadFrustum(matrix, left, right, bottom, top, near, far); 4665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Construct a matrix based on eye point, center and up direction. Based on the 4695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// man page for gluLookat(). Up must be normalized. 4705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void loadLookatMatrix(rs_matrix4x4* matrix, float3 eye, float3 center, float3 up) 4715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 f = normalize(center - eye); 4735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s = normalize(cross(f, up)); 4745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 u = cross(s, f); 4755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float m[16]; 4765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[0] = s.x; 4775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[4] = s.y; 4785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[8] = s.z; 4795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[12] = 0.0f; 4805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[1] = u.x; 4815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[5] = u.y; 4825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[9] = u.z; 4835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[13] = 0.0f; 4845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[2] = -f.x; 4855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[6] = -f.y; 4865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[10] = -f.z; 4875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[14] = 0.0f; 4885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[3] = m[7] = m[11] = 0.0f; 4895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[15] = 1.0f; 4905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoad(matrix, m); 4915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixTranslate(matrix, -eye.x, -eye.y, -eye.z); 4925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4940de20d1ebd3dc8e766f7f4f4dbc3f77dd7326e30Jim Shuma/* 4950de20d1ebd3dc8e766f7f4f4dbc3f77dd7326e30Jim Shuma * Returns true if a state represents a texture that is loaded enough to draw 4960de20d1ebd3dc8e766f7f4f4dbc3f77dd7326e30Jim Shuma */ 4970de20d1ebd3dc8e766f7f4f4dbc3f77dd7326e30Jim Shumastatic bool textureEverLoaded(int state) { 4980de20d1ebd3dc8e766f7f4f4dbc3f77dd7326e30Jim Shuma return (state == STATE_LOADED) || (state == STATE_STALE) || (state == STATE_UPDATING); 4990de20d1ebd3dc8e766f7f4f4dbc3f77dd7326e30Jim Shuma} 5000de20d1ebd3dc8e766f7f4f4dbc3f77dd7326e30Jim Shuma 5015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid setTexture(int n, rs_allocation texture) 5025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 5033df59346f395434454d310b070fff195089fbaf1Jim Miller if (n < 0 || n >= cardCount) return; 50455392759e4f1fd3b17799ef8bd75d959dcf3b0a7Stephen Hines cards[n].texture = texture; 5058441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey if (cards[n].textureState != STATE_STALE && 5068441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey cards[n].textureState != STATE_UPDATING) { 5078441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey cards[n].textureTimeStamp = rsUptimeMillis(); 5088441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey } 5097cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].textureState = (texture.p != 0) ? STATE_LOADED : STATE_INVALID; 5107cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller} 5117cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 512b378af500b36226635b6343b1d5009ee9af44fc1Jim Millervoid setDetailTexture(int n, float offx, float offy, float loffx, float loffy, rs_allocation texture) 5137cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller{ 5143df59346f395434454d310b070fff195089fbaf1Jim Miller if (n < 0 || n >= cardCount) return; 51555392759e4f1fd3b17799ef8bd75d959dcf3b0a7Stephen Hines cards[n].detailTexture = texture; 5166af401bca5f8854524d128e9df5700035fae1160Jim Shuma if (cards[n].detailTextureState != STATE_STALE && 5176af401bca5f8854524d128e9df5700035fae1160Jim Shuma cards[n].detailTextureState != STATE_UPDATING) { 5186af401bca5f8854524d128e9df5700035fae1160Jim Shuma cards[n].detailTextureTimeStamp = rsUptimeMillis(); 5196af401bca5f8854524d128e9df5700035fae1160Jim Shuma } 5207cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].detailTextureOffset.x = offx; 5217cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].detailTextureOffset.y = offy; 522b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller cards[n].detailLineOffset.x = loffx; 523b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller cards[n].detailLineOffset.y = loffy; 5247cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].detailTextureState = (texture.p != 0) ? STATE_LOADED : STATE_INVALID; 5256af401bca5f8854524d128e9df5700035fae1160Jim Shuma} 5266af401bca5f8854524d128e9df5700035fae1160Jim Shuma 5278441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkeyvoid invalidateTexture(int n, bool eraseCurrent) 5288441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey{ 5298441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey if (n < 0 || n >= cardCount) return; 5308441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey if (eraseCurrent) { 5318441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey cards[n].textureState = STATE_INVALID; 5328441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey rsClearObject(&cards[n].texture); 5338441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey } else { 5340de20d1ebd3dc8e766f7f4f4dbc3f77dd7326e30Jim Shuma cards[n].textureState = 5350de20d1ebd3dc8e766f7f4f4dbc3f77dd7326e30Jim Shuma textureEverLoaded(cards[n].textureState) ? STATE_STALE : STATE_INVALID; 5368441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey } 5378441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey} 5388441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey 5396af401bca5f8854524d128e9df5700035fae1160Jim Shumavoid invalidateDetailTexture(int n, bool eraseCurrent) 5406af401bca5f8854524d128e9df5700035fae1160Jim Shuma{ 5416af401bca5f8854524d128e9df5700035fae1160Jim Shuma if (n < 0 || n >= cardCount) return; 5426af401bca5f8854524d128e9df5700035fae1160Jim Shuma if (eraseCurrent) { 5436af401bca5f8854524d128e9df5700035fae1160Jim Shuma cards[n].detailTextureState = STATE_INVALID; 5446af401bca5f8854524d128e9df5700035fae1160Jim Shuma rsClearObject(&cards[n].detailTexture); 5456af401bca5f8854524d128e9df5700035fae1160Jim Shuma } else { 5460de20d1ebd3dc8e766f7f4f4dbc3f77dd7326e30Jim Shuma cards[n].detailTextureState = 5470de20d1ebd3dc8e766f7f4f4dbc3f77dd7326e30Jim Shuma textureEverLoaded(cards[n].detailTextureState) ? STATE_STALE : STATE_INVALID; 5486af401bca5f8854524d128e9df5700035fae1160Jim Shuma } 5495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 5505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid setGeometry(int n, rs_mesh geometry) 5525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 5533df59346f395434454d310b070fff195089fbaf1Jim Miller if (n < 0 || n >= cardCount) return; 55455392759e4f1fd3b17799ef8bd75d959dcf3b0a7Stephen Hines cards[n].geometry = geometry; 5555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[n].geometry.p != 0) 5565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].geometryState = STATE_LOADED; 5575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller else 5585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].geometryState = STATE_INVALID; 5593adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma cards[n].geometryTimeStamp = rsUptimeMillis(); 5605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 5615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 562c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shumavoid setMatrix(int n, rs_matrix4x4 matrix) { 563c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma if (n < 0 || n >= cardCount) return; 564c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma cards[n].matrix = matrix; 565c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma} 566c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma 567f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumavoid setProgramStoresCard(int n, rs_program_store programStore) 568f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma{ 56955392759e4f1fd3b17799ef8bd75d959dcf3b0a7Stephen Hines programStoresCard[n].programStore = programStore; 570f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma programStoresAllocationValid = true; 571f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma} 572f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma 573a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevichvoid setCarouselRotationAngle(float carouselRotationAngle) { 574a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich bias = carouselRotationAngleToRadians(carouselRotationAngle); 575198a060d650bc849ef0f25b597888fac9546803bJack Palevich} 576198a060d650bc849ef0f25b597888fac9546803bJack Palevich 5771eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller// Gets animated scale value for current selected card. 5781eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller// If card is currently being animated, returns true, otherwise returns false. 5791eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Millerstatic bool getAnimatedScaleForSelected(float3* scale) 5805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 581ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller static const float3 one = { 1.0f, 1.0f, 1.0f }; 582ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller static float fraction = 0.0f; 583ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller bool stillAnimating = false; 584ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller if (isDragging) { 585ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller // "scale up" animation 586ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller int64_t dt = rsUptimeMillis() - touchTime - ANIMATION_DELAY_TIME; 587ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller if (dt > 0L && enableSelection) { 588ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller float s = (float) dt / ANIMATION_SCALE_UP_TIME; 589ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller s = min(s, 1.0f); 590ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller fraction = max(s, fraction); 591ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller } 592ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller stillAnimating = dt < ANIMATION_SCALE_UP_TIME; 5931eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller } else { 594ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller // "scale down" animation 595ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller int64_t dt = rsUptimeMillis() - releaseTime; 596ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller if (dt < ANIMATION_SCALE_DOWN_TIME) { 597ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller float s = 1.0f - ((float) dt / ANIMATION_SCALE_DOWN_TIME); 598ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller fraction = min(s, fraction); 599ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller stillAnimating = true; 600ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller } else { 601ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller fraction = 0.0f; 602ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller } 6031eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller } 604ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller *scale = one + fraction * SELECTED_SCALE_FACTOR; 605ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller return stillAnimating; // still animating; 6065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 6075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 608c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// The Verhulst logistic function: http://en.wikipedia.org/wiki/Logistic_function 609c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// P(t) = 1 / (1 + e^(-t)) 610c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// Parameter t: Any real number 611c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// Returns: A float in the range (0,1), with P(0.5)=0 612c0bb8af58ae15674178f2db240283719918c6f28Jim Shumastatic float logistic(float t) { 613af8cf9a3bbe517b604b48e217b00085351ab2496Shih-wei Liao return 1.f / (1.f + exp(-t)); 614c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma} 615c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 6167c09ccce478100d75e4427d87866ff19d758ae7aJim Shumastatic float getSwayAngleForVelocity(float v, bool enableSway) 617c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma{ 6187c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma float sway = 0.0f; 6197c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma 6207c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma if (enableSway) { 621d7fa647e6fa4e832381be5bdd03065f9ea35c3f1Jim Shuma const float range = M_PI * 2./3.; // How far we can deviate from center, peak-to-peak 622d7fa647e6fa4e832381be5bdd03065f9ea35c3f1Jim Shuma sway = range * (logistic(-v * swaySensitivity) - 0.5f); 6237c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma } 624c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 625c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma return sway; 626c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma} 627c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 6285dac0398c2684197a4363f15cef292fee126038fBryan Mawhinneystatic float getCardTiltAngle(int i) { 6295dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney i /= rowCount; 6305dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney int totalSlots = (cardCount + rowCount - 1) / rowCount; 6315dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney float tiltSlotNumber = TILT_SLOT_NUMBER; 6325dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney float deltaTilt = tiltAngle / tiltSlotNumber; 6335dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney float cardTiltAngle = 0; 6345dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney if (tiltAngle > 0 && i < tiltSlotNumber) { 6355dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney // Overscroll for the front cards. 6365dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney cardTiltAngle = deltaTilt * (tiltSlotNumber - i); 6375dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney } else if (tiltAngle < 0 && i > (totalSlots - tiltSlotNumber)) { 6385dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney cardTiltAngle = deltaTilt * (i - totalSlots + tiltSlotNumber + 1); 6395dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney } 6405dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney return cardTiltAngle; 6415dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney} 6425dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney 6430cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney// Returns the vertical offset for a card in its slot, 6440cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney// depending on the number of rows configured. 6450cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinneystatic float getVerticalOffsetForCard(int i) { 6460cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney if (rowCount == 1) { 6470cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney // fast path 6480cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney return 0; 6490cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney } 650c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma const float cardHeight = (cardVertices[3].y - cardVertices[0].y) * 6517d27aa4388936d7607407d25cc52d42e00f6567aJim Shuma rsMatrixGet(&defaultCardMatrix, 1, 1); 6520cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney const float totalHeight = rowCount * (cardHeight + rowSpacing) - rowSpacing; 6531a5b4d109397ea175b5cbaa7490ca18e78eb040fSimon Wilson if (firstCardTop) 6541a5b4d109397ea175b5cbaa7490ca18e78eb040fSimon Wilson i = rowCount - (i % rowCount) - 1; 6551a5b4d109397ea175b5cbaa7490ca18e78eb040fSimon Wilson else 6561a5b4d109397ea175b5cbaa7490ca18e78eb040fSimon Wilson i = i % rowCount; 6571a5b4d109397ea175b5cbaa7490ca18e78eb040fSimon Wilson const float rowOffset = i * (cardHeight + rowSpacing); 6580cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney return (cardHeight - totalHeight) / 2 + rowOffset; 6590cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney} 6600cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney 6611eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller/* 6621eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller * Composes a matrix for the given card. 6631eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller * matrix: The output matrix. 6641eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller * i: The card we're getting the matrix for. 6651eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller * enableSway: Whether to enable swaying. (We want it on for cards, and off for detail textures.) 666c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma * enableCardMatrix: Whether to also consider the user-specified card matrix 6671eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller * 6681eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller * returns true if an animation is being applied to the given card 6691eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller */ 670c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shumastatic bool getMatrixForCard(rs_matrix4x4* matrix, int i, bool enableSway, bool enableCardMatrix) 6715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 6725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float theta = cardPosition(i); 6737c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma float swayAngle = getSwayAngleForVelocity(velocity, enableSway); 6745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixRotate(matrix, degrees(theta), 0, 1, 0); 6750cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney rsMatrixTranslate(matrix, radius, getVerticalOffsetForCard(i), 0); 6765dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney float tiltAngle = getCardTiltAngle(i); 6775dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney float rotation = cardRotation + swayAngle + tiltAngle; 67883d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney if (!cardsFaceTangent) { 67983d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney rotation -= theta; 68083d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney } 68183d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney rsMatrixRotate(matrix, degrees(rotation), 0, 1, 0); 6821eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller bool stillAnimating = false; 683ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller if (i == animatedSelection) { 6841eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller float3 scale; 6851eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller stillAnimating = getAnimatedScaleForSelected(&scale); 6865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixScale(matrix, scale.x, scale.y, scale.z); 6875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 688c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma // TODO(jshuma): Instead of ignoring this matrix for the detail texture, use card bounding box 689c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma if (enableCardMatrix) { 690c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma rsMatrixLoadMultiply(matrix, matrix, &cards[i].matrix); 691c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma } 6921eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller return stillAnimating; 6935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 6945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 695420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller/* 696f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma * Draws the requested mesh, with the appropriate program store in effect. 697f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma */ 698f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumastatic void drawMesh(rs_mesh mesh) 699f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma{ 700f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma if (programStoresCardCount == 1) { 701f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma // Draw the entire mesh, with the only available program store 702f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsgBindProgramStore(programStoresCard[0].programStore); 703f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsgDrawMesh(mesh); 704f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma } else { 705f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma // Draw each primitive in the mesh with the corresponding program store 706f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma for (int i=0; i<programStoresCardCount; ++i) { 707f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma if (programStoresCard[i].programStore.p != 0) { 708f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsgBindProgramStore(programStoresCard[i].programStore); 709f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsgDrawMesh(mesh, i); 710f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma } 711f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma } 712f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma } 713f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma} 714f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma 715f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma/* 716420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller * Draws cards around the Carousel. 717420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller * Returns true if we're still animating any property of the cards (e.g. fades). 718420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller */ 719420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerstatic bool drawCards(int64_t currentTime) 7205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 721420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller const float wedgeAngle = 2.0f * M_PI / slotCount; 722420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller const float endAngle = startAngle + visibleSlotCount * wedgeAngle; 723420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller bool stillAnimating = false; 724420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller for (int i = cardCount-1; i >= 0; i--) { 7258fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (cards[i].cardVisible) { 726420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // If this card was recently loaded, this will be < 1.0f until the animation completes 7273adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma float animatedAlpha = getAnimatedAlpha(cards[i].textureTimeStamp, currentTime, 7283adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma fadeInDuration); 7293adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma float overallAlpha = getAnimatedAlpha(cards[i].geometryTimeStamp, currentTime, 7303adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma cardCreationFadeDuration); 7313adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma if (animatedAlpha < 1.0f || overallAlpha < 1.0f) { 732420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller stillAnimating = true; 733420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } 734420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 735420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Compute fade out for cards in the distance 736420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float positionAlpha; 737420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller if (rezInCardCount > 0.0f) { 738420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller positionAlpha = (endAngle - cardPosition(i)) / wedgeAngle; 739420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller positionAlpha = min(1.0f, positionAlpha / rezInCardCount); 740420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } else { 741420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller positionAlpha = 1.0f; 742420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } 743420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 744420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Set alpha for blending between the textures 745420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller shaderConstants->fadeAmount = min(1.0f, animatedAlpha * positionAlpha); 7463adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma shaderConstants->overallAlpha = overallAlpha; 7478a357ebe4ae3063dbb3d8b3bdf6f665b05dd8e6fJason Sams rsgAllocationSyncAll(rsGetAllocation(shaderConstants)); 748420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 749b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller // Bind the appropriate shader network. If there's no alpha blend, then 750b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller // switch to single shader for better performance. 7518441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey const int state = cards[i].textureState; 7520de20d1ebd3dc8e766f7f4f4dbc3f77dd7326e30Jim Shuma bool loaded = textureEverLoaded(state) && rsIsObject(cards[i].texture); 753b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (shaderConstants->fadeAmount == 1.0f || shaderConstants->fadeAmount < 0.01f) { 7543adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma if (overallAlpha < 1.0) { 7553adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindProgramFragment(singleTextureBlendingFragmentProgram); 7563adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindTexture(singleTextureBlendingFragmentProgram, 0, 7573adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma (loaded && shaderConstants->fadeAmount == 1.0f) ? 7583adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma cards[i].texture : loadingTexture); 7593adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma } else { 7603adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindProgramFragment(singleTextureFragmentProgram); 7613adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindTexture(singleTextureFragmentProgram, 0, 7623adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma (loaded && shaderConstants->fadeAmount == 1.0f) ? 7633adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma cards[i].texture : loadingTexture); 7643adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma } 7655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 7663adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma if (overallAlpha < 1.0) { 7673adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindProgramFragment(multiTextureBlendingFragmentProgram); 7683adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindTexture(multiTextureBlendingFragmentProgram, 0, loadingTexture); 7693adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindTexture(multiTextureBlendingFragmentProgram, 1, loaded ? 7703adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma cards[i].texture : loadingTexture); 7713adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma } else { 7723adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindProgramFragment(multiTextureFragmentProgram); 7733adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindTexture(multiTextureFragmentProgram, 0, loadingTexture); 7743adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindTexture(multiTextureFragmentProgram, 1, loaded ? 7753adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma cards[i].texture : loadingTexture); 7763adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma } 7775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Draw geometry 7805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_matrix4x4 matrix = modelviewMatrix; 781c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma stillAnimating |= getMatrixForCard(&matrix, i, true, true); 7825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadModelMatrix(&matrix); 7835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].geometryState == STATE_LOADED && cards[i].geometry.p != 0) { 784f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma drawMesh(cards[i].geometry); 7855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else if (cards[i].geometryState == STATE_LOADING && loadingGeometry.p != 0) { 786f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma drawMesh(loadingGeometry); 7875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else if (defaultGeometry.p != 0) { 788f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma drawMesh(defaultGeometry); 7895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 7905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Draw place-holder geometry 791f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsgBindProgramStore(programStoresCard[0].programStore); 7925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawQuad( 7935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[0].x, cardVertices[0].y, cardVertices[0].z, 7945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[1].x, cardVertices[1].y, cardVertices[1].z, 7955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[2].x, cardVertices[2].y, cardVertices[2].z, 7965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[3].x, cardVertices[3].y, cardVertices[3].z); 7975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 800420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller return stillAnimating; 8015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 8025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8034fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma/** 8044fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma * Convert projection from normalized coordinates to pixel coordinates. 8054fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma * 8064fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma * @return True on success, false on failure. 8074fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma */ 8084fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shumastatic bool convertNormalizedToPixelCoordinates(float4 *screenCoord, float width, float height) { 8094fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // This is probably cheaper than pre-multiplying with another matrix. 8104fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma if (screenCoord->w == 0.0f) { 8114fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma rsDebug("Bad transform while converting from normalized to pixel coordinates: ", 8124fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord); 8134fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma return false; 8144fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 8154fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma *screenCoord *= 1.0f / screenCoord->w; 8164fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->x += 1.0f; 8174fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->y += 1.0f; 8184fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->z += 1.0f; 8194fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->x = round(screenCoord->x * 0.5f * width); 8204fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->y = round(screenCoord->y * 0.5f * height); 8214fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->z = - 0.5f * screenCoord->z; 8224fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma return true; 8234fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma} 8244fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma 8257cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller/* 8267cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller * Draws a screen-aligned card with the exact dimensions from the detail texture. 8274a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * This is used to display information about the object being displayed. 828420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller * Returns true if we're still animating any property of the cards (e.g. fades). 8297cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller */ 830420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerstatic bool drawDetails(int64_t currentTime) 8317cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller{ 8327cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float width = rsgGetWidth(); 8337cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float height = rsgGetHeight(); 8347cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 835420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller bool stillAnimating = false; 836420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 8377cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // We'll be drawing in screen space, sampled on pixel centers 8387cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_matrix4x4 projection, model; 8397cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsMatrixLoadOrtho(&projection, 0.0f, width, 0.0f, height, 0.0f, 1.0f); 8407cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgProgramVertexLoadProjectionMatrix(&projection); 8417cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsMatrixLoadIdentity(&model); 8427cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgProgramVertexLoadModelMatrix(&model); 8437cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller updateCamera = true; // we messed with the projection matrix. Reload on next pass... 8447cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 8457cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float yPadding = 5.0f; // draw line this far (in pixels) away from top and geometry 8467cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 847420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // This can be done once... 848a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller rsgBindTexture(multiTextureFragmentProgram, 0, detailLoadingTexture); 849a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 850a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float wedgeAngle = 2.0f * M_PI / slotCount; 851a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller // Angle where details start fading from 1.0f 852a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float startDetailFadeAngle = startAngle + (visibleDetailCount - 1) * wedgeAngle; 853a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller // Angle where detail alpha is 0.0f 854a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float endDetailFadeAngle = startDetailFadeAngle + detailFadeRate * wedgeAngle; 855420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 856a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller for (int i = cardCount-1; i >= 0; --i) { 8578fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (cards[i].cardVisible) { 8586af401bca5f8854524d128e9df5700035fae1160Jim Shuma const int state = cards[i].detailTextureState; 8590de20d1ebd3dc8e766f7f4f4dbc3f77dd7326e30Jim Shuma const bool isLoaded = textureEverLoaded(state); 8606af401bca5f8854524d128e9df5700035fae1160Jim Shuma if (isLoaded && cards[i].detailTexture.p != 0) { 8617cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float lineWidth = rsAllocationGetDimX(detailLineTexture); 8627cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 8634fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // Compute position in screen space of top corner or bottom corner of card 864af8cf9a3bbe517b604b48e217b00085351ab2496Shih-wei Liao rsMatrixLoad(&model, &modelviewMatrix); 865c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma stillAnimating |= getMatrixForCard(&model, i, false, false); 8667cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_matrix4x4 matrix; 8677cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsMatrixLoadMultiply(&matrix, &projectionMatrix, &model); 868d443c88da4c7cf1947c12b26f111cb899cc8afe4Jim Miller 8694fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma int indexLeft, indexRight; 8704fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma float4 screenCoord; 8714a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & BELOW) { 8724fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexLeft = 0; 8734fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexRight = 1; 8744fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } else { 8754fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexLeft = 3; 8764fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexRight = 2; 8774fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 8784fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma float4 screenCoordLeft = rsMatrixMultiply(&matrix, cardVertices[indexLeft]); 8794fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma float4 screenCoordRight = rsMatrixMultiply(&matrix, cardVertices[indexRight]); 8804fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma if (screenCoordLeft.w == 0.0f || screenCoordRight.w == 0.0f) { 8817cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // this shouldn't happen 8827cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsDebug("Bad transform: ", screenCoord); 8837cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller continue; 8847cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 8854a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & CENTER_VERTICAL) { 8864a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney // If we're centering vertically, we'll need the other vertices too 8874a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & BELOW) { 8884a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney indexLeft = 3; 8894a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney indexRight = 2; 8904a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } else { 8914a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney indexLeft = 0; 8924a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney indexRight = 1; 8934a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } 8944a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney float4 otherScreenLeft = rsMatrixMultiply(&matrix, cardVertices[indexLeft]); 8954a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney float4 otherScreenRight = rsMatrixMultiply(&matrix, cardVertices[indexRight]); 8964a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney screenCoordRight.y = screenCoordLeft.y = (screenCoordLeft.y + screenCoordRight.y 8974a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney + otherScreenLeft.y + otherScreenRight.y) / 4.; 8984a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } 8994fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma (void) convertNormalizedToPixelCoordinates(&screenCoordLeft, width, height); 9004fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma (void) convertNormalizedToPixelCoordinates(&screenCoordRight, width, height); 9014fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma if (debugDetails) { 9024fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma RS_DEBUG(screenCoordLeft); 9034fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma RS_DEBUG(screenCoordRight); 9044fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 9054fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord = screenCoordLeft; 9064a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & BELOW) { 9074fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord.y = min(screenCoordLeft.y, screenCoordRight.y); 9084a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } else if (detailTextureAlignment & CENTER_VERTICAL) { 909fe38385c1e5ce443adb962c066adeea185ad3d74Bryan Mawhinney screenCoord.y -= round(rsAllocationGetDimY(cards[i].detailTexture) / 2.0f); 9104fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 9114a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & CENTER_HORIZONTAL) { 912fe38385c1e5ce443adb962c066adeea185ad3d74Bryan Mawhinney screenCoord.x += round((screenCoordRight.x - screenCoordLeft.x) / 2.0f - 913fe38385c1e5ce443adb962c066adeea185ad3d74Bryan Mawhinney rsAllocationGetDimX(cards[i].detailTexture) / 2.0f); 9144fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 9157cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 916420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Compute alpha for gradually fading in details. Applied to both line and 917420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // detail texture. TODO: use a separate background texture for line. 9183adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma float animatedAlpha = getAnimatedAlpha(cards[i].detailTextureTimeStamp, 9193adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma currentTime, fadeInDuration); 920420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller if (animatedAlpha < 1.0f) { 921420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller stillAnimating = true; 922420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } 923420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 924a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller // Compute alpha based on position. We fade cards quickly so they cannot overlap 925a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller float positionAlpha = ((float)endDetailFadeAngle - cardPosition(i)) 926a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller / (endDetailFadeAngle - startDetailFadeAngle); 927a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller positionAlpha = max(0.0f, positionAlpha); 928a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller positionAlpha = min(1.0f, positionAlpha); 929a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 930a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float blendedAlpha = min(1.0f, animatedAlpha * positionAlpha); 931a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 9328fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (blendedAlpha == 0.0f) { 9338fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].detailVisible = false; 9348fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma continue; // nothing to draw 9358fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma } else { 9368fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].detailVisible = true; 9378fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma } 938b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (blendedAlpha == 1.0f) { 939b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindProgramFragment(singleTextureFragmentProgram); 940b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 941b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindProgramFragment(multiTextureFragmentProgram); 942b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 943a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 944420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Set alpha for blending between the textures 945a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller shaderConstants->fadeAmount = blendedAlpha; 9468a357ebe4ae3063dbb3d8b3bdf6f665b05dd8e6fJason Sams rsgAllocationSyncAll(rsGetAllocation(shaderConstants)); 947420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 9484a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney // Draw line from the card to the detail texture. 9494a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney // The line is drawn from the top or bottom left of the card 9504a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney // to either the top of the screen or the top of the detail 9514a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney // texture, depending on detailTextureAlignment. 9527c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma if (drawRuler) { 9534a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney float rulerTop; 9544a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney float rulerBottom; 9554a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & BELOW) { 9564a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney rulerTop = screenCoord.y; 9574a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney rulerBottom = 0; 9584a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } else { 9594a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney rulerTop = height; 9604a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney rulerBottom = screenCoord.y; 9614a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } 9627c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma const float halfWidth = lineWidth * 0.5f; 9631eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller const float x0 = trunc(cards[i].detailLineOffset.x + screenCoord.x - halfWidth); 9641eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller const float x1 = x0 + lineWidth; 965b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y0 = rulerBottom + yPadding; 966b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y1 = rulerTop - yPadding - cards[i].detailLineOffset.y; 967b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 968b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (blendedAlpha == 1.0f) { 969b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(singleTextureFragmentProgram, 0, detailLineTexture); 970b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 971b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(multiTextureFragmentProgram, 1, detailLineTexture); 972b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 973b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgDrawQuad(x0, y0, screenCoord.z, x1, y0, screenCoord.z, 974b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller x1, y1, screenCoord.z, x0, y1, screenCoord.z); 9757c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma } 9767cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 9777cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // Draw the detail texture next to it using the offsets provided. 9787cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float textureWidth = rsAllocationGetDimX(cards[i].detailTexture); 9797cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float textureHeight = rsAllocationGetDimY(cards[i].detailTexture); 9807cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float offx = cards[i].detailTextureOffset.x; 9817cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float offy = -cards[i].detailTextureOffset.y; 9824a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney const float textureTop = (detailTextureAlignment & VIEW_TOP) 9834a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney ? height : screenCoord.y; 984b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float x0 = cards[i].detailLineOffset.x + screenCoord.x + offx; 985b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float x1 = cards[i].detailLineOffset.x + screenCoord.x + offx + textureWidth; 986b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y0 = textureTop + offy - textureHeight - cards[i].detailLineOffset.y; 987b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y1 = textureTop + offy - cards[i].detailLineOffset.y; 9888fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].detailTexturePosition[0].x = x0; 9898fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].detailTexturePosition[0].y = height - y1; 9908fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].detailTexturePosition[1].x = x1; 9918fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].detailTexturePosition[1].y = height - y0; 992b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 993b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (blendedAlpha == 1.0f) { 994b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(singleTextureFragmentProgram, 0, cards[i].detailTexture); 995b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 996b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(multiTextureFragmentProgram, 1, cards[i].detailTexture); 997b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 998b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgDrawQuad(x0, y0, screenCoord.z, x1, y0, screenCoord.z, 999b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller x1, y1, screenCoord.z, x0, y1, screenCoord.z); 10007cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 10017cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 10027cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 1003420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller return stillAnimating; 10047cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller} 10057cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 10069afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Millerstatic void drawBackground() 10079afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller{ 1008b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller static bool toggle; 10099afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller if (backgroundTexture.p != 0) { 10109afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearDepth(1.0f); 10119afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rs_matrix4x4 projection, model; 10129afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsMatrixLoadOrtho(&projection, -1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f); 10139afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgProgramVertexLoadProjectionMatrix(&projection); 10149afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsMatrixLoadIdentity(&model); 10159afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgProgramVertexLoadModelMatrix(&model); 1016a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller rsgBindTexture(singleTextureFragmentProgram, 0, backgroundTexture); 10179afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller float z = -0.9999f; 10189afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgDrawQuad( 10199afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[0].x, cardVertices[0].y, z, 10209afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[1].x, cardVertices[1].y, z, 10219afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[2].x, cardVertices[2].y, z, 10229afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[3].x, cardVertices[3].y, z); 10239afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller updateCamera = true; // we mucked with the matrix. 10249afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } else { 10259afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearDepth(1.0f); 1026b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (debugRendering) { // for debugging - flash the screen so we know we're still rendering 1027b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgClearColor(toggle ? backgroundColor.x : 1.0f, 1028b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller toggle ? backgroundColor.y : 0.0f, 1029b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller toggle ? backgroundColor.z : 0.0f, 1030b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller backgroundColor.w); 10319afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller toggle = !toggle; 1032b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 10337cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgClearColor(backgroundColor.x, backgroundColor.y, backgroundColor.z, 10347cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller backgroundColor.w); 10359afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } 10369afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } 10379afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller} 10389afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller 10395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void updateCameraMatrix(float width, float height) 10405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 10415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float aspect = width / height; 10425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (aspect != camera.aspect || updateCamera) { 10435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.aspect = aspect; 10445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller loadPerspectiveMatrix(&projectionMatrix, camera.fov, camera.aspect, camera.near, camera.far); 10455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadProjectionMatrix(&projectionMatrix); 10465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller loadLookatMatrix(&modelviewMatrix, camera.from, camera.at, camera.up); 10485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadModelMatrix(&modelviewMatrix); 10495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = false; 10505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 10525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 10545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Behavior/Physics 10555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 10565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int64_t lastTime = 0L; // keep track of how much time has passed between frames 1057be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic float lastAngle = 0.0f; 10585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float2 lastPosition; 10595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool animating = false; 1060b2c785780ecbe79a5b7ba558b21985f956458c8cJim Shumastatic float stopVelocity = 0.1f * M_PI / 180.0f; // slower than this: carousel stops 1061b2c785780ecbe79a5b7ba558b21985f956458c8cJim Shumastatic float selectionVelocity = 15.0f * M_PI / 180.0f; // faster than this: tap won't select 106255b237bcd720774e27248f5fecf6c32a3f420a4cJim Millerstatic float velocityHistory[VELOCITY_HISTORY_MAX]; 106355b237bcd720774e27248f5fecf6c32a3f420a4cJim Millerstatic int velocityHistoryCount; 10645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float mass = 5.0f; // kg 10655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float G = 9.80f; // gravity constant, in m/s 10675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float springConstant = 0.0f; 10685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1069be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller// Computes a hit angle from the center of the carousel to a point on either a plane 1070be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller// or on a cylinder. If neither is hit, returns false. 1071be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic bool hitAngle(float x, float y, float *angle) 1072be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller{ 1073be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller Ray ray; 1074be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller makeRayForPixelAt(&ray, &camera, x, y); 1075be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller float t = FLT_MAX; 1076be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller if (dragModel == DRAG_MODEL_PLANE && rayPlaneIntersect(&ray, &carouselPlane, &t)) { 1077be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller const float3 point = (ray.position + t*ray.direction); 1078be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller const float3 direction = point - carouselPlane.point; 1079be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller *angle = atan2(direction.x, direction.z); 1080be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller if (debugSelection) rsDebug("Plane Angle = ", degrees(*angle)); 1081be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller return true; 1082be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller } else if ((dragModel == DRAG_MODEL_CYLINDER_INSIDE || dragModel == DRAG_MODEL_CYLINDER_OUTSIDE) 1083be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller && rayCylinderIntersect(&ray, &carouselCylinder, &t)) { 1084be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller const float3 point = (ray.position + t*ray.direction); 1085be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller const float3 direction = point - carouselCylinder.center; 1086be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller *angle = atan2(direction.x, direction.z); 1087be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller if (debugSelection) rsDebug("Cylinder Angle = ", degrees(*angle)); 1088be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller return true; 1089be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller } 1090be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller return false; 1091be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller} 1092be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller 10935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float dragFunction(float x, float y) 10945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 1095be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller float result; 1096be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller float angle; 1097be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller if (hitAngle(x, y, &angle)) { 1098be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller result = angle - lastAngle; 1099be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller // Handle singularity where atan2 switches between +- PI 1100be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller if (result < -M_PI) { 1101be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller result += 2.0f * M_PI; 1102be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller } else if (result > M_PI) { 1103be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller result -= 2.0f * M_PI; 1104be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller } 1105be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller lastAngle = angle; 1106be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller } else { 1107be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller // If we didn't hit anything or drag model wasn't plane or cylinder, we use screen delta 1108be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller result = dragFactor * ((x - lastPosition.x) / rsgGetWidth()) * M_PI; 1109be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller } 1110be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller return result; 11115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 11125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 11135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float deltaTimeInSeconds(int64_t current) 11145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 11155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return (lastTime > 0L) ? (float) (current - lastTime) / 1000.0f : 0.0f; 11165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 11175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1118370b177eb74cd8a7d9a2ab06a5ee8bb3ed25f74fStephen Hinesstatic int doSelection(float x, float y) 11195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 11205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller Ray ray; 1121b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (makeRayForPixelAt(&ray, &camera, x, y)) { 11225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bestTime = FLT_MAX; 11235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return intersectGeometry(&ray, &bestTime); 11245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return -1; 11265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 11275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1128370b177eb74cd8a7d9a2ab06a5ee8bb3ed25f74fStephen Hinesstatic void sendAnimationStarted() { 112943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller rsSendToClient(CMD_ANIMATION_STARTED); 113043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller} 113143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 1132370b177eb74cd8a7d9a2ab06a5ee8bb3ed25f74fStephen Hinesstatic void sendAnimationFinished() { 1133a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich float data[1]; 1134a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich data[0] = radiansToCarouselRotationAngle(bias); 1135a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich rsSendToClient(CMD_ANIMATION_FINISHED, (int*) data, sizeof(data)); 1136a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich} 1137a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich 11381882cebdc1b7b0551189ca33fb7cb77ef10c988bJim Millervoid doStart(float x, float y, long eventTime) 11395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 114043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller touchPosition = lastPosition = (float2) { x, y }; 1141be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller lastAngle = hitAngle(x,y, &lastAngle) ? lastAngle : 0.0f; 1142b2c785780ecbe79a5b7ba558b21985f956458c8cJim Shuma enableSelection = fabs(velocity) < selectionVelocity; 11435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocity = 0.0f; 114455b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller velocityHistory[0] = 0.0f; 114555b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller velocityHistoryCount = 0; 114655b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller 1147ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller releaseTime = lastTime; // used to disable scale down animation - any time in the past will do 11481882cebdc1b7b0551189ca33fb7cb77ef10c988bJim Miller touchTime = lastTime = eventTime; 11495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller touchBias = bias; 115043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller isDragging = true; 1151ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller isOverScrolling = false; 11525dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney tiltAngle = 0; 11535dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney overscrollBias = bias; 11545dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney 115543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller animatedSelection = doSelection(x, y); // used to provide visual feedback on touch 11568debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry stopAutoscroll(); 11575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 11585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 115955b237bcd720774e27248f5fecf6c32a3f420a4cJim Millerstatic float computeAverageVelocityFromHistory() 116055b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller{ 116155b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller if (velocityHistoryCount > 0) { 116255b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller const int count = min(VELOCITY_HISTORY_MAX, velocityHistoryCount); 116355b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller float vsum = 0.0f; 116455b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller for (int i = 0; i < count; i++) { 116555b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller vsum += velocityHistory[i]; 116655b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller } 116755b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller return vsum / count; 116855b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller } else { 116955b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller return 0.0f; 117055b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller } 117155b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller} 117255b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller 11731882cebdc1b7b0551189ca33fb7cb77ef10c988bJim Millervoid doStop(float x, float y, long eventTime) 11745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 1175ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller updateAllocationVars(); 1176ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller 1177ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller releaseTime = rsUptimeMillis(); 117843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 117943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (enableSelection) { 11808fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma int data[3]; 11818fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma int selection; 11828fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma float2 point; 11838fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma 11848fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if ((selection = intersectDetailTexture(x, y, &point)) != -1) { 11858fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (debugSelection) rsDebug("Selected detail texture on doStop():", selection); 11868fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma data[0] = selection; 11878fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma data[1] = point.x; 11888fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma data[2] = point.y; 11898fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma rsSendToClientBlocking(CMD_DETAIL_SELECTED, data, sizeof(data)); 11908fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma } 11918fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma else if ((selection = doSelection(x, y))!= -1) { 119243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (debugSelection) rsDebug("Selected item on doStop():", selection); 119343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller data[0] = selection; 119443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller rsSendToClientBlocking(CMD_CARD_SELECTED, data, sizeof(data)); 119543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 119643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller animating = false; 11975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 119855b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller velocity = computeAverageVelocityFromHistory(); 1199b2c785780ecbe79a5b7ba558b21985f956458c8cJim Shuma if (fabs(velocity) > stopVelocity) { 12005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller animating = true; 12015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 12025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 120343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller enableSelection = false; 12041882cebdc1b7b0551189ca33fb7cb77ef10c988bJim Miller lastTime = eventTime; 120543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller isDragging = false; 12065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 12075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1208594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shumavoid doLongPress() 1209594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma{ 1210594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma int64_t currentTime = rsUptimeMillis(); 1211ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller updateAllocationVars(); 121243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Selection happens for most recent position detected in doMotion() 1213e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma if (enableSelection && animatedSelection != -1) { 1214e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma if (debugSelection) rsDebug("doLongPress(), selection = ", animatedSelection); 1215e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma int data[7]; 1216e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma data[0] = animatedSelection; 1217e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma data[1] = lastPosition.x; 1218e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma data[2] = lastPosition.y; 1219e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma data[3] = cards[animatedSelection].detailTexturePosition[0].x; 1220e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma data[4] = cards[animatedSelection].detailTexturePosition[0].y; 1221e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma data[5] = cards[animatedSelection].detailTexturePosition[1].x; 1222e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma data[6] = cards[animatedSelection].detailTexturePosition[1].y; 1223594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma rsSendToClientBlocking(CMD_CARD_LONGPRESS, data, sizeof(data)); 1224e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma enableSelection = false; 1225594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma } 1226594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma lastTime = rsUptimeMillis(); 1227594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma} 1228594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma 12291882cebdc1b7b0551189ca33fb7cb77ef10c988bJim Millervoid doMotion(float x, float y, long eventTime) 12305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 123114d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma const float highBias = maximumBias(); 123214d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma const float lowBias = minimumBias(); 12335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float deltaOmega = dragFunction(x, y); 12345dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney overscrollBias += deltaOmega; 12355dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney overscrollBias = clamp(overscrollBias, lowBias - TILT_MAX_BIAS, 12365dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney highBias + TILT_MAX_BIAS); 12375dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney bias = clamp(overscrollBias, lowBias, highBias); 12385dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney isOverScrolling = tiltOverscroll(); 12395dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney 124043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float2 delta = (float2) { x, y } - touchPosition; 124143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float distance = sqrt(dot(delta, delta)); 124243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bool inside = (distance < selectionRadius); 124343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller enableSelection &= inside; 124443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller lastPosition = (float2) { x, y }; 12451882cebdc1b7b0551189ca33fb7cb77ef10c988bJim Miller float dt = deltaTimeInSeconds(eventTime); 12465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (dt > 0.0f) { 12475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float v = deltaOmega / dt; 124855b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller velocityHistory[velocityHistoryCount % VELOCITY_HISTORY_MAX] = v; 124955b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller velocityHistoryCount++; 12505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 125155b237bcd720774e27248f5fecf6c32a3f420a4cJim Miller velocity = computeAverageVelocityFromHistory(); 12521882cebdc1b7b0551189ca33fb7cb77ef10c988bJim Miller lastTime = eventTime; 12535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 12545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 12555dac0398c2684197a4363f15cef292fee126038fBryan Mawhinneybool tiltOverscroll() { 12565dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney if (overscrollBias == bias) { 12575dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney // No overscroll required. 12585dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney return false; 12595dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney } 12605dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney 12615dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney // How much we deviate from the maximum bias. 12625dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney float deltaBias = overscrollBias - bias; 12635dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney // We clamped, that means we need overscroll. 12645dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney tiltAngle = (deltaBias / TILT_MAX_BIAS) 12655dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney * TILT_MAX_ANGLE * fillDirection; 12665dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney 12675dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney return true; 12685dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney} 12695dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney 12705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 12718debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry// Autoscroll Interpolation 12728debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry//////////////////////////////////////////////////////////////////////////////////////////////////// 12738debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berrystatic int64_t autoscrollStartTime = 0L; //tracks when we actually started interpolating 12748debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berrystatic int64_t autoscrollDuration = 0L; //in milli seconds 12758debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berrystatic int autoscrollInterpolationMode = INTERPOLATION_LINEAR; 12768debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 12778debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berrystatic float autoscrollStopAngle = 0.0f; 12788debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berrystatic float autoscrollStartAngle = 0.0f; 12798debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 12808debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berryvoid setCarouselRotationAngle2( 12818debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry float endAngle, 12828debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry int milliseconds, 12838debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry int interpolationMode, 12848debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry float maxAnimatedArc) 12858debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry{ 12868debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry float actualStart = radiansToCarouselRotationAngle(bias); 12878debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 12888debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry if (maxAnimatedArc > 0) { 12898debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry //snap the current position to keep end - start under maxAnimatedArc 12908debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry if (actualStart <= endAngle) { 12918debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry if (actualStart < endAngle - maxAnimatedArc) { 12928debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry actualStart = endAngle - maxAnimatedArc; 12938debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry } 12948debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry } 12958debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry else { 12968debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry if (actualStart > endAngle + maxAnimatedArc) { 12978debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry actualStart = endAngle + maxAnimatedArc; 12988debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry } 12998debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry } 13008debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry } 13018debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 13028debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry animating = true; 1303ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller isAutoScrolling = true; 13048debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry autoscrollDuration = milliseconds; 13058debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry autoscrollInterpolationMode = interpolationMode; 13068debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry autoscrollStartAngle = carouselRotationAngleToRadians(actualStart); 13078debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry autoscrollStopAngle = carouselRotationAngleToRadians(endAngle); 13088debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 13098debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry //Make sure the start and stop angles are in the allowed range 13108debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry const float highBias = maximumBias(); 13118debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry const float lowBias = minimumBias(); 13128debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry autoscrollStartAngle = clamp(autoscrollStartAngle, lowBias, highBias); 13138debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry autoscrollStopAngle = clamp(autoscrollStopAngle, lowBias, highBias); 13148debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 13158debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry //stop other animation kinds 1316ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller isOverScrolling = false; 13178debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry velocity = 0.0f; 13188debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry} 13198debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 13208debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berrystatic void stopAutoscroll() 13218debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry{ 1322ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller isAutoScrolling = false; 13238debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry autoscrollStartTime = 0L; //reset for next time 13248debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry} 13258debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 13268debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry// This method computes the position of all the cards by updating bias based on a 13278debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry// simple interpolation model. If the cards are still in motion, returns true. 13288debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berrystatic bool doAutoscroll(float currentTime) 13298debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry{ 13308debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry if (autoscrollDuration == 0L) { 13318debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry return false; 13328debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry } 13338debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 13348debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry if (autoscrollStartTime == 0L) { 13358debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry autoscrollStartTime = currentTime; 13368debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry } 13378debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 13388debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry const int64_t interpolationEndTime = autoscrollStartTime + autoscrollDuration; 13398debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 13408debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry float timePos = (currentTime - autoscrollStartTime) / (float)autoscrollDuration; 13418debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry if (timePos > 1.0f) { 13428debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry timePos = 1.0f; 13438debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry } 13448debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 13458debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry float lambda = timePos; //default to linear 13468debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry if (autoscrollInterpolationMode == INTERPOLATION_DECELERATE_QUADRATIC) { 13478debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry lambda = 1.0f - (1.0f - timePos) * (1.0f - timePos); 13488debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry } 13498debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry else if (autoscrollInterpolationMode == INTERPOLATION_ACCELERATE_DECELERATE_CUBIC) { 13508debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry lambda = timePos * timePos * (3 - 2 * timePos); 13518debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry } 13528debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 13538debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry bias = lambda * autoscrollStopAngle + (1.0 - lambda) * autoscrollStartAngle; 13548debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 13558debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry if (currentTime > interpolationEndTime) { 13568debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry stopAutoscroll(); 13578debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry return false; 13588debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry } 13598debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry else { 13608debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry return true; 13618debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry } 13628debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry} 13638debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry 13648debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry//////////////////////////////////////////////////////////////////////////////////////////////////// 13655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Hit detection using ray casting. 13665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 136743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic const float EPSILON = 1.0e-6f; 136843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic const float tmin = 0.0f; 13695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 13705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool 137143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim MillerrayTriangleIntersect(Ray* ray, float3 p0, float3 p1, float3 p2, float* tout) 13725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 13735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 e1 = p1 - p0; 13745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 e2 = p2 - p0; 13755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s1 = cross(ray->direction, e2); 13765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 13775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float div = dot(s1, e1); 13785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (div == 0.0f) return false; // ray is parallel to plane. 13795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 13805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 d = ray->position - p0; 13815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float invDiv = 1.0f / div; 13825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 13835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float u = dot(d, s1) * invDiv; 13845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (u < 0.0f || u > 1.0f) return false; 13855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 13865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s2 = cross(d, e1); 13875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float v = dot(ray->direction, s2) * invDiv; 13885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if ( v < 0.0f || (u+v) > 1.0f) return false; 13895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 13905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float t = dot(e2, s2) * invDiv; 13915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (t < tmin || t > *tout) 13925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return false; 13935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller *tout = t; 13945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return true; 13955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 13965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 139743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 139843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// Computes ray/plane intersection. Returns false if no intersection found. 139943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 1400b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool 140143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim MillerrayPlaneIntersect(Ray* ray, Plane* plane, float* tout) 1402b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller{ 140343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float denom = dot(ray->direction, plane->normal); 140443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (fabs(denom) > EPSILON) { 140543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float t = - (plane->constant + dot(ray->position, plane->normal)) / denom; 140643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (t > tmin && t < *tout) { 140743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller *tout = t; 140843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return true; 140943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 141043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 141143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return false; 1412b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller} 1413b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 141443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 141543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// Computes ray/cylindr intersection. There are 0, 1 or 2 hits. 141643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// Returns true and sets *tout to the closest point or 141743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// returns false if no intersection found. 141843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 1419b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool 142043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim MillerrayCylinderIntersect(Ray* ray, Cylinder* cylinder, float* tout) 1421b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller{ 142243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float A = ray->direction.x * ray->direction.x + ray->direction.z * ray->direction.z; 142343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (A < EPSILON) return false; // ray misses 142443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 142543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Compute quadratic equation coefficients 142643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float B = 2.0f * (ray->direction.x * ray->position.x 142743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller + ray->direction.z * ray->position.z); 142843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float C = ray->position.x * ray->position.x 142943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller + ray->position.z * ray->position.z 143043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller - cylinder->radius * cylinder->radius; 143143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float disc = B*B - 4*A*C; 143243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 143343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (disc < 0.0f) return false; // ray misses 143443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller disc = sqrt(disc); 143543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float denom = 2.0f * A; 143643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 143743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Nearest point 143843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float t1 = (-B - disc) / denom; 1439be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller if (dragModel == DRAG_MODEL_CYLINDER_OUTSIDE && t1 > tmin && t1 < *tout) { 144043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller *tout = t1; 144143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return true; 144243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 144343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 144443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Far point 144543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float t2 = (-B + disc) / denom; 1446be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller if (dragModel == DRAG_MODEL_CYLINDER_INSIDE && t2 > tmin && t2 < *tout) { 144743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller *tout = t2; 144843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return true; 144943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 145043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return false; 1451b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller} 1452b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 1453b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller// Creates a ray for an Android pixel coordinate given a camera, ray and coordinates. 14545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Note that the Y coordinate is opposite of GL rendering coordinates. 1455b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 1456b378af500b36226635b6343b1d5009ee9af44fc1Jim MillermakeRayForPixelAt(Ray* ray, PerspectiveCamera* cam, float x, float y) 14575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 14585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (debugCamera) { 14595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("------ makeRay() -------", 0); 1460b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Camera.from:", cam->from); 1461b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Camera.at:", cam->at); 1462b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Camera.dir:", normalize(cam->at - cam->from)); 14635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 14645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 14655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Vector math. This has the potential to be much faster. 14665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO: pre-compute lowerLeftRay, du, dv to eliminate most of this math. 1467b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float u = x / rsgGetWidth(); 1468b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float v = 1.0f - (y / rsgGetHeight()); 1469b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float aspect = (float) rsgGetWidth() / rsgGetHeight(); 1470b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float tanfov2 = 2.0f * tan(radians(cam->fov / 2.0f)); 1471b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 dir = normalize(cam->at - cam->from); 1472b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 du = tanfov2 * normalize(cross(dir, cam->up)); 1473b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 dv = tanfov2 * normalize(cross(du, dir)); 1474b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller du *= aspect; 1475b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 lowerLeftRay = dir - (0.5f * du) - (0.5f * dv); 1476b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayPoint = cam->from; 1477b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayDir = normalize(lowerLeftRay + u*du + v*dv); 1478b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (debugCamera) { 1479b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Ray direction (vector math) = ", rayDir); 14805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 14815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1482b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->position = rayPoint; 1483b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->direction = rayDir; 1484b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller return true; 1485b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller} 1486b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 1487b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller// Creates a ray for an Android pixel coordinate given a model view and projection matrix. 1488b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller// Note that the Y coordinate is opposite of GL rendering coordinates. 1489b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 1490b378af500b36226635b6343b1d5009ee9af44fc1Jim MillermakeRayForPixelAt(Ray* ray, rs_matrix4x4* model, rs_matrix4x4* proj, float x, float y) 1491b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller{ 1492b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rs_matrix4x4 pm = *model; 1493b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsMatrixLoadMultiply(&pm, proj, model); 1494b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (!rsMatrixInverse(&pm)) { 1495b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("ERROR: SINGULAR PM MATRIX", 0); 1496b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller return false; 14975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 1498b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float width = rsgGetWidth(); 1499b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float height = rsgGetHeight(); 1500b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float winx = 2.0f * x / width - 1.0f; 1501b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float winy = 2.0f * y / height - 1.0f; 1502b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 1503b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float4 eye = { 0.0f, 0.0f, 0.0f, 1.0f }; 1504b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float4 at = { winx, winy, 1.0f, 1.0f }; 1505b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 1506b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller eye = rsMatrixMultiply(&pm, eye); 1507b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller eye *= 1.0f / eye.w; 15085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1509b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller at = rsMatrixMultiply(&pm, at); 1510b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller at *= 1.0f / at.w; 1511b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 1512b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayPoint = { eye.x, eye.y, eye.z }; 1513b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 atPoint = { at.x, at.y, at.z }; 1514b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayDir = normalize(atPoint - rayPoint); 1515b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (debugCamera) { 1516b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("winx: ", winx); 1517b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("winy: ", winy); 1518b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Ray position (transformed) = ", eye); 1519b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Ray direction (transformed) = ", rayDir); 1520b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 1521b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->position = rayPoint; 1522b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->direction = rayDir; 15235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return true; 15245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 15255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 15268fd40311898a9ec759a76f021642f43e617e38c4Jim Shumastatic int intersectDetailTexture(float x, float y, float2 *tapCoordinates) 15278fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma{ 15288fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma for (int id = 0; id < cardCount; id++) { 15298fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (cards[id].detailVisible) { 15308fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma const int x0 = cards[id].detailTexturePosition[0].x; 15318fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma const int y0 = cards[id].detailTexturePosition[0].y; 15328fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma const int x1 = cards[id].detailTexturePosition[1].x; 15338fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma const int y1 = cards[id].detailTexturePosition[1].y; 15348fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (x >= x0 && x <= x1 && y >= y0 && y <= y1) { 15358fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma float2 point = { x - x0, y - y0 }; 15368fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma *tapCoordinates = point; 15378fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma return id; 15388fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma } 15398fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma } 15408fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma } 15418fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma return -1; 15428fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma} 15438fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma 15445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int intersectGeometry(Ray* ray, float *bestTime) 15455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 15465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int hit = -1; 15475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int id = 0; id < cardCount; id++) { 15488fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (cards[id].cardVisible) { 15495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_matrix4x4 matrix; 15505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 p[4]; 15515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 15525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Transform card vertices to world space 15535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadIdentity(&matrix); 1554c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma getMatrixForCard(&matrix, id, true, true); 15555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int vertex = 0; vertex < 4; vertex++) { 15565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float4 tmp = rsMatrixMultiply(&matrix, cardVertices[vertex]); 15575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (tmp.w != 0.0f) { 15585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].x = tmp.x; 15595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].y = tmp.y; 15605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].z = tmp.z; 15615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex] *= 1.0f / tmp.w; 15625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 15635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Bad w coord: ", tmp); 15645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 15675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Intersect card geometry 15685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (rayTriangleIntersect(ray, p[0], p[1], p[2], bestTime) 15695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller || rayTriangleIntersect(ray, p[2], p[3], p[0], bestTime)) { 15705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller hit = id; 15715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return hit; 15755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 15765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 15775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// This method computes the position of all the cards by updating bias based on a 157843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// simple physics model. If the cards are still in motion, returns true. 157943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic bool doPhysics(float dt) 158043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller{ 158143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float minStepTime = 1.0f / 300.0f; // ~5 steps per frame 158243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const int N = (dt > minStepTime) ? (1 + round(dt / minStepTime)) : 1; 158343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller dt /= N; 158443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller for (int i = 0; i < N; i++) { 158543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Force friction - always opposes motion 158643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float Ff = -frictionCoeff * velocity; 158743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 158843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Restoring force to match cards with slots 158943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float theta = startAngle + bias; 159043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float dtheta = 2.0f * M_PI / slotCount; 159143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float position = theta / dtheta; 159243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float fraction = position - floor(position); // fractional position between slots 159343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float x; 159443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (fraction > 0.5f) { 159543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller x = - (1.0f - fraction); 159643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 159743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller x = fraction; 159843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 159943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float Fr = - springConstant * x; 160043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 160143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // compute velocity 160243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float momentum = mass * velocity + (Ff + Fr)*dt; 160343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller velocity = momentum / mass; 160443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bias += velocity * dt; 160543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 1606b2c785780ecbe79a5b7ba558b21985f956458c8cJim Shuma return fabs(velocity) > stopVelocity; 160743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller} 160843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 160943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic float easeOut(float x) 161043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller{ 161143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return x; 161243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller} 161343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 16148debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry// Computes the next value for bias using the current animation (physics/overscroll/autoscrolling) 16155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool updateNextPosition(int64_t currentTime) 16165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 161743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller static const float biasMin = 1e-4f; // close enough if we're within this margin of result 16185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 161943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float dt = deltaTimeInSeconds(currentTime); 16205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 162143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (dt <= 0.0f) { 162243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (debugRendering) rsDebug("Time delta was <= 0", dt); 162343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return true; 16245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 16255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 16265dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney const float firstBias = maximumBias(); 16275dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney const float lastBias = minimumBias(); 162843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bool stillAnimating = false; 1629ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller if (isOverScrolling) { 16305dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney if (tiltAngle > TILT_MIN_ANGLE) { 16315dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney tiltAngle -= dt * TILT_MAX_ANGLE; 16325dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney stillAnimating = true; 16335dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney } else if (tiltAngle < -TILT_MIN_ANGLE) { 16345dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney tiltAngle += dt * TILT_MAX_ANGLE; 16355dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney stillAnimating = true; 163643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 16375dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney isOverScrolling = false; 16385dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney tiltAngle = false; 16395dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney velocity = 0.0f; 164043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 1641ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller } else if (isAutoScrolling) { 16428debeb8a0a785f0ad66bc75200cdb47c137602bcArnaud Berry stillAnimating = doAutoscroll(currentTime); 164343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 164443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller stillAnimating = doPhysics(dt); 16455dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney isOverScrolling = tiltAngle != 0; 1646ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller if (isOverScrolling) { 1647cfe41767a3596a65eef91b6f68286fd0f916a4c7Jim Miller velocity = 0.0f; // prevent bouncing due to v > 0 after overscroll animation. 16485dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney stillAnimating = true; 1649cfe41767a3596a65eef91b6f68286fd0f916a4c7Jim Miller } 1650c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma } 16515dac0398c2684197a4363f15cef292fee126038fBryan Mawhinney bias = clamp(bias, lastBias, firstBias); 165243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return stillAnimating; 16535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 16545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 16555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Cull cards based on visibility and visibleSlotCount. 16565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// If visibleSlotCount is > 0, then only show those slots and cull the rest. 16575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Otherwise, it should cull based on bounds of geometry. 16585ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinneystatic void cullCards() 16595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 1660b4959ac31abdaf6ab7309c17f56fceaa1baabed7Bryan Mawhinney // Calculate the first and last angles of visible slots. We include 1661b4959ac31abdaf6ab7309c17f56fceaa1baabed7Bryan Mawhinney // VISIBLE_SLOT_PADDING slots on either side of visibleSlotCount to allow 1662b4959ac31abdaf6ab7309c17f56fceaa1baabed7Bryan Mawhinney // cards to slide in / out at either side, and rely on the view frustrum 1663b4959ac31abdaf6ab7309c17f56fceaa1baabed7Bryan Mawhinney // for accurate clipping. 1664b4959ac31abdaf6ab7309c17f56fceaa1baabed7Bryan Mawhinney const float visibleFirst = slotPosition(-VISIBLE_SLOT_PADDING); 1665b4959ac31abdaf6ab7309c17f56fceaa1baabed7Bryan Mawhinney const float visibleLast = slotPosition(visibleSlotCount + VISIBLE_SLOT_PADDING); 16665ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney 16675ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney // We'll load but not draw prefetchCardCountPerSide cards 16685ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney // from either side of the visible slots. 1669b4959ac31abdaf6ab7309c17f56fceaa1baabed7Bryan Mawhinney const int prefetchCardCountPerSide = max(prefetchCardCount / 2, VISIBLE_SLOT_PADDING); 1670b4959ac31abdaf6ab7309c17f56fceaa1baabed7Bryan Mawhinney const float prefetchFirst = slotPosition(-prefetchCardCountPerSide); 16715ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney const float prefetchLast = slotPosition(visibleSlotCount + prefetchCardCountPerSide); 16725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < cardCount; i++) { 16735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (visibleSlotCount > 0) { 16745ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney // If visibleSlotCount is specified then only show cards between visibleFirst and visibleLast 16755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float p = cardPosition(i); 167646a02894a12775c16c4588a168c3cbc767a6f983Jim Miller if ((p >= prefetchFirst && p < prefetchLast) 167746a02894a12775c16c4588a168c3cbc767a6f983Jim Miller || (p <= prefetchFirst && p > prefetchLast)) { 16785ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney cards[i].shouldPrefetch = true; 167946a02894a12775c16c4588a168c3cbc767a6f983Jim Miller cards[i].cardVisible = (p >= visibleFirst && p < visibleLast) 1680376a291b7a3016cc85501ee1c044629cce60e75cLogan Chien || (p <= visibleFirst && p > visibleLast); 16818fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma // cards[i].detailVisible will be set at draw time 16825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 16835ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney cards[i].shouldPrefetch = false; 16848fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].cardVisible = false; 16858fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].detailVisible = false; 16865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 16875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 16885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Cull the rest of the cards using bounding box of geometry. 16895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO 16908fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].cardVisible = true; 16918fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma // cards[i].detailVisible will be set at draw time 16925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 16935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 16945ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney} 16955ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney 16965ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney// Request missing texture/geometry for a single card 16975ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinneystatic void requestCardResources(int i) { 16985ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney if (debugTextureLoading) rsDebug("*** Texture stamp: ", (int)cards[i].textureTimeStamp); 169946a02894a12775c16c4588a168c3cbc767a6f983Jim Miller int data[1] = { i }; 170046a02894a12775c16c4588a168c3cbc767a6f983Jim Miller 17015ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney // request texture from client if not loaded 17025ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney if (cards[i].textureState == STATE_INVALID) { 170346a02894a12775c16c4588a168c3cbc767a6f983Jim Miller if (debugTextureLoading) rsDebug("Requesting card because state is STATE_INVALID", i); 17045ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data)); 17055ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney if (enqueued) { 17065ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney cards[i].textureState = STATE_LOADING; 17075ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } else { 170846a02894a12775c16c4588a168c3cbc767a6f983Jim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_TEXTURE", i); 17095ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } 17105ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } else if (cards[i].textureState == STATE_STALE) { 171146a02894a12775c16c4588a168c3cbc767a6f983Jim Miller if (debugTextureLoading) rsDebug("Requesting card because state is STATE_STALE", i); 17125ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data)); 17135ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney if (enqueued) { 17145ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney cards[i].textureState = STATE_UPDATING; 17155ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } else { 171646a02894a12775c16c4588a168c3cbc767a6f983Jim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_TEXTURE", i); 17175ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } 17185ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } 171946a02894a12775c16c4588a168c3cbc767a6f983Jim Miller 17205ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney // request detail texture from client if not loaded 17215ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney if (cards[i].detailTextureState == STATE_INVALID) { 17225ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney bool enqueued = rsSendToClient(CMD_REQUEST_DETAIL_TEXTURE, data, sizeof(data)); 17235ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney if (enqueued) { 17245ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney cards[i].detailTextureState = STATE_LOADING; 17255ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } else { 172646a02894a12775c16c4588a168c3cbc767a6f983Jim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_DETAIL_TEXTURE", i); 17275ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } 17285ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } else if (cards[i].detailTextureState == STATE_STALE) { 17295ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney bool enqueued = rsSendToClient(CMD_REQUEST_DETAIL_TEXTURE, data, sizeof(data)); 17305ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney if (enqueued) { 17315ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney cards[i].detailTextureState = STATE_UPDATING; 17325ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } else { 173346a02894a12775c16c4588a168c3cbc767a6f983Jim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_DETAIL_TEXTURE", i); 17345ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } 17355ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } 173646a02894a12775c16c4588a168c3cbc767a6f983Jim Miller 17375ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney // request geometry from client if not loaded 17385ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney if (cards[i].geometryState == STATE_INVALID) { 17395ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney bool enqueued = rsSendToClient(CMD_REQUEST_GEOMETRY, data, sizeof(data)); 17405ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney if (enqueued) { 17415ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney cards[i].geometryState = STATE_LOADING; 17425ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } else { 174346a02894a12775c16c4588a168c3cbc767a6f983Jim Miller if (debugGeometryLoading) rsDebug("Couldn't send CMD_REQUEST_GEOMETRY", i); 17445ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } 17455ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } 17465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 17475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 17485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Request texture/geometry for items that have come into view 17495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// or doesn't have a texture yet. 1750420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerstatic void updateCardResources(int64_t currentTime) 17515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 17525ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney // First process any visible cards 1753a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller for (int i = cardCount-1; i >= 0; --i) { 17548fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (cards[i].cardVisible) { 17555ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney requestCardResources(i); 17565ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } 17575ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney } 17582ba04e061b52c488a154739379501dc833e39f79Jim Miller 17595ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney // Then the rest 17605ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney for (int i = cardCount-1; i >= 0; --i) { 17615ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney if (cards[i].cardVisible) { 17625ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney // already requested above 176346a02894a12775c16c4588a168c3cbc767a6f983Jim Miller } else if (cards[i].shouldPrefetch) { 17645ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney requestCardResources(i); 17655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 17665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // ask the host to remove the texture 17675ac1de00d4441748a4b183b5d406298cd18f2d27Bryan Mawhinney int data[1]; 1768dce9af330efceae2b8d1d7c25e7e236b4e21719bJack Palevich if (cards[i].textureState != STATE_INVALID) { 17695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 17705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_TEXTURE, data, sizeof(data)); 17715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 17725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].textureState = STATE_INVALID; 1773420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[i].textureTimeStamp = currentTime; 17745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 17757cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_INVALIDATE_TEXTURE", 0); 17767cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 17777cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 17787cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // ask the host to remove the detail texture 17797cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (cards[i].detailTextureState != STATE_INVALID) { 17807cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller data[0] = i; 17817cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_DETAIL_TEXTURE, data, sizeof(data)); 17827cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (enqueued) { 17837cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[i].detailTextureState = STATE_INVALID; 1784420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[i].detailTextureTimeStamp = currentTime; 17857cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } else { 17867cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Can't send CMD_INVALIDATE_DETAIL_TEXTURE", 0); 17875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 17885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 17895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // ask the host to remove the geometry 1790dce9af330efceae2b8d1d7c25e7e236b4e21719bJack Palevich if (cards[i].geometryState != STATE_INVALID) { 17915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 17925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_GEOMETRY, data, sizeof(data)); 17935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 17945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].geometryState = STATE_INVALID; 17955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 17967cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugGeometryLoading) rsDebug("Couldn't send CMD_INVALIDATE_GEOMETRY", 0); 17975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 17985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 17995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 18005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 18015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 18025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 18035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Places dots on geometry to visually inspect that objects can be seen by rays. 18045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// NOTE: the color of the dot is somewhat random, as it depends on texture of previously-rendered 18055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// card. 18065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void renderWithRays() 18075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 18085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float w = rsgGetWidth(); 18095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float h = rsgGetHeight(); 18105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const int skip = 8; 18118a357ebe4ae3063dbb3d8b3bdf6f665b05dd8e6fJason Sams 18128a357ebe4ae3063dbb3d8b3bdf6f665b05dd8e6fJason Sams rsgProgramFragmentConstantColor(singleTextureFragmentProgram, 1.0f, 0.0f, 0.0f, 1.0f); 18135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int j = 0; j < (int) h; j+=skip) { 18145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float posY = (float) j; 18155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < (int) w; i+=skip) { 18165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float posX = (float) i; 18175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller Ray ray; 1818b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (makeRayForPixelAt(&ray, &camera, posX, posY)) { 18195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bestTime = FLT_MAX; 18205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (intersectGeometry(&ray, &bestTime) != -1) { 18215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawSpriteScreenspace(posX, h - posY - 1, 0.0f, 2.0f, 2.0f); 18225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 18235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 18245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 18255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 18265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 18275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 18285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint root() { 18295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t currentTime = rsUptimeMillis(); 18305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 18315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramVertex(vertexProgram); 18325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramRaster(rasterProgram); 1833b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindSampler(singleTextureFragmentProgram, 0, linearClamp); 1834b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindSampler(multiTextureFragmentProgram, 0, linearClamp); 1835b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindSampler(multiTextureFragmentProgram, 1, linearClamp); 18365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1837ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller updateAllocationVars(); 18385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1839a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller rsgBindProgramFragment(singleTextureFragmentProgram); 1840fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller // rsgClearDepth() currently follows the value of glDepthMask(), so it's disabled when 1841fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller // the mask is disabled. We may want to change the following to always draw w/o Z for 1842fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller // the background if we can guarantee the depth buffer will get cleared and 1843fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller // there's a performance advantage. 1844f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsgBindProgramStore(programStoreBackground); 18459afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller drawBackground(); 18465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 18475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCameraMatrix(rsgGetWidth(), rsgGetHeight()); 18485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1849ceae13b8f2ae7342506ecb4e4fcce956dbb12af7Jim Miller bool stillAnimating = (currentTime - touchTime) <= ANIMATION_SCALE_UP_TIME; 185043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 185143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (!isDragging && animating) { 185243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller stillAnimating = updateNextPosition(currentTime); 185343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 185443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 185543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller lastTime = currentTime; 18565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 18575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cullCards(); 18585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1859420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller updateCardResources(currentTime); 18605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1861bfc5ce2da9e0d8d0ec2535c465624574d98418d7Jim Shuma // Draw cards opaque only if requested, and always draw detail textures with blending. 1862bf39450b962d91ec78af53db39826d55ddb39902Jim Shuma stillAnimating |= drawCards(currentTime); 1863f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsgBindProgramStore(programStoreDetail); 1864bf39450b962d91ec78af53db39826d55ddb39902Jim Shuma stillAnimating |= drawDetails(currentTime); 18655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 186643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (stillAnimating != animating) { 186743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (stillAnimating) { 186843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // we just started animating 186943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller sendAnimationStarted(); 187043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 187143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // we were animating but stopped animating just now 187243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller sendAnimationFinished(); 187343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 187443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller animating = stillAnimating; 187543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 187643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 187743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (debugRays) { 18785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller renderWithRays(); 18795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 18805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 18815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //rsSendToClient(CMD_PING); 18825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 188343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return animating ? 1 : 0; 18845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 1885