carousel.rs revision 420b44b8b11ec1c309ea130e69a6876325dbfef9
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 { 247cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_allocation texture; // basic card texture 257cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_allocation detailTexture; // screen-aligned detail texture 267cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller float2 detailTextureOffset; // offset to add, in screen coordinates 275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_mesh geometry; 287cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_matrix4x4 matrix; // custom transform for this card/geometry 297cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller int textureState; // whether or not the primary card texture is loaded. 307cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller int detailTextureState; // whether or not the detail for the card is loaded. 315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int geometryState; // whether or not geometry is loaded 325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int visible; // not bool because of packing bug? 33420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // TODO: Change when int64_t is supported. This will break after ~40 days of uptime. 34420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller unsigned int textureTimeStamp; // time when this texture was last updated, in seconds 35420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller unsigned int detailTextureTimeStamp; // time when this texture was last updated, in seconds 365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} Card_t; 375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millertypedef struct Ray_s { 395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 position; 405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 direction; 415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} Ray; 425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millertypedef struct PerspectiveCamera_s { 445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 from; 455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 at; 465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 up; 475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float fov; 485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float aspect; 495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float near; 505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float far; 515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} PerspectiveCamera; 525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 53420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millertypedef struct FragmentShaderConstants_s { 54420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float fadeAmount; 55420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller} FragmentShaderConstants; 56420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Request states. Used for loading 3D object properties from the Java client. 585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Typical properties: texture, geometry and matrices. 595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerenum { 605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_INVALID = 0, // item hasn't been loaded 615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_LOADING, // we've requested an item but are waiting for it to load 625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_LOADED // item was delivered 635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Client messages *** THIS LIST MUST MATCH THOSE IN CarouselRS.java. *** 665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_CARD_SELECTED = 100; 675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_REQUEST_TEXTURE = 200; 685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_INVALIDATE_TEXTURE = 210; 695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_REQUEST_GEOMETRY = 300; 705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_INVALIDATE_GEOMETRY = 310; 715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_ANIMATION_STARTED = 400; 725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_ANIMATION_FINISHED = 500; 737cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_REQUEST_DETAIL_TEXTURE = 600; 747cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_INVALIDATE_DETAIL_TEXTURE = 610; 757cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_REPORT_FIRST_CARD_POSITION = 700; 767cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_PING = 1000; 775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Constants 795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int ANIMATION_SCALE_TIME = 200; // Time it takes to animate selected card, in ms 805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float3 SELECTED_SCALE_FACTOR = { 0.2f, 0.2f, 0.2f }; // increase by this % 815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Debug flags 837cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugCamera = false; // dumps ray/camera coordinate stuff 847cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugPicking = false; // renders picking area on top of geometry 857cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugTextureLoading = false; // for debugging texture load/unload 867cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugGeometryLoading = false; // for debugging geometry load/unload 877c09ccce478100d75e4427d87866ff19d758ae7aJim Shumaconst bool debugDetails = false; // for debugging detail texture geometry 885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Exported variables. These will be reflected to Java set_* variables. 905ce730797a8a7278dfe19dac8a9460b25675fed0Jim MillerCard_t *cards; // array of cards to draw 915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat startAngle; // position of initial card, in radians 925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint slotCount; // number of positions where a card can be 935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint cardCount; // number of cards in stack 945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint visibleSlotCount; // number of visible slots (for culling) 957cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerint visibleDetailCount; // number of visible detail textures to show 967c09ccce478100d75e4427d87866ff19d758ae7aJim Shumabool drawDetailBelowCard; // whether detail goes above (false) or below (true) the card 977c09ccce478100d75e4427d87866ff19d758ae7aJim Shumabool drawRuler; // whether to draw a ruler from the card to the detail texture 985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat radius; // carousel radius. Cards will be centered on a circle with this radius 995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat cardRotation; // rotation of card in XY plane relative to Z=1 100c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat swaySensitivity; // how much to rotate cards in relation to the rotation velocity 101c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat frictionCoeff; // how much to slow down the carousel over time 102c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat dragFactor; // a scale factor for how sensitive the carousel is to user dragging 103420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerint fadeInDuration; // amount of time (in ms) for smoothly switching out textures 104420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerfloat rezInCardCount; // this controls how rapidly distant card textures will be rez-ed in 1055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_store programStore; 1065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_fragment fragmentProgram; 1075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_vertex vertexProgram; 1085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_raster rasterProgram; 1095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_allocation defaultTexture; // shown when no other texture is assigned 1105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_allocation loadingTexture; // progress texture (shown when app is fetching the texture) 1119afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Millerrs_allocation backgroundTexture; // drawn behind everything, if set 1127cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerrs_allocation detailLineTexture; // used to draw detail line (as a quad, of course) 113420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerrs_allocation detailLoadingTexture; // used when detail texture is loading 1145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_mesh defaultGeometry; // shown when no geometry is loaded 1155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_mesh loadingGeometry; // shown when geometry is loading 1165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_matrix4x4 projectionMatrix; 1175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_matrix4x4 modelviewMatrix; 118420b44b8b11ec1c309ea130e69a6876325dbfef9Jim MillerFragmentShaderConstants* shaderConstants; 119420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerrs_sampler linearClamp; 1205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 121b0f070636c29ad178f4e21306f301fe3d20c183bJim Miller#pragma rs export_var(radius, cards, slotCount, visibleSlotCount, cardRotation, backgroundColor) 122c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma#pragma rs export_var(swaySensitivity, frictionCoeff, dragFactor) 1237c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma#pragma rs export_var(visibleDetailCount, drawDetailBelowCard, drawRuler) 1247cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller#pragma rs export_var(programStore, fragmentProgram, vertexProgram, rasterProgram) 125420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller#pragma rs export_var(detailLineTexture, detailLoadingTexture, backgroundTexture) 126420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller#pragma rs export_var(linearClamp, shaderConstants) 1275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller#pragma rs export_var(startAngle, defaultTexture, loadingTexture, defaultGeometry, loadingGeometry) 128420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller#pragma rs export_var(fadeInDuration, rezInCardCount) 1297cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller#pragma rs export_func(createCards, lookAt, doStart, doStop, doMotion, doSelection) 1307cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller#pragma rs export_func(setTexture, setGeometry, setDetailTexture, debugCamera, debugPicking) 131198a060d650bc849ef0f25b597888fac9546803bJack Palevich#pragma rs export_func(requestFirstCardPosition) 1325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Local variables 1345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float bias; // rotation bias, in radians. Used for animation and dragging. 1355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool updateCamera; // force a recompute of projection and lookat matrices 1365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool initialized; 137b0f070636c29ad178f4e21306f301fe3d20c183bJim Millerstatic float4 backgroundColor; 1385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float FLT_MAX = 1.0e37; 1395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int currentSelection = -1; 140198a060d650bc849ef0f25b597888fac9546803bJack Palevichstatic int currentFirstCard = -1; 1415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int64_t touchTime = -1; // time of first touch (see doStart()) 1425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float touchBias = 0.0f; // bias on first touch 143c0bb8af58ae15674178f2db240283719918c6f28Jim Shumastatic float velocity = 0.0f; // angular velocity in radians/s 1445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1453df59346f395434454d310b070fff195089fbaf1Jim Miller// Because allocations can't have 0 dimensions, we have to track whether or not 1463df59346f395434454d310b070fff195089fbaf1Jim Miller// cards are valid separately. 1473df59346f395434454d310b070fff195089fbaf1Jim Miller// TODO: Remove this dependency once allocations can have a zero dimension. 1483df59346f395434454d310b070fff195089fbaf1Jim Millerstatic bool cardAllocationValid = false; 1493df59346f395434454d310b070fff195089fbaf1Jim Miller 1505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Default geometry when card.geometry is not set. 1515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float3 cardVertices[4] = { 1525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { -1.0, -1.0, 0.0 }, 1535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { 1.0, -1.0, 0.0 }, 1545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { 1.0, 1.0, 0.0 }, 1555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {-1.0, 1.0, 0.0 } 1565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 1575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Default camera 1595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic PerspectiveCamera camera = { 1605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {2,2,2}, // from 1615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {0,0,0}, // at 1625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {0,1,0}, // up 1635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 25.0f, // field of view 1645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1.0f, // aspect 1655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 0.1f, // near 1665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 100.0f // far 1675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 1685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Forward references 1705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int intersectGeometry(Ray* ray, float *bestTime); 1715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool makeRayForPixelAt(Ray* ray, float x, float y); 1725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float deltaTimeInSeconds(int64_t current); 1735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid init() { 1755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // initializers currently have a problem when the variables are exported, so initialize 1765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // globals here. 1777cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // rsDebug("Renderscript: init()", 0); 1785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller startAngle = 0.0f; 1795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller slotCount = 10; 1805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller visibleSlotCount = 1; 1817cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller visibleDetailCount = 3; 1825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bias = 0.0f; 1835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller radius = 1.0f; 1845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardRotation = 0.0f; 1855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = true; 1865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller initialized = false; 187b0f070636c29ad178f4e21306f301fe3d20c183bJim Miller backgroundColor = (float4) { 0.0f, 0.0f, 0.0f, 1.0f }; 1883df59346f395434454d310b070fff195089fbaf1Jim Miller cardAllocationValid = false; 1893df59346f395434454d310b070fff195089fbaf1Jim Miller cardCount = 0; 190420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller fadeInDuration = 250; 191420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rezInCardCount = 0.0f; // alpha will ramp to 1.0f over this many cards (0.0f means disabled) 1925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 1935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void updateAllocationVars() 1955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 1965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Cards 1975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_allocation cardAlloc = rsGetAllocation(cards); 1985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO: use new rsIsObject() 1993df59346f395434454d310b070fff195089fbaf1Jim Miller cardCount = (cardAllocationValid && cardAlloc.p != 0) ? rsAllocationGetDimX(cardAlloc) : 0; 2005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid createCards(int n) 2035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2047cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("CreateCards: ", n); 2053df59346f395434454d310b070fff195089fbaf1Jim Miller cardAllocationValid = n > 0; 2065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller initialized = false; 2075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateAllocationVars(); 2085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 210420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller// Computes an alpha value for a card using elapsed time and constant fadeInDuration 211420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerfloat getAnimatedAlpha(int64_t startTime, int64_t currentTime) 212420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller{ 213420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller double timeElapsed = (double) (currentTime - startTime); // in ms 214420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller double alpha = (double) timeElapsed / fadeInDuration; 215420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller return min(1.0f, (float) alpha); 216420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller} 217420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 2185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Return angle for position p. Typically p will be an integer position, but can be fractional. 2195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float cardPosition(float p) 2205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return startAngle + bias + 2.0f * M_PI * p / slotCount; 2225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Return slot for a card in position p. Typically p will be an integer slot, but can be fractional. 2255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float slotPosition(float p) 2265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return startAngle + 2.0f * M_PI * p / slotCount; 2285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Return the lowest slot number for a given angular position. 2315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int cardIndex(float angle) 2325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return floor(angle - startAngle - bias) * slotCount / (2.0f * M_PI); 2345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Set basic camera properties: 2375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// from - position of the camera in x,y,z 2385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// at - target we're looking at - used to compute view direction 2395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// up - a normalized vector indicating up (typically { 0, 1, 0}) 2405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// 2415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// NOTE: the view direction and up vector cannot be parallel/antiparallel with each other 2425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid lookAt(float fromX, float fromY, float fromZ, 2435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float atX, float atY, float atZ, 2445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float upX, float upY, float upZ) 2455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.x = fromX; 2475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.y = fromY; 2485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.z = fromZ; 2495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.x = atX; 2505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.y = atY; 2515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.z = atZ; 2525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.x = upX; 2535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.y = upY; 2545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.z = upZ; 2555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = true; 2565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Load a projection matrix for the given parameters. This is equivalent to gluPerspective() 2595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void loadPerspectiveMatrix(rs_matrix4x4* matrix, float fovy, float aspect, float near, float far) 2605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadIdentity(matrix); 2625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float top = near * tan((float) (fovy * M_PI / 360.0f)); 2635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bottom = -top; 2645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float left = bottom * aspect; 2655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float right = top * aspect; 2665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadFrustum(matrix, left, right, bottom, top, near, far); 2675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Construct a matrix based on eye point, center and up direction. Based on the 2705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// man page for gluLookat(). Up must be normalized. 2715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void loadLookatMatrix(rs_matrix4x4* matrix, float3 eye, float3 center, float3 up) 2725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 f = normalize(center - eye); 2745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s = normalize(cross(f, up)); 2755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 u = cross(s, f); 2765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float m[16]; 2775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[0] = s.x; 2785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[4] = s.y; 2795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[8] = s.z; 2805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[12] = 0.0f; 2815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[1] = u.x; 2825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[5] = u.y; 2835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[9] = u.z; 2845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[13] = 0.0f; 2855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[2] = -f.x; 2865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[6] = -f.y; 2875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[10] = -f.z; 2885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[14] = 0.0f; 2895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[3] = m[7] = m[11] = 0.0f; 2905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[15] = 1.0f; 2915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoad(matrix, m); 2925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixTranslate(matrix, -eye.x, -eye.y, -eye.z); 2935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid setTexture(int n, rs_allocation texture) 2965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2973df59346f395434454d310b070fff195089fbaf1Jim Miller if (n < 0 || n >= cardCount) return; 298c4c6f38bf410af40e10c63b152befd5a39df87c8Jim Miller rsSetObject(&cards[n].texture, texture); 2997cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].textureState = (texture.p != 0) ? STATE_LOADED : STATE_INVALID; 300420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[n].textureTimeStamp = rsUptimeMillis(); 3017cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller} 3027cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 3037cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millervoid setDetailTexture(int n, float offx, float offy, rs_allocation texture) 3047cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller{ 3053df59346f395434454d310b070fff195089fbaf1Jim Miller if (n < 0 || n >= cardCount) return; 306c4c6f38bf410af40e10c63b152befd5a39df87c8Jim Miller rsSetObject(&cards[n].detailTexture, texture); 3077cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].detailTextureOffset.x = offx; 3087cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].detailTextureOffset.y = offy; 3097cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].detailTextureState = (texture.p != 0) ? STATE_LOADED : STATE_INVALID; 310420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[n].detailTextureTimeStamp = rsUptimeMillis(); 3115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid setGeometry(int n, rs_mesh geometry) 3145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3153df59346f395434454d310b070fff195089fbaf1Jim Miller if (n < 0 || n >= cardCount) return; 316c4c6f38bf410af40e10c63b152befd5a39df87c8Jim Miller rsSetObject(&cards[n].geometry, geometry); 3175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[n].geometry.p != 0) 3185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].geometryState = STATE_LOADED; 3195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller else 3205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].geometryState = STATE_INVALID; 3215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 323198a060d650bc849ef0f25b597888fac9546803bJack Palevichvoid requestFirstCardPosition() 324198a060d650bc849ef0f25b597888fac9546803bJack Palevich{ 325198a060d650bc849ef0f25b597888fac9546803bJack Palevich int data[1]; 326198a060d650bc849ef0f25b597888fac9546803bJack Palevich data[0] = currentFirstCard; 327198a060d650bc849ef0f25b597888fac9546803bJack Palevich bool enqueued = rsSendToClient(CMD_REPORT_FIRST_CARD_POSITION, data, sizeof(data)); 328198a060d650bc849ef0f25b597888fac9546803bJack Palevich if (!enqueued) { 329198a060d650bc849ef0f25b597888fac9546803bJack Palevich rsDebug("Couldn't send CMD_REPORT_FIRST_CARD_POSITION", 0); 330198a060d650bc849ef0f25b597888fac9546803bJack Palevich } 331198a060d650bc849ef0f25b597888fac9546803bJack Palevich} 332198a060d650bc849ef0f25b597888fac9546803bJack Palevich 3335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float3 getAnimatedScaleForSelected() 3345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t dt = (rsUptimeMillis() - touchTime); 3365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float fraction = (dt < ANIMATION_SCALE_TIME) ? (float) dt / ANIMATION_SCALE_TIME : 1.0f; 3375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float3 one = { 1.0f, 1.0f, 1.0f }; 3385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return one + fraction * SELECTED_SCALE_FACTOR; 3395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 341c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// The Verhulst logistic function: http://en.wikipedia.org/wiki/Logistic_function 342c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// P(t) = 1 / (1 + e^(-t)) 343c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// Parameter t: Any real number 344c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// Returns: A float in the range (0,1), with P(0.5)=0 345c0bb8af58ae15674178f2db240283719918c6f28Jim Shumastatic float logistic(float t) { 346af8cf9a3bbe517b604b48e217b00085351ab2496Shih-wei Liao return 1.f / (1.f + exp(-t)); 347c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma} 348c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 3497c09ccce478100d75e4427d87866ff19d758ae7aJim Shumastatic float getSwayAngleForVelocity(float v, bool enableSway) 350c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma{ 3517c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma float sway = 0.0f; 3527c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma 3537c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma if (enableSway) { 3547c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma const float range = M_PI; // How far we can deviate from center, peak-to-peak 355af8cf9a3bbe517b604b48e217b00085351ab2496Shih-wei Liao sway = M_PI * (logistic(-v * swaySensitivity) - 0.5f); 3567c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma } 357c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 358c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma return sway; 359c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma} 360c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 3617c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma// matrix: The output matrix. 3627c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma// i: The card we're getting the matrix for. 3637c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma// enableSway: Whether to enable swaying. (We want it on for cards, and off for detail textures.) 3647c09ccce478100d75e4427d87866ff19d758ae7aJim Shumastatic void getMatrixForCard(rs_matrix4x4* matrix, int i, bool enableSway) 3655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float theta = cardPosition(i); 3677c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma float swayAngle = getSwayAngleForVelocity(velocity, enableSway); 3685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixRotate(matrix, degrees(theta), 0, 1, 0); 3695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixTranslate(matrix, radius, 0, 0); 370c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma rsMatrixRotate(matrix, degrees(-theta + cardRotation + swayAngle), 0, 1, 0); 3715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (i == currentSelection) { 3725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 scale = getAnimatedScaleForSelected(); 3735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixScale(matrix, scale.x, scale.y, scale.z); 3745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 3755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO: apply custom matrix for cards[i].geometry 3765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 378420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller/* 379420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller * Draws cards around the Carousel. 380420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller * Returns true if we're still animating any property of the cards (e.g. fades). 381420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller */ 382420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerstatic bool drawCards(int64_t currentTime) 3835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 384420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller const float wedgeAngle = 2.0f * M_PI / slotCount; 385420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller const float endAngle = startAngle + visibleSlotCount * wedgeAngle; 386420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller bool stillAnimating = false; 387420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller for (int i = cardCount-1; i >= 0; i--) { 3885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].visible) { 389420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // If this card was recently loaded, this will be < 1.0f until the animation completes 390420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float animatedAlpha = getAnimatedAlpha(cards[i].textureTimeStamp, currentTime); 391420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller if (animatedAlpha < 1.0f) { 392420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller stillAnimating = true; 393420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } 394420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 395420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Compute fade out for cards in the distance 396420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float positionAlpha; 397420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller if (rezInCardCount > 0.0f) { 398420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller positionAlpha = (endAngle - cardPosition(i)) / wedgeAngle; 399420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller positionAlpha = min(1.0f, positionAlpha / rezInCardCount); 400420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } else { 401420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller positionAlpha = 1.0f; 402420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } 403420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 404420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Set alpha for blending between the textures 405420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller shaderConstants->fadeAmount = min(1.0f, animatedAlpha * positionAlpha); 406420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsAllocationMarkDirty(rsGetAllocation(shaderConstants)); 407420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 408420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Bind place-holder texture 409420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsgBindSampler(fragmentProgram, 0, linearClamp); 410420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsgBindTexture(fragmentProgram, 0, loadingTexture); 411420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 412420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Bind artwork texture, if loaded 413420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsgBindSampler(fragmentProgram, 1, linearClamp); 4145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].textureState == STATE_LOADED) { 415420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsgBindTexture(fragmentProgram, 1, cards[i].texture); 4165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 417420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsgBindTexture(fragmentProgram, 1, loadingTexture); 4185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 4195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Draw geometry 4215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_matrix4x4 matrix = modelviewMatrix; 4227c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma getMatrixForCard(&matrix, i, true); 4235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadModelMatrix(&matrix); 4245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].geometryState == STATE_LOADED && cards[i].geometry.p != 0) { 4255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawMesh(cards[i].geometry); 4265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else if (cards[i].geometryState == STATE_LOADING && loadingGeometry.p != 0) { 4275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawMesh(loadingGeometry); 4285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else if (defaultGeometry.p != 0) { 4295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawMesh(defaultGeometry); 4305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 4315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Draw place-holder geometry 4325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawQuad( 4335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[0].x, cardVertices[0].y, cardVertices[0].z, 4345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[1].x, cardVertices[1].y, cardVertices[1].z, 4355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[2].x, cardVertices[2].y, cardVertices[2].z, 4365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[3].x, cardVertices[3].y, cardVertices[3].z); 4375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 4385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 4395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 440420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller return stillAnimating; 4415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4437cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller/* 4447cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller * Draws a screen-aligned card with the exact dimensions from the detail texture. 4457cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller * This is used to display information about the object being displayed above the geomertry. 446420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller * Returns true if we're still animating any property of the cards (e.g. fades). 4477cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller */ 448420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerstatic bool drawDetails(int64_t currentTime) 4497cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller{ 4507cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float width = rsgGetWidth(); 4517cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float height = rsgGetHeight(); 4527cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 453420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller bool stillAnimating = false; 454420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 4557cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // We'll be drawing in screen space, sampled on pixel centers 4567cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_matrix4x4 projection, model; 4577cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsMatrixLoadOrtho(&projection, 0.0f, width, 0.0f, height, 0.0f, 1.0f); 4587cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgProgramVertexLoadProjectionMatrix(&projection); 4597cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsMatrixLoadIdentity(&model); 4607cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgProgramVertexLoadModelMatrix(&model); 4617cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller updateCamera = true; // we messed with the projection matrix. Reload on next pass... 4627cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 4637cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float yPadding = 5.0f; // draw line this far (in pixels) away from top and geometry 4647cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 4657cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller int drawn = 0; // number of details drawn 466420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 467420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // This can be done once... 468420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsgBindSampler(fragmentProgram, 0, linearClamp); 469420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsgBindTexture(fragmentProgram, 0, detailLoadingTexture); 470420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 4717cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller for (int i = 0; i < cardCount && drawn < visibleDetailCount; i++) { 4727cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (cards[i].visible) { 4737cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (cards[i].detailTextureState == STATE_LOADED && cards[i].detailTexture.p != 0) { 4747cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float lineWidth = rsAllocationGetDimX(detailLineTexture); 4757cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 4767cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // Compute position in screen space of upper left corner of card 477af8cf9a3bbe517b604b48e217b00085351ab2496Shih-wei Liao rsMatrixLoad(&model, &modelviewMatrix); 4787c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma getMatrixForCard(&model, i, false); 4797cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_matrix4x4 matrix; 4807cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsMatrixLoadMultiply(&matrix, &projectionMatrix, &model); 481d443c88da4c7cf1947c12b26f111cb899cc8afe4Jim Miller 4827c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma float4 screenCoord = rsMatrixMultiply(&matrix, 4837c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma cardVertices[drawDetailBelowCard ? 0 : 3]); 4847cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (screenCoord.w == 0.0f) { 4857cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // this shouldn't happen 4867cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsDebug("Bad transform: ", screenCoord); 4877cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller continue; 4887cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 4897cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 490420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Compute alpha for gradually fading in details. Applied to both line and 491420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // detail texture. TODO: use a separate background texture for line. 492420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float animatedAlpha = getAnimatedAlpha(cards[i].detailTextureTimeStamp, currentTime); 493420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller if (animatedAlpha < 1.0f) { 494420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller stillAnimating = true; 495420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } 496420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 497420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Set alpha for blending between the textures 498420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller shaderConstants->fadeAmount = min(1.0f, animatedAlpha); 499420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsAllocationMarkDirty(rsGetAllocation(shaderConstants)); 500420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 5017cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // Convert projection from normalized coordinates to pixel coordinates. 5027cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // This is probably cheaper than pre-multiplying the above with another matrix. 5037cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller screenCoord *= 1.0f / screenCoord.w; 5047cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller screenCoord.x += 1.0f; 5057cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller screenCoord.y += 1.0f; 5067cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller screenCoord.z = 0.0f; // make sure it's in front 5077cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller screenCoord.x = round(screenCoord.x * 0.5f * width); 5087cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller screenCoord.y = round(screenCoord.y * 0.5f * height); 5097c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma if (debugDetails) { 5107c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma RS_DEBUG(screenCoord); 5117c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma } 5127cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 5137cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // Draw line from upper left card corner to the top of the screen 5147c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma if (drawRuler) { 5157c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma const float halfWidth = lineWidth * 0.5f; 5167c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma const float rulerTop = drawDetailBelowCard ? screenCoord.y : height; 5177c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma const float rulerBottom = drawDetailBelowCard ? 0 : screenCoord.y; 518420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsgBindSampler(fragmentProgram, 1, linearClamp); 519420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsgBindTexture(fragmentProgram, 1, detailLineTexture); 5207c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma rsgDrawQuad( 5217c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma screenCoord.x - halfWidth, rulerBottom + yPadding, 0, 5227c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma screenCoord.x + halfWidth, rulerBottom + yPadding, 0, 5237c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma screenCoord.x + halfWidth, rulerTop - yPadding, 0, 5247c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma screenCoord.x - halfWidth, rulerTop - yPadding, 0); 5257c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma } 5267cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 5277cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // Draw the detail texture next to it using the offsets provided. 5287cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float textureWidth = rsAllocationGetDimX(cards[i].detailTexture); 5297cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float textureHeight = rsAllocationGetDimY(cards[i].detailTexture); 5307cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float offx = cards[i].detailTextureOffset.x; 5317cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float offy = -cards[i].detailTextureOffset.y; 5327c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma const float textureTop = drawDetailBelowCard ? screenCoord.y : height; 533420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsgBindSampler(fragmentProgram, 1, linearClamp); 534420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsgBindTexture(fragmentProgram, 1, cards[i].detailTexture); 5357cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgDrawQuad( 5367c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma screenCoord.x + offx, textureTop + offy - textureHeight, 0, 5377c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma screenCoord.x + offx + textureWidth, textureTop + offy - textureHeight, 0, 5387c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma screenCoord.x + offx + textureWidth, textureTop + offy, 0, 5397c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma screenCoord.x + offx, textureTop + offy, 0); 5407cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 5417cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller drawn++; 5427cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 5437cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 5447cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 545420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller return stillAnimating; 5467cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller} 5477cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 5489afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Millerstatic void drawBackground() 5499afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller{ 5509afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller if (backgroundTexture.p != 0) { 5519afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearDepth(1.0f); 5529afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rs_matrix4x4 projection, model; 5539afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsMatrixLoadOrtho(&projection, -1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f); 5549afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgProgramVertexLoadProjectionMatrix(&projection); 5559afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsMatrixLoadIdentity(&model); 5569afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgProgramVertexLoadModelMatrix(&model); 5579afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgBindTexture(fragmentProgram, 0, backgroundTexture); 558420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsgBindTexture(fragmentProgram, 1, backgroundTexture); // TODO: background blending 5599afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller float z = -0.9999f; 5609afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgDrawQuad( 5619afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[0].x, cardVertices[0].y, z, 5629afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[1].x, cardVertices[1].y, z, 5639afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[2].x, cardVertices[2].y, z, 5649afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[3].x, cardVertices[3].y, z); 5659afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller updateCamera = true; // we mucked with the matrix. 5669afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } else { 5679afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearDepth(1.0f); 5689afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller if (false) { // for debugging - flash the screen so we know we're still rendering 5699afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller static bool toggle; 5709afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller if (toggle) 5717cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgClearColor(backgroundColor.x, backgroundColor.y, backgroundColor.z, 5727cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller backgroundColor.w); 5739afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller else 5749afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearColor(1.0f, 0.0f, 0.0f, 1.f); 5759afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller toggle = !toggle; 5769afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } else { 5777cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgClearColor(backgroundColor.x, backgroundColor.y, backgroundColor.z, 5787cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller backgroundColor.w); 5799afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } 5809afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } 5819afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller} 5829afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller 5835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void updateCameraMatrix(float width, float height) 5845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 5855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float aspect = width / height; 5865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (aspect != camera.aspect || updateCamera) { 5875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.aspect = aspect; 5885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller loadPerspectiveMatrix(&projectionMatrix, camera.fov, camera.aspect, camera.near, camera.far); 5895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadProjectionMatrix(&projectionMatrix); 5905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller loadLookatMatrix(&modelviewMatrix, camera.from, camera.at, camera.up); 5925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadModelMatrix(&modelviewMatrix); 5935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = false; 5945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 5955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 5965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 5985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Behavior/Physics 5995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 6005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool isDragging; 6015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int64_t lastTime = 0L; // keep track of how much time has passed between frames 6025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float2 lastPosition; 6035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool animating = false; 6045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float velocityThreshold = 0.1f * M_PI / 180.0f; 6055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float velocityTracker; 6065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int velocityTrackerCount; 6075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float mass = 5.0f; // kg 6085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float G = 9.80f; // gravity constant, in m/s 6105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float springConstant = 0.0f; 6115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float dragFunction(float x, float y) 6135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 6145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return dragFactor * ((x - lastPosition.x) / rsgGetWidth()) * M_PI; 6155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 6165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float deltaTimeInSeconds(int64_t current) 6185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 6195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return (lastTime > 0L) ? (float) (current - lastTime) / 1000.0f : 0.0f; 6205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 6215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint doSelection(float x, float y) 6235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 6245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller Ray ray; 6255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (makeRayForPixelAt(&ray, x, y)) { 6265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bestTime = FLT_MAX; 6275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return intersectGeometry(&ray, &bestTime); 6285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return -1; 6305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 6315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid doStart(float x, float y) 6335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 6345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastPosition.x = x; 6355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastPosition.y = y; 6365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocity = 0.0f; 6375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (animating) { 6385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsSendToClient(CMD_ANIMATION_FINISHED); 6395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller animating = false; 6407867abe6e7af226fc29285890d6decb0ce3daa0fJim Shuma currentSelection = -1; 6417867abe6e7af226fc29285890d6decb0ce3daa0fJim Shuma } else { 6427867abe6e7af226fc29285890d6decb0ce3daa0fJim Shuma currentSelection = doSelection(x, y); 6435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTracker = 0.0f; 6455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTrackerCount = 0; 6465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller touchTime = rsUptimeMillis(); 6475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller touchBias = bias; 6485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 6495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid doStop(float x, float y) 6525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 6535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t currentTime = rsUptimeMillis(); 6545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateAllocationVars(); 6555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (currentSelection != -1 && (currentTime - touchTime) < ANIMATION_SCALE_TIME) { 6567cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // rsDebug("HIT!", currentSelection); 6575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int data[1]; 6585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = currentSelection; 6595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsSendToClientBlocking(CMD_CARD_SELECTED, data, sizeof(data)); 6605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 6615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocity = velocityTrackerCount > 0 ? 6625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller (velocityTracker / velocityTrackerCount) : 0.0f; // avg velocity 6635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (fabs(velocity) > velocityThreshold) { 6645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller animating = true; 6655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsSendToClient(CMD_ANIMATION_STARTED); 6665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller currentSelection = -1; 6695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastTime = rsUptimeMillis(); 6705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 6715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid doMotion(float x, float y) 6735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 6745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t currentTime = rsUptimeMillis(); 6755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float deltaOmega = dragFunction(x, y); 6765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bias += deltaOmega; 6775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastPosition.x = x; 6785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastPosition.y = y; 6795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float dt = deltaTimeInSeconds(currentTime); 6805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (dt > 0.0f) { 6815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float v = deltaOmega / dt; 6825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //if ((velocityTracker > 0.0f) == (v > 0.0f)) { 6835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTracker += v; 6845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTrackerCount++; 6855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //} else { 6865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // velocityTracker = v; 6875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // velocityTrackerCount = 1; 6885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //} 6895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 690c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma velocity = velocityTrackerCount > 0 ? 691c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma (velocityTracker / velocityTrackerCount) : 0.0f; // avg velocity 6925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Drop current selection if user drags position +- a partial slot 6945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (currentSelection != -1) { 6955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float slotMargin = 0.5f * (2.0f * M_PI / slotCount); 6965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (fabs(touchBias - bias) > slotMargin) { 6975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller currentSelection = -1; 6985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastTime = currentTime; 7015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 7025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 7045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Hit detection using ray casting. 7055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 7065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool 7085ce730797a8a7278dfe19dac8a9460b25675fed0Jim MillerrayTriangleIntersect(Ray* ray, float3 p0, float3 p1, float3 p2, float *tout) 7095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 7105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller static const float tmin = 0.0f; 7115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 e1 = p1 - p0; 7135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 e2 = p2 - p0; 7145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s1 = cross(ray->direction, e2); 7155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float div = dot(s1, e1); 7175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (div == 0.0f) return false; // ray is parallel to plane. 7185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 d = ray->position - p0; 7205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float invDiv = 1.0f / div; 7215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float u = dot(d, s1) * invDiv; 7235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (u < 0.0f || u > 1.0f) return false; 7245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s2 = cross(d, e1); 7265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float v = dot(ray->direction, s2) * invDiv; 7275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if ( v < 0.0f || (u+v) > 1.0f) return false; 7285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float t = dot(e2, s2) * invDiv; 7305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (t < tmin || t > *tout) 7315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return false; 7325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller *tout = t; 7335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return true; 7345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 7355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Creates a ray for an Android pixel coordinate. 7375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Note that the Y coordinate is opposite of GL rendering coordinates. 7385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool makeRayForPixelAt(Ray* ray, float x, float y) 7395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 7405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (debugCamera) { 7415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("------ makeRay() -------", 0); 7425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Camera.from:", camera.from); 7435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Camera.at:", camera.at); 7445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Camera.dir:", normalize(camera.at - camera.from)); 7455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Vector math. This has the potential to be much faster. 7485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO: pre-compute lowerLeftRay, du, dv to eliminate most of this math. 7495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (true) { 7505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float u = x / rsgGetWidth(); 7515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float v = 1.0f - (y / rsgGetHeight()); 7525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float aspect = (float) rsgGetWidth() / rsgGetHeight(); 7535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float tanfov2 = 2.0f * tan(radians(camera.fov / 2.0f)); 7545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 dir = normalize(camera.at - camera.from); 7555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 du = tanfov2 * normalize(cross(dir, camera.up)); 7565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 dv = tanfov2 * normalize(cross(du, dir)); 7575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller du *= aspect; 7585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 lowerLeftRay = dir - (0.5f * du) - (0.5f * dv); 7595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float3 rayPoint = camera.from; 7605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float3 rayDir = normalize(lowerLeftRay + u*du + v*dv); 7615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (debugCamera) { 7625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Ray direction (vector math) = ", rayDir); 7635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller ray->position = rayPoint; 7665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller ray->direction = rayDir; 7675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Matrix math. This is more generic if we allow setting model view and projection matrices 7705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // directly 7715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller else { 7725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_matrix4x4 pm = modelviewMatrix; 7735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadMultiply(&pm, &projectionMatrix, &modelviewMatrix); 7745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (!rsMatrixInverse(&pm)) { 7755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("ERROR: SINGULAR PM MATRIX", 0); 7765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return false; 7775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float width = rsgGetWidth(); 7795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float height = rsgGetHeight(); 7805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float winx = 2.0f * x / width - 1.0f; 7815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float winy = 2.0f * y / height - 1.0f; 7825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float4 eye = { 0.0f, 0.0f, 0.0f, 1.0f }; 7845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float4 at = { winx, winy, 1.0f, 1.0f }; 7855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller eye = rsMatrixMultiply(&pm, eye); 7875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller eye *= 1.0f / eye.w; 7885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller at = rsMatrixMultiply(&pm, at); 7905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller at *= 1.0f / at.w; 7915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float3 rayPoint = { eye.x, eye.y, eye.z }; 7935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float3 atPoint = { at.x, at.y, at.z }; 7945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float3 rayDir = normalize(atPoint - rayPoint); 7955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (debugCamera) { 7965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("winx: ", winx); 7975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("winy: ", winy); 7985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Ray position (transformed) = ", eye); 7995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Ray direction (transformed) = ", rayDir); 8005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller ray->position = rayPoint; 8025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller ray->direction = rayDir; 8035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return true; 8065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 8075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int intersectGeometry(Ray* ray, float *bestTime) 8095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 8105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int hit = -1; 8115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int id = 0; id < cardCount; id++) { 8125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[id].visible) { 8135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_matrix4x4 matrix; 8145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 p[4]; 8155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Transform card vertices to world space 8175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadIdentity(&matrix); 8187c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma getMatrixForCard(&matrix, id, true); 8195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int vertex = 0; vertex < 4; vertex++) { 8205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float4 tmp = rsMatrixMultiply(&matrix, cardVertices[vertex]); 8215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (tmp.w != 0.0f) { 8225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].x = tmp.x; 8235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].y = tmp.y; 8245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].z = tmp.z; 8255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex] *= 1.0f / tmp.w; 8265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 8275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Bad w coord: ", tmp); 8285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Intersect card geometry 8325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (rayTriangleIntersect(ray, p[0], p[1], p[2], bestTime) 8335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller || rayTriangleIntersect(ray, p[2], p[3], p[0], bestTime)) { 8345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller hit = id; 8355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return hit; 8395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 8405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// This method computes the position of all the cards by updating bias based on a 8425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// simple physics model. 8435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// If the cards are still in motion, returns true. 8445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool updateNextPosition(int64_t currentTime) 8455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 8465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (animating) { 8475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float dt = deltaTimeInSeconds(currentTime); 8485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (dt <= 0.0f) 8495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return animating; 8505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float minStepTime = 1.0f / 300.0f; // ~5 steps per frame 8515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const int N = (dt > minStepTime) ? (1 + round(dt / minStepTime)) : 1; 8525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller dt /= N; 8535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < N; i++) { 8545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Force friction - always opposes motion 8555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float Ff = -frictionCoeff * velocity; 8565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Restoring force to match cards with slots 8585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float theta = startAngle + bias; 8595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float dtheta = 2.0f * M_PI / slotCount; 8605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float position = theta / dtheta; 8615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float fraction = position - floor(position); // fractional position between slots 8625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float x; 8635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (fraction > 0.5f) { 8645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller x = - (1.0f - fraction); 8655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 8665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller x = fraction; 8675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float Fr = - springConstant * x; 8695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // compute velocity 8715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float momentum = mass * velocity + (Ff + Fr)*dt; 8725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocity = momentum / mass; 8735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bias += velocity * dt; 8745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller animating = fabs(velocity) > velocityThreshold; 8775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (!animating) { 8785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsSendToClient(CMD_ANIMATION_FINISHED); 8795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastTime = currentTime; 8825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 883c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma // TODO: Add animation to smoothly move back to slots. Currently snaps to location. 884c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma if (cardCount <= visibleSlotCount) { 885c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma // TODO: this aligns the cards to the first slot (theta = startAngle) when there aren't 886c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma // enough visible cards. It should be generalized to allow alignment to front, 887c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma // middle or back of the stack. 888c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma if (cardPosition(0) != slotPosition(0)) { 889c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma bias = 0.0f; 890c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma } 891c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma } else { 892c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma if (cardPosition(cardCount) < 0.0f) { 893c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma bias = -slotPosition(cardCount); 894c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma } else if (cardPosition(0) > slotPosition(0)) { 895c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma bias = 0.0f; 896c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma } 897c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma } 898c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 8995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return animating; 9005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 9015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Cull cards based on visibility and visibleSlotCount. 9035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// If visibleSlotCount is > 0, then only show those slots and cull the rest. 9045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Otherwise, it should cull based on bounds of geometry. 9055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int cullCards() 9065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 9075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float thetaFirst = slotPosition(-1); // -1 keeps the card in front around a bit longer 908198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaSelected = slotPosition(0); 909198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaHalfAngle = (thetaSelected - thetaFirst) * 0.5f; 910198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaSelectedLow = thetaSelected - thetaHalfAngle; 911198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaSelectedHigh = thetaSelected + thetaHalfAngle; 9125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float thetaLast = slotPosition(visibleSlotCount); 9135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int count = 0; 915198a060d650bc849ef0f25b597888fac9546803bJack Palevich int firstVisible = -1; 9165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < cardCount; i++) { 9175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (visibleSlotCount > 0) { 9185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // If visibleSlotCount is specified, then only show up to visibleSlotCount cards. 9195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float p = cardPosition(i); 9205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (p >= thetaFirst && p < thetaLast) { 921198a060d650bc849ef0f25b597888fac9546803bJack Palevich if (firstVisible == -1 && p >= thetaSelectedLow && p < thetaSelectedHigh) { 922198a060d650bc849ef0f25b597888fac9546803bJack Palevich firstVisible = i; 923198a060d650bc849ef0f25b597888fac9546803bJack Palevich } 9245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].visible = true; 9255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller count++; 9265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 9275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].visible = false; 9285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 9295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 9305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Cull the rest of the cards using bounding box of geometry. 9315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO 9325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].visible = true; 9335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller count++; 9345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 9355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 936198a060d650bc849ef0f25b597888fac9546803bJack Palevich currentFirstCard = firstVisible; 9375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return count; 9385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 9395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Request texture/geometry for items that have come into view 9415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// or doesn't have a texture yet. 942420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerstatic void updateCardResources(int64_t currentTime) 9435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 9445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < cardCount; i++) { 9455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int data[1]; 9465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].visible) { 9475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // request texture from client if not loaded 9485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].textureState == STATE_INVALID) { 9495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 9505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data)); 9515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 9525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].textureState = STATE_LOADING; 9535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 9547cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_TEXTURE", 0); 9557cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 9567cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 9577cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // request detail texture from client if not loaded 9587cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (cards[i].detailTextureState == STATE_INVALID) { 9597cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller data[0] = i; 9607cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller bool enqueued = rsSendToClient(CMD_REQUEST_DETAIL_TEXTURE, data, sizeof(data)); 9617cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (enqueued) { 9627cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[i].detailTextureState = STATE_LOADING; 9637cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } else { 9647cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_DETAIL_TEXTURE", 0); 9655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 9665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 9675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // request geometry from client if not loaded 9685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].geometryState == STATE_INVALID) { 9695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 9705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_REQUEST_GEOMETRY, data, sizeof(data)); 9715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 9725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].geometryState = STATE_LOADING; 9735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 9747cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugGeometryLoading) rsDebug("Couldn't send CMD_REQUEST_GEOMETRY", 0); 9755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 9765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 9775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 9785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // ask the host to remove the texture 979dce9af330efceae2b8d1d7c25e7e236b4e21719bJack Palevich if (cards[i].textureState != STATE_INVALID) { 9805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 9815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_TEXTURE, data, sizeof(data)); 9825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 9835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].textureState = STATE_INVALID; 984420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[i].textureTimeStamp = currentTime; 9855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 9867cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_INVALIDATE_TEXTURE", 0); 9877cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 9887cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 9897cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // ask the host to remove the detail texture 9907cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (cards[i].detailTextureState != STATE_INVALID) { 9917cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller data[0] = i; 9927cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_DETAIL_TEXTURE, data, sizeof(data)); 9937cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (enqueued) { 9947cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[i].detailTextureState = STATE_INVALID; 995420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[i].detailTextureTimeStamp = currentTime; 9967cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } else { 9977cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Can't send CMD_INVALIDATE_DETAIL_TEXTURE", 0); 9985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 9995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // ask the host to remove the geometry 1001dce9af330efceae2b8d1d7c25e7e236b4e21719bJack Palevich if (cards[i].geometryState != STATE_INVALID) { 10025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 10035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_GEOMETRY, data, sizeof(data)); 10045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 10055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].geometryState = STATE_INVALID; 10065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 10077cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugGeometryLoading) rsDebug("Couldn't send CMD_INVALIDATE_GEOMETRY", 0); 10085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 10145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Places dots on geometry to visually inspect that objects can be seen by rays. 10165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// NOTE: the color of the dot is somewhat random, as it depends on texture of previously-rendered 10175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// card. 10185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void renderWithRays() 10195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 10205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float w = rsgGetWidth(); 10215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float h = rsgGetHeight(); 10225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const int skip = 8; 10235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller color(1.0f, 0.0f, 0.0f, 1.0f); 10245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int j = 0; j < (int) h; j+=skip) { 10255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float posY = (float) j; 10265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < (int) w; i+=skip) { 10275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float posX = (float) i; 10285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller Ray ray; 10295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (makeRayForPixelAt(&ray, posX, posY)) { 10305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bestTime = FLT_MAX; 10315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (intersectGeometry(&ray, &bestTime) != -1) { 10325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawSpriteScreenspace(posX, h - posY - 1, 0.0f, 2.0f, 2.0f); 10335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 10385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint root() { 10405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t currentTime = rsUptimeMillis(); 10415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramVertex(vertexProgram); 10435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramFragment(fragmentProgram); 10445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramStore(programStore); 10455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramRaster(rasterProgram); 10465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateAllocationVars(); 10485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (!initialized) { 10507cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float2 zero = {0.0f, 0.0f}; 10517cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller for (int i = 0; i < cardCount; i++) { 10525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].textureState = STATE_INVALID; 10537cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[i].detailTextureState = STATE_INVALID; 10547cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[i].detailTextureOffset = zero; 10557cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 10565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller initialized = true; 10575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10599afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller drawBackground(); 10605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCameraMatrix(rsgGetWidth(), rsgGetHeight()); 10625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const bool timeExpired = (currentTime - touchTime) > ANIMATION_SCALE_TIME; 10645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool stillAnimating = updateNextPosition(currentTime) || !timeExpired; 10655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cullCards(); 10675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1068420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller updateCardResources(currentTime); 10695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1070420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller stillAnimating |= drawCards(currentTime); 1071420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller drawDetails(currentTime); 10725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (debugPicking) { 10745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller renderWithRays(); 10755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //rsSendToClient(CMD_PING); 10785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return stillAnimating ? 1 : 0; 10805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 1081