carousel.rs revision bf39450b962d91ec78af53db39826d55ddb39902
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 { 248b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich // *** Update copyCard 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 295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_mesh geometry; 307cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_matrix4x4 matrix; // custom transform for this card/geometry 317cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller int textureState; // whether or not the primary card texture is loaded. 327cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller int detailTextureState; // whether or not the detail for the card is loaded. 335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int geometryState; // whether or not geometry is loaded 345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int visible; // not bool because of packing bug? 35420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // TODO: Change when int64_t is supported. This will break after ~40 days of uptime. 36420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller unsigned int textureTimeStamp; // time when this texture was last updated, in seconds 37420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller unsigned int detailTextureTimeStamp; // time when this texture was last updated, in seconds 385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} Card_t; 395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millertypedef struct Ray_s { 415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 position; 425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 direction; 435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} Ray; 445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millertypedef struct PerspectiveCamera_s { 465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 from; 475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 at; 485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 up; 495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float fov; 505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float aspect; 515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float near; 525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float far; 535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} PerspectiveCamera; 545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 55420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millertypedef struct FragmentShaderConstants_s { 56420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float fadeAmount; 57420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller} FragmentShaderConstants; 58420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Request states. Used for loading 3D object properties from the Java client. 605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Typical properties: texture, geometry and matrices. 615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerenum { 625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_INVALID = 0, // item hasn't been loaded 635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_LOADING, // we've requested an item but are waiting for it to load 645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_LOADED // item was delivered 655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Client messages *** THIS LIST MUST MATCH THOSE IN CarouselRS.java. *** 685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_CARD_SELECTED = 100; 69594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shumastatic const int CMD_CARD_LONGPRESS = 110; 705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_REQUEST_TEXTURE = 200; 715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_INVALIDATE_TEXTURE = 210; 725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_REQUEST_GEOMETRY = 300; 735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_INVALIDATE_GEOMETRY = 310; 745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_ANIMATION_STARTED = 400; 755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_ANIMATION_FINISHED = 500; 767cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_REQUEST_DETAIL_TEXTURE = 600; 777cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_INVALIDATE_DETAIL_TEXTURE = 610; 787cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_REPORT_FIRST_CARD_POSITION = 700; 797cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_PING = 1000; 805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Constants 825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int ANIMATION_SCALE_TIME = 200; // Time it takes to animate selected card, in ms 835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float3 SELECTED_SCALE_FACTOR = { 0.2f, 0.2f, 0.2f }; // increase by this % 845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Debug flags 867cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugCamera = false; // dumps ray/camera coordinate stuff 877cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugPicking = false; // renders picking area on top of geometry 887cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugTextureLoading = false; // for debugging texture load/unload 897cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugGeometryLoading = false; // for debugging geometry load/unload 907c09ccce478100d75e4427d87866ff19d758ae7aJim Shumaconst bool debugDetails = false; // for debugging detail texture geometry 91b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerconst bool debugRendering = false; // flashes display when the frame changes 925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Exported variables. These will be reflected to Java set_* variables. 945ce730797a8a7278dfe19dac8a9460b25675fed0Jim MillerCard_t *cards; // array of cards to draw 958b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich// TODO: remove tmpCards code when allocations support resizing 968b55d7500c1e5a88c415dae8dcead16b152d7929Jack PalevichCard_t *tmpCards; // temporary array used to prevent flashing when we add more cards 975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat startAngle; // position of initial card, in radians 985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint slotCount; // number of positions where a card can be 995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint cardCount; // number of cards in stack 1005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint visibleSlotCount; // number of visible slots (for culling) 1017cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerint visibleDetailCount; // number of visible detail textures to show 1024fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shumaint prefetchCardCount; // how many cards to keep in memory 1037c09ccce478100d75e4427d87866ff19d758ae7aJim Shumabool drawDetailBelowCard; // whether detail goes above (false) or below (true) the card 1044fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma// TODO(jshuma): Replace detailTexturesCentered with a detailTextureAlignment mode enum 1054fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shumabool detailTexturesCentered; // line up detail center and card center (instead of left edges) 106bfc5ce2da9e0d8d0ec2535c465624574d98418d7Jim Shumabool drawCardsWithBlending; // Enable blending while drawing cards (for translucent card textures) 1077c09ccce478100d75e4427d87866ff19d758ae7aJim Shumabool drawRuler; // whether to draw a ruler from the card to the detail texture 1085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat radius; // carousel radius. Cards will be centered on a circle with this radius 1095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat cardRotation; // rotation of card in XY plane relative to Z=1 11083d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinneybool cardsFaceTangent; // whether cards are rotated to face along a tangent to the circle 111c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat swaySensitivity; // how much to rotate cards in relation to the rotation velocity 112c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat frictionCoeff; // how much to slow down the carousel over time 113c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat dragFactor; // a scale factor for how sensitive the carousel is to user dragging 114420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerint fadeInDuration; // amount of time (in ms) for smoothly switching out textures 115420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerfloat rezInCardCount; // this controls how rapidly distant card textures will be rez-ed in 116a9e9c4bef076e718094786edfe0290f798e1db4bJim Millerfloat detailFadeRate; // rate at which details fade as they move into the distance 1175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_store programStore; 118bfc5ce2da9e0d8d0ec2535c465624574d98418d7Jim Shumars_program_store programStoreOpaque; 119bf39450b962d91ec78af53db39826d55ddb39902Jim Shumars_program_store programStoreDetail; 120a9e9c4bef076e718094786edfe0290f798e1db4bJim Millerrs_program_fragment singleTextureFragmentProgram; 121a9e9c4bef076e718094786edfe0290f798e1db4bJim Millerrs_program_fragment multiTextureFragmentProgram; 1225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_vertex vertexProgram; 1235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_raster rasterProgram; 1245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_allocation defaultTexture; // shown when no other texture is assigned 1255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_allocation loadingTexture; // progress texture (shown when app is fetching the texture) 1269afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Millerrs_allocation backgroundTexture; // drawn behind everything, if set 1277cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerrs_allocation detailLineTexture; // used to draw detail line (as a quad, of course) 128420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerrs_allocation detailLoadingTexture; // used when detail texture is loading 1295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_mesh defaultGeometry; // shown when no geometry is loaded 1305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_mesh loadingGeometry; // shown when geometry is loading 1315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_matrix4x4 projectionMatrix; 1325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_matrix4x4 modelviewMatrix; 133420b44b8b11ec1c309ea130e69a6876325dbfef9Jim MillerFragmentShaderConstants* shaderConstants; 134420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerrs_sampler linearClamp; 1355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 136594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma#pragma rs export_func(createCards, copyCards, lookAt) 137594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma#pragma rs export_func(doStart, doStop, doMotion, doLongPress, doSelection) 1387cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller#pragma rs export_func(setTexture, setGeometry, setDetailTexture, debugCamera, debugPicking) 139198a060d650bc849ef0f25b597888fac9546803bJack Palevich#pragma rs export_func(requestFirstCardPosition) 1405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Local variables 1425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float bias; // rotation bias, in radians. Used for animation and dragging. 1435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool updateCamera; // force a recompute of projection and lookat matrices 1445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool initialized; 145b06b5d3798e2668b8b5660da625c13c743daf469Stephen Hinesfloat4 backgroundColor; 1465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float FLT_MAX = 1.0e37; 1475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int currentSelection = -1; 148198a060d650bc849ef0f25b597888fac9546803bJack Palevichstatic int currentFirstCard = -1; 1495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int64_t touchTime = -1; // time of first touch (see doStart()) 1505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float touchBias = 0.0f; // bias on first touch 151c0bb8af58ae15674178f2db240283719918c6f28Jim Shumastatic float velocity = 0.0f; // angular velocity in radians/s 1525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1533df59346f395434454d310b070fff195089fbaf1Jim Miller// Because allocations can't have 0 dimensions, we have to track whether or not 1543df59346f395434454d310b070fff195089fbaf1Jim Miller// cards are valid separately. 1553df59346f395434454d310b070fff195089fbaf1Jim Miller// TODO: Remove this dependency once allocations can have a zero dimension. 1563df59346f395434454d310b070fff195089fbaf1Jim Millerstatic bool cardAllocationValid = false; 1573df59346f395434454d310b070fff195089fbaf1Jim Miller 1585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Default geometry when card.geometry is not set. 1595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float3 cardVertices[4] = { 1605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { -1.0, -1.0, 0.0 }, 1615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { 1.0, -1.0, 0.0 }, 1625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { 1.0, 1.0, 0.0 }, 1635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {-1.0, 1.0, 0.0 } 1645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 1655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Default camera 1675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic PerspectiveCamera camera = { 1685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {2,2,2}, // from 1695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {0,0,0}, // at 1705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {0,1,0}, // up 1715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 25.0f, // field of view 1725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1.0f, // aspect 1735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 0.1f, // near 1745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 100.0f // far 1755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 1765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Forward references 1785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int intersectGeometry(Ray* ray, float *bestTime); 179b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 180b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller makeRayForPixelAt(Ray* ray, PerspectiveCamera* cam, float x, float y); 181b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 182b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller makeRayForPixelAt(Ray* ray, rs_matrix4x4* model, rs_matrix4x4* proj, float x, float y); 1835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float deltaTimeInSeconds(int64_t current); 1845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid init() { 1865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // initializers currently have a problem when the variables are exported, so initialize 1875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // globals here. 1882ba04e061b52c488a154739379501dc833e39f79Jim Miller if (debugTextureLoading) rsDebug("Renderscript: init()", 0); 1895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller startAngle = 0.0f; 1905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller slotCount = 10; 1915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller visibleSlotCount = 1; 1927cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller visibleDetailCount = 3; 1935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bias = 0.0f; 1945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller radius = 1.0f; 1955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardRotation = 0.0f; 19683d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney cardsFaceTangent = false; 1975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = true; 1985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller initialized = false; 199b0f070636c29ad178f4e21306f301fe3d20c183bJim Miller backgroundColor = (float4) { 0.0f, 0.0f, 0.0f, 1.0f }; 2003df59346f395434454d310b070fff195089fbaf1Jim Miller cardAllocationValid = false; 2013df59346f395434454d310b070fff195089fbaf1Jim Miller cardCount = 0; 202420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller fadeInDuration = 250; 203420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rezInCardCount = 0.0f; // alpha will ramp to 1.0f over this many cards (0.0f means disabled) 204a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller detailFadeRate = 0.5f; // fade details over this many slot positions. 2055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2078b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevichstatic void updateAllocationVars(Card_t* newcards) 2085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Cards 2108b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich rs_allocation cardAlloc = rsGetAllocation(newcards); 2115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO: use new rsIsObject() 2123df59346f395434454d310b070fff195089fbaf1Jim Miller cardCount = (cardAllocationValid && cardAlloc.p != 0) ? rsAllocationGetDimX(cardAlloc) : 0; 2135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid createCards(int n) 2165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2172ba04e061b52c488a154739379501dc833e39f79Jim Miller if (debugTextureLoading) { 2182ba04e061b52c488a154739379501dc833e39f79Jim Miller rsDebug("*** CreateCards with count", n); 2192ba04e061b52c488a154739379501dc833e39f79Jim Miller } 2202ba04e061b52c488a154739379501dc833e39f79Jim Miller 2212ba04e061b52c488a154739379501dc833e39f79Jim Miller // Since allocations can't have 0-size, we track validity ourselves based on the call to 2222ba04e061b52c488a154739379501dc833e39f79Jim Miller // this method. 2233df59346f395434454d310b070fff195089fbaf1Jim Miller cardAllocationValid = n > 0; 2242ba04e061b52c488a154739379501dc833e39f79Jim Miller 2255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller initialized = false; 2268b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich updateAllocationVars(cards); 2278b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich} 2288b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich 2298b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevichvoid copyCard(Card_t* dest, Card_t * src) 2308b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich{ 2318b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich rsSetObject(&dest->texture, src->texture); 2328b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich rsSetObject(&dest->detailTexture, src->detailTexture); 2338b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->detailTextureOffset = src->detailTextureOffset; 234b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller dest->detailLineOffset = src->detailLineOffset; 2358b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich rsSetObject(&dest->geometry, src->geometry); 2368b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->matrix = src->matrix; 2378b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->textureState = src->textureState; 2388b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->detailTextureState = src->detailTextureState; 2398b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->geometryState = src->geometryState; 2408b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->visible = src->visible; 2418b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->textureTimeStamp = src->textureTimeStamp; 2428b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->detailTextureTimeStamp = src->detailTextureTimeStamp; 2432ba04e061b52c488a154739379501dc833e39f79Jim Miller} 2448b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich 2452ba04e061b52c488a154739379501dc833e39f79Jim Millervoid initCard(Card_t* card) 2468b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich{ 2472ba04e061b52c488a154739379501dc833e39f79Jim Miller static const float2 zero = {0.0f, 0.0f}; 2482ba04e061b52c488a154739379501dc833e39f79Jim Miller rsClearObject(&card->texture); 2492ba04e061b52c488a154739379501dc833e39f79Jim Miller rsClearObject(&card->detailTexture); 2502ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailTextureOffset = zero; 2512ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailLineOffset = zero; 2522ba04e061b52c488a154739379501dc833e39f79Jim Miller rsClearObject(&card->geometry); 2532ba04e061b52c488a154739379501dc833e39f79Jim Miller rsMatrixLoadIdentity(&card->matrix); 2542ba04e061b52c488a154739379501dc833e39f79Jim Miller card->textureState = STATE_INVALID; 2552ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailTextureState = STATE_INVALID; 2562ba04e061b52c488a154739379501dc833e39f79Jim Miller card->geometryState = STATE_INVALID; 2572ba04e061b52c488a154739379501dc833e39f79Jim Miller card->visible = false; 2582ba04e061b52c488a154739379501dc833e39f79Jim Miller card->textureTimeStamp = 0; 2592ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailTextureTimeStamp = 0; 2602ba04e061b52c488a154739379501dc833e39f79Jim Miller} 2612ba04e061b52c488a154739379501dc833e39f79Jim Miller 2622ba04e061b52c488a154739379501dc833e39f79Jim Millervoid copyCards(int n) 2632ba04e061b52c488a154739379501dc833e39f79Jim Miller{ 2642ba04e061b52c488a154739379501dc833e39f79Jim Miller unsigned int oldsize = cardAllocationValid ? rsAllocationGetDimX(rsGetAllocation(cards)) : 0; 2658b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich unsigned int newsize = rsAllocationGetDimX(rsGetAllocation(tmpCards)); 2662ba04e061b52c488a154739379501dc833e39f79Jim Miller unsigned int copysize = min(oldsize, newsize); 2672ba04e061b52c488a154739379501dc833e39f79Jim Miller 2682ba04e061b52c488a154739379501dc833e39f79Jim Miller // Copy existing cards 2692ba04e061b52c488a154739379501dc833e39f79Jim Miller for (int i = 0; i < copysize; i++) { 2702ba04e061b52c488a154739379501dc833e39f79Jim Miller if (debugTextureLoading) { 2718b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich rsDebug("copying card ", i); 2728b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich } 2732ba04e061b52c488a154739379501dc833e39f79Jim Miller copyCard(tmpCards + i, cards + i); 2742ba04e061b52c488a154739379501dc833e39f79Jim Miller // Release these now so we don't have to wait for GC for cards allocation. 2752ba04e061b52c488a154739379501dc833e39f79Jim Miller // Assumes we're done with the cards allocation structure. 2762ba04e061b52c488a154739379501dc833e39f79Jim Miller rsClearObject(&cards[i].texture); 2772ba04e061b52c488a154739379501dc833e39f79Jim Miller rsClearObject(&cards[i].detailTexture); 2782ba04e061b52c488a154739379501dc833e39f79Jim Miller rsClearObject(&cards[i].geometry); 2792ba04e061b52c488a154739379501dc833e39f79Jim Miller cards[i].textureState = STATE_INVALID; 2802ba04e061b52c488a154739379501dc833e39f79Jim Miller cards[i].detailTextureState = STATE_INVALID; 2812ba04e061b52c488a154739379501dc833e39f79Jim Miller cards[i].geometryState = STATE_INVALID; 2828b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich } 2832ba04e061b52c488a154739379501dc833e39f79Jim Miller 2842ba04e061b52c488a154739379501dc833e39f79Jim Miller // Initialize remaining cards. 2852ba04e061b52c488a154739379501dc833e39f79Jim Miller int first = cardAllocationValid ? min(oldsize, newsize) : 0; 2862ba04e061b52c488a154739379501dc833e39f79Jim Miller for (int k = first; k < newsize; k++) { 2872ba04e061b52c488a154739379501dc833e39f79Jim Miller initCard(tmpCards + k); 2882ba04e061b52c488a154739379501dc833e39f79Jim Miller } 2892ba04e061b52c488a154739379501dc833e39f79Jim Miller 2902ba04e061b52c488a154739379501dc833e39f79Jim Miller // Since allocations can't have 0-size, we use the same trick as createCards() where 2912ba04e061b52c488a154739379501dc833e39f79Jim Miller // we track validity ourselves. Grrr. 2922ba04e061b52c488a154739379501dc833e39f79Jim Miller cardAllocationValid = n > 0; 2932ba04e061b52c488a154739379501dc833e39f79Jim Miller 2948b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich updateAllocationVars(tmpCards); 2955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 297420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller// Computes an alpha value for a card using elapsed time and constant fadeInDuration 298420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerfloat getAnimatedAlpha(int64_t startTime, int64_t currentTime) 299420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller{ 300420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller double timeElapsed = (double) (currentTime - startTime); // in ms 301420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller double alpha = (double) timeElapsed / fadeInDuration; 302420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller return min(1.0f, (float) alpha); 303420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller} 304420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 3055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Return angle for position p. Typically p will be an integer position, but can be fractional. 3065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float cardPosition(float p) 3075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return startAngle + bias + 2.0f * M_PI * p / slotCount; 3095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Return slot for a card in position p. Typically p will be an integer slot, but can be fractional. 3125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float slotPosition(float p) 3135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return startAngle + 2.0f * M_PI * p / slotCount; 3155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 317f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller// Returns total angle for given number of cards 318f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Millerstatic float wedgeAngle(float cards) 319f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller{ 320f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller return cards * 2.0f * M_PI / slotCount; 321f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller} 322f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller 3235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Return the lowest slot number for a given angular position. 3245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int cardIndex(float angle) 3255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return floor(angle - startAngle - bias) * slotCount / (2.0f * M_PI); 3275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Set basic camera properties: 3305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// from - position of the camera in x,y,z 3315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// at - target we're looking at - used to compute view direction 3325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// up - a normalized vector indicating up (typically { 0, 1, 0}) 3335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// 3345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// NOTE: the view direction and up vector cannot be parallel/antiparallel with each other 3355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid lookAt(float fromX, float fromY, float fromZ, 3365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float atX, float atY, float atZ, 3375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float upX, float upY, float upZ) 3385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.x = fromX; 3405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.y = fromY; 3415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.z = fromZ; 3425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.x = atX; 3435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.y = atY; 3445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.z = atZ; 3455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.x = upX; 3465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.y = upY; 3475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.z = upZ; 3485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = true; 3495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Load a projection matrix for the given parameters. This is equivalent to gluPerspective() 3525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void loadPerspectiveMatrix(rs_matrix4x4* matrix, float fovy, float aspect, float near, float far) 3535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadIdentity(matrix); 3555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float top = near * tan((float) (fovy * M_PI / 360.0f)); 3565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bottom = -top; 3575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float left = bottom * aspect; 3585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float right = top * aspect; 3595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadFrustum(matrix, left, right, bottom, top, near, far); 3605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Construct a matrix based on eye point, center and up direction. Based on the 3635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// man page for gluLookat(). Up must be normalized. 3645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void loadLookatMatrix(rs_matrix4x4* matrix, float3 eye, float3 center, float3 up) 3655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 f = normalize(center - eye); 3675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s = normalize(cross(f, up)); 3685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 u = cross(s, f); 3695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float m[16]; 3705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[0] = s.x; 3715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[4] = s.y; 3725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[8] = s.z; 3735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[12] = 0.0f; 3745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[1] = u.x; 3755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[5] = u.y; 3765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[9] = u.z; 3775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[13] = 0.0f; 3785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[2] = -f.x; 3795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[6] = -f.y; 3805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[10] = -f.z; 3815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[14] = 0.0f; 3825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[3] = m[7] = m[11] = 0.0f; 3835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[15] = 1.0f; 3845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoad(matrix, m); 3855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixTranslate(matrix, -eye.x, -eye.y, -eye.z); 3865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid setTexture(int n, rs_allocation texture) 3895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3903df59346f395434454d310b070fff195089fbaf1Jim Miller if (n < 0 || n >= cardCount) return; 391c4c6f38bf410af40e10c63b152befd5a39df87c8Jim Miller rsSetObject(&cards[n].texture, texture); 3927cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].textureState = (texture.p != 0) ? STATE_LOADED : STATE_INVALID; 393420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[n].textureTimeStamp = rsUptimeMillis(); 3947cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller} 3957cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 396b378af500b36226635b6343b1d5009ee9af44fc1Jim Millervoid setDetailTexture(int n, float offx, float offy, float loffx, float loffy, rs_allocation texture) 3977cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller{ 3983df59346f395434454d310b070fff195089fbaf1Jim Miller if (n < 0 || n >= cardCount) return; 399c4c6f38bf410af40e10c63b152befd5a39df87c8Jim Miller rsSetObject(&cards[n].detailTexture, texture); 4007cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].detailTextureOffset.x = offx; 4017cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].detailTextureOffset.y = offy; 402b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller cards[n].detailLineOffset.x = loffx; 403b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller cards[n].detailLineOffset.y = loffy; 4047cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].detailTextureState = (texture.p != 0) ? STATE_LOADED : STATE_INVALID; 405420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[n].detailTextureTimeStamp = rsUptimeMillis(); 4065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid setGeometry(int n, rs_mesh geometry) 4095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4103df59346f395434454d310b070fff195089fbaf1Jim Miller if (n < 0 || n >= cardCount) return; 411c4c6f38bf410af40e10c63b152befd5a39df87c8Jim Miller rsSetObject(&cards[n].geometry, geometry); 4125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[n].geometry.p != 0) 4135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].geometryState = STATE_LOADED; 4145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller else 4155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].geometryState = STATE_INVALID; 4165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 418198a060d650bc849ef0f25b597888fac9546803bJack Palevichvoid requestFirstCardPosition() 419198a060d650bc849ef0f25b597888fac9546803bJack Palevich{ 420198a060d650bc849ef0f25b597888fac9546803bJack Palevich int data[1]; 421198a060d650bc849ef0f25b597888fac9546803bJack Palevich data[0] = currentFirstCard; 422198a060d650bc849ef0f25b597888fac9546803bJack Palevich bool enqueued = rsSendToClient(CMD_REPORT_FIRST_CARD_POSITION, data, sizeof(data)); 423198a060d650bc849ef0f25b597888fac9546803bJack Palevich if (!enqueued) { 424198a060d650bc849ef0f25b597888fac9546803bJack Palevich rsDebug("Couldn't send CMD_REPORT_FIRST_CARD_POSITION", 0); 425198a060d650bc849ef0f25b597888fac9546803bJack Palevich } 426198a060d650bc849ef0f25b597888fac9546803bJack Palevich} 427198a060d650bc849ef0f25b597888fac9546803bJack Palevich 4285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float3 getAnimatedScaleForSelected() 4295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t dt = (rsUptimeMillis() - touchTime); 4315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float fraction = (dt < ANIMATION_SCALE_TIME) ? (float) dt / ANIMATION_SCALE_TIME : 1.0f; 4325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float3 one = { 1.0f, 1.0f, 1.0f }; 4335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return one + fraction * SELECTED_SCALE_FACTOR; 4345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 436c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// The Verhulst logistic function: http://en.wikipedia.org/wiki/Logistic_function 437c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// P(t) = 1 / (1 + e^(-t)) 438c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// Parameter t: Any real number 439c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// Returns: A float in the range (0,1), with P(0.5)=0 440c0bb8af58ae15674178f2db240283719918c6f28Jim Shumastatic float logistic(float t) { 441af8cf9a3bbe517b604b48e217b00085351ab2496Shih-wei Liao return 1.f / (1.f + exp(-t)); 442c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma} 443c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 4447c09ccce478100d75e4427d87866ff19d758ae7aJim Shumastatic float getSwayAngleForVelocity(float v, bool enableSway) 445c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma{ 4467c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma float sway = 0.0f; 4477c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma 4487c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma if (enableSway) { 449d7fa647e6fa4e832381be5bdd03065f9ea35c3f1Jim Shuma const float range = M_PI * 2./3.; // How far we can deviate from center, peak-to-peak 450d7fa647e6fa4e832381be5bdd03065f9ea35c3f1Jim Shuma sway = range * (logistic(-v * swaySensitivity) - 0.5f); 4517c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma } 452c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 453c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma return sway; 454c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma} 455c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 4567c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma// matrix: The output matrix. 4577c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma// i: The card we're getting the matrix for. 4587c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma// enableSway: Whether to enable swaying. (We want it on for cards, and off for detail textures.) 4597c09ccce478100d75e4427d87866ff19d758ae7aJim Shumastatic void getMatrixForCard(rs_matrix4x4* matrix, int i, bool enableSway) 4605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float theta = cardPosition(i); 4627c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma float swayAngle = getSwayAngleForVelocity(velocity, enableSway); 4635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixRotate(matrix, degrees(theta), 0, 1, 0); 4645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixTranslate(matrix, radius, 0, 0); 46583d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney float rotation = cardRotation + swayAngle; 46683d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney if (!cardsFaceTangent) { 46783d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney rotation -= theta; 46883d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney } 46983d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney rsMatrixRotate(matrix, degrees(rotation), 0, 1, 0); 4705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (i == currentSelection) { 4715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 scale = getAnimatedScaleForSelected(); 4725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixScale(matrix, scale.x, scale.y, scale.z); 4735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 4745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO: apply custom matrix for cards[i].geometry 4755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 477420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller/* 478420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller * Draws cards around the Carousel. 479420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller * Returns true if we're still animating any property of the cards (e.g. fades). 480420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller */ 481420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerstatic bool drawCards(int64_t currentTime) 4825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 483420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller const float wedgeAngle = 2.0f * M_PI / slotCount; 484420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller const float endAngle = startAngle + visibleSlotCount * wedgeAngle; 485420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller bool stillAnimating = false; 486420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller for (int i = cardCount-1; i >= 0; i--) { 4875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].visible) { 488420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // If this card was recently loaded, this will be < 1.0f until the animation completes 489420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float animatedAlpha = getAnimatedAlpha(cards[i].textureTimeStamp, currentTime); 490420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller if (animatedAlpha < 1.0f) { 491420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller stillAnimating = true; 492420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } 493420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 494420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Compute fade out for cards in the distance 495420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float positionAlpha; 496420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller if (rezInCardCount > 0.0f) { 497420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller positionAlpha = (endAngle - cardPosition(i)) / wedgeAngle; 498420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller positionAlpha = min(1.0f, positionAlpha / rezInCardCount); 499420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } else { 500420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller positionAlpha = 1.0f; 501420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } 502420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 503420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Set alpha for blending between the textures 504420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller shaderConstants->fadeAmount = min(1.0f, animatedAlpha * positionAlpha); 505420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsAllocationMarkDirty(rsGetAllocation(shaderConstants)); 506420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 507b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller // Bind the appropriate shader network. If there's no alpha blend, then 508b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller // switch to single shader for better performance. 509b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const bool loaded = cards[i].textureState == STATE_LOADED; 510b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (shaderConstants->fadeAmount == 1.0f || shaderConstants->fadeAmount < 0.01f) { 511b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindProgramFragment(singleTextureFragmentProgram); 512b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(singleTextureFragmentProgram, 0, 513b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller (loaded && shaderConstants->fadeAmount == 1.0f) ? 514b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller cards[i].texture : loadingTexture); 5155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 516b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindProgramFragment(multiTextureFragmentProgram); 517b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(multiTextureFragmentProgram, 0, loadingTexture); 518b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(multiTextureFragmentProgram, 1, loaded ? 519b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller cards[i].texture : loadingTexture); 5205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 5215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Draw geometry 5235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_matrix4x4 matrix = modelviewMatrix; 5247c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma getMatrixForCard(&matrix, i, true); 5255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadModelMatrix(&matrix); 5265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].geometryState == STATE_LOADED && cards[i].geometry.p != 0) { 5275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawMesh(cards[i].geometry); 5285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else if (cards[i].geometryState == STATE_LOADING && loadingGeometry.p != 0) { 5295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawMesh(loadingGeometry); 5305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else if (defaultGeometry.p != 0) { 5315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawMesh(defaultGeometry); 5325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 5335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Draw place-holder geometry 5345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawQuad( 5355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[0].x, cardVertices[0].y, cardVertices[0].z, 5365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[1].x, cardVertices[1].y, cardVertices[1].z, 5375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[2].x, cardVertices[2].y, cardVertices[2].z, 5385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[3].x, cardVertices[3].y, cardVertices[3].z); 5395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 5405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 5415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 542420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller return stillAnimating; 5435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 5445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5454fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma/** 5464fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma * Convert projection from normalized coordinates to pixel coordinates. 5474fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma * 5484fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma * @return True on success, false on failure. 5494fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma */ 5504fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shumastatic bool convertNormalizedToPixelCoordinates(float4 *screenCoord, float width, float height) { 5514fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // This is probably cheaper than pre-multiplying with another matrix. 5524fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma if (screenCoord->w == 0.0f) { 5534fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma rsDebug("Bad transform while converting from normalized to pixel coordinates: ", 5544fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord); 5554fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma return false; 5564fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 5574fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma *screenCoord *= 1.0f / screenCoord->w; 5584fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->x += 1.0f; 5594fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->y += 1.0f; 5604fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->z += 1.0f; 5614fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->x = round(screenCoord->x * 0.5f * width); 5624fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->y = round(screenCoord->y * 0.5f * height); 5634fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->z = - 0.5f * screenCoord->z; 5644fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma return true; 5654fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma} 5664fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma 5677cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller/* 5687cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller * Draws a screen-aligned card with the exact dimensions from the detail texture. 5697cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller * This is used to display information about the object being displayed above the geomertry. 570420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller * Returns true if we're still animating any property of the cards (e.g. fades). 5717cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller */ 572420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerstatic bool drawDetails(int64_t currentTime) 5737cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller{ 5747cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float width = rsgGetWidth(); 5757cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float height = rsgGetHeight(); 5767cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 577420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller bool stillAnimating = false; 578420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 5797cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // We'll be drawing in screen space, sampled on pixel centers 5807cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_matrix4x4 projection, model; 5817cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsMatrixLoadOrtho(&projection, 0.0f, width, 0.0f, height, 0.0f, 1.0f); 5827cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgProgramVertexLoadProjectionMatrix(&projection); 5837cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsMatrixLoadIdentity(&model); 5847cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgProgramVertexLoadModelMatrix(&model); 5857cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller updateCamera = true; // we messed with the projection matrix. Reload on next pass... 5867cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 5877cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float yPadding = 5.0f; // draw line this far (in pixels) away from top and geometry 5887cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 589420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // This can be done once... 590a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller rsgBindTexture(multiTextureFragmentProgram, 0, detailLoadingTexture); 591a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 592a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float wedgeAngle = 2.0f * M_PI / slotCount; 593a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller // Angle where details start fading from 1.0f 594a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float startDetailFadeAngle = startAngle + (visibleDetailCount - 1) * wedgeAngle; 595a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller // Angle where detail alpha is 0.0f 596a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float endDetailFadeAngle = startDetailFadeAngle + detailFadeRate * wedgeAngle; 597420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 598a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller for (int i = cardCount-1; i >= 0; --i) { 5997cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (cards[i].visible) { 6007cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (cards[i].detailTextureState == STATE_LOADED && cards[i].detailTexture.p != 0) { 6017cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float lineWidth = rsAllocationGetDimX(detailLineTexture); 6027cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 6034fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // Compute position in screen space of top corner or bottom corner of card 604af8cf9a3bbe517b604b48e217b00085351ab2496Shih-wei Liao rsMatrixLoad(&model, &modelviewMatrix); 6057c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma getMatrixForCard(&model, i, false); 6067cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_matrix4x4 matrix; 6077cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsMatrixLoadMultiply(&matrix, &projectionMatrix, &model); 608d443c88da4c7cf1947c12b26f111cb899cc8afe4Jim Miller 6094fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma int indexLeft, indexRight; 6104fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma float4 screenCoord; 6114fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma if (drawDetailBelowCard) { 6124fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexLeft = 0; 6134fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexRight = 1; 6144fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } else { 6154fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexLeft = 3; 6164fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexRight = 2; 6174fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 6184fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma float4 screenCoordLeft = rsMatrixMultiply(&matrix, cardVertices[indexLeft]); 6194fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma float4 screenCoordRight = rsMatrixMultiply(&matrix, cardVertices[indexRight]); 6204fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma if (screenCoordLeft.w == 0.0f || screenCoordRight.w == 0.0f) { 6217cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // this shouldn't happen 6227cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsDebug("Bad transform: ", screenCoord); 6237cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller continue; 6247cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 6254fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma (void) convertNormalizedToPixelCoordinates(&screenCoordLeft, width, height); 6264fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma (void) convertNormalizedToPixelCoordinates(&screenCoordRight, width, height); 6274fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma if (debugDetails) { 6284fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma RS_DEBUG(screenCoordLeft); 6294fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma RS_DEBUG(screenCoordRight); 6304fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 6314fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord = screenCoordLeft; 6324fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma if (drawDetailBelowCard) { 6334fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord.y = min(screenCoordLeft.y, screenCoordRight.y); 6344fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 6354fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma if (detailTexturesCentered) { 6364fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord.x += (screenCoordRight.x - screenCoordLeft.x) / 2. - 6374fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma rsAllocationGetDimX(cards[i].detailTexture) / 2.; 6384fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 6397cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 640420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Compute alpha for gradually fading in details. Applied to both line and 641420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // detail texture. TODO: use a separate background texture for line. 642420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float animatedAlpha = getAnimatedAlpha(cards[i].detailTextureTimeStamp, currentTime); 643420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller if (animatedAlpha < 1.0f) { 644420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller stillAnimating = true; 645420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } 646420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 647a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller // Compute alpha based on position. We fade cards quickly so they cannot overlap 648a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller float positionAlpha = ((float)endDetailFadeAngle - cardPosition(i)) 649a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller / (endDetailFadeAngle - startDetailFadeAngle); 650a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller positionAlpha = max(0.0f, positionAlpha); 651a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller positionAlpha = min(1.0f, positionAlpha); 652a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 653a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float blendedAlpha = min(1.0f, animatedAlpha * positionAlpha); 654a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 655b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (blendedAlpha == 0.0f) continue; // nothing to draw 656b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (blendedAlpha == 1.0f) { 657b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindProgramFragment(singleTextureFragmentProgram); 658b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 659b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindProgramFragment(multiTextureFragmentProgram); 660b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 661a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 662420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Set alpha for blending between the textures 663a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller shaderConstants->fadeAmount = blendedAlpha; 664420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsAllocationMarkDirty(rsGetAllocation(shaderConstants)); 665420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 6667cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // Draw line from upper left card corner to the top of the screen 6677c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma if (drawRuler) { 6687c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma const float halfWidth = lineWidth * 0.5f; 6697c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma const float rulerTop = drawDetailBelowCard ? screenCoord.y : height; 6707c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma const float rulerBottom = drawDetailBelowCard ? 0 : screenCoord.y; 671b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float x0 = cards[i].detailLineOffset.x + screenCoord.x - halfWidth; 672b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float x1 = cards[i].detailLineOffset.x + screenCoord.x + halfWidth; 673b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y0 = rulerBottom + yPadding; 674b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y1 = rulerTop - yPadding - cards[i].detailLineOffset.y; 675b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 676b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (blendedAlpha == 1.0f) { 677b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(singleTextureFragmentProgram, 0, detailLineTexture); 678b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 679b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(multiTextureFragmentProgram, 1, detailLineTexture); 680b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 681b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgDrawQuad(x0, y0, screenCoord.z, x1, y0, screenCoord.z, 682b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller x1, y1, screenCoord.z, x0, y1, screenCoord.z); 6837c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma } 6847cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 6857cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // Draw the detail texture next to it using the offsets provided. 6867cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float textureWidth = rsAllocationGetDimX(cards[i].detailTexture); 6877cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float textureHeight = rsAllocationGetDimY(cards[i].detailTexture); 6887cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float offx = cards[i].detailTextureOffset.x; 6897cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float offy = -cards[i].detailTextureOffset.y; 6907c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma const float textureTop = drawDetailBelowCard ? screenCoord.y : height; 691b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float x0 = cards[i].detailLineOffset.x + screenCoord.x + offx; 692b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float x1 = cards[i].detailLineOffset.x + screenCoord.x + offx + textureWidth; 693b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y0 = textureTop + offy - textureHeight - cards[i].detailLineOffset.y; 694b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y1 = textureTop + offy - cards[i].detailLineOffset.y; 695b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 696b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (blendedAlpha == 1.0f) { 697b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(singleTextureFragmentProgram, 0, cards[i].detailTexture); 698b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 699b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(multiTextureFragmentProgram, 1, cards[i].detailTexture); 700b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 701b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgDrawQuad(x0, y0, screenCoord.z, x1, y0, screenCoord.z, 702b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller x1, y1, screenCoord.z, x0, y1, screenCoord.z); 7037cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 7047cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 7057cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 706420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller return stillAnimating; 7077cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller} 7087cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 7099afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Millerstatic void drawBackground() 7109afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller{ 711b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller static bool toggle; 7129afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller if (backgroundTexture.p != 0) { 7139afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearDepth(1.0f); 7149afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rs_matrix4x4 projection, model; 7159afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsMatrixLoadOrtho(&projection, -1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f); 7169afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgProgramVertexLoadProjectionMatrix(&projection); 7179afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsMatrixLoadIdentity(&model); 7189afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgProgramVertexLoadModelMatrix(&model); 719a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller rsgBindTexture(singleTextureFragmentProgram, 0, backgroundTexture); 7209afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller float z = -0.9999f; 7219afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgDrawQuad( 7229afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[0].x, cardVertices[0].y, z, 7239afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[1].x, cardVertices[1].y, z, 7249afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[2].x, cardVertices[2].y, z, 7259afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[3].x, cardVertices[3].y, z); 7269afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller updateCamera = true; // we mucked with the matrix. 7279afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } else { 7289afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearDepth(1.0f); 729b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (debugRendering) { // for debugging - flash the screen so we know we're still rendering 730b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgClearColor(toggle ? backgroundColor.x : 1.0f, 731b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller toggle ? backgroundColor.y : 0.0f, 732b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller toggle ? backgroundColor.z : 0.0f, 733b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller backgroundColor.w); 7349afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller toggle = !toggle; 735b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 7367cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgClearColor(backgroundColor.x, backgroundColor.y, backgroundColor.z, 7377cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller backgroundColor.w); 7389afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } 7399afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } 7409afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller} 7419afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller 7425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void updateCameraMatrix(float width, float height) 7435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 7445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float aspect = width / height; 7455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (aspect != camera.aspect || updateCamera) { 7465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.aspect = aspect; 7475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller loadPerspectiveMatrix(&projectionMatrix, camera.fov, camera.aspect, camera.near, camera.far); 7485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadProjectionMatrix(&projectionMatrix); 7495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller loadLookatMatrix(&modelviewMatrix, camera.from, camera.at, camera.up); 7515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadModelMatrix(&modelviewMatrix); 7525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = false; 7535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 7555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 7575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Behavior/Physics 7585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 7595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool isDragging; 7605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int64_t lastTime = 0L; // keep track of how much time has passed between frames 7615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float2 lastPosition; 7625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool animating = false; 7635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float velocityThreshold = 0.1f * M_PI / 180.0f; 7645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float velocityTracker; 7655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int velocityTrackerCount; 7665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float mass = 5.0f; // kg 7675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float G = 9.80f; // gravity constant, in m/s 7695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float springConstant = 0.0f; 7705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float dragFunction(float x, float y) 7725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 7735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return dragFactor * ((x - lastPosition.x) / rsgGetWidth()) * M_PI; 7745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 7755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float deltaTimeInSeconds(int64_t current) 7775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 7785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return (lastTime > 0L) ? (float) (current - lastTime) / 1000.0f : 0.0f; 7795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 7805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint doSelection(float x, float y) 7825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 7835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller Ray ray; 784b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (makeRayForPixelAt(&ray, &camera, x, y)) { 7855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bestTime = FLT_MAX; 7865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return intersectGeometry(&ray, &bestTime); 7875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return -1; 7895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 7905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid doStart(float x, float y) 7925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 7935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastPosition.x = x; 7945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastPosition.y = y; 7955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocity = 0.0f; 7965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (animating) { 7975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsSendToClient(CMD_ANIMATION_FINISHED); 7985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller animating = false; 7997867abe6e7af226fc29285890d6decb0ce3daa0fJim Shuma currentSelection = -1; 8007867abe6e7af226fc29285890d6decb0ce3daa0fJim Shuma } else { 8017867abe6e7af226fc29285890d6decb0ce3daa0fJim Shuma currentSelection = doSelection(x, y); 8025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTracker = 0.0f; 8045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTrackerCount = 0; 8055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller touchTime = rsUptimeMillis(); 8065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller touchBias = bias; 8075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 8085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid doStop(float x, float y) 8115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 8125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t currentTime = rsUptimeMillis(); 8138b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich updateAllocationVars(cards); 8145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (currentSelection != -1 && (currentTime - touchTime) < ANIMATION_SCALE_TIME) { 8157cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // rsDebug("HIT!", currentSelection); 8165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int data[1]; 8175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = currentSelection; 8185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsSendToClientBlocking(CMD_CARD_SELECTED, data, sizeof(data)); 8195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 8205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocity = velocityTrackerCount > 0 ? 8215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller (velocityTracker / velocityTrackerCount) : 0.0f; // avg velocity 8225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (fabs(velocity) > velocityThreshold) { 8235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller animating = true; 8245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsSendToClient(CMD_ANIMATION_STARTED); 8255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller currentSelection = -1; 8285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastTime = rsUptimeMillis(); 8295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 8305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 831594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shumavoid doLongPress() 832594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma{ 833594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma int64_t currentTime = rsUptimeMillis(); 834594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma updateAllocationVars(cards); 835594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma if (currentSelection != -1) { 836594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma // rsDebug("HIT!", currentSelection); 837594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma int data[1]; 838594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma data[0] = currentSelection; 839594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma rsSendToClientBlocking(CMD_CARD_LONGPRESS, data, sizeof(data)); 840594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma } 841594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma currentSelection = -1; 842594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma lastTime = rsUptimeMillis(); 843594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma} 844594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma 8455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid doMotion(float x, float y) 8465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 8475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t currentTime = rsUptimeMillis(); 8485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float deltaOmega = dragFunction(x, y); 8495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bias += deltaOmega; 8505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastPosition.x = x; 8515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastPosition.y = y; 8525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float dt = deltaTimeInSeconds(currentTime); 8535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (dt > 0.0f) { 8545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float v = deltaOmega / dt; 8555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //if ((velocityTracker > 0.0f) == (v > 0.0f)) { 8565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTracker += v; 8575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTrackerCount++; 8585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //} else { 8595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // velocityTracker = v; 8605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // velocityTrackerCount = 1; 8615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //} 8625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 863c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma velocity = velocityTrackerCount > 0 ? 864c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma (velocityTracker / velocityTrackerCount) : 0.0f; // avg velocity 8655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Drop current selection if user drags position +- a partial slot 8675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (currentSelection != -1) { 8685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float slotMargin = 0.5f * (2.0f * M_PI / slotCount); 8695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (fabs(touchBias - bias) > slotMargin) { 8705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller currentSelection = -1; 8715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastTime = currentTime; 8745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 8755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 8775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Hit detection using ray casting. 8785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 8795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool 8815ce730797a8a7278dfe19dac8a9460b25675fed0Jim MillerrayTriangleIntersect(Ray* ray, float3 p0, float3 p1, float3 p2, float *tout) 8825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 8835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller static const float tmin = 0.0f; 8845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 e1 = p1 - p0; 8865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 e2 = p2 - p0; 8875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s1 = cross(ray->direction, e2); 8885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float div = dot(s1, e1); 8905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (div == 0.0f) return false; // ray is parallel to plane. 8915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 d = ray->position - p0; 8935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float invDiv = 1.0f / div; 8945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float u = dot(d, s1) * invDiv; 8965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (u < 0.0f || u > 1.0f) return false; 8975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s2 = cross(d, e1); 8995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float v = dot(ray->direction, s2) * invDiv; 9005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if ( v < 0.0f || (u+v) > 1.0f) return false; 9015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float t = dot(e2, s2) * invDiv; 9035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (t < tmin || t > *tout) 9045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return false; 9055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller *tout = t; 9065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return true; 9075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 9085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 909b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool 910b378af500b36226635b6343b1d5009ee9af44fc1Jim MillerrayPlaneIntersect(Ray* ray, float3 point, float3 normal) 911b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller{ 912b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller return false; // TODO 913b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller} 914b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 915b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool 916b378af500b36226635b6343b1d5009ee9af44fc1Jim MillerrayCylinderIntersect(Ray* ray, float3 center, float radius) 917b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller{ 918b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller return false; // TODO 919b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller} 920b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 921b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller// Creates a ray for an Android pixel coordinate given a camera, ray and coordinates. 9225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Note that the Y coordinate is opposite of GL rendering coordinates. 923b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 924b378af500b36226635b6343b1d5009ee9af44fc1Jim MillermakeRayForPixelAt(Ray* ray, PerspectiveCamera* cam, float x, float y) 9255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 9265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (debugCamera) { 9275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("------ makeRay() -------", 0); 928b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Camera.from:", cam->from); 929b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Camera.at:", cam->at); 930b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Camera.dir:", normalize(cam->at - cam->from)); 9315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 9325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Vector math. This has the potential to be much faster. 9345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO: pre-compute lowerLeftRay, du, dv to eliminate most of this math. 935b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float u = x / rsgGetWidth(); 936b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float v = 1.0f - (y / rsgGetHeight()); 937b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float aspect = (float) rsgGetWidth() / rsgGetHeight(); 938b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float tanfov2 = 2.0f * tan(radians(cam->fov / 2.0f)); 939b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 dir = normalize(cam->at - cam->from); 940b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 du = tanfov2 * normalize(cross(dir, cam->up)); 941b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 dv = tanfov2 * normalize(cross(du, dir)); 942b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller du *= aspect; 943b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 lowerLeftRay = dir - (0.5f * du) - (0.5f * dv); 944b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayPoint = cam->from; 945b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayDir = normalize(lowerLeftRay + u*du + v*dv); 946b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (debugCamera) { 947b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Ray direction (vector math) = ", rayDir); 9485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 9495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 950b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->position = rayPoint; 951b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->direction = rayDir; 952b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller return true; 953b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller} 954b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 955b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller// Creates a ray for an Android pixel coordinate given a model view and projection matrix. 956b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller// Note that the Y coordinate is opposite of GL rendering coordinates. 957b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 958b378af500b36226635b6343b1d5009ee9af44fc1Jim MillermakeRayForPixelAt(Ray* ray, rs_matrix4x4* model, rs_matrix4x4* proj, float x, float y) 959b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller{ 960b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rs_matrix4x4 pm = *model; 961b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsMatrixLoadMultiply(&pm, proj, model); 962b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (!rsMatrixInverse(&pm)) { 963b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("ERROR: SINGULAR PM MATRIX", 0); 964b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller return false; 9655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 966b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float width = rsgGetWidth(); 967b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float height = rsgGetHeight(); 968b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float winx = 2.0f * x / width - 1.0f; 969b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float winy = 2.0f * y / height - 1.0f; 970b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 971b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float4 eye = { 0.0f, 0.0f, 0.0f, 1.0f }; 972b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float4 at = { winx, winy, 1.0f, 1.0f }; 973b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 974b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller eye = rsMatrixMultiply(&pm, eye); 975b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller eye *= 1.0f / eye.w; 9765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 977b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller at = rsMatrixMultiply(&pm, at); 978b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller at *= 1.0f / at.w; 979b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 980b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayPoint = { eye.x, eye.y, eye.z }; 981b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 atPoint = { at.x, at.y, at.z }; 982b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayDir = normalize(atPoint - rayPoint); 983b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (debugCamera) { 984b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("winx: ", winx); 985b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("winy: ", winy); 986b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Ray position (transformed) = ", eye); 987b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Ray direction (transformed) = ", rayDir); 988b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 989b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->position = rayPoint; 990b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->direction = rayDir; 9915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return true; 9925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 9935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int intersectGeometry(Ray* ray, float *bestTime) 9955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 9965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int hit = -1; 9975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int id = 0; id < cardCount; id++) { 9985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[id].visible) { 9995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_matrix4x4 matrix; 10005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 p[4]; 10015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Transform card vertices to world space 10035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadIdentity(&matrix); 10047c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma getMatrixForCard(&matrix, id, true); 10055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int vertex = 0; vertex < 4; vertex++) { 10065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float4 tmp = rsMatrixMultiply(&matrix, cardVertices[vertex]); 10075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (tmp.w != 0.0f) { 10085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].x = tmp.x; 10095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].y = tmp.y; 10105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].z = tmp.z; 10115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex] *= 1.0f / tmp.w; 10125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 10135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Bad w coord: ", tmp); 10145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Intersect card geometry 10185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (rayTriangleIntersect(ray, p[0], p[1], p[2], bestTime) 10195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller || rayTriangleIntersect(ray, p[2], p[3], p[0], bestTime)) { 10205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller hit = id; 10215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return hit; 10255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 10265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// This method computes the position of all the cards by updating bias based on a 10285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// simple physics model. 10295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// If the cards are still in motion, returns true. 10305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool updateNextPosition(int64_t currentTime) 10315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 10325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (animating) { 10335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float dt = deltaTimeInSeconds(currentTime); 10345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (dt <= 0.0f) 10355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return animating; 10365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float minStepTime = 1.0f / 300.0f; // ~5 steps per frame 10375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const int N = (dt > minStepTime) ? (1 + round(dt / minStepTime)) : 1; 10385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller dt /= N; 10395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < N; i++) { 10405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Force friction - always opposes motion 10415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float Ff = -frictionCoeff * velocity; 10425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Restoring force to match cards with slots 10445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float theta = startAngle + bias; 10455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float dtheta = 2.0f * M_PI / slotCount; 10465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float position = theta / dtheta; 10475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float fraction = position - floor(position); // fractional position between slots 10485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float x; 10495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (fraction > 0.5f) { 10505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller x = - (1.0f - fraction); 10515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 10525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller x = fraction; 10535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float Fr = - springConstant * x; 10555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // compute velocity 10575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float momentum = mass * velocity + (Ff + Fr)*dt; 10585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocity = momentum / mass; 10595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bias += velocity * dt; 10605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller animating = fabs(velocity) > velocityThreshold; 10635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (!animating) { 10645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsSendToClient(CMD_ANIMATION_FINISHED); 10655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastTime = currentTime; 10685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1069f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller const float firstBias = wedgeAngle(0.0f); 1070f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller const float lastBias = -max(0.0f, wedgeAngle(cardCount - visibleDetailCount)); 1071f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller 1072f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller if (bias > firstBias) { 1073f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller bias = firstBias; 1074f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller } else if (bias < lastBias) { 1075f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller bias = lastBias; 1076c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma } 1077c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 10785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return animating; 10795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 10805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Cull cards based on visibility and visibleSlotCount. 10825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// If visibleSlotCount is > 0, then only show those slots and cull the rest. 10835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Otherwise, it should cull based on bounds of geometry. 10845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int cullCards() 10855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 10864fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // TODO(jshuma): Instead of fully fetching prefetchCardCount cards, make a distinction between 10874fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // STATE_LOADED and a new STATE_PRELOADING, which will keep the textures loaded but will not 10884fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // attempt to actually draw them. 10894fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma const int prefetchCardCountPerSide = prefetchCardCount / 2; 10904fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma const float thetaFirst = slotPosition(-prefetchCardCountPerSide); 1091198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaSelected = slotPosition(0); 1092198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaHalfAngle = (thetaSelected - thetaFirst) * 0.5f; 1093198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaSelectedLow = thetaSelected - thetaHalfAngle; 1094198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaSelectedHigh = thetaSelected + thetaHalfAngle; 10954fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma const float thetaLast = slotPosition(visibleSlotCount - 1 + prefetchCardCountPerSide); 10965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int count = 0; 1098198a060d650bc849ef0f25b597888fac9546803bJack Palevich int firstVisible = -1; 10995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < cardCount; i++) { 11005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (visibleSlotCount > 0) { 11015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // If visibleSlotCount is specified, then only show up to visibleSlotCount cards. 11025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float p = cardPosition(i); 11035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (p >= thetaFirst && p < thetaLast) { 1104198a060d650bc849ef0f25b597888fac9546803bJack Palevich if (firstVisible == -1 && p >= thetaSelectedLow && p < thetaSelectedHigh) { 1105198a060d650bc849ef0f25b597888fac9546803bJack Palevich firstVisible = i; 1106198a060d650bc849ef0f25b597888fac9546803bJack Palevich } 11075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].visible = true; 11085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller count++; 11095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 11105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].visible = false; 11115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 11135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Cull the rest of the cards using bounding box of geometry. 11145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO 11155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].visible = true; 11165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller count++; 11175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 1119198a060d650bc849ef0f25b597888fac9546803bJack Palevich currentFirstCard = firstVisible; 11205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return count; 11215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 11225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 11235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Request texture/geometry for items that have come into view 11245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// or doesn't have a texture yet. 1125420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerstatic void updateCardResources(int64_t currentTime) 11265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 1127a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller for (int i = cardCount-1; i >= 0; --i) { 11285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int data[1]; 11295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].visible) { 11302ba04e061b52c488a154739379501dc833e39f79Jim Miller if (debugTextureLoading) rsDebug("*** Texture stamp: ", cards[i].textureTimeStamp); 11312ba04e061b52c488a154739379501dc833e39f79Jim Miller 11325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // request texture from client if not loaded 11335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].textureState == STATE_INVALID) { 11345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 11355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data)); 11365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 11375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].textureState = STATE_LOADING; 11385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 11397cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_TEXTURE", 0); 11407cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 11417cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 11427cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // request detail texture from client if not loaded 11437cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (cards[i].detailTextureState == STATE_INVALID) { 11447cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller data[0] = i; 11457cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller bool enqueued = rsSendToClient(CMD_REQUEST_DETAIL_TEXTURE, data, sizeof(data)); 11467cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (enqueued) { 11477cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[i].detailTextureState = STATE_LOADING; 11487cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } else { 11497cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_DETAIL_TEXTURE", 0); 11505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // request geometry from client if not loaded 11535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].geometryState == STATE_INVALID) { 11545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 11555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_REQUEST_GEOMETRY, data, sizeof(data)); 11565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 11575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].geometryState = STATE_LOADING; 11585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 11597cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugGeometryLoading) rsDebug("Couldn't send CMD_REQUEST_GEOMETRY", 0); 11605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 11635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // ask the host to remove the texture 1164dce9af330efceae2b8d1d7c25e7e236b4e21719bJack Palevich if (cards[i].textureState != STATE_INVALID) { 11655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 11665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_TEXTURE, data, sizeof(data)); 11675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 11685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].textureState = STATE_INVALID; 1169420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[i].textureTimeStamp = currentTime; 11705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 11717cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_INVALIDATE_TEXTURE", 0); 11727cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 11737cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 11747cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // ask the host to remove the detail texture 11757cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (cards[i].detailTextureState != STATE_INVALID) { 11767cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller data[0] = i; 11777cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_DETAIL_TEXTURE, data, sizeof(data)); 11787cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (enqueued) { 11797cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[i].detailTextureState = STATE_INVALID; 1180420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[i].detailTextureTimeStamp = currentTime; 11817cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } else { 11827cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Can't send CMD_INVALIDATE_DETAIL_TEXTURE", 0); 11835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // ask the host to remove the geometry 1186dce9af330efceae2b8d1d7c25e7e236b4e21719bJack Palevich if (cards[i].geometryState != STATE_INVALID) { 11875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 11885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_GEOMETRY, data, sizeof(data)); 11895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 11905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].geometryState = STATE_INVALID; 11915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 11927cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugGeometryLoading) rsDebug("Couldn't send CMD_INVALIDATE_GEOMETRY", 0); 11935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 11985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 11995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Places dots on geometry to visually inspect that objects can be seen by rays. 12005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// NOTE: the color of the dot is somewhat random, as it depends on texture of previously-rendered 12015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// card. 12025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void renderWithRays() 12035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 12045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float w = rsgGetWidth(); 12055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float h = rsgGetHeight(); 12065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const int skip = 8; 12075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller color(1.0f, 0.0f, 0.0f, 1.0f); 12085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int j = 0; j < (int) h; j+=skip) { 12095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float posY = (float) j; 12105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < (int) w; i+=skip) { 12115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float posX = (float) i; 12125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller Ray ray; 1213b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (makeRayForPixelAt(&ray, &camera, posX, posY)) { 12145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bestTime = FLT_MAX; 12155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (intersectGeometry(&ray, &bestTime) != -1) { 12165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawSpriteScreenspace(posX, h - posY - 1, 0.0f, 2.0f, 2.0f); 12175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 12185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 12195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 12205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 12215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 12225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 12235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint root() { 12245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t currentTime = rsUptimeMillis(); 12255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 12265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramVertex(vertexProgram); 12275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramRaster(rasterProgram); 1228b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindSampler(singleTextureFragmentProgram, 0, linearClamp); 1229b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindSampler(multiTextureFragmentProgram, 0, linearClamp); 1230b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindSampler(multiTextureFragmentProgram, 1, linearClamp); 12315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 12328b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich updateAllocationVars(cards); 12335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 12345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (!initialized) { 12352ba04e061b52c488a154739379501dc833e39f79Jim Miller if (debugTextureLoading){ 12362ba04e061b52c488a154739379501dc833e39f79Jim Miller rsDebug("*** initialized was false, updating all cards (cards = ", cards); 12372ba04e061b52c488a154739379501dc833e39f79Jim Miller } 12387cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float2 zero = {0.0f, 0.0f}; 12397cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller for (int i = 0; i < cardCount; i++) { 12402ba04e061b52c488a154739379501dc833e39f79Jim Miller initCard(cards + i); 12417cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 12425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller initialized = true; 12435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 12445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1245a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller rsgBindProgramFragment(singleTextureFragmentProgram); 1246bfc5ce2da9e0d8d0ec2535c465624574d98418d7Jim Shuma rsgBindProgramStore(programStoreOpaque); 12479afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller drawBackground(); 12485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 12495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCameraMatrix(rsgGetWidth(), rsgGetHeight()); 12505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 12515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const bool timeExpired = (currentTime - touchTime) > ANIMATION_SCALE_TIME; 12525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool stillAnimating = updateNextPosition(currentTime) || !timeExpired; 12535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 12545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cullCards(); 12555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1256420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller updateCardResources(currentTime); 12575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1258bfc5ce2da9e0d8d0ec2535c465624574d98418d7Jim Shuma // Draw cards opaque only if requested, and always draw detail textures with blending. 1259bfc5ce2da9e0d8d0ec2535c465624574d98418d7Jim Shuma if (drawCardsWithBlending) { 1260bfc5ce2da9e0d8d0ec2535c465624574d98418d7Jim Shuma rsgBindProgramStore(programStore); 1261bfc5ce2da9e0d8d0ec2535c465624574d98418d7Jim Shuma } else { 1262bfc5ce2da9e0d8d0ec2535c465624574d98418d7Jim Shuma // programStoreOpaque is already bound 1263bfc5ce2da9e0d8d0ec2535c465624574d98418d7Jim Shuma } 1264bf39450b962d91ec78af53db39826d55ddb39902Jim Shuma stillAnimating |= drawCards(currentTime); 1265bf39450b962d91ec78af53db39826d55ddb39902Jim Shuma rsgBindProgramStore(programStoreDetail); 1266bf39450b962d91ec78af53db39826d55ddb39902Jim Shuma stillAnimating |= drawDetails(currentTime); 12675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 12685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (debugPicking) { 12695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller renderWithRays(); 12705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 12715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 12725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //rsSendToClient(CMD_PING); 12735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 12745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return stillAnimating ? 1 : 0; 12755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 1276