carousel.rs revision c2baf88a763ae0e3694c8a10c13f203db9aec363
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? 373adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma int64_t textureTimeStamp; // time when this texture was last updated, in ms 383adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma int64_t detailTextureTimeStamp; // time when this texture was last updated, in ms 393adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma int64_t geometryTimeStamp; // time when the card itself was last updated, in ms 405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} Card_t; 415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millertypedef struct Ray_s { 435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 position; 445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 direction; 455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} Ray; 465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millertypedef struct Plane_s { 4843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float3 point; 4943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float3 normal; 5043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float constant; 5143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller} Plane; 5243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 5343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millertypedef struct Cylinder_s { 5443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float3 center; // center of a y-axis-aligned infinite cylinder 5543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float radius; 5643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller} Cylinder; 5743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millertypedef struct PerspectiveCamera_s { 595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 from; 605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 at; 615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 up; 625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float fov; 635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float aspect; 645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float near; 655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float far; 665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} PerspectiveCamera; 675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 68f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumatypedef struct ProgramStore_s { 69f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rs_program_store programStore; 70f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma} ProgramStore_t; 71f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma 72420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millertypedef struct FragmentShaderConstants_s { 73420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float fadeAmount; 743adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma float overallAlpha; 75420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller} FragmentShaderConstants; 76420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Request states. Used for loading 3D object properties from the Java client. 785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Typical properties: texture, geometry and matrices. 795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerenum { 805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_INVALID = 0, // item hasn't been loaded 815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_LOADING, // we've requested an item but are waiting for it to load 826af401bca5f8854524d128e9df5700035fae1160Jim Shuma STATE_STALE, // we have an old item, but should request an update 836af401bca5f8854524d128e9df5700035fae1160Jim Shuma STATE_UPDATING, // we've requested an update, and will display the old one in the meantime 845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_LOADED // item was delivered 855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 874a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney// Detail texture alignments ** THIS LIST MUST MATCH THOSE IN CarouselView.java *** 884a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinneyenum { 894a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Detail is centered vertically with respect to the card **/ 904a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney CENTER_VERTICAL = 1, 914a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Detail is aligned with the top edge of the carousel view **/ 924a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney VIEW_TOP = 1 << 1, 934a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Detail is aligned with the bottom edge of the carousel view (not yet implemented) **/ 944a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney VIEW_BOTTOM = 1 << 2, 954a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Detail is positioned above the card (not yet implemented) **/ 964a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney ABOVE = 1 << 3, 974a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Detail is positioned below the card **/ 984a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney BELOW = 1 << 4, 994a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Mask that selects those bits that control vertical alignment **/ 1004a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney VERTICAL_ALIGNMENT_MASK = 0xff, 1014a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney 1024a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** 1034a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * Detail is centered horizontally with respect to either the top or bottom 1044a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * extent of the card, depending on whether the detail is above or below the card. 1054a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney */ 1064a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney CENTER_HORIZONTAL = 1 << 8, 1074a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** 1084a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * Detail is aligned with the left edge of either the top or the bottom of 1094a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * the card, depending on whether the detail is above or below the card. 1104a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney */ 1114a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney LEFT = 1 << 9, 1124a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** 1134a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * Detail is aligned with the right edge of either the top or the bottom of 1144a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * the card, depending on whether the detail is above or below the card. 1154a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * (not yet implemented) 1164a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney */ 1174a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney RIGHT = 1 << 10, 1184a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Mask that selects those bits that control horizontal alignment **/ 1194a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney HORIZONTAL_ALIGNMENT_MASK = 0xff00, 1204a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney}; 1214a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney 1225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Client messages *** THIS LIST MUST MATCH THOSE IN CarouselRS.java. *** 1235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_CARD_SELECTED = 100; 1248fd40311898a9ec759a76f021642f43e617e38c4Jim Shumastatic const int CMD_DETAIL_SELECTED = 105; 125594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shumastatic const int CMD_CARD_LONGPRESS = 110; 1265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_REQUEST_TEXTURE = 200; 1275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_INVALIDATE_TEXTURE = 210; 1285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_REQUEST_GEOMETRY = 300; 1295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_INVALIDATE_GEOMETRY = 310; 1305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_ANIMATION_STARTED = 400; 1315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_ANIMATION_FINISHED = 500; 1327cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_REQUEST_DETAIL_TEXTURE = 600; 1337cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_INVALIDATE_DETAIL_TEXTURE = 610; 1347cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_PING = 1000; 1355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 136be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller// Drag model *** THIS LIST MUST MATCH THOSE IN CarouselRS.java. *** 137be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic const int DRAG_MODEL_SCREEN_DELTA = 0; // Drag relative to x coordinate of motion vector 138be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic const int DRAG_MODEL_PLANE = 1; // Drag relative to projected point on plane of carousel 139be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic const int DRAG_MODEL_CYLINDER_INSIDE = 2; // Drag relative to point on inside of cylinder 140be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic const int DRAG_MODEL_CYLINDER_OUTSIDE = 3; // Drag relative to point on outside of cylinder 141be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller 1425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Constants 1431eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Millerstatic const int ANIMATION_DELAY_TIME = 100; // hold off scale animation until this time 1445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int ANIMATION_SCALE_TIME = 200; // Time it takes to animate selected card, in ms 1451eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Millerstatic const float3 SELECTED_SCALE_FACTOR = { 0.1f, 0.1f, 0.1f }; // increase by this % 14643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic const float OVERSCROLL_SLOTS = 1.0f; // amount of allowed overscroll (in slots) 1475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Debug flags 1497cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugCamera = false; // dumps ray/camera coordinate stuff 15043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerconst bool debugSelection = false; // logs selection events 1517cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugTextureLoading = false; // for debugging texture load/unload 1527cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugGeometryLoading = false; // for debugging geometry load/unload 1537c09ccce478100d75e4427d87866ff19d758ae7aJim Shumaconst bool debugDetails = false; // for debugging detail texture geometry 154b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerconst bool debugRendering = false; // flashes display when the frame changes 15543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerconst bool debugRays = false; // shows visual depiction of hit tests, See renderWithRays(). 1565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Exported variables. These will be reflected to Java set_* variables. 1585ce730797a8a7278dfe19dac8a9460b25675fed0Jim MillerCard_t *cards; // array of cards to draw 1595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat startAngle; // position of initial card, in radians 1605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint slotCount; // number of positions where a card can be 1615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint cardCount; // number of cards in stack 162f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumaint programStoresCardCount; // number of program fragment stores 1635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint visibleSlotCount; // number of visible slots (for culling) 1647cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerint visibleDetailCount; // number of visible detail textures to show 1654fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shumaint prefetchCardCount; // how many cards to keep in memory 1664a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinneyint detailTextureAlignment; // How to align detail texture with respect to card 1677c09ccce478100d75e4427d87866ff19d758ae7aJim Shumabool drawRuler; // whether to draw a ruler from the card to the detail texture 1685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat radius; // carousel radius. Cards will be centered on a circle with this radius 1695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat cardRotation; // rotation of card in XY plane relative to Z=1 17083d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinneybool cardsFaceTangent; // whether cards are rotated to face along a tangent to the circle 171c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat swaySensitivity; // how much to rotate cards in relation to the rotation velocity 172c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat frictionCoeff; // how much to slow down the carousel over time 173c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat dragFactor; // a scale factor for how sensitive the carousel is to user dragging 174420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerint fadeInDuration; // amount of time (in ms) for smoothly switching out textures 1753adf712e636f67265da7a6ff425c87e63fc20884Jim Shumaint cardCreationFadeDuration; // amount of time (in ms) to fade while initially showing a card 176420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerfloat rezInCardCount; // this controls how rapidly distant card textures will be rez-ed in 177a9e9c4bef076e718094786edfe0290f798e1db4bJim Millerfloat detailFadeRate; // rate at which details fade as they move into the distance 17843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerfloat4 backgroundColor; 1790cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinneyint rowCount; // number of rows of cards in a given slot, default 1 1800cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinneyfloat rowSpacing; // spacing between rows of cards 1811a5b4d109397ea175b5cbaa7490ca18e78eb040fSimon Wilsonbool firstCardTop; // set true for first card on top row when multiple rows used 1820cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney 183be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerint dragModel = DRAG_MODEL_SCREEN_DELTA; 18414d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shumaint fillDirection; // the order in which to lay out cards: +1 for CCW (default), -1 for CW 185f664659f79399e92025e1dfe1ffbb682ff05613cJim ShumaProgramStore_t *programStoresCard; 186f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumars_program_store programStoreBackground; 187f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumars_program_store programStoreDetail; 188a9e9c4bef076e718094786edfe0290f798e1db4bJim Millerrs_program_fragment singleTextureFragmentProgram; 1893adf712e636f67265da7a6ff425c87e63fc20884Jim Shumars_program_fragment singleTextureBlendingFragmentProgram; 190a9e9c4bef076e718094786edfe0290f798e1db4bJim Millerrs_program_fragment multiTextureFragmentProgram; 1913adf712e636f67265da7a6ff425c87e63fc20884Jim Shumars_program_fragment multiTextureBlendingFragmentProgram; 1925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_vertex vertexProgram; 1935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_raster rasterProgram; 1945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_allocation defaultTexture; // shown when no other texture is assigned 1955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_allocation loadingTexture; // progress texture (shown when app is fetching the texture) 1969afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Millerrs_allocation backgroundTexture; // drawn behind everything, if set 1977cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerrs_allocation detailLineTexture; // used to draw detail line (as a quad, of course) 198420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerrs_allocation detailLoadingTexture; // used when detail texture is loading 1995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_mesh defaultGeometry; // shown when no geometry is loaded 2005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_mesh loadingGeometry; // shown when geometry is loading 20151dd0196e4f3bd4086545f5bf30038ca9ad9ac27Bryan Mawhinneyrs_matrix4x4 defaultCardMatrix; 2025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_matrix4x4 projectionMatrix; 2035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_matrix4x4 modelviewMatrix; 204420b44b8b11ec1c309ea130e69a6876325dbfef9Jim MillerFragmentShaderConstants* shaderConstants; 205420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerrs_sampler linearClamp; 2065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Local variables 2085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float bias; // rotation bias, in radians. Used for animation and dragging. 2095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool updateCamera; // force a recompute of projection and lookat matrices 2105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float FLT_MAX = 1.0e37; 21143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic int animatedSelection = -1; 21243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic int currentFirstCard = -1; 2135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int64_t touchTime = -1; // time of first touch (see doStart()) 2145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float touchBias = 0.0f; // bias on first touch 21543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic float2 touchPosition; // position of first touch, as defined by last call to doStart(x,y) 216c0bb8af58ae15674178f2db240283719918c6f28Jim Shumastatic float velocity = 0.0f; // angular velocity in radians/s 21743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic bool overscroll = false; // whether we're in the overscroll animation 21843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic bool isDragging = false; // true while the user is dragging the carousel 21943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic float selectionRadius = 50.0f; // movement greater than this will result in no selection 22043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic bool enableSelection = false; // enabled until the user drags outside of selectionRadius 22143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 22243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// Default plane of the carousel. Used for angular motion estimation in view. 22343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic Plane carouselPlane = { 22443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller { 0.0f, 0.0f, 0.0f }, // point 22543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller { 0.0f, 1.0f, 0.0f }, // normal 22643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 0.0f // plane constant (= -dot(P, N)) 22743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller}; 2285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 229be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic Cylinder carouselCylinder = { 230be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller {0.0f, 0.0f, 0.0f }, // center 231be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller 1.0f // radius - update with carousel radius. 232be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller}; 233be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller 2343df59346f395434454d310b070fff195089fbaf1Jim Miller// Because allocations can't have 0 dimensions, we have to track whether or not 235f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma// cards and program stores are valid separately. 2363df59346f395434454d310b070fff195089fbaf1Jim Miller// TODO: Remove this dependency once allocations can have a zero dimension. 2373df59346f395434454d310b070fff195089fbaf1Jim Millerstatic bool cardAllocationValid = false; 238f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumastatic bool programStoresAllocationValid = false; 2393df59346f395434454d310b070fff195089fbaf1Jim Miller 2405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Default geometry when card.geometry is not set. 2415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float3 cardVertices[4] = { 2425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { -1.0, -1.0, 0.0 }, 2435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { 1.0, -1.0, 0.0 }, 2445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { 1.0, 1.0, 0.0 }, 2455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {-1.0, 1.0, 0.0 } 2465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 2475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Default camera 2495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic PerspectiveCamera camera = { 2505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {2,2,2}, // from 2515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {0,0,0}, // at 2525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {0,1,0}, // up 2535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 25.0f, // field of view 2545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1.0f, // aspect 2555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 0.1f, // near 2565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 100.0f // far 2575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 2585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Forward references 2605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int intersectGeometry(Ray* ray, float *bestTime); 2618fd40311898a9ec759a76f021642f43e617e38c4Jim Shumastatic int intersectDetailTexture(float x, float y, float2 *tapCoordinates); 262b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 263b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller makeRayForPixelAt(Ray* ray, PerspectiveCamera* cam, float x, float y); 264b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 265b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller makeRayForPixelAt(Ray* ray, rs_matrix4x4* model, rs_matrix4x4* proj, float x, float y); 2665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float deltaTimeInSeconds(int64_t current); 267be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic bool rayPlaneIntersect(Ray* ray, Plane* plane, float* tout); 268be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic bool rayCylinderIntersect(Ray* ray, Cylinder* cylinder, float* tout); 2695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid init() { 2715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // initializers currently have a problem when the variables are exported, so initialize 2725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // globals here. 2732ba04e061b52c488a154739379501dc833e39f79Jim Miller if (debugTextureLoading) rsDebug("Renderscript: init()", 0); 2745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller startAngle = 0.0f; 2755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller slotCount = 10; 2765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller visibleSlotCount = 1; 2777cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller visibleDetailCount = 3; 2785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bias = 0.0f; 279be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller radius = carouselCylinder.radius = 1.0f; 2805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardRotation = 0.0f; 28183d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney cardsFaceTangent = false; 2825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = true; 283b0f070636c29ad178f4e21306f301fe3d20c183bJim Miller backgroundColor = (float4) { 0.0f, 0.0f, 0.0f, 1.0f }; 2843df59346f395434454d310b070fff195089fbaf1Jim Miller cardAllocationValid = false; 285f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma programStoresAllocationValid = false; 2863df59346f395434454d310b070fff195089fbaf1Jim Miller cardCount = 0; 2870cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney rowCount = 1; 2880cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney rowSpacing = 0.0f; 2891a5b4d109397ea175b5cbaa7490ca18e78eb040fSimon Wilson firstCardTop = false; 290420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller fadeInDuration = 250; 291420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rezInCardCount = 0.0f; // alpha will ramp to 1.0f over this many cards (0.0f means disabled) 292a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller detailFadeRate = 0.5f; // fade details over this many slot positions. 29351dd0196e4f3bd4086545f5bf30038ca9ad9ac27Bryan Mawhinney rsMatrixLoadIdentity(&defaultCardMatrix); 2945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 296f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumastatic void updateAllocationVars() 2975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Cards 299ed5cdfa293ec57cb14b98cdc3fa00ac5ec1c1ed4Stephen Hines rs_allocation cardAlloc; 300f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsSetObject(&cardAlloc, rsGetAllocation(cards)); 301dcfb45adbcf37de68920c181322aaa9e4e4b58d8Stephen Hines cardCount = (cardAllocationValid && rsIsObject(cardAlloc)) ? rsAllocationGetDimX(cardAlloc) : 0; 302f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma 303f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma // Program stores 304f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rs_allocation psAlloc; 305f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsSetObject(&psAlloc, rsGetAllocation(programStoresCard)); 306f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma programStoresCardCount = (programStoresAllocationValid && rsIsObject(psAlloc) ? 307f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsAllocationGetDimX(psAlloc) : 0); 3085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 310be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millervoid setRadius(float rad) 311be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller{ 312be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller radius = carouselCylinder.radius = rad; 313be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller} 314be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller 315370b177eb74cd8a7d9a2ab06a5ee8bb3ed25f74fStephen Hinesstatic void initCard(Card_t* card) 3168b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich{ 3176f2cc8cf611860467315ecc542f71a225625eb1cJason Sams // Object refs are always initilized cleared. 3182ba04e061b52c488a154739379501dc833e39f79Jim Miller static const float2 zero = {0.0f, 0.0f}; 3192ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailTextureOffset = zero; 3202ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailLineOffset = zero; 32151dd0196e4f3bd4086545f5bf30038ca9ad9ac27Bryan Mawhinney rsMatrixLoad(&card->matrix, &defaultCardMatrix); 3222ba04e061b52c488a154739379501dc833e39f79Jim Miller card->textureState = STATE_INVALID; 3232ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailTextureState = STATE_INVALID; 3242ba04e061b52c488a154739379501dc833e39f79Jim Miller card->geometryState = STATE_INVALID; 3258fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma card->cardVisible = false; 3268fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma card->detailVisible = false; 3272ba04e061b52c488a154739379501dc833e39f79Jim Miller card->textureTimeStamp = 0; 3282ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailTextureTimeStamp = 0; 3293adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma card->geometryTimeStamp = rsUptimeMillis(); 3302ba04e061b52c488a154739379501dc833e39f79Jim Miller} 3312ba04e061b52c488a154739379501dc833e39f79Jim Miller 3326f2cc8cf611860467315ecc542f71a225625eb1cJason Samsvoid createCards(int start, int total) 3332ba04e061b52c488a154739379501dc833e39f79Jim Miller{ 3345b54f405b4a1afcf57b5ccee2026a00a1004be20Bryan Mawhinney if (!cardAllocationValid) { 3355b54f405b4a1afcf57b5ccee2026a00a1004be20Bryan Mawhinney // If the allocation is invalid, it contains a single place-holder 3365b54f405b4a1afcf57b5ccee2026a00a1004be20Bryan Mawhinney // card that has not yet been initialized (see CarouselRS.createCards). 3375b54f405b4a1afcf57b5ccee2026a00a1004be20Bryan Mawhinney // Here we ensure that it is initialized when growing the total. 3385b54f405b4a1afcf57b5ccee2026a00a1004be20Bryan Mawhinney start = 0; 3395b54f405b4a1afcf57b5ccee2026a00a1004be20Bryan Mawhinney } 3406f2cc8cf611860467315ecc542f71a225625eb1cJason Sams for (int k = start; k < total; k++) { 3416f2cc8cf611860467315ecc542f71a225625eb1cJason Sams initCard(cards + k); 3422ba04e061b52c488a154739379501dc833e39f79Jim Miller } 3432ba04e061b52c488a154739379501dc833e39f79Jim Miller 3446f2cc8cf611860467315ecc542f71a225625eb1cJason Sams // Since allocations can't have 0-size, we track validity ourselves based on the call to 3456f2cc8cf611860467315ecc542f71a225625eb1cJason Sams // this method. 3466f2cc8cf611860467315ecc542f71a225625eb1cJason Sams cardAllocationValid = total > 0; 3472ba04e061b52c488a154739379501dc833e39f79Jim Miller 3486f2cc8cf611860467315ecc542f71a225625eb1cJason Sams updateAllocationVars(cards); 3495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 351420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller// Computes an alpha value for a card using elapsed time and constant fadeInDuration 3523adf712e636f67265da7a6ff425c87e63fc20884Jim Shumastatic float getAnimatedAlpha(int64_t startTime, int64_t currentTime, int64_t duration) 353420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller{ 354420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller double timeElapsed = (double) (currentTime - startTime); // in ms 3553adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma double alpha = duration > 0 ? (double) timeElapsed / duration : 1.0; 356420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller return min(1.0f, (float) alpha); 357420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller} 358420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 3590cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney// Returns total angle for given number of slots 3600cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinneystatic float wedgeAngle(float slots) 3615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3620cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney return slots * 2.0f * M_PI / slotCount; 3635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3650cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney// Return angle of slot in position p. 3660cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinneystatic float slotPosition(int p) 3675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 36814d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma return startAngle + wedgeAngle(p) * fillDirection; 3695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3710cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney// Return angle for card in position p. 3720cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinneystatic float cardPosition(int p) 373f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller{ 3740cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney return bias + slotPosition(p / rowCount); 375f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller} 376f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller 37714d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma// Return the lowest possible bias value, based on the fill direction 37814d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shumastatic float minimumBias() 37914d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma{ 38014d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma const int totalSlots = (cardCount + rowCount - 1) / rowCount; 38114d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma return (fillDirection > 0) ? 38214d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma -max(0.0f, wedgeAngle(totalSlots - visibleDetailCount)) : 38314d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma wedgeAngle(0.0f); 38414d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma} 38514d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma 38614d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma// Return the highest possible bias value, based on the fill direction 38714d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shumastatic float maximumBias() 38814d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma{ 38914d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma const int totalSlots = (cardCount + rowCount - 1) / rowCount; 39014d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma return (fillDirection > 0) ? 39114d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma wedgeAngle(0.0f) : 39214d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma max(0.0f, wedgeAngle(totalSlots - visibleDetailCount)); 39314d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma} 39414d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma 39514d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma 396a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich// convert from carousel rotation angle (in card slot units) to radians. 397a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevichstatic float carouselRotationAngleToRadians(float carouselRotationAngle) 398a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich{ 399a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich return -wedgeAngle(carouselRotationAngle); 400a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich} 401a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich 402a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich// convert from radians to carousel rotation angle (in card slot units). 403a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevichstatic float radiansToCarouselRotationAngle(float angle) 404a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich{ 405a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich return -angle * slotCount / ( 2.0f * M_PI ); 406a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich} 407a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich 4085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Set basic camera properties: 4095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// from - position of the camera in x,y,z 4105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// at - target we're looking at - used to compute view direction 4115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// up - a normalized vector indicating up (typically { 0, 1, 0}) 4125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// 4135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// NOTE: the view direction and up vector cannot be parallel/antiparallel with each other 4145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid lookAt(float fromX, float fromY, float fromZ, 4155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float atX, float atY, float atZ, 4165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float upX, float upY, float upZ) 4175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.x = fromX; 4195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.y = fromY; 4205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.z = fromZ; 4215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.x = atX; 4225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.y = atY; 4235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.z = atZ; 4245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.x = upX; 4255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.y = upY; 4265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.z = upZ; 4275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = true; 4285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Load a projection matrix for the given parameters. This is equivalent to gluPerspective() 4315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void loadPerspectiveMatrix(rs_matrix4x4* matrix, float fovy, float aspect, float near, float far) 4325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadIdentity(matrix); 4345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float top = near * tan((float) (fovy * M_PI / 360.0f)); 4355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bottom = -top; 4365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float left = bottom * aspect; 4375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float right = top * aspect; 4385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadFrustum(matrix, left, right, bottom, top, near, far); 4395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Construct a matrix based on eye point, center and up direction. Based on the 4425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// man page for gluLookat(). Up must be normalized. 4435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void loadLookatMatrix(rs_matrix4x4* matrix, float3 eye, float3 center, float3 up) 4445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 f = normalize(center - eye); 4465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s = normalize(cross(f, up)); 4475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 u = cross(s, f); 4485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float m[16]; 4495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[0] = s.x; 4505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[4] = s.y; 4515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[8] = s.z; 4525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[12] = 0.0f; 4535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[1] = u.x; 4545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[5] = u.y; 4555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[9] = u.z; 4565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[13] = 0.0f; 4575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[2] = -f.x; 4585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[6] = -f.y; 4595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[10] = -f.z; 4605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[14] = 0.0f; 4615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[3] = m[7] = m[11] = 0.0f; 4625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[15] = 1.0f; 4635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoad(matrix, m); 4645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixTranslate(matrix, -eye.x, -eye.y, -eye.z); 4655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid setTexture(int n, rs_allocation texture) 4685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4693df59346f395434454d310b070fff195089fbaf1Jim Miller if (n < 0 || n >= cardCount) return; 470c4c6f38bf410af40e10c63b152befd5a39df87c8Jim Miller rsSetObject(&cards[n].texture, texture); 4718441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey if (cards[n].textureState != STATE_STALE && 4728441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey cards[n].textureState != STATE_UPDATING) { 4738441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey cards[n].textureTimeStamp = rsUptimeMillis(); 4748441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey } 4757cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].textureState = (texture.p != 0) ? STATE_LOADED : STATE_INVALID; 4767cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller} 4777cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 478b378af500b36226635b6343b1d5009ee9af44fc1Jim Millervoid setDetailTexture(int n, float offx, float offy, float loffx, float loffy, rs_allocation texture) 4797cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller{ 4803df59346f395434454d310b070fff195089fbaf1Jim Miller if (n < 0 || n >= cardCount) return; 481c4c6f38bf410af40e10c63b152befd5a39df87c8Jim Miller rsSetObject(&cards[n].detailTexture, texture); 4826af401bca5f8854524d128e9df5700035fae1160Jim Shuma if (cards[n].detailTextureState != STATE_STALE && 4836af401bca5f8854524d128e9df5700035fae1160Jim Shuma cards[n].detailTextureState != STATE_UPDATING) { 4846af401bca5f8854524d128e9df5700035fae1160Jim Shuma cards[n].detailTextureTimeStamp = rsUptimeMillis(); 4856af401bca5f8854524d128e9df5700035fae1160Jim Shuma } 4867cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].detailTextureOffset.x = offx; 4877cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].detailTextureOffset.y = offy; 488b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller cards[n].detailLineOffset.x = loffx; 489b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller cards[n].detailLineOffset.y = loffy; 4907cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].detailTextureState = (texture.p != 0) ? STATE_LOADED : STATE_INVALID; 4916af401bca5f8854524d128e9df5700035fae1160Jim Shuma} 4926af401bca5f8854524d128e9df5700035fae1160Jim Shuma 4938441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkeyvoid invalidateTexture(int n, bool eraseCurrent) 4948441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey{ 4958441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey if (n < 0 || n >= cardCount) return; 4968441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey if (eraseCurrent) { 4978441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey cards[n].textureState = STATE_INVALID; 4988441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey rsClearObject(&cards[n].texture); 4998441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey } else { 5008441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey cards[n].textureState = STATE_STALE; 5018441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey } 5028441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey} 5038441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey 5046af401bca5f8854524d128e9df5700035fae1160Jim Shumavoid invalidateDetailTexture(int n, bool eraseCurrent) 5056af401bca5f8854524d128e9df5700035fae1160Jim Shuma{ 5066af401bca5f8854524d128e9df5700035fae1160Jim Shuma if (n < 0 || n >= cardCount) return; 5076af401bca5f8854524d128e9df5700035fae1160Jim Shuma if (eraseCurrent) { 5086af401bca5f8854524d128e9df5700035fae1160Jim Shuma cards[n].detailTextureState = STATE_INVALID; 5096af401bca5f8854524d128e9df5700035fae1160Jim Shuma rsClearObject(&cards[n].detailTexture); 5106af401bca5f8854524d128e9df5700035fae1160Jim Shuma } else { 5116af401bca5f8854524d128e9df5700035fae1160Jim Shuma cards[n].detailTextureState = STATE_STALE; 5126af401bca5f8854524d128e9df5700035fae1160Jim Shuma } 5135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 5145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid setGeometry(int n, rs_mesh geometry) 5165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 5173df59346f395434454d310b070fff195089fbaf1Jim Miller if (n < 0 || n >= cardCount) return; 518c4c6f38bf410af40e10c63b152befd5a39df87c8Jim Miller rsSetObject(&cards[n].geometry, geometry); 5195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[n].geometry.p != 0) 5205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].geometryState = STATE_LOADED; 5215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller else 5225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].geometryState = STATE_INVALID; 5233adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma cards[n].geometryTimeStamp = rsUptimeMillis(); 5245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 5255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 526c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shumavoid setMatrix(int n, rs_matrix4x4 matrix) { 527c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma if (n < 0 || n >= cardCount) return; 528c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma cards[n].matrix = matrix; 529c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma} 530c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma 531f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumavoid setProgramStoresCard(int n, rs_program_store programStore) 532f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma{ 533f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsSetObject(&programStoresCard[n].programStore, programStore); 534f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma programStoresAllocationValid = true; 535f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma} 536f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma 537a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevichvoid setCarouselRotationAngle(float carouselRotationAngle) { 538a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich bias = carouselRotationAngleToRadians(carouselRotationAngle); 539198a060d650bc849ef0f25b597888fac9546803bJack Palevich} 540198a060d650bc849ef0f25b597888fac9546803bJack Palevich 5411eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller// Gets animated scale value for current selected card. 5421eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller// If card is currently being animated, returns true, otherwise returns false. 5431eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Millerstatic bool getAnimatedScaleForSelected(float3* scale) 5445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 5455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float3 one = { 1.0f, 1.0f, 1.0f }; 5461eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller int64_t dt = rsUptimeMillis() - touchTime; 5471eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller if (dt >= ANIMATION_DELAY_TIME) { 5481eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller float fraction = (float) (dt - ANIMATION_DELAY_TIME) / ANIMATION_SCALE_TIME; 5491eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller fraction = min(fraction, 1.0f); 5501eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller *scale = one + fraction * SELECTED_SCALE_FACTOR; 5511eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller } else { 5521eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller *scale = one; 5531eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller } 5541eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller return dt < (ANIMATION_DELAY_TIME + ANIMATION_SCALE_TIME); // still animating; 5555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 5565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 557c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// The Verhulst logistic function: http://en.wikipedia.org/wiki/Logistic_function 558c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// P(t) = 1 / (1 + e^(-t)) 559c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// Parameter t: Any real number 560c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// Returns: A float in the range (0,1), with P(0.5)=0 561c0bb8af58ae15674178f2db240283719918c6f28Jim Shumastatic float logistic(float t) { 562af8cf9a3bbe517b604b48e217b00085351ab2496Shih-wei Liao return 1.f / (1.f + exp(-t)); 563c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma} 564c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 5657c09ccce478100d75e4427d87866ff19d758ae7aJim Shumastatic float getSwayAngleForVelocity(float v, bool enableSway) 566c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma{ 5677c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma float sway = 0.0f; 5687c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma 5697c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma if (enableSway) { 570d7fa647e6fa4e832381be5bdd03065f9ea35c3f1Jim Shuma const float range = M_PI * 2./3.; // How far we can deviate from center, peak-to-peak 571d7fa647e6fa4e832381be5bdd03065f9ea35c3f1Jim Shuma sway = range * (logistic(-v * swaySensitivity) - 0.5f); 5727c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma } 573c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 574c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma return sway; 575c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma} 576c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 5770cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney// Returns the vertical offset for a card in its slot, 5780cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney// depending on the number of rows configured. 5790cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinneystatic float getVerticalOffsetForCard(int i) { 5800cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney if (rowCount == 1) { 5810cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney // fast path 5820cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney return 0; 5830cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney } 584c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma const float cardHeight = (cardVertices[3].y - cardVertices[0].y) * 585c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma rsMatrixGet(&cards[i].matrix, 1, 1); 5860cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney const float totalHeight = rowCount * (cardHeight + rowSpacing) - rowSpacing; 5871a5b4d109397ea175b5cbaa7490ca18e78eb040fSimon Wilson if (firstCardTop) 5881a5b4d109397ea175b5cbaa7490ca18e78eb040fSimon Wilson i = rowCount - (i % rowCount) - 1; 5891a5b4d109397ea175b5cbaa7490ca18e78eb040fSimon Wilson else 5901a5b4d109397ea175b5cbaa7490ca18e78eb040fSimon Wilson i = i % rowCount; 5911a5b4d109397ea175b5cbaa7490ca18e78eb040fSimon Wilson const float rowOffset = i * (cardHeight + rowSpacing); 5920cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney return (cardHeight - totalHeight) / 2 + rowOffset; 5930cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney} 5940cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney 5951eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller/* 5961eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller * Composes a matrix for the given card. 5971eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller * matrix: The output matrix. 5981eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller * i: The card we're getting the matrix for. 5991eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller * enableSway: Whether to enable swaying. (We want it on for cards, and off for detail textures.) 600c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma * enableCardMatrix: Whether to also consider the user-specified card matrix 6011eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller * 6021eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller * returns true if an animation is being applied to the given card 6031eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller */ 604c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shumastatic bool getMatrixForCard(rs_matrix4x4* matrix, int i, bool enableSway, bool enableCardMatrix) 6055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 6065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float theta = cardPosition(i); 6077c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma float swayAngle = getSwayAngleForVelocity(velocity, enableSway); 6085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixRotate(matrix, degrees(theta), 0, 1, 0); 6090cec8afdb4f9d78adf88c9b9b41e993aef617beaBryan Mawhinney rsMatrixTranslate(matrix, radius, getVerticalOffsetForCard(i), 0); 61083d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney float rotation = cardRotation + swayAngle; 61183d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney if (!cardsFaceTangent) { 61283d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney rotation -= theta; 61383d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney } 61483d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney rsMatrixRotate(matrix, degrees(rotation), 0, 1, 0); 6151eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller bool stillAnimating = false; 61643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (i == animatedSelection && enableSelection) { 6171eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller float3 scale; 6181eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller stillAnimating = getAnimatedScaleForSelected(&scale); 6195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixScale(matrix, scale.x, scale.y, scale.z); 6205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 621c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma // TODO(jshuma): Instead of ignoring this matrix for the detail texture, use card bounding box 622c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma if (enableCardMatrix) { 623c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma rsMatrixLoadMultiply(matrix, matrix, &cards[i].matrix); 624c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma } 6251eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller return stillAnimating; 6265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 6275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 628420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller/* 629f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma * Draws the requested mesh, with the appropriate program store in effect. 630f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma */ 631f664659f79399e92025e1dfe1ffbb682ff05613cJim Shumastatic void drawMesh(rs_mesh mesh) 632f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma{ 633f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma if (programStoresCardCount == 1) { 634f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma // Draw the entire mesh, with the only available program store 635f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsgBindProgramStore(programStoresCard[0].programStore); 636f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsgDrawMesh(mesh); 637f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma } else { 638f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma // Draw each primitive in the mesh with the corresponding program store 639f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma for (int i=0; i<programStoresCardCount; ++i) { 640f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma if (programStoresCard[i].programStore.p != 0) { 641f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsgBindProgramStore(programStoresCard[i].programStore); 642f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsgDrawMesh(mesh, i); 643f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma } 644f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma } 645f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma } 646f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma} 647f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma 648f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma/* 649420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller * Draws cards around the Carousel. 650420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller * Returns true if we're still animating any property of the cards (e.g. fades). 651420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller */ 652420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerstatic bool drawCards(int64_t currentTime) 6535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 654420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller const float wedgeAngle = 2.0f * M_PI / slotCount; 655420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller const float endAngle = startAngle + visibleSlotCount * wedgeAngle; 656420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller bool stillAnimating = false; 657420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller for (int i = cardCount-1; i >= 0; i--) { 6588fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (cards[i].cardVisible) { 659420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // If this card was recently loaded, this will be < 1.0f until the animation completes 6603adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma float animatedAlpha = getAnimatedAlpha(cards[i].textureTimeStamp, currentTime, 6613adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma fadeInDuration); 6623adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma float overallAlpha = getAnimatedAlpha(cards[i].geometryTimeStamp, currentTime, 6633adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma cardCreationFadeDuration); 6643adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma if (animatedAlpha < 1.0f || overallAlpha < 1.0f) { 665420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller stillAnimating = true; 666420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } 667420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 668420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Compute fade out for cards in the distance 669420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float positionAlpha; 670420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller if (rezInCardCount > 0.0f) { 671420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller positionAlpha = (endAngle - cardPosition(i)) / wedgeAngle; 672420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller positionAlpha = min(1.0f, positionAlpha / rezInCardCount); 673420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } else { 674420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller positionAlpha = 1.0f; 675420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } 676420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 677420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Set alpha for blending between the textures 678420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller shaderConstants->fadeAmount = min(1.0f, animatedAlpha * positionAlpha); 6793adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma shaderConstants->overallAlpha = overallAlpha; 680420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsAllocationMarkDirty(rsGetAllocation(shaderConstants)); 681420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 682b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller // Bind the appropriate shader network. If there's no alpha blend, then 683b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller // switch to single shader for better performance. 6848441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey const int state = cards[i].textureState; 6858441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey const bool loaded = (state == STATE_LOADED) || (state == STATE_STALE) || 6868441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey (state == STATE_UPDATING); 687b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (shaderConstants->fadeAmount == 1.0f || shaderConstants->fadeAmount < 0.01f) { 6883adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma if (overallAlpha < 1.0) { 6893adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindProgramFragment(singleTextureBlendingFragmentProgram); 6903adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindTexture(singleTextureBlendingFragmentProgram, 0, 6913adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma (loaded && shaderConstants->fadeAmount == 1.0f) ? 6923adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma cards[i].texture : loadingTexture); 6933adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma } else { 6943adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindProgramFragment(singleTextureFragmentProgram); 6953adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindTexture(singleTextureFragmentProgram, 0, 6963adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma (loaded && shaderConstants->fadeAmount == 1.0f) ? 6973adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma cards[i].texture : loadingTexture); 6983adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma } 6995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 7003adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma if (overallAlpha < 1.0) { 7013adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindProgramFragment(multiTextureBlendingFragmentProgram); 7023adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindTexture(multiTextureBlendingFragmentProgram, 0, loadingTexture); 7033adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindTexture(multiTextureBlendingFragmentProgram, 1, loaded ? 7043adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma cards[i].texture : loadingTexture); 7053adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma } else { 7063adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindProgramFragment(multiTextureFragmentProgram); 7073adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindTexture(multiTextureFragmentProgram, 0, loadingTexture); 7083adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma rsgBindTexture(multiTextureFragmentProgram, 1, loaded ? 7093adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma cards[i].texture : loadingTexture); 7103adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma } 7115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Draw geometry 7145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_matrix4x4 matrix = modelviewMatrix; 715c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma stillAnimating |= getMatrixForCard(&matrix, i, true, true); 7165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadModelMatrix(&matrix); 7175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].geometryState == STATE_LOADED && cards[i].geometry.p != 0) { 718f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma drawMesh(cards[i].geometry); 7195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else if (cards[i].geometryState == STATE_LOADING && loadingGeometry.p != 0) { 720f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma drawMesh(loadingGeometry); 7215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else if (defaultGeometry.p != 0) { 722f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma drawMesh(defaultGeometry); 7235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 7245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Draw place-holder geometry 725f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsgBindProgramStore(programStoresCard[0].programStore); 7265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawQuad( 7275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[0].x, cardVertices[0].y, cardVertices[0].z, 7285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[1].x, cardVertices[1].y, cardVertices[1].z, 7295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[2].x, cardVertices[2].y, cardVertices[2].z, 7305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[3].x, cardVertices[3].y, cardVertices[3].z); 7315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 734420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller return stillAnimating; 7355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 7365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7374fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma/** 7384fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma * Convert projection from normalized coordinates to pixel coordinates. 7394fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma * 7404fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma * @return True on success, false on failure. 7414fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma */ 7424fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shumastatic bool convertNormalizedToPixelCoordinates(float4 *screenCoord, float width, float height) { 7434fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // This is probably cheaper than pre-multiplying with another matrix. 7444fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma if (screenCoord->w == 0.0f) { 7454fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma rsDebug("Bad transform while converting from normalized to pixel coordinates: ", 7464fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord); 7474fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma return false; 7484fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 7494fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma *screenCoord *= 1.0f / screenCoord->w; 7504fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->x += 1.0f; 7514fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->y += 1.0f; 7524fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->z += 1.0f; 7534fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->x = round(screenCoord->x * 0.5f * width); 7544fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->y = round(screenCoord->y * 0.5f * height); 7554fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->z = - 0.5f * screenCoord->z; 7564fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma return true; 7574fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma} 7584fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma 7597cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller/* 7607cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller * Draws a screen-aligned card with the exact dimensions from the detail texture. 7614a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * This is used to display information about the object being displayed. 762420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller * Returns true if we're still animating any property of the cards (e.g. fades). 7637cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller */ 764420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerstatic bool drawDetails(int64_t currentTime) 7657cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller{ 7667cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float width = rsgGetWidth(); 7677cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float height = rsgGetHeight(); 7687cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 769420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller bool stillAnimating = false; 770420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 7717cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // We'll be drawing in screen space, sampled on pixel centers 7727cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_matrix4x4 projection, model; 7737cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsMatrixLoadOrtho(&projection, 0.0f, width, 0.0f, height, 0.0f, 1.0f); 7747cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgProgramVertexLoadProjectionMatrix(&projection); 7757cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsMatrixLoadIdentity(&model); 7767cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgProgramVertexLoadModelMatrix(&model); 7777cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller updateCamera = true; // we messed with the projection matrix. Reload on next pass... 7787cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 7797cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float yPadding = 5.0f; // draw line this far (in pixels) away from top and geometry 7807cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 781420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // This can be done once... 782a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller rsgBindTexture(multiTextureFragmentProgram, 0, detailLoadingTexture); 783a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 784a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float wedgeAngle = 2.0f * M_PI / slotCount; 785a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller // Angle where details start fading from 1.0f 786a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float startDetailFadeAngle = startAngle + (visibleDetailCount - 1) * wedgeAngle; 787a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller // Angle where detail alpha is 0.0f 788a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float endDetailFadeAngle = startDetailFadeAngle + detailFadeRate * wedgeAngle; 789420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 790a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller for (int i = cardCount-1; i >= 0; --i) { 7918fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (cards[i].cardVisible) { 7926af401bca5f8854524d128e9df5700035fae1160Jim Shuma const int state = cards[i].detailTextureState; 7936af401bca5f8854524d128e9df5700035fae1160Jim Shuma const bool isLoaded = (state == STATE_LOADED) || (state == STATE_STALE) || 7946af401bca5f8854524d128e9df5700035fae1160Jim Shuma (state == STATE_UPDATING); 7956af401bca5f8854524d128e9df5700035fae1160Jim Shuma if (isLoaded && cards[i].detailTexture.p != 0) { 7967cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float lineWidth = rsAllocationGetDimX(detailLineTexture); 7977cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 7984fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // Compute position in screen space of top corner or bottom corner of card 799af8cf9a3bbe517b604b48e217b00085351ab2496Shih-wei Liao rsMatrixLoad(&model, &modelviewMatrix); 800c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma stillAnimating |= getMatrixForCard(&model, i, false, false); 8017cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_matrix4x4 matrix; 8027cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsMatrixLoadMultiply(&matrix, &projectionMatrix, &model); 803d443c88da4c7cf1947c12b26f111cb899cc8afe4Jim Miller 8044fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma int indexLeft, indexRight; 8054fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma float4 screenCoord; 8064a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & BELOW) { 8074fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexLeft = 0; 8084fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexRight = 1; 8094fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } else { 8104fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexLeft = 3; 8114fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexRight = 2; 8124fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 8134fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma float4 screenCoordLeft = rsMatrixMultiply(&matrix, cardVertices[indexLeft]); 8144fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma float4 screenCoordRight = rsMatrixMultiply(&matrix, cardVertices[indexRight]); 8154fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma if (screenCoordLeft.w == 0.0f || screenCoordRight.w == 0.0f) { 8167cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // this shouldn't happen 8177cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsDebug("Bad transform: ", screenCoord); 8187cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller continue; 8197cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 8204a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & CENTER_VERTICAL) { 8214a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney // If we're centering vertically, we'll need the other vertices too 8224a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & BELOW) { 8234a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney indexLeft = 3; 8244a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney indexRight = 2; 8254a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } else { 8264a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney indexLeft = 0; 8274a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney indexRight = 1; 8284a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } 8294a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney float4 otherScreenLeft = rsMatrixMultiply(&matrix, cardVertices[indexLeft]); 8304a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney float4 otherScreenRight = rsMatrixMultiply(&matrix, cardVertices[indexRight]); 8314a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney screenCoordRight.y = screenCoordLeft.y = (screenCoordLeft.y + screenCoordRight.y 8324a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney + otherScreenLeft.y + otherScreenRight.y) / 4.; 8334a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } 8344fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma (void) convertNormalizedToPixelCoordinates(&screenCoordLeft, width, height); 8354fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma (void) convertNormalizedToPixelCoordinates(&screenCoordRight, width, height); 8364fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma if (debugDetails) { 8374fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma RS_DEBUG(screenCoordLeft); 8384fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma RS_DEBUG(screenCoordRight); 8394fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 8404fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord = screenCoordLeft; 8414a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & BELOW) { 8424fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord.y = min(screenCoordLeft.y, screenCoordRight.y); 8434a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } else if (detailTextureAlignment & CENTER_VERTICAL) { 844fe38385c1e5ce443adb962c066adeea185ad3d74Bryan Mawhinney screenCoord.y -= round(rsAllocationGetDimY(cards[i].detailTexture) / 2.0f); 8454fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 8464a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & CENTER_HORIZONTAL) { 847fe38385c1e5ce443adb962c066adeea185ad3d74Bryan Mawhinney screenCoord.x += round((screenCoordRight.x - screenCoordLeft.x) / 2.0f - 848fe38385c1e5ce443adb962c066adeea185ad3d74Bryan Mawhinney rsAllocationGetDimX(cards[i].detailTexture) / 2.0f); 8494fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 8507cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 851420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Compute alpha for gradually fading in details. Applied to both line and 852420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // detail texture. TODO: use a separate background texture for line. 8533adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma float animatedAlpha = getAnimatedAlpha(cards[i].detailTextureTimeStamp, 8543adf712e636f67265da7a6ff425c87e63fc20884Jim Shuma currentTime, fadeInDuration); 855420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller if (animatedAlpha < 1.0f) { 856420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller stillAnimating = true; 857420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } 858420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 859a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller // Compute alpha based on position. We fade cards quickly so they cannot overlap 860a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller float positionAlpha = ((float)endDetailFadeAngle - cardPosition(i)) 861a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller / (endDetailFadeAngle - startDetailFadeAngle); 862a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller positionAlpha = max(0.0f, positionAlpha); 863a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller positionAlpha = min(1.0f, positionAlpha); 864a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 865a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float blendedAlpha = min(1.0f, animatedAlpha * positionAlpha); 866a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 8678fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (blendedAlpha == 0.0f) { 8688fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].detailVisible = false; 8698fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma continue; // nothing to draw 8708fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma } else { 8718fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].detailVisible = true; 8728fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma } 873b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (blendedAlpha == 1.0f) { 874b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindProgramFragment(singleTextureFragmentProgram); 875b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 876b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindProgramFragment(multiTextureFragmentProgram); 877b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 878a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 879420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Set alpha for blending between the textures 880a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller shaderConstants->fadeAmount = blendedAlpha; 881420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsAllocationMarkDirty(rsGetAllocation(shaderConstants)); 882420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 8834a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney // Draw line from the card to the detail texture. 8844a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney // The line is drawn from the top or bottom left of the card 8854a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney // to either the top of the screen or the top of the detail 8864a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney // texture, depending on detailTextureAlignment. 8877c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma if (drawRuler) { 8884a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney float rulerTop; 8894a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney float rulerBottom; 8904a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & BELOW) { 8914a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney rulerTop = screenCoord.y; 8924a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney rulerBottom = 0; 8934a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } else { 8944a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney rulerTop = height; 8954a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney rulerBottom = screenCoord.y; 8964a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } 8977c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma const float halfWidth = lineWidth * 0.5f; 8981eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller const float x0 = trunc(cards[i].detailLineOffset.x + screenCoord.x - halfWidth); 8991eccd028e704c15e842c1f23254d77a1a0a4cae0Jim Miller const float x1 = x0 + lineWidth; 900b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y0 = rulerBottom + yPadding; 901b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y1 = rulerTop - yPadding - cards[i].detailLineOffset.y; 902b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 903b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (blendedAlpha == 1.0f) { 904b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(singleTextureFragmentProgram, 0, detailLineTexture); 905b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 906b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(multiTextureFragmentProgram, 1, detailLineTexture); 907b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 908b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgDrawQuad(x0, y0, screenCoord.z, x1, y0, screenCoord.z, 909b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller x1, y1, screenCoord.z, x0, y1, screenCoord.z); 9107c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma } 9117cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 9127cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // Draw the detail texture next to it using the offsets provided. 9137cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float textureWidth = rsAllocationGetDimX(cards[i].detailTexture); 9147cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float textureHeight = rsAllocationGetDimY(cards[i].detailTexture); 9157cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float offx = cards[i].detailTextureOffset.x; 9167cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float offy = -cards[i].detailTextureOffset.y; 9174a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney const float textureTop = (detailTextureAlignment & VIEW_TOP) 9184a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney ? height : screenCoord.y; 919b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float x0 = cards[i].detailLineOffset.x + screenCoord.x + offx; 920b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float x1 = cards[i].detailLineOffset.x + screenCoord.x + offx + textureWidth; 921b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y0 = textureTop + offy - textureHeight - cards[i].detailLineOffset.y; 922b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y1 = textureTop + offy - cards[i].detailLineOffset.y; 9238fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].detailTexturePosition[0].x = x0; 9248fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].detailTexturePosition[0].y = height - y1; 9258fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].detailTexturePosition[1].x = x1; 9268fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].detailTexturePosition[1].y = height - y0; 927b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 928b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (blendedAlpha == 1.0f) { 929b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(singleTextureFragmentProgram, 0, cards[i].detailTexture); 930b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 931b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(multiTextureFragmentProgram, 1, cards[i].detailTexture); 932b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 933b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgDrawQuad(x0, y0, screenCoord.z, x1, y0, screenCoord.z, 934b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller x1, y1, screenCoord.z, x0, y1, screenCoord.z); 9357cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 9367cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 9377cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 938420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller return stillAnimating; 9397cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller} 9407cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 9419afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Millerstatic void drawBackground() 9429afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller{ 943b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller static bool toggle; 9449afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller if (backgroundTexture.p != 0) { 9459afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearDepth(1.0f); 9469afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rs_matrix4x4 projection, model; 9479afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsMatrixLoadOrtho(&projection, -1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f); 9489afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgProgramVertexLoadProjectionMatrix(&projection); 9499afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsMatrixLoadIdentity(&model); 9509afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgProgramVertexLoadModelMatrix(&model); 951a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller rsgBindTexture(singleTextureFragmentProgram, 0, backgroundTexture); 9529afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller float z = -0.9999f; 9539afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgDrawQuad( 9549afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[0].x, cardVertices[0].y, z, 9559afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[1].x, cardVertices[1].y, z, 9569afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[2].x, cardVertices[2].y, z, 9579afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[3].x, cardVertices[3].y, z); 9589afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller updateCamera = true; // we mucked with the matrix. 9599afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } else { 9609afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearDepth(1.0f); 961b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (debugRendering) { // for debugging - flash the screen so we know we're still rendering 962b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgClearColor(toggle ? backgroundColor.x : 1.0f, 963b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller toggle ? backgroundColor.y : 0.0f, 964b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller toggle ? backgroundColor.z : 0.0f, 965b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller backgroundColor.w); 9669afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller toggle = !toggle; 967b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 9687cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgClearColor(backgroundColor.x, backgroundColor.y, backgroundColor.z, 9697cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller backgroundColor.w); 9709afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } 9719afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } 9729afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller} 9739afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller 9745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void updateCameraMatrix(float width, float height) 9755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 9765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float aspect = width / height; 9775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (aspect != camera.aspect || updateCamera) { 9785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.aspect = aspect; 9795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller loadPerspectiveMatrix(&projectionMatrix, camera.fov, camera.aspect, camera.near, camera.far); 9805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadProjectionMatrix(&projectionMatrix); 9815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller loadLookatMatrix(&modelviewMatrix, camera.from, camera.at, camera.up); 9835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadModelMatrix(&modelviewMatrix); 9845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = false; 9855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 9865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 9875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 9895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Behavior/Physics 9905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 9915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int64_t lastTime = 0L; // keep track of how much time has passed between frames 992be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic float lastAngle = 0.0f; 9935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float2 lastPosition; 9945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool animating = false; 995b2c785780ecbe79a5b7ba558b21985f956458c8cJim Shumastatic float stopVelocity = 0.1f * M_PI / 180.0f; // slower than this: carousel stops 996b2c785780ecbe79a5b7ba558b21985f956458c8cJim Shumastatic float selectionVelocity = 15.0f * M_PI / 180.0f; // faster than this: tap won't select 9975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float velocityTracker; 9985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int velocityTrackerCount; 9995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float mass = 5.0f; // kg 10005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float G = 9.80f; // gravity constant, in m/s 10025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float springConstant = 0.0f; 10035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1004be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller// Computes a hit angle from the center of the carousel to a point on either a plane 1005be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller// or on a cylinder. If neither is hit, returns false. 1006be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Millerstatic bool hitAngle(float x, float y, float *angle) 1007be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller{ 1008be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller Ray ray; 1009be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller makeRayForPixelAt(&ray, &camera, x, y); 1010be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller float t = FLT_MAX; 1011be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller if (dragModel == DRAG_MODEL_PLANE && rayPlaneIntersect(&ray, &carouselPlane, &t)) { 1012be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller const float3 point = (ray.position + t*ray.direction); 1013be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller const float3 direction = point - carouselPlane.point; 1014be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller *angle = atan2(direction.x, direction.z); 1015be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller if (debugSelection) rsDebug("Plane Angle = ", degrees(*angle)); 1016be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller return true; 1017be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller } else if ((dragModel == DRAG_MODEL_CYLINDER_INSIDE || dragModel == DRAG_MODEL_CYLINDER_OUTSIDE) 1018be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller && rayCylinderIntersect(&ray, &carouselCylinder, &t)) { 1019be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller const float3 point = (ray.position + t*ray.direction); 1020be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller const float3 direction = point - carouselCylinder.center; 1021be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller *angle = atan2(direction.x, direction.z); 1022be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller if (debugSelection) rsDebug("Cylinder Angle = ", degrees(*angle)); 1023be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller return true; 1024be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller } 1025be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller return false; 1026be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller} 1027be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller 10285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float dragFunction(float x, float y) 10295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 1030be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller float result; 1031be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller float angle; 1032be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller if (hitAngle(x, y, &angle)) { 1033be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller result = angle - lastAngle; 1034be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller // Handle singularity where atan2 switches between +- PI 1035be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller if (result < -M_PI) { 1036be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller result += 2.0f * M_PI; 1037be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller } else if (result > M_PI) { 1038be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller result -= 2.0f * M_PI; 1039be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller } 1040be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller lastAngle = angle; 1041be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller } else { 1042be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller // If we didn't hit anything or drag model wasn't plane or cylinder, we use screen delta 1043be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller result = dragFactor * ((x - lastPosition.x) / rsgGetWidth()) * M_PI; 1044be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller } 1045be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller return result; 10465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 10475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float deltaTimeInSeconds(int64_t current) 10495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 10505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return (lastTime > 0L) ? (float) (current - lastTime) / 1000.0f : 0.0f; 10515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 10525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1053370b177eb74cd8a7d9a2ab06a5ee8bb3ed25f74fStephen Hinesstatic int doSelection(float x, float y) 10545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 10555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller Ray ray; 1056b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (makeRayForPixelAt(&ray, &camera, x, y)) { 10575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bestTime = FLT_MAX; 10585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return intersectGeometry(&ray, &bestTime); 10595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return -1; 10615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 10625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1063370b177eb74cd8a7d9a2ab06a5ee8bb3ed25f74fStephen Hinesstatic void sendAnimationStarted() { 106443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller rsSendToClient(CMD_ANIMATION_STARTED); 106543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller} 106643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 1067370b177eb74cd8a7d9a2ab06a5ee8bb3ed25f74fStephen Hinesstatic void sendAnimationFinished() { 1068a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich float data[1]; 1069a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich data[0] = radiansToCarouselRotationAngle(bias); 1070a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich rsSendToClient(CMD_ANIMATION_FINISHED, (int*) data, sizeof(data)); 1071a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich} 1072a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich 10731882cebdc1b7b0551189ca33fb7cb77ef10c988bJim Millervoid doStart(float x, float y, long eventTime) 10745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 107543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller touchPosition = lastPosition = (float2) { x, y }; 1076be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller lastAngle = hitAngle(x,y, &lastAngle) ? lastAngle : 0.0f; 1077b2c785780ecbe79a5b7ba558b21985f956458c8cJim Shuma enableSelection = fabs(velocity) < selectionVelocity; 10785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocity = 0.0f; 10795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTracker = 0.0f; 10805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTrackerCount = 0; 10811882cebdc1b7b0551189ca33fb7cb77ef10c988bJim Miller touchTime = lastTime = eventTime; 10825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller touchBias = bias; 108343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller isDragging = true; 1084e26fadd96cff3251d7ed391e10ab9c372ce5e825Bryan Mawhinney overscroll = false; 108543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller animatedSelection = doSelection(x, y); // used to provide visual feedback on touch 10865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 10875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10881882cebdc1b7b0551189ca33fb7cb77ef10c988bJim Millervoid doStop(float x, float y, long eventTime) 10895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 10908b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich updateAllocationVars(cards); 109143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 109243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (enableSelection) { 10938fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma int data[3]; 10948fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma int selection; 10958fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma float2 point; 10968fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma 10978fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if ((selection = intersectDetailTexture(x, y, &point)) != -1) { 10988fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (debugSelection) rsDebug("Selected detail texture on doStop():", selection); 10998fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma data[0] = selection; 11008fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma data[1] = point.x; 11018fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma data[2] = point.y; 11028fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma rsSendToClientBlocking(CMD_DETAIL_SELECTED, data, sizeof(data)); 11038fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma } 11048fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma else if ((selection = doSelection(x, y))!= -1) { 110543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (debugSelection) rsDebug("Selected item on doStop():", selection); 110643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller data[0] = selection; 110743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller rsSendToClientBlocking(CMD_CARD_SELECTED, data, sizeof(data)); 110843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 110943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller animating = false; 11105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 111143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // TODO: move velocity tracking to Java 11125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocity = velocityTrackerCount > 0 ? 11135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller (velocityTracker / velocityTrackerCount) : 0.0f; // avg velocity 1114b2c785780ecbe79a5b7ba558b21985f956458c8cJim Shuma if (fabs(velocity) > stopVelocity) { 11155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller animating = true; 11165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 111843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller enableSelection = false; 11191882cebdc1b7b0551189ca33fb7cb77ef10c988bJim Miller lastTime = eventTime; 112043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller isDragging = false; 11215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 11225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1123594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shumavoid doLongPress() 1124594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma{ 1125594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma int64_t currentTime = rsUptimeMillis(); 1126594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma updateAllocationVars(cards); 112743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Selection happens for most recent position detected in doMotion() 1128e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma if (enableSelection && animatedSelection != -1) { 1129e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma if (debugSelection) rsDebug("doLongPress(), selection = ", animatedSelection); 1130e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma int data[7]; 1131e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma data[0] = animatedSelection; 1132e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma data[1] = lastPosition.x; 1133e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma data[2] = lastPosition.y; 1134e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma data[3] = cards[animatedSelection].detailTexturePosition[0].x; 1135e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma data[4] = cards[animatedSelection].detailTexturePosition[0].y; 1136e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma data[5] = cards[animatedSelection].detailTexturePosition[1].x; 1137e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma data[6] = cards[animatedSelection].detailTexturePosition[1].y; 1138594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma rsSendToClientBlocking(CMD_CARD_LONGPRESS, data, sizeof(data)); 1139e8cab95c5f73ddf6843d82793decc3adb4692860Jim Shuma enableSelection = false; 1140594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma } 1141594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma lastTime = rsUptimeMillis(); 1142594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma} 1143594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma 11441882cebdc1b7b0551189ca33fb7cb77ef10c988bJim Millervoid doMotion(float x, float y, long eventTime) 11455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 114614d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma const float highBias = maximumBias(); 114714d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma const float lowBias = minimumBias(); 11485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float deltaOmega = dragFunction(x, y); 114943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (!enableSelection) { 115043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bias += deltaOmega; 115114d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma bias = clamp(bias, lowBias - wedgeAngle(OVERSCROLL_SLOTS), 115214d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma highBias + wedgeAngle(OVERSCROLL_SLOTS)); 115343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 115443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float2 delta = (float2) { x, y } - touchPosition; 115543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float distance = sqrt(dot(delta, delta)); 115643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bool inside = (distance < selectionRadius); 115743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller enableSelection &= inside; 115843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller lastPosition = (float2) { x, y }; 11591882cebdc1b7b0551189ca33fb7cb77ef10c988bJim Miller float dt = deltaTimeInSeconds(eventTime); 11605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (dt > 0.0f) { 11615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float v = deltaOmega / dt; 116243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller velocityTracker += v; 116343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller velocityTrackerCount++; 11645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 1165c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma velocity = velocityTrackerCount > 0 ? 1166c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma (velocityTracker / velocityTrackerCount) : 0.0f; // avg velocity 11671882cebdc1b7b0551189ca33fb7cb77ef10c988bJim Miller lastTime = eventTime; 11685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 11695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 11705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 11715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Hit detection using ray casting. 11725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 117343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic const float EPSILON = 1.0e-6f; 117443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic const float tmin = 0.0f; 11755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 11765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool 117743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim MillerrayTriangleIntersect(Ray* ray, float3 p0, float3 p1, float3 p2, float* tout) 11785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 11795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 e1 = p1 - p0; 11805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 e2 = p2 - p0; 11815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s1 = cross(ray->direction, e2); 11825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 11835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float div = dot(s1, e1); 11845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (div == 0.0f) return false; // ray is parallel to plane. 11855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 11865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 d = ray->position - p0; 11875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float invDiv = 1.0f / div; 11885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 11895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float u = dot(d, s1) * invDiv; 11905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (u < 0.0f || u > 1.0f) return false; 11915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 11925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s2 = cross(d, e1); 11935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float v = dot(ray->direction, s2) * invDiv; 11945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if ( v < 0.0f || (u+v) > 1.0f) return false; 11955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 11965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float t = dot(e2, s2) * invDiv; 11975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (t < tmin || t > *tout) 11985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return false; 11995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller *tout = t; 12005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return true; 12015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 12025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 120343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 120443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// Computes ray/plane intersection. Returns false if no intersection found. 120543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 1206b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool 120743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim MillerrayPlaneIntersect(Ray* ray, Plane* plane, float* tout) 1208b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller{ 120943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float denom = dot(ray->direction, plane->normal); 121043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (fabs(denom) > EPSILON) { 121143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float t = - (plane->constant + dot(ray->position, plane->normal)) / denom; 121243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (t > tmin && t < *tout) { 121343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller *tout = t; 121443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return true; 121543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 121643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 121743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return false; 1218b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller} 1219b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 122043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 122143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// Computes ray/cylindr intersection. There are 0, 1 or 2 hits. 122243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// Returns true and sets *tout to the closest point or 122343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// returns false if no intersection found. 122443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 1225b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool 122643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim MillerrayCylinderIntersect(Ray* ray, Cylinder* cylinder, float* tout) 1227b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller{ 122843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float A = ray->direction.x * ray->direction.x + ray->direction.z * ray->direction.z; 122943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (A < EPSILON) return false; // ray misses 123043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 123143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Compute quadratic equation coefficients 123243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float B = 2.0f * (ray->direction.x * ray->position.x 123343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller + ray->direction.z * ray->position.z); 123443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float C = ray->position.x * ray->position.x 123543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller + ray->position.z * ray->position.z 123643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller - cylinder->radius * cylinder->radius; 123743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float disc = B*B - 4*A*C; 123843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 123943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (disc < 0.0f) return false; // ray misses 124043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller disc = sqrt(disc); 124143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float denom = 2.0f * A; 124243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 124343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Nearest point 124443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float t1 = (-B - disc) / denom; 1245be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller if (dragModel == DRAG_MODEL_CYLINDER_OUTSIDE && t1 > tmin && t1 < *tout) { 124643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller *tout = t1; 124743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return true; 124843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 124943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 125043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Far point 125143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float t2 = (-B + disc) / denom; 1252be5482f170e191aa98a3c2ecefdeaf936b7df412Jim Miller if (dragModel == DRAG_MODEL_CYLINDER_INSIDE && t2 > tmin && t2 < *tout) { 125343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller *tout = t2; 125443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return true; 125543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 125643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return false; 1257b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller} 1258b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 1259b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller// Creates a ray for an Android pixel coordinate given a camera, ray and coordinates. 12605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Note that the Y coordinate is opposite of GL rendering coordinates. 1261b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 1262b378af500b36226635b6343b1d5009ee9af44fc1Jim MillermakeRayForPixelAt(Ray* ray, PerspectiveCamera* cam, float x, float y) 12635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 12645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (debugCamera) { 12655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("------ makeRay() -------", 0); 1266b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Camera.from:", cam->from); 1267b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Camera.at:", cam->at); 1268b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Camera.dir:", normalize(cam->at - cam->from)); 12695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 12705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 12715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Vector math. This has the potential to be much faster. 12725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO: pre-compute lowerLeftRay, du, dv to eliminate most of this math. 1273b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float u = x / rsgGetWidth(); 1274b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float v = 1.0f - (y / rsgGetHeight()); 1275b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float aspect = (float) rsgGetWidth() / rsgGetHeight(); 1276b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float tanfov2 = 2.0f * tan(radians(cam->fov / 2.0f)); 1277b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 dir = normalize(cam->at - cam->from); 1278b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 du = tanfov2 * normalize(cross(dir, cam->up)); 1279b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 dv = tanfov2 * normalize(cross(du, dir)); 1280b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller du *= aspect; 1281b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 lowerLeftRay = dir - (0.5f * du) - (0.5f * dv); 1282b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayPoint = cam->from; 1283b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayDir = normalize(lowerLeftRay + u*du + v*dv); 1284b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (debugCamera) { 1285b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Ray direction (vector math) = ", rayDir); 12865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 12875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1288b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->position = rayPoint; 1289b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->direction = rayDir; 1290b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller return true; 1291b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller} 1292b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 1293b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller// Creates a ray for an Android pixel coordinate given a model view and projection matrix. 1294b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller// Note that the Y coordinate is opposite of GL rendering coordinates. 1295b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 1296b378af500b36226635b6343b1d5009ee9af44fc1Jim MillermakeRayForPixelAt(Ray* ray, rs_matrix4x4* model, rs_matrix4x4* proj, float x, float y) 1297b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller{ 1298b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rs_matrix4x4 pm = *model; 1299b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsMatrixLoadMultiply(&pm, proj, model); 1300b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (!rsMatrixInverse(&pm)) { 1301b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("ERROR: SINGULAR PM MATRIX", 0); 1302b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller return false; 13035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 1304b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float width = rsgGetWidth(); 1305b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float height = rsgGetHeight(); 1306b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float winx = 2.0f * x / width - 1.0f; 1307b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float winy = 2.0f * y / height - 1.0f; 1308b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 1309b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float4 eye = { 0.0f, 0.0f, 0.0f, 1.0f }; 1310b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float4 at = { winx, winy, 1.0f, 1.0f }; 1311b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 1312b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller eye = rsMatrixMultiply(&pm, eye); 1313b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller eye *= 1.0f / eye.w; 13145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1315b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller at = rsMatrixMultiply(&pm, at); 1316b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller at *= 1.0f / at.w; 1317b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 1318b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayPoint = { eye.x, eye.y, eye.z }; 1319b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 atPoint = { at.x, at.y, at.z }; 1320b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayDir = normalize(atPoint - rayPoint); 1321b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (debugCamera) { 1322b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("winx: ", winx); 1323b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("winy: ", winy); 1324b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Ray position (transformed) = ", eye); 1325b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Ray direction (transformed) = ", rayDir); 1326b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 1327b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->position = rayPoint; 1328b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->direction = rayDir; 13295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return true; 13305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 13315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 13328fd40311898a9ec759a76f021642f43e617e38c4Jim Shumastatic int intersectDetailTexture(float x, float y, float2 *tapCoordinates) 13338fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma{ 13348fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma for (int id = 0; id < cardCount; id++) { 13358fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (cards[id].detailVisible) { 13368fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma const int x0 = cards[id].detailTexturePosition[0].x; 13378fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma const int y0 = cards[id].detailTexturePosition[0].y; 13388fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma const int x1 = cards[id].detailTexturePosition[1].x; 13398fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma const int y1 = cards[id].detailTexturePosition[1].y; 13408fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (x >= x0 && x <= x1 && y >= y0 && y <= y1) { 13418fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma float2 point = { x - x0, y - y0 }; 13428fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma *tapCoordinates = point; 13438fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma return id; 13448fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma } 13458fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma } 13468fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma } 13478fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma return -1; 13488fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma} 13498fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma 13505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int intersectGeometry(Ray* ray, float *bestTime) 13515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 13525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int hit = -1; 13535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int id = 0; id < cardCount; id++) { 13548fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (cards[id].cardVisible) { 13555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_matrix4x4 matrix; 13565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 p[4]; 13575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 13585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Transform card vertices to world space 13595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadIdentity(&matrix); 1360c2baf88a763ae0e3694c8a10c13f203db9aec363Jim Shuma getMatrixForCard(&matrix, id, true, true); 13615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int vertex = 0; vertex < 4; vertex++) { 13625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float4 tmp = rsMatrixMultiply(&matrix, cardVertices[vertex]); 13635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (tmp.w != 0.0f) { 13645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].x = tmp.x; 13655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].y = tmp.y; 13665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].z = tmp.z; 13675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex] *= 1.0f / tmp.w; 13685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 13695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Bad w coord: ", tmp); 13705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 13735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Intersect card geometry 13745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (rayTriangleIntersect(ray, p[0], p[1], p[2], bestTime) 13755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller || rayTriangleIntersect(ray, p[2], p[3], p[0], bestTime)) { 13765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller hit = id; 13775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return hit; 13815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 13825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 13835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// This method computes the position of all the cards by updating bias based on a 138443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// simple physics model. If the cards are still in motion, returns true. 138543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic bool doPhysics(float dt) 138643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller{ 138743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float minStepTime = 1.0f / 300.0f; // ~5 steps per frame 138843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const int N = (dt > minStepTime) ? (1 + round(dt / minStepTime)) : 1; 138943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller dt /= N; 139043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller for (int i = 0; i < N; i++) { 139143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Force friction - always opposes motion 139243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float Ff = -frictionCoeff * velocity; 139343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 139443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Restoring force to match cards with slots 139543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float theta = startAngle + bias; 139643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float dtheta = 2.0f * M_PI / slotCount; 139743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float position = theta / dtheta; 139843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float fraction = position - floor(position); // fractional position between slots 139943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float x; 140043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (fraction > 0.5f) { 140143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller x = - (1.0f - fraction); 140243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 140343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller x = fraction; 140443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 140543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float Fr = - springConstant * x; 140643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 140743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // compute velocity 140843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float momentum = mass * velocity + (Ff + Fr)*dt; 140943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller velocity = momentum / mass; 141043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bias += velocity * dt; 141143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 1412b2c785780ecbe79a5b7ba558b21985f956458c8cJim Shuma return fabs(velocity) > stopVelocity; 141343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller} 141443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 141543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic float easeOut(float x) 141643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller{ 141743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return x; 141843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller} 141943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 142043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// Computes the next value for bias using the current animation (physics or overscroll) 14215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool updateNextPosition(int64_t currentTime) 14225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 142343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller static const float biasMin = 1e-4f; // close enough if we're within this margin of result 14245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 142543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float dt = deltaTimeInSeconds(currentTime); 14265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 142743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (dt <= 0.0f) { 142843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (debugRendering) rsDebug("Time delta was <= 0", dt); 142943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return true; 14305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 14315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 143214d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma const float highBias = maximumBias(); 143314d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma const float lowBias = minimumBias(); 143443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bool stillAnimating = false; 143543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (overscroll) { 143614d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma if (bias > highBias) { 143714d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma bias -= 4.0f * dt * easeOut((bias - highBias) * 2.0f); 143814d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma if (fabs(bias - highBias) < biasMin) { 143914d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma bias = highBias; 144043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 144143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller stillAnimating = true; 144243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 144314d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma } else if (bias < lowBias) { 144414d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma bias += 4.0f * dt * easeOut((lowBias - bias) * 2.0f); 144514d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma if (fabs(bias - lowBias) < biasMin) { 144614d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma bias = lowBias; 144743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 144843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller stillAnimating = true; 144943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 145043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 145143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller overscroll = false; 145243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 145343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 145443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller stillAnimating = doPhysics(dt); 145514d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma overscroll = bias > highBias || bias < lowBias; 1456cfe41767a3596a65eef91b6f68286fd0f916a4c7Jim Miller if (overscroll) { 1457cfe41767a3596a65eef91b6f68286fd0f916a4c7Jim Miller velocity = 0.0f; // prevent bouncing due to v > 0 after overscroll animation. 1458cfe41767a3596a65eef91b6f68286fd0f916a4c7Jim Miller } 1459c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma } 146014d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma float newbias = clamp(bias, lowBias - wedgeAngle(OVERSCROLL_SLOTS), 146114d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma highBias + wedgeAngle(OVERSCROLL_SLOTS)); 146243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (newbias != bias) { // we clamped 146343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller velocity = 0.0f; 146443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller overscroll = true; 146543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 146643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bias = newbias; 146743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return stillAnimating; 14685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 14695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 14705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Cull cards based on visibility and visibleSlotCount. 14715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// If visibleSlotCount is > 0, then only show those slots and cull the rest. 14725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Otherwise, it should cull based on bounds of geometry. 14735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int cullCards() 14745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 14754fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // TODO(jshuma): Instead of fully fetching prefetchCardCount cards, make a distinction between 14764fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // STATE_LOADED and a new STATE_PRELOADING, which will keep the textures loaded but will not 14774fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // attempt to actually draw them. 14784fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma const int prefetchCardCountPerSide = prefetchCardCount / 2; 14794fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma const float thetaFirst = slotPosition(-prefetchCardCountPerSide); 1480198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaSelected = slotPosition(0); 1481198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaHalfAngle = (thetaSelected - thetaFirst) * 0.5f; 1482198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaSelectedLow = thetaSelected - thetaHalfAngle; 1483198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaSelectedHigh = thetaSelected + thetaHalfAngle; 14844fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma const float thetaLast = slotPosition(visibleSlotCount - 1 + prefetchCardCountPerSide); 14855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 14865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int count = 0; 14875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < cardCount; i++) { 14885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (visibleSlotCount > 0) { 14895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // If visibleSlotCount is specified, then only show up to visibleSlotCount cards. 14905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float p = cardPosition(i); 149114d2c1ec52bb04b5120c2bfdd1a8811a238573ceJim Shuma if (p >= thetaFirst && p < thetaLast || p <= thetaFirst && p > thetaLast) { 14928fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].cardVisible = true; 14938fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma // cards[i].detailVisible will be set at draw time 14945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller count++; 14955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 14968fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].cardVisible = false; 14978fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].detailVisible = false; 14985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 14995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 15005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Cull the rest of the cards using bounding box of geometry. 15015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO 15028fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma cards[i].cardVisible = true; 15038fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma // cards[i].detailVisible will be set at draw time 15045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller count++; 15055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return count; 15085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 15095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 15105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Request texture/geometry for items that have come into view 15115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// or doesn't have a texture yet. 1512420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerstatic void updateCardResources(int64_t currentTime) 15135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 1514a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller for (int i = cardCount-1; i >= 0; --i) { 15155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int data[1]; 15168fd40311898a9ec759a76f021642f43e617e38c4Jim Shuma if (cards[i].cardVisible) { 151743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (debugTextureLoading) rsDebug("*** Texture stamp: ", (int)cards[i].textureTimeStamp); 15182ba04e061b52c488a154739379501dc833e39f79Jim Miller 15195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // request texture from client if not loaded 15205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].textureState == STATE_INVALID) { 15215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 15225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data)); 15235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 15245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].textureState = STATE_LOADING; 15255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 15267cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_TEXTURE", 0); 15277cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 15288441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey } else if (cards[i].textureState == STATE_STALE) { 15298441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey data[0] = i; 15308441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data)); 15318441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey if (enqueued) { 15328441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey cards[i].textureState = STATE_UPDATING; 15338441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey } else { 15348441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_TEXTURE", 0); 15358441b1025afe64748f9e3483baacee92171bbfa3Jeff Sharkey } 15367cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 15377cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // request detail texture from client if not loaded 15387cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (cards[i].detailTextureState == STATE_INVALID) { 15397cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller data[0] = i; 15407cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller bool enqueued = rsSendToClient(CMD_REQUEST_DETAIL_TEXTURE, data, sizeof(data)); 15417cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (enqueued) { 15427cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[i].detailTextureState = STATE_LOADING; 15437cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } else { 15447cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_DETAIL_TEXTURE", 0); 15455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15466af401bca5f8854524d128e9df5700035fae1160Jim Shuma } else if (cards[i].detailTextureState == STATE_STALE) { 15476af401bca5f8854524d128e9df5700035fae1160Jim Shuma data[0] = i; 15486af401bca5f8854524d128e9df5700035fae1160Jim Shuma bool enqueued = rsSendToClient(CMD_REQUEST_DETAIL_TEXTURE, data, sizeof(data)); 15496af401bca5f8854524d128e9df5700035fae1160Jim Shuma if (enqueued) { 15506af401bca5f8854524d128e9df5700035fae1160Jim Shuma cards[i].detailTextureState = STATE_UPDATING; 15516af401bca5f8854524d128e9df5700035fae1160Jim Shuma } else { 15526af401bca5f8854524d128e9df5700035fae1160Jim Shuma if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_DETAIL_TEXTURE", 0); 15536af401bca5f8854524d128e9df5700035fae1160Jim Shuma } 15545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // request geometry from client if not loaded 15565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].geometryState == STATE_INVALID) { 15575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 15585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_REQUEST_GEOMETRY, data, sizeof(data)); 15595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 15605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].geometryState = STATE_LOADING; 15615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 15627cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugGeometryLoading) rsDebug("Couldn't send CMD_REQUEST_GEOMETRY", 0); 15635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 15665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // ask the host to remove the texture 1567dce9af330efceae2b8d1d7c25e7e236b4e21719bJack Palevich if (cards[i].textureState != STATE_INVALID) { 15685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 15695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_TEXTURE, data, sizeof(data)); 15705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 15715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].textureState = STATE_INVALID; 1572420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[i].textureTimeStamp = currentTime; 15735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 15747cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_INVALIDATE_TEXTURE", 0); 15757cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 15767cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 15777cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // ask the host to remove the detail texture 15787cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (cards[i].detailTextureState != STATE_INVALID) { 15797cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller data[0] = i; 15807cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_DETAIL_TEXTURE, data, sizeof(data)); 15817cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (enqueued) { 15827cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[i].detailTextureState = STATE_INVALID; 1583420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[i].detailTextureTimeStamp = currentTime; 15847cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } else { 15857cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Can't send CMD_INVALIDATE_DETAIL_TEXTURE", 0); 15865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // ask the host to remove the geometry 1589dce9af330efceae2b8d1d7c25e7e236b4e21719bJack Palevich if (cards[i].geometryState != STATE_INVALID) { 15905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 15915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_GEOMETRY, data, sizeof(data)); 15925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 15935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].geometryState = STATE_INVALID; 15945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 15957cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugGeometryLoading) rsDebug("Couldn't send CMD_INVALIDATE_GEOMETRY", 0); 15965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 15995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 16005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 16015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 16025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Places dots on geometry to visually inspect that objects can be seen by rays. 16035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// NOTE: the color of the dot is somewhat random, as it depends on texture of previously-rendered 16045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// card. 16055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void renderWithRays() 16065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 16075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float w = rsgGetWidth(); 16085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float h = rsgGetHeight(); 16095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const int skip = 8; 16105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller color(1.0f, 0.0f, 0.0f, 1.0f); 16115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int j = 0; j < (int) h; j+=skip) { 16125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float posY = (float) j; 16135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < (int) w; i+=skip) { 16145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float posX = (float) i; 16155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller Ray ray; 1616b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (makeRayForPixelAt(&ray, &camera, posX, posY)) { 16175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bestTime = FLT_MAX; 16185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (intersectGeometry(&ray, &bestTime) != -1) { 16195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawSpriteScreenspace(posX, h - posY - 1, 0.0f, 2.0f, 2.0f); 16205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 16215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 16225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 16235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 16245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 16255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 16265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint root() { 16275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t currentTime = rsUptimeMillis(); 16285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 16295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramVertex(vertexProgram); 16305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramRaster(rasterProgram); 1631b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindSampler(singleTextureFragmentProgram, 0, linearClamp); 1632b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindSampler(multiTextureFragmentProgram, 0, linearClamp); 1633b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindSampler(multiTextureFragmentProgram, 1, linearClamp); 16345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 16358b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich updateAllocationVars(cards); 16365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1637a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller rsgBindProgramFragment(singleTextureFragmentProgram); 1638fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller // rsgClearDepth() currently follows the value of glDepthMask(), so it's disabled when 1639fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller // the mask is disabled. We may want to change the following to always draw w/o Z for 1640fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller // the background if we can guarantee the depth buffer will get cleared and 1641fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller // there's a performance advantage. 1642f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsgBindProgramStore(programStoreBackground); 16439afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller drawBackground(); 16445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 16455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCameraMatrix(rsgGetWidth(), rsgGetHeight()); 16465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 164743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bool stillAnimating = (currentTime - touchTime) <= ANIMATION_SCALE_TIME; 164843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 164943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (!isDragging && animating) { 165043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller stillAnimating = updateNextPosition(currentTime); 165143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 165243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 165343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller lastTime = currentTime; 16545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 16555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cullCards(); 16565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1657420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller updateCardResources(currentTime); 16585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1659bfc5ce2da9e0d8d0ec2535c465624574d98418d7Jim Shuma // Draw cards opaque only if requested, and always draw detail textures with blending. 1660bf39450b962d91ec78af53db39826d55ddb39902Jim Shuma stillAnimating |= drawCards(currentTime); 1661f664659f79399e92025e1dfe1ffbb682ff05613cJim Shuma rsgBindProgramStore(programStoreDetail); 1662bf39450b962d91ec78af53db39826d55ddb39902Jim Shuma stillAnimating |= drawDetails(currentTime); 16635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 166443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (stillAnimating != animating) { 166543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (stillAnimating) { 166643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // we just started animating 166743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller sendAnimationStarted(); 166843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 166943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // we were animating but stopped animating just now 167043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller sendAnimationFinished(); 167143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 167243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller animating = stillAnimating; 167343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 167443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 167543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (debugRays) { 16765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller renderWithRays(); 16775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 16785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 16795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //rsSendToClient(CMD_PING); 16805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 168143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return animating ? 1 : 0; 16825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 1683