carousel.rs revision c0bb8af58ae15674178f2db240283719918c6f28
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 { 245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_allocation texture; 255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_mesh geometry; 265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //rs_matrix4x4 matrix; // custom transform for this card/geometry 275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int textureState; // whether or not the texture is loaded. 285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int geometryState; // whether or not geometry is loaded 295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int visible; // not bool because of packing bug? 305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} Card_t; 315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millertypedef struct Ray_s { 335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 position; 345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 direction; 355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} Ray; 365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millertypedef struct PerspectiveCamera_s { 385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 from; 395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 at; 405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 up; 415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float fov; 425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float aspect; 435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float near; 445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float far; 455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} PerspectiveCamera; 465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Request states. Used for loading 3D object properties from the Java client. 485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Typical properties: texture, geometry and matrices. 495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerenum { 505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_INVALID = 0, // item hasn't been loaded 515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_LOADING, // we've requested an item but are waiting for it to load 525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_LOADED // item was delivered 535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Client messages *** THIS LIST MUST MATCH THOSE IN CarouselRS.java. *** 565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_CARD_SELECTED = 100; 575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_REQUEST_TEXTURE = 200; 585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_INVALIDATE_TEXTURE = 210; 595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_REQUEST_GEOMETRY = 300; 605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_INVALIDATE_GEOMETRY = 310; 615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_ANIMATION_STARTED = 400; 625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_ANIMATION_FINISHED = 500; 635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_PING = 600; 645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Constants 665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int ANIMATION_SCALE_TIME = 200; // Time it takes to animate selected card, in ms 675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float3 SELECTED_SCALE_FACTOR = { 0.2f, 0.2f, 0.2f }; // increase by this % 685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Debug flags 705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerbool debugCamera = false; // dumps ray/camera coordinate stuff 715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerbool debugPicking = false; // renders picking area on top of geometry 725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Exported variables. These will be reflected to Java set_* variables. 745ce730797a8a7278dfe19dac8a9460b25675fed0Jim MillerCard_t *cards; // array of cards to draw 755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat startAngle; // position of initial card, in radians 765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint slotCount; // number of positions where a card can be 775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint cardCount; // number of cards in stack 785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint visibleSlotCount; // number of visible slots (for culling) 795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat radius; // carousel radius. Cards will be centered on a circle with this radius 805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat cardRotation; // rotation of card in XY plane relative to Z=1 81c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat swaySensitivity; // how much to rotate cards in relation to the rotation velocity 82c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat frictionCoeff; // how much to slow down the carousel over time 83c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat dragFactor; // a scale factor for how sensitive the carousel is to user dragging 845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_store programStore; 855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_fragment fragmentProgram; 865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_vertex vertexProgram; 875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_raster rasterProgram; 885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_allocation defaultTexture; // shown when no other texture is assigned 895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_allocation loadingTexture; // progress texture (shown when app is fetching the texture) 909afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Millerrs_allocation backgroundTexture; // drawn behind everything, if set 915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_mesh defaultGeometry; // shown when no geometry is loaded 925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_mesh loadingGeometry; // shown when geometry is loading 935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_matrix4x4 projectionMatrix; 945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_matrix4x4 modelviewMatrix; 955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller#pragma rs export_var(radius, cards, slotCount, visibleSlotCount, cardRotation) 97c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma#pragma rs export_var(swaySensitivity, frictionCoeff, dragFactor) 989afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller#pragma rs export_var(programStore, fragmentProgram, vertexProgram, rasterProgram, backgroundTexture) 995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller#pragma rs export_var(startAngle, defaultTexture, loadingTexture, defaultGeometry, loadingGeometry) 1005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller#pragma rs export_func(createCards, lookAt, doStart, doStop, doMotion, doSelection, setTexture) 1015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller#pragma rs export_func(setGeometry, debugCamera, debugPicking) 1025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Local variables 1045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float bias; // rotation bias, in radians. Used for animation and dragging. 1055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool updateCamera; // force a recompute of projection and lookat matrices 1065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool initialized; 1075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float3 backgroundColor = { 0.0f, 0.0f, 0.0f }; 1085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float FLT_MAX = 1.0e37; 1095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int currentSelection = -1; 1105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int64_t touchTime = -1; // time of first touch (see doStart()) 1115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float touchBias = 0.0f; // bias on first touch 112c0bb8af58ae15674178f2db240283719918c6f28Jim Shumastatic float velocity = 0.0f; // angular velocity in radians/s 1135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Default geometry when card.geometry is not set. 1155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float3 cardVertices[4] = { 1165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { -1.0, -1.0, 0.0 }, 1175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { 1.0, -1.0, 0.0 }, 1185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { 1.0, 1.0, 0.0 }, 1195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {-1.0, 1.0, 0.0 } 1205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 1215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Default camera 1235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic PerspectiveCamera camera = { 1245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {2,2,2}, // from 1255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {0,0,0}, // at 1265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {0,1,0}, // up 1275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 25.0f, // field of view 1285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1.0f, // aspect 1295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 0.1f, // near 1305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 100.0f // far 1315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 1325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Forward references 1345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int intersectGeometry(Ray* ray, float *bestTime); 1355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool makeRayForPixelAt(Ray* ray, float x, float y); 1365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float deltaTimeInSeconds(int64_t current); 1375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid init() { 1395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // initializers currently have a problem when the variables are exported, so initialize 1405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // globals here. 1415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Renderscript: init()", 0); 1425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller startAngle = 0.0f; 1435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller slotCount = 10; 1445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller visibleSlotCount = 1; 1455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bias = 0.0f; 1465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller radius = 1.0f; 1475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardRotation = 0.0f; 1485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = true; 1495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller initialized = false; 1505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 1515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void updateAllocationVars() 1535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 1545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Cards 1555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_allocation cardAlloc = rsGetAllocation(cards); 1565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO: use new rsIsObject() 1575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardCount = cardAlloc.p != 0 ? rsAllocationGetDimX(cardAlloc) : 0; 1585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 1595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid createCards(int n) 1615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 1625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("CreateCards: ", n); 1635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller initialized = false; 1645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateAllocationVars(); 1655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 1665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Return angle for position p. Typically p will be an integer position, but can be fractional. 1685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float cardPosition(float p) 1695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 1705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return startAngle + bias + 2.0f * M_PI * p / slotCount; 1715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 1725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Return slot for a card in position p. Typically p will be an integer slot, but can be fractional. 1745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float slotPosition(float p) 1755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 1765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return startAngle + 2.0f * M_PI * p / slotCount; 1775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 1785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Return the lowest slot number for a given angular position. 1805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int cardIndex(float angle) 1815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 1825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return floor(angle - startAngle - bias) * slotCount / (2.0f * M_PI); 1835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 1845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Set basic camera properties: 1865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// from - position of the camera in x,y,z 1875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// at - target we're looking at - used to compute view direction 1885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// up - a normalized vector indicating up (typically { 0, 1, 0}) 1895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// 1905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// NOTE: the view direction and up vector cannot be parallel/antiparallel with each other 1915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid lookAt(float fromX, float fromY, float fromZ, 1925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float atX, float atY, float atZ, 1935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float upX, float upY, float upZ) 1945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 1955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.x = fromX; 1965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.y = fromY; 1975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.z = fromZ; 1985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.x = atX; 1995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.y = atY; 2005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.z = atZ; 2015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.x = upX; 2025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.y = upY; 2035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.z = upZ; 2045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = true; 2055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Load a projection matrix for the given parameters. This is equivalent to gluPerspective() 2085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void loadPerspectiveMatrix(rs_matrix4x4* matrix, float fovy, float aspect, float near, float far) 2095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadIdentity(matrix); 2115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float top = near * tan((float) (fovy * M_PI / 360.0f)); 2125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bottom = -top; 2135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float left = bottom * aspect; 2145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float right = top * aspect; 2155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadFrustum(matrix, left, right, bottom, top, near, far); 2165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Construct a matrix based on eye point, center and up direction. Based on the 2195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// man page for gluLookat(). Up must be normalized. 2205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void loadLookatMatrix(rs_matrix4x4* matrix, float3 eye, float3 center, float3 up) 2215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 f = normalize(center - eye); 2235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s = normalize(cross(f, up)); 2245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 u = cross(s, f); 2255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float m[16]; 2265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[0] = s.x; 2275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[4] = s.y; 2285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[8] = s.z; 2295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[12] = 0.0f; 2305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[1] = u.x; 2315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[5] = u.y; 2325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[9] = u.z; 2335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[13] = 0.0f; 2345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[2] = -f.x; 2355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[6] = -f.y; 2365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[10] = -f.z; 2375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[14] = 0.0f; 2385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[3] = m[7] = m[11] = 0.0f; 2395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[15] = 1.0f; 2405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoad(matrix, m); 2415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixTranslate(matrix, -eye.x, -eye.y, -eye.z); 2425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid setTexture(int n, rs_allocation texture) 2455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].texture = texture; 2475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[n].texture.p != 0) 2485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].textureState = STATE_LOADED; 2495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller else 2505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].textureState = STATE_INVALID; 2515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid setGeometry(int n, rs_mesh geometry) 2545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].geometry = geometry; 2565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[n].geometry.p != 0) 2575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].geometryState = STATE_LOADED; 2585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller else 2595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].geometryState = STATE_INVALID; 2605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float3 getAnimatedScaleForSelected() 2635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t dt = (rsUptimeMillis() - touchTime); 2655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float fraction = (dt < ANIMATION_SCALE_TIME) ? (float) dt / ANIMATION_SCALE_TIME : 1.0f; 2665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float3 one = { 1.0f, 1.0f, 1.0f }; 2675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return one + fraction * SELECTED_SCALE_FACTOR; 2685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 270c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// The Verhulst logistic function: http://en.wikipedia.org/wiki/Logistic_function 271c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// P(t) = 1 / (1 + e^(-t)) 272c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// Parameter t: Any real number 273c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// Returns: A float in the range (0,1), with P(0.5)=0 274c0bb8af58ae15674178f2db240283719918c6f28Jim Shumastatic float logistic(float t) { 275c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma return 1. / (1. + exp(-t)); 276c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma} 277c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 278c0bb8af58ae15674178f2db240283719918c6f28Jim Shumastatic float getSwayAngleForVelocity(float v) 279c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma{ 280c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma const float range = M_PI; // How far we can deviate from center, peak-to-peak 281c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma float sway = M_PI * (logistic(-v * swaySensitivity) - 0.5); 282c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 283c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma return sway; 284c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma} 285c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 2865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void getMatrixForCard(rs_matrix4x4* matrix, int i) 2875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float theta = cardPosition(i); 289c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma float swayAngle = getSwayAngleForVelocity(velocity); 2905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixRotate(matrix, degrees(theta), 0, 1, 0); 2915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixTranslate(matrix, radius, 0, 0); 292c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma rsMatrixRotate(matrix, degrees(-theta + cardRotation + swayAngle), 0, 1, 0); 2935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (i == currentSelection) { 2945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 scale = getAnimatedScaleForSelected(); 2955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixScale(matrix, scale.x, scale.y, scale.z); 2965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 2975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO: apply custom matrix for cards[i].geometry 2985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void drawCards() 3015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float depth = 1.0f; 3035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < cardCount; i++) { 3045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].visible) { 3055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Bind texture 3065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].textureState == STATE_LOADED) { 3075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindTexture(fragmentProgram, 0, cards[i].texture); 3085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else if (cards[i].textureState == STATE_LOADING) { 3095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindTexture(fragmentProgram, 0, loadingTexture); 3105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 3115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindTexture(fragmentProgram, 0, defaultTexture); 3125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 3135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Draw geometry 3155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_matrix4x4 matrix = modelviewMatrix; 3165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller getMatrixForCard(&matrix, i); 3175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadModelMatrix(&matrix); 3185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].geometryState == STATE_LOADED && cards[i].geometry.p != 0) { 3195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawMesh(cards[i].geometry); 3205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else if (cards[i].geometryState == STATE_LOADING && loadingGeometry.p != 0) { 3215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawMesh(loadingGeometry); 3225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else if (defaultGeometry.p != 0) { 3235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawMesh(defaultGeometry); 3245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 3255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Draw place-holder geometry 3265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawQuad( 3275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[0].x, cardVertices[0].y, cardVertices[0].z, 3285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[1].x, cardVertices[1].y, cardVertices[1].z, 3295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[2].x, cardVertices[2].y, cardVertices[2].z, 3305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[3].x, cardVertices[3].y, cardVertices[3].z); 3315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 3325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 3335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 3345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3369afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Millerstatic void drawBackground() 3379afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller{ 3389afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller if (backgroundTexture.p != 0) { 3399afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearDepth(1.0f); 3409afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rs_matrix4x4 projection, model; 3419afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsMatrixLoadOrtho(&projection, -1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f); 3429afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgProgramVertexLoadProjectionMatrix(&projection); 3439afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsMatrixLoadIdentity(&model); 3449afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgProgramVertexLoadModelMatrix(&model); 3459afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgBindTexture(fragmentProgram, 0, backgroundTexture); 3469afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller float z = -0.9999f; 3479afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgDrawQuad( 3489afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[0].x, cardVertices[0].y, z, 3499afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[1].x, cardVertices[1].y, z, 3509afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[2].x, cardVertices[2].y, z, 3519afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[3].x, cardVertices[3].y, z); 3529afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller updateCamera = true; // we mucked with the matrix. 3539afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } else { 3549afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearDepth(1.0f); 3559afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller if (false) { // for debugging - flash the screen so we know we're still rendering 3569afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller static bool toggle; 3579afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller if (toggle) 3589afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearColor(backgroundColor.x, backgroundColor.y, backgroundColor.z, 1.0); 3599afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller else 3609afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearColor(1.0f, 0.0f, 0.0f, 1.f); 3619afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller toggle = !toggle; 3629afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } else { 3639afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearColor(backgroundColor.x, backgroundColor.y, backgroundColor.z, 1.0); 3649afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } 3659afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } 3669afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller} 3679afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller 3685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void updateCameraMatrix(float width, float height) 3695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float aspect = width / height; 3715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (aspect != camera.aspect || updateCamera) { 3725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.aspect = aspect; 3735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller loadPerspectiveMatrix(&projectionMatrix, camera.fov, camera.aspect, camera.near, camera.far); 3745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadProjectionMatrix(&projectionMatrix); 3755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller loadLookatMatrix(&modelviewMatrix, camera.from, camera.at, camera.up); 3775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadModelMatrix(&modelviewMatrix); 3785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = false; 3795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 3805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 3835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Behavior/Physics 3845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 3855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool isDragging; 3865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int64_t lastTime = 0L; // keep track of how much time has passed between frames 3875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float2 lastPosition; 3885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool animating = false; 3895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float velocityThreshold = 0.1f * M_PI / 180.0f; 3905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float velocityTracker; 3915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int velocityTrackerCount; 3925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float mass = 5.0f; // kg 3935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float G = 9.80f; // gravity constant, in m/s 3955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float springConstant = 0.0f; 3965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float dragFunction(float x, float y) 3985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return dragFactor * ((x - lastPosition.x) / rsgGetWidth()) * M_PI; 4005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float deltaTimeInSeconds(int64_t current) 4035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return (lastTime > 0L) ? (float) (current - lastTime) / 1000.0f : 0.0f; 4055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint doSelection(float x, float y) 4085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller Ray ray; 4105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (makeRayForPixelAt(&ray, x, y)) { 4115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bestTime = FLT_MAX; 4125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return intersectGeometry(&ray, &bestTime); 4135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 4145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return -1; 4155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid doStart(float x, float y) 4185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastPosition.x = x; 4205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastPosition.y = y; 4215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocity = 0.0f; 4225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (animating) { 4235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsSendToClient(CMD_ANIMATION_FINISHED); 4245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller animating = false; 4255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 4265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTracker = 0.0f; 4275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTrackerCount = 0; 4285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller touchTime = rsUptimeMillis(); 4295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller touchBias = bias; 4305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller currentSelection = doSelection(x, y); 4315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid doStop(float x, float y) 4355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t currentTime = rsUptimeMillis(); 4375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateAllocationVars(); 4385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (currentSelection != -1 && (currentTime - touchTime) < ANIMATION_SCALE_TIME) { 4395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("HIT!", currentSelection); 4405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int data[1]; 4415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = currentSelection; 4425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsSendToClientBlocking(CMD_CARD_SELECTED, data, sizeof(data)); 4435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 4445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocity = velocityTrackerCount > 0 ? 4455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller (velocityTracker / velocityTrackerCount) : 0.0f; // avg velocity 4465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (fabs(velocity) > velocityThreshold) { 4475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller animating = true; 4485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsSendToClient(CMD_ANIMATION_STARTED); 4495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 4505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 4515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller currentSelection = -1; 4525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastTime = rsUptimeMillis(); 4535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid doMotion(float x, float y) 4565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t currentTime = rsUptimeMillis(); 4585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float deltaOmega = dragFunction(x, y); 4595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bias += deltaOmega; 4605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastPosition.x = x; 4615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastPosition.y = y; 4625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float dt = deltaTimeInSeconds(currentTime); 4635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (dt > 0.0f) { 4645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float v = deltaOmega / dt; 4655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //if ((velocityTracker > 0.0f) == (v > 0.0f)) { 4665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTracker += v; 4675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTrackerCount++; 4685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //} else { 4695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // velocityTracker = v; 4705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // velocityTrackerCount = 1; 4715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //} 4725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 473c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma velocity = velocityTrackerCount > 0 ? 474c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma (velocityTracker / velocityTrackerCount) : 0.0f; // avg velocity 4755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Drop current selection if user drags position +- a partial slot 4775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (currentSelection != -1) { 4785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float slotMargin = 0.5f * (2.0f * M_PI / slotCount); 4795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (fabs(touchBias - bias) > slotMargin) { 4805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller currentSelection = -1; 4815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 4825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 4835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastTime = currentTime; 4845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 4875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Hit detection using ray casting. 4885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 4895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool 4915ce730797a8a7278dfe19dac8a9460b25675fed0Jim MillerrayTriangleIntersect(Ray* ray, float3 p0, float3 p1, float3 p2, float *tout) 4925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller static const float tmin = 0.0f; 4945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 e1 = p1 - p0; 4965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 e2 = p2 - p0; 4975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s1 = cross(ray->direction, e2); 4985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float div = dot(s1, e1); 5005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (div == 0.0f) return false; // ray is parallel to plane. 5015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 d = ray->position - p0; 5035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float invDiv = 1.0f / div; 5045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float u = dot(d, s1) * invDiv; 5065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (u < 0.0f || u > 1.0f) return false; 5075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s2 = cross(d, e1); 5095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float v = dot(ray->direction, s2) * invDiv; 5105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if ( v < 0.0f || (u+v) > 1.0f) return false; 5115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float t = dot(e2, s2) * invDiv; 5135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (t < tmin || t > *tout) 5145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return false; 5155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller *tout = t; 5165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return true; 5175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 5185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Creates a ray for an Android pixel coordinate. 5205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Note that the Y coordinate is opposite of GL rendering coordinates. 5215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool makeRayForPixelAt(Ray* ray, float x, float y) 5225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 5235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (debugCamera) { 5245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("------ makeRay() -------", 0); 5255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Camera.from:", camera.from); 5265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Camera.at:", camera.at); 5275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Camera.dir:", normalize(camera.at - camera.from)); 5285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 5295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Vector math. This has the potential to be much faster. 5315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO: pre-compute lowerLeftRay, du, dv to eliminate most of this math. 5325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (true) { 5335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float u = x / rsgGetWidth(); 5345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float v = 1.0f - (y / rsgGetHeight()); 5355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float aspect = (float) rsgGetWidth() / rsgGetHeight(); 5365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float tanfov2 = 2.0f * tan(radians(camera.fov / 2.0f)); 5375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 dir = normalize(camera.at - camera.from); 5385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 du = tanfov2 * normalize(cross(dir, camera.up)); 5395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 dv = tanfov2 * normalize(cross(du, dir)); 5405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller du *= aspect; 5415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 lowerLeftRay = dir - (0.5f * du) - (0.5f * dv); 5425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float3 rayPoint = camera.from; 5435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float3 rayDir = normalize(lowerLeftRay + u*du + v*dv); 5445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (debugCamera) { 5455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Ray direction (vector math) = ", rayDir); 5465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 5475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller ray->position = rayPoint; 5495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller ray->direction = rayDir; 5505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 5515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Matrix math. This is more generic if we allow setting model view and projection matrices 5535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // directly 5545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller else { 5555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_matrix4x4 pm = modelviewMatrix; 5565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadMultiply(&pm, &projectionMatrix, &modelviewMatrix); 5575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (!rsMatrixInverse(&pm)) { 5585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("ERROR: SINGULAR PM MATRIX", 0); 5595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return false; 5605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 5615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float width = rsgGetWidth(); 5625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float height = rsgGetHeight(); 5635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float winx = 2.0f * x / width - 1.0f; 5645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float winy = 2.0f * y / height - 1.0f; 5655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float4 eye = { 0.0f, 0.0f, 0.0f, 1.0f }; 5675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float4 at = { winx, winy, 1.0f, 1.0f }; 5685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller eye = rsMatrixMultiply(&pm, eye); 5705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller eye *= 1.0f / eye.w; 5715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller at = rsMatrixMultiply(&pm, at); 5735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller at *= 1.0f / at.w; 5745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float3 rayPoint = { eye.x, eye.y, eye.z }; 5765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float3 atPoint = { at.x, at.y, at.z }; 5775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float3 rayDir = normalize(atPoint - rayPoint); 5785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (debugCamera) { 5795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("winx: ", winx); 5805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("winy: ", winy); 5815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Ray position (transformed) = ", eye); 5825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Ray direction (transformed) = ", rayDir); 5835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 5845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller ray->position = rayPoint; 5855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller ray->direction = rayDir; 5865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 5875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return true; 5895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 5905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int intersectGeometry(Ray* ray, float *bestTime) 5925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 5935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int hit = -1; 5945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int id = 0; id < cardCount; id++) { 5955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[id].visible) { 5965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_matrix4x4 matrix; 5975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 p[4]; 5985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Transform card vertices to world space 6005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadIdentity(&matrix); 6015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller getMatrixForCard(&matrix, id); 6025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int vertex = 0; vertex < 4; vertex++) { 6035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float4 tmp = rsMatrixMultiply(&matrix, cardVertices[vertex]); 6045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (tmp.w != 0.0f) { 6055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].x = tmp.x; 6065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].y = tmp.y; 6075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].z = tmp.z; 6085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex] *= 1.0f / tmp.w; 6095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 6105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Bad w coord: ", tmp); 6115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Intersect card geometry 6155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (rayTriangleIntersect(ray, p[0], p[1], p[2], bestTime) 6165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller || rayTriangleIntersect(ray, p[2], p[3], p[0], bestTime)) { 6175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller hit = id; 6185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return hit; 6225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 6235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// This method computes the position of all the cards by updating bias based on a 6255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// simple physics model. 6265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// If the cards are still in motion, returns true. 6275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool updateNextPosition(int64_t currentTime) 6285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 6295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (animating) { 6305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float dt = deltaTimeInSeconds(currentTime); 6315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (dt <= 0.0f) 6325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return animating; 6335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float minStepTime = 1.0f / 300.0f; // ~5 steps per frame 6345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const int N = (dt > minStepTime) ? (1 + round(dt / minStepTime)) : 1; 6355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller dt /= N; 6365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < N; i++) { 6375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Force friction - always opposes motion 6385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float Ff = -frictionCoeff * velocity; 6395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Restoring force to match cards with slots 6415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float theta = startAngle + bias; 6425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float dtheta = 2.0f * M_PI / slotCount; 6435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float position = theta / dtheta; 6445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float fraction = position - floor(position); // fractional position between slots 6455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float x; 6465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (fraction > 0.5f) { 6475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller x = - (1.0f - fraction); 6485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 6495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller x = fraction; 6505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float Fr = - springConstant * x; 6525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // compute velocity 6545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float momentum = mass * velocity + (Ff + Fr)*dt; 6555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocity = momentum / mass; 6565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bias += velocity * dt; 6575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller animating = fabs(velocity) > velocityThreshold; 6605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (!animating) { 6615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsSendToClient(CMD_ANIMATION_FINISHED); 6625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastTime = currentTime; 6655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 666c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma // TODO: Add animation to smoothly move back to slots. Currently snaps to location. 667c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma if (cardCount <= visibleSlotCount) { 668c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma // TODO: this aligns the cards to the first slot (theta = startAngle) when there aren't 669c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma // enough visible cards. It should be generalized to allow alignment to front, 670c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma // middle or back of the stack. 671c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma if (cardPosition(0) != slotPosition(0)) { 672c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma bias = 0.0f; 673c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma } 674c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma } else { 675c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma if (cardPosition(cardCount) < 0.0f) { 676c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma bias = -slotPosition(cardCount); 677c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma } else if (cardPosition(0) > slotPosition(0)) { 678c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma bias = 0.0f; 679c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma } 680c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma } 681c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 6825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return animating; 6835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 6845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Cull cards based on visibility and visibleSlotCount. 6865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// If visibleSlotCount is > 0, then only show those slots and cull the rest. 6875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Otherwise, it should cull based on bounds of geometry. 6885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int cullCards() 6895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 6905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float thetaFirst = slotPosition(-1); // -1 keeps the card in front around a bit longer 6915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float thetaLast = slotPosition(visibleSlotCount); 6925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int count = 0; 6945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < cardCount; i++) { 6955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (visibleSlotCount > 0) { 6965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // If visibleSlotCount is specified, then only show up to visibleSlotCount cards. 6975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float p = cardPosition(i); 6985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (p >= thetaFirst && p < thetaLast) { 6995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].visible = true; 7005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller count++; 7015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 7025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].visible = false; 7035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 7055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Cull the rest of the cards using bounding box of geometry. 7065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO 7075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].visible = true; 7085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller count++; 7095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return count; 7125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 7135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Request texture/geometry for items that have come into view 7155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// or doesn't have a texture yet. 7165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void updateCardResources() 7175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 7185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < cardCount; i++) { 7195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int data[1]; 7205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].visible) { 7215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // request texture from client if not loaded 7225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].textureState == STATE_INVALID) { 7235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 7245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data)); 7255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 7265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].textureState = STATE_LOADING; 7275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 7285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Couldn't send CMD_REQUEST_TEXTURE", 0); 7295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // request geometry from client if not loaded 7325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].geometryState == STATE_INVALID) { 7335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 7345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_REQUEST_GEOMETRY, data, sizeof(data)); 7355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 7365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].geometryState = STATE_LOADING; 7375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 7385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Couldn't send CMD_REQUEST_GEOMETRY", 0); 7395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 7425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // ask the host to remove the texture 743dce9af330efceae2b8d1d7c25e7e236b4e21719bJack Palevich if (cards[i].textureState != STATE_INVALID) { 7445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 7455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_TEXTURE, data, sizeof(data)); 7465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 7475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].textureState = STATE_INVALID; 7485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 7495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Couldn't send CMD_INVALIDATE_TEXTURE", 0); 7505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // ask the host to remove the geometry 753dce9af330efceae2b8d1d7c25e7e236b4e21719bJack Palevich if (cards[i].geometryState != STATE_INVALID) { 7545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 7555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_GEOMETRY, data, sizeof(data)); 7565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 7575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].geometryState = STATE_INVALID; 7585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 7595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Couldn't send CMD_INVALIDATE_GEOMETRY", 0); 7605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 7665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Places dots on geometry to visually inspect that objects can be seen by rays. 7685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// NOTE: the color of the dot is somewhat random, as it depends on texture of previously-rendered 7695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// card. 7705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void renderWithRays() 7715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 7725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float w = rsgGetWidth(); 7735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float h = rsgGetHeight(); 7745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const int skip = 8; 7755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller color(1.0f, 0.0f, 0.0f, 1.0f); 7765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int j = 0; j < (int) h; j+=skip) { 7775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float posY = (float) j; 7785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < (int) w; i+=skip) { 7795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float posX = (float) i; 7805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller Ray ray; 7815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (makeRayForPixelAt(&ray, posX, posY)) { 7825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bestTime = FLT_MAX; 7835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (intersectGeometry(&ray, &bestTime) != -1) { 7845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawSpriteScreenspace(posX, h - posY - 1, 0.0f, 2.0f, 2.0f); 7855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 7895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 7905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint root() { 7925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t currentTime = rsUptimeMillis(); 7935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramVertex(vertexProgram); 7955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramFragment(fragmentProgram); 7965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramStore(programStore); 7975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramRaster(rasterProgram); 7985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 7995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateAllocationVars(); 8005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (!initialized) { 8025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < cardCount; i++) 8035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].textureState = STATE_INVALID; 8045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller initialized = true; 8055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8079afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller drawBackground(); 8085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCameraMatrix(rsgGetWidth(), rsgGetHeight()); 8105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const bool timeExpired = (currentTime - touchTime) > ANIMATION_SCALE_TIME; 8125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (timeExpired) { 8135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //currentSelection = -1; 8145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool stillAnimating = updateNextPosition(currentTime) || !timeExpired; 8165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cullCards(); 8185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCardResources(); 8205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller drawCards(); 8225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (debugPicking) { 8245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller renderWithRays(); 8255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //rsSendToClient(CMD_PING); 8285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return stillAnimating ? 1 : 0; 8305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 831