carousel.rs revision fb179e7afd8f02be63061b478b0283e3085fc25f
15ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller/* 25ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * Copyright (C) 2010 The Android Open Source Project 35ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * 45ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * Licensed under the Apache License, Version 2.0 (the "License"); 55ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * you may not use this file except in compliance with the License. 65ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * You may obtain a copy of the License at 75ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * 85ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * http://www.apache.org/licenses/LICENSE-2.0 95ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * 105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * Unless required by applicable law or agreed to in writing, software 115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * distributed under the License is distributed on an "AS IS" BASIS, 125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * See the License for the specific language governing permissions and 145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller * limitations under the License. 155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller */ 165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller#pragma version(1) 185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller#pragma rs java_package_name(com.android.ex.carousel); 195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller#pragma rs set_reflect_license() 205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller#include "rs_graphics.rsh" 225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millertypedef struct __attribute__((aligned(4))) Card { 248b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich // *** Update copyCard if you add/remove fields here. 257cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_allocation texture; // basic card texture 267cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_allocation detailTexture; // screen-aligned detail texture 277cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller float2 detailTextureOffset; // offset to add, in screen coordinates 28b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float2 detailLineOffset; // offset to add to detail line, in screen coordinates 295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_mesh geometry; 307cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_matrix4x4 matrix; // custom transform for this card/geometry 317cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller int textureState; // whether or not the primary card texture is loaded. 327cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller int detailTextureState; // whether or not the detail for the card is loaded. 335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int geometryState; // whether or not geometry is loaded 345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int visible; // not bool because of packing bug? 3543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller int64_t textureTimeStamp; // time when this texture was last updated, in seconds 3643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller int64_t detailTextureTimeStamp; // time when this texture was last updated, in seconds 375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} Card_t; 385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millertypedef struct Ray_s { 405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 position; 415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 direction; 425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} Ray; 435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millertypedef struct Plane_s { 4543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float3 point; 4643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float3 normal; 4743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float constant; 4843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller} Plane; 4943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 5043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millertypedef struct Cylinder_s { 5143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float3 center; // center of a y-axis-aligned infinite cylinder 5243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float radius; 5343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller} Cylinder; 5443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millertypedef struct PerspectiveCamera_s { 565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 from; 575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 at; 585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 up; 595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float fov; 605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float aspect; 615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float near; 625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float far; 635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} PerspectiveCamera; 645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 65420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millertypedef struct FragmentShaderConstants_s { 66420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float fadeAmount; 67420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller} FragmentShaderConstants; 68420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Request states. Used for loading 3D object properties from the Java client. 705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Typical properties: texture, geometry and matrices. 715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerenum { 725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_INVALID = 0, // item hasn't been loaded 735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_LOADING, // we've requested an item but are waiting for it to load 745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller STATE_LOADED // item was delivered 755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 774a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney// Detail texture alignments ** THIS LIST MUST MATCH THOSE IN CarouselView.java *** 784a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinneyenum { 794a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Detail is centered vertically with respect to the card **/ 804a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney CENTER_VERTICAL = 1, 814a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Detail is aligned with the top edge of the carousel view **/ 824a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney VIEW_TOP = 1 << 1, 834a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Detail is aligned with the bottom edge of the carousel view (not yet implemented) **/ 844a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney VIEW_BOTTOM = 1 << 2, 854a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Detail is positioned above the card (not yet implemented) **/ 864a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney ABOVE = 1 << 3, 874a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Detail is positioned below the card **/ 884a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney BELOW = 1 << 4, 894a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Mask that selects those bits that control vertical alignment **/ 904a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney VERTICAL_ALIGNMENT_MASK = 0xff, 914a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney 924a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** 934a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * Detail is centered horizontally with respect to either the top or bottom 944a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * extent of the card, depending on whether the detail is above or below the card. 954a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney */ 964a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney CENTER_HORIZONTAL = 1 << 8, 974a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** 984a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * Detail is aligned with the left edge of either the top or the bottom of 994a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * the card, depending on whether the detail is above or below the card. 1004a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney */ 1014a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney LEFT = 1 << 9, 1024a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** 1034a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * Detail is aligned with the right edge of either the top or the bottom of 1044a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * the card, depending on whether the detail is above or below the card. 1054a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * (not yet implemented) 1064a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney */ 1074a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney RIGHT = 1 << 10, 1084a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney /** Mask that selects those bits that control horizontal alignment **/ 1094a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney HORIZONTAL_ALIGNMENT_MASK = 0xff00, 1104a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney}; 1114a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney 1125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Client messages *** THIS LIST MUST MATCH THOSE IN CarouselRS.java. *** 1135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_CARD_SELECTED = 100; 114594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shumastatic const int CMD_CARD_LONGPRESS = 110; 1155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_REQUEST_TEXTURE = 200; 1165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_INVALIDATE_TEXTURE = 210; 1175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_REQUEST_GEOMETRY = 300; 1185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_INVALIDATE_GEOMETRY = 310; 1195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_ANIMATION_STARTED = 400; 1205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int CMD_ANIMATION_FINISHED = 500; 1217cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_REQUEST_DETAIL_TEXTURE = 600; 1227cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_INVALIDATE_DETAIL_TEXTURE = 610; 1237cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerstatic const int CMD_PING = 1000; 1245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Constants 1265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const int ANIMATION_SCALE_TIME = 200; // Time it takes to animate selected card, in ms 1275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float3 SELECTED_SCALE_FACTOR = { 0.2f, 0.2f, 0.2f }; // increase by this % 12843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic const float OVERSCROLL_SLOTS = 1.0f; // amount of allowed overscroll (in slots) 1295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Debug flags 1317cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugCamera = false; // dumps ray/camera coordinate stuff 13243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerconst bool debugSelection = false; // logs selection events 1337cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugTextureLoading = false; // for debugging texture load/unload 1347cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerconst bool debugGeometryLoading = false; // for debugging geometry load/unload 1357c09ccce478100d75e4427d87866ff19d758ae7aJim Shumaconst bool debugDetails = false; // for debugging detail texture geometry 136b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerconst bool debugRendering = false; // flashes display when the frame changes 13743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerconst bool debugRays = false; // shows visual depiction of hit tests, See renderWithRays(). 1385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Exported variables. These will be reflected to Java set_* variables. 1405ce730797a8a7278dfe19dac8a9460b25675fed0Jim MillerCard_t *cards; // array of cards to draw 1418b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich// TODO: remove tmpCards code when allocations support resizing 1428b55d7500c1e5a88c415dae8dcead16b152d7929Jack PalevichCard_t *tmpCards; // temporary array used to prevent flashing when we add more cards 1435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat startAngle; // position of initial card, in radians 1445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint slotCount; // number of positions where a card can be 1455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint cardCount; // number of cards in stack 1465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint visibleSlotCount; // number of visible slots (for culling) 1477cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerint visibleDetailCount; // number of visible detail textures to show 1484fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shumaint prefetchCardCount; // how many cards to keep in memory 1494a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinneyint detailTextureAlignment; // How to align detail texture with respect to card 150fb179e7afd8f02be63061b478b0283e3085fc25fJim Millerbool forceBlendCardsWithZ; // Enable depth buffer while blending 1517c09ccce478100d75e4427d87866ff19d758ae7aJim Shumabool drawRuler; // whether to draw a ruler from the card to the detail texture 1525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat radius; // carousel radius. Cards will be centered on a circle with this radius 1535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerfloat cardRotation; // rotation of card in XY plane relative to Z=1 15483d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinneybool cardsFaceTangent; // whether cards are rotated to face along a tangent to the circle 155c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat swaySensitivity; // how much to rotate cards in relation to the rotation velocity 156c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat frictionCoeff; // how much to slow down the carousel over time 157c0bb8af58ae15674178f2db240283719918c6f28Jim Shumafloat dragFactor; // a scale factor for how sensitive the carousel is to user dragging 158420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerint fadeInDuration; // amount of time (in ms) for smoothly switching out textures 159420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerfloat rezInCardCount; // this controls how rapidly distant card textures will be rez-ed in 160a9e9c4bef076e718094786edfe0290f798e1db4bJim Millerfloat detailFadeRate; // rate at which details fade as they move into the distance 16143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerfloat4 backgroundColor; 162fb179e7afd8f02be63061b478b0283e3085fc25fJim Millerrs_program_store programStoreAlphaZ; 163fb179e7afd8f02be63061b478b0283e3085fc25fJim Millerrs_program_store programStoreAlphaNoZ; 164fb179e7afd8f02be63061b478b0283e3085fc25fJim Millerrs_program_store programStoreNoAlphaZ; 165fb179e7afd8f02be63061b478b0283e3085fc25fJim Millerrs_program_store programStoreNoAlphaNoZ; 166a9e9c4bef076e718094786edfe0290f798e1db4bJim Millerrs_program_fragment singleTextureFragmentProgram; 167a9e9c4bef076e718094786edfe0290f798e1db4bJim Millerrs_program_fragment multiTextureFragmentProgram; 1685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_vertex vertexProgram; 1695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_program_raster rasterProgram; 1705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_allocation defaultTexture; // shown when no other texture is assigned 1715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_allocation loadingTexture; // progress texture (shown when app is fetching the texture) 1729afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Millerrs_allocation backgroundTexture; // drawn behind everything, if set 1737cb0068e59dde61ef0e649735199e5ba31c9c6afJim Millerrs_allocation detailLineTexture; // used to draw detail line (as a quad, of course) 174420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerrs_allocation detailLoadingTexture; // used when detail texture is loading 1755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_mesh defaultGeometry; // shown when no geometry is loaded 1765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_mesh loadingGeometry; // shown when geometry is loading 1775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_matrix4x4 projectionMatrix; 1785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerrs_matrix4x4 modelviewMatrix; 179420b44b8b11ec1c309ea130e69a6876325dbfef9Jim MillerFragmentShaderConstants* shaderConstants; 180420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerrs_sampler linearClamp; 1815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 182594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma#pragma rs export_func(createCards, copyCards, lookAt) 183594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma#pragma rs export_func(doStart, doStop, doMotion, doLongPress, doSelection) 18443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller#pragma rs export_func(setTexture, setGeometry, setDetailTexture, debugCamera) 185a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich#pragma rs export_func(setCarouselRotationAngle) 1865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Local variables 1885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float bias; // rotation bias, in radians. Used for animation and dragging. 1895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool updateCamera; // force a recompute of projection and lookat matrices 1905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool initialized; 1915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float FLT_MAX = 1.0e37; 19243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic int animatedSelection = -1; 19343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic int currentFirstCard = -1; 1945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int64_t touchTime = -1; // time of first touch (see doStart()) 1955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float touchBias = 0.0f; // bias on first touch 19643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic float2 touchPosition; // position of first touch, as defined by last call to doStart(x,y) 197c0bb8af58ae15674178f2db240283719918c6f28Jim Shumastatic float velocity = 0.0f; // angular velocity in radians/s 19843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic bool overscroll = false; // whether we're in the overscroll animation 19943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic bool isDragging = false; // true while the user is dragging the carousel 20043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic float selectionRadius = 50.0f; // movement greater than this will result in no selection 20143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic bool enableSelection = false; // enabled until the user drags outside of selectionRadius 20243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 20343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// Default plane of the carousel. Used for angular motion estimation in view. 20443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic Plane carouselPlane = { 20543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller { 0.0f, 0.0f, 0.0f }, // point 20643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller { 0.0f, 1.0f, 0.0f }, // normal 20743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 0.0f // plane constant (= -dot(P, N)) 20843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller}; 2095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2103df59346f395434454d310b070fff195089fbaf1Jim Miller// Because allocations can't have 0 dimensions, we have to track whether or not 2113df59346f395434454d310b070fff195089fbaf1Jim Miller// cards are valid separately. 2123df59346f395434454d310b070fff195089fbaf1Jim Miller// TODO: Remove this dependency once allocations can have a zero dimension. 2133df59346f395434454d310b070fff195089fbaf1Jim Millerstatic bool cardAllocationValid = false; 2143df59346f395434454d310b070fff195089fbaf1Jim Miller 2155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Default geometry when card.geometry is not set. 2165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float3 cardVertices[4] = { 2175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { -1.0, -1.0, 0.0 }, 2185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { 1.0, -1.0, 0.0 }, 2195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller { 1.0, 1.0, 0.0 }, 2205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {-1.0, 1.0, 0.0 } 2215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 2225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Default camera 2245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic PerspectiveCamera camera = { 2255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {2,2,2}, // from 2265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {0,0,0}, // at 2275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller {0,1,0}, // up 2285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 25.0f, // field of view 2295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1.0f, // aspect 2305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 0.1f, // near 2315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 100.0f // far 2325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller}; 2335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Forward references 2355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int intersectGeometry(Ray* ray, float *bestTime); 236b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 237b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller makeRayForPixelAt(Ray* ray, PerspectiveCamera* cam, float x, float y); 238b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 239b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller makeRayForPixelAt(Ray* ray, rs_matrix4x4* model, rs_matrix4x4* proj, float x, float y); 2405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float deltaTimeInSeconds(int64_t current); 2415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid init() { 2435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // initializers currently have a problem when the variables are exported, so initialize 2445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // globals here. 2452ba04e061b52c488a154739379501dc833e39f79Jim Miller if (debugTextureLoading) rsDebug("Renderscript: init()", 0); 2465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller startAngle = 0.0f; 2475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller slotCount = 10; 2485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller visibleSlotCount = 1; 2497cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller visibleDetailCount = 3; 2505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bias = 0.0f; 2515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller radius = 1.0f; 2525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardRotation = 0.0f; 25383d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney cardsFaceTangent = false; 2545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = true; 2555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller initialized = false; 256b0f070636c29ad178f4e21306f301fe3d20c183bJim Miller backgroundColor = (float4) { 0.0f, 0.0f, 0.0f, 1.0f }; 2573df59346f395434454d310b070fff195089fbaf1Jim Miller cardAllocationValid = false; 2583df59346f395434454d310b070fff195089fbaf1Jim Miller cardCount = 0; 259420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller fadeInDuration = 250; 260420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rezInCardCount = 0.0f; // alpha will ramp to 1.0f over this many cards (0.0f means disabled) 261a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller detailFadeRate = 0.5f; // fade details over this many slot positions. 2625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2648b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevichstatic void updateAllocationVars(Card_t* newcards) 2655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Cards 2678b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich rs_allocation cardAlloc = rsGetAllocation(newcards); 2685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO: use new rsIsObject() 2693df59346f395434454d310b070fff195089fbaf1Jim Miller cardCount = (cardAllocationValid && cardAlloc.p != 0) ? rsAllocationGetDimX(cardAlloc) : 0; 2705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 2715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 2725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid createCards(int n) 2735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 2742ba04e061b52c488a154739379501dc833e39f79Jim Miller if (debugTextureLoading) { 2752ba04e061b52c488a154739379501dc833e39f79Jim Miller rsDebug("*** CreateCards with count", n); 2762ba04e061b52c488a154739379501dc833e39f79Jim Miller } 2772ba04e061b52c488a154739379501dc833e39f79Jim Miller 2782ba04e061b52c488a154739379501dc833e39f79Jim Miller // Since allocations can't have 0-size, we track validity ourselves based on the call to 2792ba04e061b52c488a154739379501dc833e39f79Jim Miller // this method. 2803df59346f395434454d310b070fff195089fbaf1Jim Miller cardAllocationValid = n > 0; 2812ba04e061b52c488a154739379501dc833e39f79Jim Miller 2825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller initialized = false; 2838b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich updateAllocationVars(cards); 2848b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich} 2858b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich 2868b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevichvoid copyCard(Card_t* dest, Card_t * src) 2878b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich{ 2888b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich rsSetObject(&dest->texture, src->texture); 2898b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich rsSetObject(&dest->detailTexture, src->detailTexture); 2908b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->detailTextureOffset = src->detailTextureOffset; 291b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller dest->detailLineOffset = src->detailLineOffset; 2928b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich rsSetObject(&dest->geometry, src->geometry); 2938b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->matrix = src->matrix; 2948b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->textureState = src->textureState; 2958b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->detailTextureState = src->detailTextureState; 2968b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->geometryState = src->geometryState; 2978b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->visible = src->visible; 2988b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->textureTimeStamp = src->textureTimeStamp; 2998b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich dest->detailTextureTimeStamp = src->detailTextureTimeStamp; 3002ba04e061b52c488a154739379501dc833e39f79Jim Miller} 3018b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich 3022ba04e061b52c488a154739379501dc833e39f79Jim Millervoid initCard(Card_t* card) 3038b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich{ 3042ba04e061b52c488a154739379501dc833e39f79Jim Miller static const float2 zero = {0.0f, 0.0f}; 3052ba04e061b52c488a154739379501dc833e39f79Jim Miller rsClearObject(&card->texture); 3062ba04e061b52c488a154739379501dc833e39f79Jim Miller rsClearObject(&card->detailTexture); 3072ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailTextureOffset = zero; 3082ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailLineOffset = zero; 3092ba04e061b52c488a154739379501dc833e39f79Jim Miller rsClearObject(&card->geometry); 3102ba04e061b52c488a154739379501dc833e39f79Jim Miller rsMatrixLoadIdentity(&card->matrix); 3112ba04e061b52c488a154739379501dc833e39f79Jim Miller card->textureState = STATE_INVALID; 3122ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailTextureState = STATE_INVALID; 3132ba04e061b52c488a154739379501dc833e39f79Jim Miller card->geometryState = STATE_INVALID; 3142ba04e061b52c488a154739379501dc833e39f79Jim Miller card->visible = false; 3152ba04e061b52c488a154739379501dc833e39f79Jim Miller card->textureTimeStamp = 0; 3162ba04e061b52c488a154739379501dc833e39f79Jim Miller card->detailTextureTimeStamp = 0; 3172ba04e061b52c488a154739379501dc833e39f79Jim Miller} 3182ba04e061b52c488a154739379501dc833e39f79Jim Miller 3192ba04e061b52c488a154739379501dc833e39f79Jim Millervoid copyCards(int n) 3202ba04e061b52c488a154739379501dc833e39f79Jim Miller{ 3212ba04e061b52c488a154739379501dc833e39f79Jim Miller unsigned int oldsize = cardAllocationValid ? rsAllocationGetDimX(rsGetAllocation(cards)) : 0; 3228b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich unsigned int newsize = rsAllocationGetDimX(rsGetAllocation(tmpCards)); 3232ba04e061b52c488a154739379501dc833e39f79Jim Miller unsigned int copysize = min(oldsize, newsize); 3242ba04e061b52c488a154739379501dc833e39f79Jim Miller 3252ba04e061b52c488a154739379501dc833e39f79Jim Miller // Copy existing cards 3262ba04e061b52c488a154739379501dc833e39f79Jim Miller for (int i = 0; i < copysize; i++) { 3272ba04e061b52c488a154739379501dc833e39f79Jim Miller if (debugTextureLoading) { 3288b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich rsDebug("copying card ", i); 3298b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich } 3302ba04e061b52c488a154739379501dc833e39f79Jim Miller copyCard(tmpCards + i, cards + i); 3312ba04e061b52c488a154739379501dc833e39f79Jim Miller // Release these now so we don't have to wait for GC for cards allocation. 3322ba04e061b52c488a154739379501dc833e39f79Jim Miller // Assumes we're done with the cards allocation structure. 3332ba04e061b52c488a154739379501dc833e39f79Jim Miller rsClearObject(&cards[i].texture); 3342ba04e061b52c488a154739379501dc833e39f79Jim Miller rsClearObject(&cards[i].detailTexture); 3352ba04e061b52c488a154739379501dc833e39f79Jim Miller rsClearObject(&cards[i].geometry); 3362ba04e061b52c488a154739379501dc833e39f79Jim Miller cards[i].textureState = STATE_INVALID; 3372ba04e061b52c488a154739379501dc833e39f79Jim Miller cards[i].detailTextureState = STATE_INVALID; 3382ba04e061b52c488a154739379501dc833e39f79Jim Miller cards[i].geometryState = STATE_INVALID; 3398b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich } 3402ba04e061b52c488a154739379501dc833e39f79Jim Miller 3412ba04e061b52c488a154739379501dc833e39f79Jim Miller // Initialize remaining cards. 3422ba04e061b52c488a154739379501dc833e39f79Jim Miller int first = cardAllocationValid ? min(oldsize, newsize) : 0; 3432ba04e061b52c488a154739379501dc833e39f79Jim Miller for (int k = first; k < newsize; k++) { 3442ba04e061b52c488a154739379501dc833e39f79Jim Miller initCard(tmpCards + k); 3452ba04e061b52c488a154739379501dc833e39f79Jim Miller } 3462ba04e061b52c488a154739379501dc833e39f79Jim Miller 3472ba04e061b52c488a154739379501dc833e39f79Jim Miller // Since allocations can't have 0-size, we use the same trick as createCards() where 3482ba04e061b52c488a154739379501dc833e39f79Jim Miller // we track validity ourselves. Grrr. 3492ba04e061b52c488a154739379501dc833e39f79Jim Miller cardAllocationValid = n > 0; 3502ba04e061b52c488a154739379501dc833e39f79Jim Miller 3518b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich updateAllocationVars(tmpCards); 3525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 354420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller// Computes an alpha value for a card using elapsed time and constant fadeInDuration 355420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerfloat getAnimatedAlpha(int64_t startTime, int64_t currentTime) 356420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller{ 357420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller double timeElapsed = (double) (currentTime - startTime); // in ms 358420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller double alpha = (double) timeElapsed / fadeInDuration; 359420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller return min(1.0f, (float) alpha); 360420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller} 361420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 3625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Return angle for position p. Typically p will be an integer position, but can be fractional. 3635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float cardPosition(float p) 3645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return startAngle + bias + 2.0f * M_PI * p / slotCount; 3665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Return slot for a card in position p. Typically p will be an integer slot, but can be fractional. 3695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float slotPosition(float p) 3705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return startAngle + 2.0f * M_PI * p / slotCount; 3725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 374f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller// Returns total angle for given number of cards 375f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Millerstatic float wedgeAngle(float cards) 376f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller{ 377f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller return cards * 2.0f * M_PI / slotCount; 378f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller} 379f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller 380a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich// convert from carousel rotation angle (in card slot units) to radians. 381a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevichstatic float carouselRotationAngleToRadians(float carouselRotationAngle) 382a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich{ 383a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich return -wedgeAngle(carouselRotationAngle); 384a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich} 385a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich 386a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich// convert from radians to carousel rotation angle (in card slot units). 387a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevichstatic float radiansToCarouselRotationAngle(float angle) 388a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich{ 389a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich return -angle * slotCount / ( 2.0f * M_PI ); 390a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich} 391a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich 392a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich 3935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Return the lowest slot number for a given angular position. 3945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int cardIndex(float angle) 3955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 3965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return floor(angle - startAngle - bias) * slotCount / (2.0f * M_PI); 3975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 3985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 3995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Set basic camera properties: 4005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// from - position of the camera in x,y,z 4015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// at - target we're looking at - used to compute view direction 4025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// up - a normalized vector indicating up (typically { 0, 1, 0}) 4035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// 4045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// NOTE: the view direction and up vector cannot be parallel/antiparallel with each other 4055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid lookAt(float fromX, float fromY, float fromZ, 4065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float atX, float atY, float atZ, 4075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float upX, float upY, float upZ) 4085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.x = fromX; 4105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.y = fromY; 4115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.from.z = fromZ; 4125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.x = atX; 4135ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.y = atY; 4145ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.at.z = atZ; 4155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.x = upX; 4165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.y = upY; 4175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.up.z = upZ; 4185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = true; 4195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Load a projection matrix for the given parameters. This is equivalent to gluPerspective() 4225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void loadPerspectiveMatrix(rs_matrix4x4* matrix, float fovy, float aspect, float near, float far) 4235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadIdentity(matrix); 4255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float top = near * tan((float) (fovy * M_PI / 360.0f)); 4265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bottom = -top; 4275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float left = bottom * aspect; 4285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float right = top * aspect; 4295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadFrustum(matrix, left, right, bottom, top, near, far); 4305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Construct a matrix based on eye point, center and up direction. Based on the 4335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// man page for gluLookat(). Up must be normalized. 4345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void loadLookatMatrix(rs_matrix4x4* matrix, float3 eye, float3 center, float3 up) 4355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 f = normalize(center - eye); 4375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s = normalize(cross(f, up)); 4385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 u = cross(s, f); 4395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float m[16]; 4405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[0] = s.x; 4415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[4] = s.y; 4425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[8] = s.z; 4435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[12] = 0.0f; 4445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[1] = u.x; 4455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[5] = u.y; 4465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[9] = u.z; 4475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[13] = 0.0f; 4485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[2] = -f.x; 4495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[6] = -f.y; 4505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[10] = -f.z; 4515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[14] = 0.0f; 4525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[3] = m[7] = m[11] = 0.0f; 4535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller m[15] = 1.0f; 4545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoad(matrix, m); 4555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixTranslate(matrix, -eye.x, -eye.y, -eye.z); 4565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid setTexture(int n, rs_allocation texture) 4595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4603df59346f395434454d310b070fff195089fbaf1Jim Miller if (n < 0 || n >= cardCount) return; 461c4c6f38bf410af40e10c63b152befd5a39df87c8Jim Miller rsSetObject(&cards[n].texture, texture); 4627cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].textureState = (texture.p != 0) ? STATE_LOADED : STATE_INVALID; 463420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[n].textureTimeStamp = rsUptimeMillis(); 4647cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller} 4657cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 466b378af500b36226635b6343b1d5009ee9af44fc1Jim Millervoid setDetailTexture(int n, float offx, float offy, float loffx, float loffy, rs_allocation texture) 4677cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller{ 4683df59346f395434454d310b070fff195089fbaf1Jim Miller if (n < 0 || n >= cardCount) return; 469c4c6f38bf410af40e10c63b152befd5a39df87c8Jim Miller rsSetObject(&cards[n].detailTexture, texture); 4707cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].detailTextureOffset.x = offx; 4717cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].detailTextureOffset.y = offy; 472b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller cards[n].detailLineOffset.x = loffx; 473b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller cards[n].detailLineOffset.y = loffy; 4747cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[n].detailTextureState = (texture.p != 0) ? STATE_LOADED : STATE_INVALID; 475420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[n].detailTextureTimeStamp = rsUptimeMillis(); 4765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 4785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid setGeometry(int n, rs_mesh geometry) 4795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4803df59346f395434454d310b070fff195089fbaf1Jim Miller if (n < 0 || n >= cardCount) return; 481c4c6f38bf410af40e10c63b152befd5a39df87c8Jim Miller rsSetObject(&cards[n].geometry, geometry); 4825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[n].geometry.p != 0) 4835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].geometryState = STATE_LOADED; 4845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller else 4855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[n].geometryState = STATE_INVALID; 4865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 488a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevichvoid setCarouselRotationAngle(float carouselRotationAngle) { 489a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich bias = carouselRotationAngleToRadians(carouselRotationAngle); 490198a060d650bc849ef0f25b597888fac9546803bJack Palevich} 491198a060d650bc849ef0f25b597888fac9546803bJack Palevich 4925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float3 getAnimatedScaleForSelected() 4935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 4945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t dt = (rsUptimeMillis() - touchTime); 4955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float fraction = (dt < ANIMATION_SCALE_TIME) ? (float) dt / ANIMATION_SCALE_TIME : 1.0f; 4965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float3 one = { 1.0f, 1.0f, 1.0f }; 4975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return one + fraction * SELECTED_SCALE_FACTOR; 4985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 4995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 500c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// The Verhulst logistic function: http://en.wikipedia.org/wiki/Logistic_function 501c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// P(t) = 1 / (1 + e^(-t)) 502c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// Parameter t: Any real number 503c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma// Returns: A float in the range (0,1), with P(0.5)=0 504c0bb8af58ae15674178f2db240283719918c6f28Jim Shumastatic float logistic(float t) { 505af8cf9a3bbe517b604b48e217b00085351ab2496Shih-wei Liao return 1.f / (1.f + exp(-t)); 506c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma} 507c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 5087c09ccce478100d75e4427d87866ff19d758ae7aJim Shumastatic float getSwayAngleForVelocity(float v, bool enableSway) 509c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma{ 5107c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma float sway = 0.0f; 5117c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma 5127c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma if (enableSway) { 513d7fa647e6fa4e832381be5bdd03065f9ea35c3f1Jim Shuma const float range = M_PI * 2./3.; // How far we can deviate from center, peak-to-peak 514d7fa647e6fa4e832381be5bdd03065f9ea35c3f1Jim Shuma sway = range * (logistic(-v * swaySensitivity) - 0.5f); 5157c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma } 516c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 517c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma return sway; 518c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma} 519c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma 5207c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma// matrix: The output matrix. 5217c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma// i: The card we're getting the matrix for. 5227c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma// enableSway: Whether to enable swaying. (We want it on for cards, and off for detail textures.) 5237c09ccce478100d75e4427d87866ff19d758ae7aJim Shumastatic void getMatrixForCard(rs_matrix4x4* matrix, int i, bool enableSway) 5245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 5255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float theta = cardPosition(i); 5267c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma float swayAngle = getSwayAngleForVelocity(velocity, enableSway); 5275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixRotate(matrix, degrees(theta), 0, 1, 0); 5285ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixTranslate(matrix, radius, 0, 0); 52983d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney float rotation = cardRotation + swayAngle; 53083d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney if (!cardsFaceTangent) { 53183d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney rotation -= theta; 53283d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney } 53383d7a5f03e6511372f73e3e4e03a6d403b20125dBryan Mawhinney rsMatrixRotate(matrix, degrees(rotation), 0, 1, 0); 53443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (i == animatedSelection && enableSelection) { 5355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 scale = getAnimatedScaleForSelected(); 5365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixScale(matrix, scale.x, scale.y, scale.z); 5375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 5385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO: apply custom matrix for cards[i].geometry 5395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 5405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 541420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller/* 542420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller * Draws cards around the Carousel. 543420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller * Returns true if we're still animating any property of the cards (e.g. fades). 544420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller */ 545420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerstatic bool drawCards(int64_t currentTime) 5465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 547420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller const float wedgeAngle = 2.0f * M_PI / slotCount; 548420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller const float endAngle = startAngle + visibleSlotCount * wedgeAngle; 549420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller bool stillAnimating = false; 550420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller for (int i = cardCount-1; i >= 0; i--) { 5515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].visible) { 552420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // If this card was recently loaded, this will be < 1.0f until the animation completes 553420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float animatedAlpha = getAnimatedAlpha(cards[i].textureTimeStamp, currentTime); 554420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller if (animatedAlpha < 1.0f) { 555420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller stillAnimating = true; 556420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } 557420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 558420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Compute fade out for cards in the distance 559420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float positionAlpha; 560420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller if (rezInCardCount > 0.0f) { 561420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller positionAlpha = (endAngle - cardPosition(i)) / wedgeAngle; 562420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller positionAlpha = min(1.0f, positionAlpha / rezInCardCount); 563420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } else { 564420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller positionAlpha = 1.0f; 565420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } 566420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 567420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Set alpha for blending between the textures 568420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller shaderConstants->fadeAmount = min(1.0f, animatedAlpha * positionAlpha); 569420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsAllocationMarkDirty(rsGetAllocation(shaderConstants)); 570420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 571b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller // Bind the appropriate shader network. If there's no alpha blend, then 572b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller // switch to single shader for better performance. 573b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const bool loaded = cards[i].textureState == STATE_LOADED; 574b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (shaderConstants->fadeAmount == 1.0f || shaderConstants->fadeAmount < 0.01f) { 575b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindProgramFragment(singleTextureFragmentProgram); 576b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(singleTextureFragmentProgram, 0, 577b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller (loaded && shaderConstants->fadeAmount == 1.0f) ? 578b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller cards[i].texture : loadingTexture); 5795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 580b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindProgramFragment(multiTextureFragmentProgram); 581b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(multiTextureFragmentProgram, 0, loadingTexture); 582b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(multiTextureFragmentProgram, 1, loaded ? 583b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller cards[i].texture : loadingTexture); 5845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 5855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 5865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Draw geometry 5875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_matrix4x4 matrix = modelviewMatrix; 5887c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma getMatrixForCard(&matrix, i, true); 5895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadModelMatrix(&matrix); 5905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].geometryState == STATE_LOADED && cards[i].geometry.p != 0) { 5915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawMesh(cards[i].geometry); 5925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else if (cards[i].geometryState == STATE_LOADING && loadingGeometry.p != 0) { 5935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawMesh(loadingGeometry); 5945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else if (defaultGeometry.p != 0) { 5955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawMesh(defaultGeometry); 5965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 5975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Draw place-holder geometry 5985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawQuad( 5995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[0].x, cardVertices[0].y, cardVertices[0].z, 6005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[1].x, cardVertices[1].y, cardVertices[1].z, 6015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[2].x, cardVertices[2].y, cardVertices[2].z, 6025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cardVertices[3].x, cardVertices[3].y, cardVertices[3].z); 6035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 6055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 606420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller return stillAnimating; 6075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 6085ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 6094fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma/** 6104fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma * Convert projection from normalized coordinates to pixel coordinates. 6114fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma * 6124fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma * @return True on success, false on failure. 6134fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma */ 6144fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shumastatic bool convertNormalizedToPixelCoordinates(float4 *screenCoord, float width, float height) { 6154fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // This is probably cheaper than pre-multiplying with another matrix. 6164fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma if (screenCoord->w == 0.0f) { 6174fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma rsDebug("Bad transform while converting from normalized to pixel coordinates: ", 6184fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord); 6194fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma return false; 6204fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 6214fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma *screenCoord *= 1.0f / screenCoord->w; 6224fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->x += 1.0f; 6234fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->y += 1.0f; 6244fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->z += 1.0f; 6254fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->x = round(screenCoord->x * 0.5f * width); 6264fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->y = round(screenCoord->y * 0.5f * height); 6274fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord->z = - 0.5f * screenCoord->z; 6284fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma return true; 6294fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma} 6304fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma 6317cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller/* 6327cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller * Draws a screen-aligned card with the exact dimensions from the detail texture. 6334a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney * This is used to display information about the object being displayed. 634420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller * Returns true if we're still animating any property of the cards (e.g. fades). 6357cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller */ 636420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerstatic bool drawDetails(int64_t currentTime) 6377cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller{ 6387cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float width = rsgGetWidth(); 6397cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float height = rsgGetHeight(); 6407cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 641420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller bool stillAnimating = false; 642420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 6437cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // We'll be drawing in screen space, sampled on pixel centers 6447cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_matrix4x4 projection, model; 6457cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsMatrixLoadOrtho(&projection, 0.0f, width, 0.0f, height, 0.0f, 1.0f); 6467cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgProgramVertexLoadProjectionMatrix(&projection); 6477cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsMatrixLoadIdentity(&model); 6487cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgProgramVertexLoadModelMatrix(&model); 6497cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller updateCamera = true; // we messed with the projection matrix. Reload on next pass... 6507cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 6517cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float yPadding = 5.0f; // draw line this far (in pixels) away from top and geometry 6527cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 653420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // This can be done once... 654a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller rsgBindTexture(multiTextureFragmentProgram, 0, detailLoadingTexture); 655a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 656a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float wedgeAngle = 2.0f * M_PI / slotCount; 657a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller // Angle where details start fading from 1.0f 658a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float startDetailFadeAngle = startAngle + (visibleDetailCount - 1) * wedgeAngle; 659a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller // Angle where detail alpha is 0.0f 660a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float endDetailFadeAngle = startDetailFadeAngle + detailFadeRate * wedgeAngle; 661420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 662a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller for (int i = cardCount-1; i >= 0; --i) { 6637cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (cards[i].visible) { 6647cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (cards[i].detailTextureState == STATE_LOADED && cards[i].detailTexture.p != 0) { 6657cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float lineWidth = rsAllocationGetDimX(detailLineTexture); 6667cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 6674fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // Compute position in screen space of top corner or bottom corner of card 668af8cf9a3bbe517b604b48e217b00085351ab2496Shih-wei Liao rsMatrixLoad(&model, &modelviewMatrix); 6697c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma getMatrixForCard(&model, i, false); 6707cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rs_matrix4x4 matrix; 6717cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsMatrixLoadMultiply(&matrix, &projectionMatrix, &model); 672d443c88da4c7cf1947c12b26f111cb899cc8afe4Jim Miller 6734fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma int indexLeft, indexRight; 6744fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma float4 screenCoord; 6754a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & BELOW) { 6764fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexLeft = 0; 6774fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexRight = 1; 6784fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } else { 6794fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexLeft = 3; 6804fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma indexRight = 2; 6814fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 6824fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma float4 screenCoordLeft = rsMatrixMultiply(&matrix, cardVertices[indexLeft]); 6834fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma float4 screenCoordRight = rsMatrixMultiply(&matrix, cardVertices[indexRight]); 6844fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma if (screenCoordLeft.w == 0.0f || screenCoordRight.w == 0.0f) { 6857cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // this shouldn't happen 6867cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsDebug("Bad transform: ", screenCoord); 6877cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller continue; 6887cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 6894a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & CENTER_VERTICAL) { 6904a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney // If we're centering vertically, we'll need the other vertices too 6914a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & BELOW) { 6924a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney indexLeft = 3; 6934a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney indexRight = 2; 6944a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } else { 6954a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney indexLeft = 0; 6964a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney indexRight = 1; 6974a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } 6984a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney float4 otherScreenLeft = rsMatrixMultiply(&matrix, cardVertices[indexLeft]); 6994a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney float4 otherScreenRight = rsMatrixMultiply(&matrix, cardVertices[indexRight]); 7004a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney screenCoordRight.y = screenCoordLeft.y = (screenCoordLeft.y + screenCoordRight.y 7014a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney + otherScreenLeft.y + otherScreenRight.y) / 4.; 7024a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } 7034fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma (void) convertNormalizedToPixelCoordinates(&screenCoordLeft, width, height); 7044fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma (void) convertNormalizedToPixelCoordinates(&screenCoordRight, width, height); 7054fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma if (debugDetails) { 7064fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma RS_DEBUG(screenCoordLeft); 7074fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma RS_DEBUG(screenCoordRight); 7084fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 7094fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord = screenCoordLeft; 7104a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & BELOW) { 7114fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma screenCoord.y = min(screenCoordLeft.y, screenCoordRight.y); 7124a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } else if (detailTextureAlignment & CENTER_VERTICAL) { 713fe38385c1e5ce443adb962c066adeea185ad3d74Bryan Mawhinney screenCoord.y -= round(rsAllocationGetDimY(cards[i].detailTexture) / 2.0f); 7144fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 7154a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & CENTER_HORIZONTAL) { 716fe38385c1e5ce443adb962c066adeea185ad3d74Bryan Mawhinney screenCoord.x += round((screenCoordRight.x - screenCoordLeft.x) / 2.0f - 717fe38385c1e5ce443adb962c066adeea185ad3d74Bryan Mawhinney rsAllocationGetDimX(cards[i].detailTexture) / 2.0f); 7184fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma } 7197cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 720420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Compute alpha for gradually fading in details. Applied to both line and 721420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // detail texture. TODO: use a separate background texture for line. 722420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller float animatedAlpha = getAnimatedAlpha(cards[i].detailTextureTimeStamp, currentTime); 723420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller if (animatedAlpha < 1.0f) { 724420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller stillAnimating = true; 725420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller } 726420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 727a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller // Compute alpha based on position. We fade cards quickly so they cannot overlap 728a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller float positionAlpha = ((float)endDetailFadeAngle - cardPosition(i)) 729a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller / (endDetailFadeAngle - startDetailFadeAngle); 730a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller positionAlpha = max(0.0f, positionAlpha); 731a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller positionAlpha = min(1.0f, positionAlpha); 732a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 733a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller const float blendedAlpha = min(1.0f, animatedAlpha * positionAlpha); 734a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 735b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (blendedAlpha == 0.0f) continue; // nothing to draw 736b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (blendedAlpha == 1.0f) { 737b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindProgramFragment(singleTextureFragmentProgram); 738b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 739b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindProgramFragment(multiTextureFragmentProgram); 740b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 741a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller 742420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller // Set alpha for blending between the textures 743a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller shaderConstants->fadeAmount = blendedAlpha; 744420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller rsAllocationMarkDirty(rsGetAllocation(shaderConstants)); 745420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller 7464a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney // Draw line from the card to the detail texture. 7474a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney // The line is drawn from the top or bottom left of the card 7484a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney // to either the top of the screen or the top of the detail 7494a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney // texture, depending on detailTextureAlignment. 7507c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma if (drawRuler) { 7514a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney float rulerTop; 7524a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney float rulerBottom; 7534a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney if (detailTextureAlignment & BELOW) { 7544a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney rulerTop = screenCoord.y; 7554a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney rulerBottom = 0; 7564a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } else { 7574a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney rulerTop = height; 7584a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney rulerBottom = screenCoord.y; 7594a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney } 7607c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma const float halfWidth = lineWidth * 0.5f; 761b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float x0 = cards[i].detailLineOffset.x + screenCoord.x - halfWidth; 762b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float x1 = cards[i].detailLineOffset.x + screenCoord.x + halfWidth; 763b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y0 = rulerBottom + yPadding; 764b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y1 = rulerTop - yPadding - cards[i].detailLineOffset.y; 765b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 766b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (blendedAlpha == 1.0f) { 767b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(singleTextureFragmentProgram, 0, detailLineTexture); 768b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 769b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(multiTextureFragmentProgram, 1, detailLineTexture); 770b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 771b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgDrawQuad(x0, y0, screenCoord.z, x1, y0, screenCoord.z, 772b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller x1, y1, screenCoord.z, x0, y1, screenCoord.z); 7737c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma } 7747cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 7757cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // Draw the detail texture next to it using the offsets provided. 7767cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float textureWidth = rsAllocationGetDimX(cards[i].detailTexture); 7777cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float textureHeight = rsAllocationGetDimY(cards[i].detailTexture); 7787cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float offx = cards[i].detailTextureOffset.x; 7797cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller const float offy = -cards[i].detailTextureOffset.y; 7804a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney const float textureTop = (detailTextureAlignment & VIEW_TOP) 7814a8736e22d7b40ab9dfa3fbd8a10de92144912b3Bryan Mawhinney ? height : screenCoord.y; 782b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float x0 = cards[i].detailLineOffset.x + screenCoord.x + offx; 783b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float x1 = cards[i].detailLineOffset.x + screenCoord.x + offx + textureWidth; 784b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y0 = textureTop + offy - textureHeight - cards[i].detailLineOffset.y; 785b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float y1 = textureTop + offy - cards[i].detailLineOffset.y; 786b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 787b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (blendedAlpha == 1.0f) { 788b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(singleTextureFragmentProgram, 0, cards[i].detailTexture); 789b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 790b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindTexture(multiTextureFragmentProgram, 1, cards[i].detailTexture); 791b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 792b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgDrawQuad(x0, y0, screenCoord.z, x1, y0, screenCoord.z, 793b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller x1, y1, screenCoord.z, x0, y1, screenCoord.z); 7947cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 7957cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 7967cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 797420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller return stillAnimating; 7987cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller} 7997cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller 8009afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Millerstatic void drawBackground() 8019afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller{ 802b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller static bool toggle; 8039afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller if (backgroundTexture.p != 0) { 8049afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearDepth(1.0f); 8059afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rs_matrix4x4 projection, model; 8069afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsMatrixLoadOrtho(&projection, -1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f); 8079afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgProgramVertexLoadProjectionMatrix(&projection); 8089afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsMatrixLoadIdentity(&model); 8099afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgProgramVertexLoadModelMatrix(&model); 810a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller rsgBindTexture(singleTextureFragmentProgram, 0, backgroundTexture); 8119afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller float z = -0.9999f; 8129afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgDrawQuad( 8139afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[0].x, cardVertices[0].y, z, 8149afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[1].x, cardVertices[1].y, z, 8159afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[2].x, cardVertices[2].y, z, 8169afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller cardVertices[3].x, cardVertices[3].y, z); 8179afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller updateCamera = true; // we mucked with the matrix. 8189afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } else { 8199afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller rsgClearDepth(1.0f); 820b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (debugRendering) { // for debugging - flash the screen so we know we're still rendering 821b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgClearColor(toggle ? backgroundColor.x : 1.0f, 822b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller toggle ? backgroundColor.y : 0.0f, 823b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller toggle ? backgroundColor.z : 0.0f, 824b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller backgroundColor.w); 8259afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller toggle = !toggle; 826b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } else { 8277cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller rsgClearColor(backgroundColor.x, backgroundColor.y, backgroundColor.z, 8287cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller backgroundColor.w); 8299afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } 8309afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller } 8319afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller} 8329afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller 8335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void updateCameraMatrix(float width, float height) 8345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 8355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float aspect = width / height; 8365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (aspect != camera.aspect || updateCamera) { 8375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller camera.aspect = aspect; 8385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller loadPerspectiveMatrix(&projectionMatrix, camera.fov, camera.aspect, camera.near, camera.far); 8395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadProjectionMatrix(&projectionMatrix); 8405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller loadLookatMatrix(&modelviewMatrix, camera.from, camera.at, camera.up); 8425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgProgramVertexLoadModelMatrix(&modelviewMatrix); 8435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCamera = false; 8445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8455ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 8465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 8485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Behavior/Physics 8495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 8505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int64_t lastTime = 0L; // keep track of how much time has passed between frames 8515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float2 lastPosition; 8525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool animating = false; 8535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float velocityThreshold = 0.1f * M_PI / 180.0f; 8545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float velocityTracker; 8555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int velocityTrackerCount; 8565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float mass = 5.0f; // kg 8575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float G = 9.80f; // gravity constant, in m/s 8595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic const float springConstant = 0.0f; 8605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float dragFunction(float x, float y) 8625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 8635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return dragFactor * ((x - lastPosition.x) / rsgGetWidth()) * M_PI; 8645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 8655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic float deltaTimeInSeconds(int64_t current) 8675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 8685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return (lastTime > 0L) ? (float) (current - lastTime) / 1000.0f : 0.0f; 8695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 8705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 8715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint doSelection(float x, float y) 8725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 8735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller Ray ray; 874b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (makeRayForPixelAt(&ray, &camera, x, y)) { 8755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bestTime = FLT_MAX; 8765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return intersectGeometry(&ray, &bestTime); 8775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 8785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return -1; 8795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 8805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 88143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millervoid sendAnimationStarted() { 88243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller rsSendToClient(CMD_ANIMATION_STARTED); 88343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller} 88443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 885a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevichvoid sendAnimationFinished() { 886a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich float data[1]; 887a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich data[0] = radiansToCarouselRotationAngle(bias); 888a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich rsSendToClient(CMD_ANIMATION_FINISHED, (int*) data, sizeof(data)); 889a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich} 890a84feeb7e4dc1a75ec6d0b1f2494893987fc3ca3Jack Palevich 8915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid doStart(float x, float y) 8925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 89343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller touchPosition = lastPosition = (float2) { x, y }; 8945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocity = 0.0f; 8955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTracker = 0.0f; 8965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocityTrackerCount = 0; 8975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller touchTime = rsUptimeMillis(); 8985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller touchBias = bias; 89943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller isDragging = true; 90043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller enableSelection = true; 90143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller animatedSelection = doSelection(x, y); // used to provide visual feedback on touch 9025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 9035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid doStop(float x, float y) 9055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 9065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t currentTime = rsUptimeMillis(); 9078b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich updateAllocationVars(cards); 90843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 90943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (enableSelection) { 9105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int data[1]; 91143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller int selection = doSelection(x, y); 91243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (selection != -1) { 91343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (debugSelection) rsDebug("Selected item on doStop():", selection); 91443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller data[0] = selection; 91543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller rsSendToClientBlocking(CMD_CARD_SELECTED, data, sizeof(data)); 91643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 91743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller animating = false; 9185ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 91943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // TODO: move velocity tracking to Java 9205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller velocity = velocityTrackerCount > 0 ? 9215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller (velocityTracker / velocityTrackerCount) : 0.0f; // avg velocity 9225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (fabs(velocity) > velocityThreshold) { 9235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller animating = true; 9245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 9255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 92643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller enableSelection = false; 9275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastTime = rsUptimeMillis(); 92843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller isDragging = false; 9295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 9305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 931594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shumavoid doLongPress() 932594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma{ 933594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma int64_t currentTime = rsUptimeMillis(); 934594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma updateAllocationVars(cards); 93543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Selection happens for most recent position detected in doMotion() 93643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller int selection = doSelection(lastPosition.x, lastPosition.y); 93743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (selection != -1) { 93843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (debugSelection) rsDebug("doLongPress(), selection = ", selection); 939594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma int data[1]; 94043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller data[0] = selection; 941594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma rsSendToClientBlocking(CMD_CARD_LONGPRESS, data, sizeof(data)); 942594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma } 943594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma lastTime = rsUptimeMillis(); 944594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma} 945594ff62c170509c0d69b30f4c2a5e71d4799a9c8Jim Shuma 9465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millervoid doMotion(float x, float y) 9475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 94843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float firstBias = wedgeAngle(0.0f); 94943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float lastBias = -max(0.0f, wedgeAngle(cardCount - visibleDetailCount)); 9505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t currentTime = rsUptimeMillis(); 9515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float deltaOmega = dragFunction(x, y); 95243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (!enableSelection) { 95343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bias += deltaOmega; 95443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bias = clamp(bias, lastBias - wedgeAngle(OVERSCROLL_SLOTS), 95543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller firstBias + wedgeAngle(OVERSCROLL_SLOTS)); 95643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 95743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float2 delta = (float2) { x, y } - touchPosition; 95843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float distance = sqrt(dot(delta, delta)); 95943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bool inside = (distance < selectionRadius); 96043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller enableSelection &= inside; 96143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller lastPosition = (float2) { x, y }; 9625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float dt = deltaTimeInSeconds(currentTime); 9635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (dt > 0.0f) { 9645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float v = deltaOmega / dt; 96543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller velocityTracker += v; 96643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller velocityTrackerCount++; 9675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 968c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma velocity = velocityTrackerCount > 0 ? 969c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma (velocityTracker / velocityTrackerCount) : 0.0f; // avg velocity 9705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller lastTime = currentTime; 9715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 9725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 9745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Hit detection using ray casting. 9755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 97643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic const float EPSILON = 1.0e-6f; 97743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic const float tmin = 0.0f; 9785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool 98043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim MillerrayTriangleIntersect(Ray* ray, float3 p0, float3 p1, float3 p2, float* tout) 9815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 9825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 e1 = p1 - p0; 9835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 e2 = p2 - p0; 9845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s1 = cross(ray->direction, e2); 9855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float div = dot(s1, e1); 9875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (div == 0.0f) return false; // ray is parallel to plane. 9885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 d = ray->position - p0; 9905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float invDiv = 1.0f / div; 9915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float u = dot(d, s1) * invDiv; 9935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (u < 0.0f || u > 1.0f) return false; 9945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 s2 = cross(d, e1); 9965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float v = dot(ray->direction, s2) * invDiv; 9975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if ( v < 0.0f || (u+v) > 1.0f) return false; 9985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 9995ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float t = dot(e2, s2) * invDiv; 10005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (t < tmin || t > *tout) 10015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return false; 10025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller *tout = t; 10035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return true; 10045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 10055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 100643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 100743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// Computes ray/plane intersection. Returns false if no intersection found. 100843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 1009b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool 101043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim MillerrayPlaneIntersect(Ray* ray, Plane* plane, float* tout) 1011b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller{ 101243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float denom = dot(ray->direction, plane->normal); 101343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (fabs(denom) > EPSILON) { 101443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float t = - (plane->constant + dot(ray->position, plane->normal)) / denom; 101543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (t > tmin && t < *tout) { 101643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller *tout = t; 101743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return true; 101843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 101943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 102043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return false; 1021b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller} 1022b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 102343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 102443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// Computes ray/cylindr intersection. There are 0, 1 or 2 hits. 102543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// Returns true and sets *tout to the closest point or 102643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// returns false if no intersection found. 102743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller//////////////////////////////////////////////////////////////////////////////////////////////////// 1028b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool 102943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim MillerrayCylinderIntersect(Ray* ray, Cylinder* cylinder, float* tout) 1030b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller{ 103143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float A = ray->direction.x * ray->direction.x + ray->direction.z * ray->direction.z; 103243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (A < EPSILON) return false; // ray misses 103343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 103443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Compute quadratic equation coefficients 103543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float B = 2.0f * (ray->direction.x * ray->position.x 103643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller + ray->direction.z * ray->position.z); 103743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float C = ray->position.x * ray->position.x 103843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller + ray->position.z * ray->position.z 103943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller - cylinder->radius * cylinder->radius; 104043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float disc = B*B - 4*A*C; 104143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 104243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (disc < 0.0f) return false; // ray misses 104343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller disc = sqrt(disc); 104443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float denom = 2.0f * A; 104543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 104643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Nearest point 104743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float t1 = (-B - disc) / denom; 104843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (t1 > tmin && t1 < *tout) { 104943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller *tout = t1; 105043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return true; 105143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 105243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 105343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Far point 105443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float t2 = (-B + disc) / denom; 105543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (t2 > tmin && t2 < *tout) { 105643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller *tout = t2; 105743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return true; 105843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 105943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return false; 1060b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller} 1061b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 1062b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller// Creates a ray for an Android pixel coordinate given a camera, ray and coordinates. 10635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Note that the Y coordinate is opposite of GL rendering coordinates. 1064b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 1065b378af500b36226635b6343b1d5009ee9af44fc1Jim MillermakeRayForPixelAt(Ray* ray, PerspectiveCamera* cam, float x, float y) 10665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 10675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (debugCamera) { 10685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("------ makeRay() -------", 0); 1069b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Camera.from:", cam->from); 1070b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Camera.at:", cam->at); 1071b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Camera.dir:", normalize(cam->at - cam->from)); 10725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 10745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Vector math. This has the potential to be much faster. 10755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO: pre-compute lowerLeftRay, du, dv to eliminate most of this math. 1076b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float u = x / rsgGetWidth(); 1077b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float v = 1.0f - (y / rsgGetHeight()); 1078b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float aspect = (float) rsgGetWidth() / rsgGetHeight(); 1079b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float tanfov2 = 2.0f * tan(radians(cam->fov / 2.0f)); 1080b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 dir = normalize(cam->at - cam->from); 1081b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 du = tanfov2 * normalize(cross(dir, cam->up)); 1082b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 dv = tanfov2 * normalize(cross(du, dir)); 1083b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller du *= aspect; 1084b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float3 lowerLeftRay = dir - (0.5f * du) - (0.5f * dv); 1085b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayPoint = cam->from; 1086b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayDir = normalize(lowerLeftRay + u*du + v*dv); 1087b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (debugCamera) { 1088b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Ray direction (vector math) = ", rayDir); 10895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 10905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1091b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->position = rayPoint; 1092b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->direction = rayDir; 1093b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller return true; 1094b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller} 1095b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 1096b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller// Creates a ray for an Android pixel coordinate given a model view and projection matrix. 1097b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller// Note that the Y coordinate is opposite of GL rendering coordinates. 1098b378af500b36226635b6343b1d5009ee9af44fc1Jim Millerstatic bool __attribute__((overloadable)) 1099b378af500b36226635b6343b1d5009ee9af44fc1Jim MillermakeRayForPixelAt(Ray* ray, rs_matrix4x4* model, rs_matrix4x4* proj, float x, float y) 1100b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller{ 1101b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rs_matrix4x4 pm = *model; 1102b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsMatrixLoadMultiply(&pm, proj, model); 1103b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (!rsMatrixInverse(&pm)) { 1104b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("ERROR: SINGULAR PM MATRIX", 0); 1105b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller return false; 11065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 1107b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float width = rsgGetWidth(); 1108b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float height = rsgGetHeight(); 1109b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float winx = 2.0f * x / width - 1.0f; 1110b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float winy = 2.0f * y / height - 1.0f; 1111b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 1112b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float4 eye = { 0.0f, 0.0f, 0.0f, 1.0f }; 1113b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller float4 at = { winx, winy, 1.0f, 1.0f }; 1114b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 1115b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller eye = rsMatrixMultiply(&pm, eye); 1116b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller eye *= 1.0f / eye.w; 11175ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1118b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller at = rsMatrixMultiply(&pm, at); 1119b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller at *= 1.0f / at.w; 1120b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller 1121b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayPoint = { eye.x, eye.y, eye.z }; 1122b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 atPoint = { at.x, at.y, at.z }; 1123b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller const float3 rayDir = normalize(atPoint - rayPoint); 1124b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (debugCamera) { 1125b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("winx: ", winx); 1126b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("winy: ", winy); 1127b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Ray position (transformed) = ", eye); 1128b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsDebug("Ray direction (transformed) = ", rayDir); 1129b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller } 1130b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->position = rayPoint; 1131b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller ray->direction = rayDir; 11325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return true; 11335ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 11345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 11355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int intersectGeometry(Ray* ray, float *bestTime) 11365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 11375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int hit = -1; 11385ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int id = 0; id < cardCount; id++) { 11395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[id].visible) { 11405ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rs_matrix4x4 matrix; 11415ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float3 p[4]; 11425ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 11435ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Transform card vertices to world space 11445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsMatrixLoadIdentity(&matrix); 11457c09ccce478100d75e4427d87866ff19d758ae7aJim Shuma getMatrixForCard(&matrix, id, true); 11465ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int vertex = 0; vertex < 4; vertex++) { 11475ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float4 tmp = rsMatrixMultiply(&matrix, cardVertices[vertex]); 11485ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (tmp.w != 0.0f) { 11495ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].x = tmp.x; 11505ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].y = tmp.y; 11515ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex].z = tmp.z; 11525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller p[vertex] *= 1.0f / tmp.w; 11535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 11545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsDebug("Bad w coord: ", tmp); 11555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 11585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Intersect card geometry 11595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (rayTriangleIntersect(ray, p[0], p[1], p[2], bestTime) 11605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller || rayTriangleIntersect(ray, p[2], p[3], p[0], bestTime)) { 11615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller hit = id; 11625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 11655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return hit; 11665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 11675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 11685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// This method computes the position of all the cards by updating bias based on a 116943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// simple physics model. If the cards are still in motion, returns true. 117043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic bool doPhysics(float dt) 117143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller{ 117243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float minStepTime = 1.0f / 300.0f; // ~5 steps per frame 117343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const int N = (dt > minStepTime) ? (1 + round(dt / minStepTime)) : 1; 117443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller dt /= N; 117543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller for (int i = 0; i < N; i++) { 117643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Force friction - always opposes motion 117743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float Ff = -frictionCoeff * velocity; 117843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 117943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // Restoring force to match cards with slots 118043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float theta = startAngle + bias; 118143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float dtheta = 2.0f * M_PI / slotCount; 118243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float position = theta / dtheta; 118343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float fraction = position - floor(position); // fractional position between slots 118443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float x; 118543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (fraction > 0.5f) { 118643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller x = - (1.0f - fraction); 118743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 118843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller x = fraction; 118943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 119043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float Fr = - springConstant * x; 119143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 119243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // compute velocity 119343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller const float momentum = mass * velocity + (Ff + Fr)*dt; 119443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller velocity = momentum / mass; 119543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bias += velocity * dt; 119643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 119743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return fabs(velocity) > velocityThreshold; 119843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller} 119943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 120043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Millerstatic float easeOut(float x) 120143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller{ 120243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return x; 120343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller} 120443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 120543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller// Computes the next value for bias using the current animation (physics or overscroll) 12065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic bool updateNextPosition(int64_t currentTime) 12075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 120843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller static const float biasMin = 1e-4f; // close enough if we're within this margin of result 12095ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 121043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float dt = deltaTimeInSeconds(currentTime); 12115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 121243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (dt <= 0.0f) { 121343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (debugRendering) rsDebug("Time delta was <= 0", dt); 121443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return true; 12155ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 12165ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1217f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller const float firstBias = wedgeAngle(0.0f); 1218f7c724da4bb4fcd3cd02add04a7bb8052e07e4c3Jim Miller const float lastBias = -max(0.0f, wedgeAngle(cardCount - visibleDetailCount)); 121943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bool stillAnimating = false; 122043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (overscroll) { 122143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (bias > firstBias) { 122243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bias -= 4.0f * dt * easeOut((bias - firstBias) * 2.0f); 122343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (fabs(bias - firstBias) < biasMin) { 122443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bias = firstBias; 122543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 122643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller stillAnimating = true; 122743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 122843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else if (bias < lastBias) { 122943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bias += 4.0f * dt * easeOut((lastBias - bias) * 2.0f); 123043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (fabs(bias - lastBias) < biasMin) { 123143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bias = lastBias; 123243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 123343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller stillAnimating = true; 123443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 123543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 123643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller overscroll = false; 123743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 123843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 123943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller stillAnimating = doPhysics(dt); 124043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller overscroll = bias > firstBias || bias < lastBias; 1241cfe41767a3596a65eef91b6f68286fd0f916a4c7Jim Miller if (overscroll) { 1242cfe41767a3596a65eef91b6f68286fd0f916a4c7Jim Miller velocity = 0.0f; // prevent bouncing due to v > 0 after overscroll animation. 1243cfe41767a3596a65eef91b6f68286fd0f916a4c7Jim Miller } 1244c0bb8af58ae15674178f2db240283719918c6f28Jim Shuma } 124543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller float newbias = clamp(bias, lastBias - wedgeAngle(OVERSCROLL_SLOTS), 124643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller firstBias + wedgeAngle(OVERSCROLL_SLOTS)); 124743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (newbias != bias) { // we clamped 124843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller velocity = 0.0f; 124943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller overscroll = true; 125043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 125143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bias = newbias; 125243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return stillAnimating; 12535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 12545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 12555ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Cull cards based on visibility and visibleSlotCount. 12565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// If visibleSlotCount is > 0, then only show those slots and cull the rest. 12575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Otherwise, it should cull based on bounds of geometry. 12585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic int cullCards() 12595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 12604fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // TODO(jshuma): Instead of fully fetching prefetchCardCount cards, make a distinction between 12614fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // STATE_LOADED and a new STATE_PRELOADING, which will keep the textures loaded but will not 12624fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma // attempt to actually draw them. 12634fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma const int prefetchCardCountPerSide = prefetchCardCount / 2; 12644fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma const float thetaFirst = slotPosition(-prefetchCardCountPerSide); 1265198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaSelected = slotPosition(0); 1266198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaHalfAngle = (thetaSelected - thetaFirst) * 0.5f; 1267198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaSelectedLow = thetaSelected - thetaHalfAngle; 1268198a060d650bc849ef0f25b597888fac9546803bJack Palevich const float thetaSelectedHigh = thetaSelected + thetaHalfAngle; 12694fe6ea729d1fc44c8126de7a92a710c3885fb2ecJim Shuma const float thetaLast = slotPosition(visibleSlotCount - 1 + prefetchCardCountPerSide); 12705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 12715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int count = 0; 12725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < cardCount; i++) { 12735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (visibleSlotCount > 0) { 12745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // If visibleSlotCount is specified, then only show up to visibleSlotCount cards. 12755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float p = cardPosition(i); 12765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (p >= thetaFirst && p < thetaLast) { 12775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].visible = true; 12785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller count++; 12795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 12805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].visible = false; 12815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 12825ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 12835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // Cull the rest of the cards using bounding box of geometry. 12845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // TODO 12855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].visible = true; 12865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller count++; 12875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 12885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 12895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller return count; 12905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 12915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 12925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Request texture/geometry for items that have come into view 12935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// or doesn't have a texture yet. 1294420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Millerstatic void updateCardResources(int64_t currentTime) 12955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 1296a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller for (int i = cardCount-1; i >= 0; --i) { 12975ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int data[1]; 12985ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].visible) { 129943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (debugTextureLoading) rsDebug("*** Texture stamp: ", (int)cards[i].textureTimeStamp); 13002ba04e061b52c488a154739379501dc833e39f79Jim Miller 13015ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // request texture from client if not loaded 13025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].textureState == STATE_INVALID) { 13035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 13045ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data)); 13055ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 13065ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].textureState = STATE_LOADING; 13075ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 13087cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_TEXTURE", 0); 13097cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 13107cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 13117cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // request detail texture from client if not loaded 13127cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (cards[i].detailTextureState == STATE_INVALID) { 13137cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller data[0] = i; 13147cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller bool enqueued = rsSendToClient(CMD_REQUEST_DETAIL_TEXTURE, data, sizeof(data)); 13157cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (enqueued) { 13167cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[i].detailTextureState = STATE_LOADING; 13177cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } else { 13187cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_DETAIL_TEXTURE", 0); 13195ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // request geometry from client if not loaded 13225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (cards[i].geometryState == STATE_INVALID) { 13235ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 13245ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_REQUEST_GEOMETRY, data, sizeof(data)); 13255ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 13265ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].geometryState = STATE_LOADING; 13275ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 13287cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugGeometryLoading) rsDebug("Couldn't send CMD_REQUEST_GEOMETRY", 0); 13295ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 13325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // ask the host to remove the texture 1333dce9af330efceae2b8d1d7c25e7e236b4e21719bJack Palevich if (cards[i].textureState != STATE_INVALID) { 13345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 13355ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_TEXTURE, data, sizeof(data)); 13365ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 13375ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].textureState = STATE_INVALID; 1338420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[i].textureTimeStamp = currentTime; 13395ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 13407cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Couldn't send CMD_INVALIDATE_TEXTURE", 0); 13417cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 13427cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 13437cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller // ask the host to remove the detail texture 13447cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (cards[i].detailTextureState != STATE_INVALID) { 13457cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller data[0] = i; 13467cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_DETAIL_TEXTURE, data, sizeof(data)); 13477cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (enqueued) { 13487cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller cards[i].detailTextureState = STATE_INVALID; 1349420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller cards[i].detailTextureTimeStamp = currentTime; 13507cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } else { 13517cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugTextureLoading) rsDebug("Can't send CMD_INVALIDATE_DETAIL_TEXTURE", 0); 13525ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13535ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13545ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller // ask the host to remove the geometry 1355dce9af330efceae2b8d1d7c25e7e236b4e21719bJack Palevich if (cards[i].geometryState != STATE_INVALID) { 13565ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller data[0] = i; 13575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller bool enqueued = rsSendToClient(CMD_INVALIDATE_GEOMETRY, data, sizeof(data)); 13585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (enqueued) { 13595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cards[i].geometryState = STATE_INVALID; 13605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } else { 13617cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller if (debugGeometryLoading) rsDebug("Couldn't send CMD_INVALIDATE_GEOMETRY", 0); 13625ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13645ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13655ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13665ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 13675ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 13685ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// Places dots on geometry to visually inspect that objects can be seen by rays. 13695ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// NOTE: the color of the dot is somewhat random, as it depends on texture of previously-rendered 13705ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller// card. 13715ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerstatic void renderWithRays() 13725ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller{ 13735ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float w = rsgGetWidth(); 13745ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const float h = rsgGetHeight(); 13755ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller const int skip = 8; 13765ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller color(1.0f, 0.0f, 0.0f, 1.0f); 13775ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int j = 0; j < (int) h; j+=skip) { 13785ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float posY = (float) j; 13795ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller for (int i = 0; i < (int) w; i+=skip) { 13805ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float posX = (float) i; 13815ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller Ray ray; 1382b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller if (makeRayForPixelAt(&ray, &camera, posX, posY)) { 13835ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller float bestTime = FLT_MAX; 13845ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (intersectGeometry(&ray, &bestTime) != -1) { 13855ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgDrawSpriteScreenspace(posX, h - posY - 1, 0.0f, 2.0f, 2.0f); 13865ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13875ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13885ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13895ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 13905ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 13915ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 13925ce730797a8a7278dfe19dac8a9460b25675fed0Jim Millerint root() { 13935ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller int64_t currentTime = rsUptimeMillis(); 13945ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 13955ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramVertex(vertexProgram); 13965ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller rsgBindProgramRaster(rasterProgram); 1397b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindSampler(singleTextureFragmentProgram, 0, linearClamp); 1398b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindSampler(multiTextureFragmentProgram, 0, linearClamp); 1399b378af500b36226635b6343b1d5009ee9af44fc1Jim Miller rsgBindSampler(multiTextureFragmentProgram, 1, linearClamp); 14005ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 14018b55d7500c1e5a88c415dae8dcead16b152d7929Jack Palevich updateAllocationVars(cards); 14025ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 14035ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller if (!initialized) { 140443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (debugTextureLoading) { 14052ba04e061b52c488a154739379501dc833e39f79Jim Miller rsDebug("*** initialized was false, updating all cards (cards = ", cards); 14062ba04e061b52c488a154739379501dc833e39f79Jim Miller } 14077cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller for (int i = 0; i < cardCount; i++) { 14082ba04e061b52c488a154739379501dc833e39f79Jim Miller initCard(cards + i); 14097cb0068e59dde61ef0e649735199e5ba31c9c6afJim Miller } 14105ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller initialized = true; 14115ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 14125ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1413a9e9c4bef076e718094786edfe0290f798e1db4bJim Miller rsgBindProgramFragment(singleTextureFragmentProgram); 1414fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller // rsgClearDepth() currently follows the value of glDepthMask(), so it's disabled when 1415fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller // the mask is disabled. We may want to change the following to always draw w/o Z for 1416fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller // the background if we can guarantee the depth buffer will get cleared and 1417fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller // there's a performance advantage. 1418fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller rsgBindProgramStore(forceBlendCardsWithZ ? programStoreNoAlphaZ : programStoreNoAlphaNoZ); 14199afba8c61f6aff94c68acbfaae1cc58bd28c13eaJim Miller drawBackground(); 14205ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 14215ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller updateCameraMatrix(rsgGetWidth(), rsgGetHeight()); 14225ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 142343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller bool stillAnimating = (currentTime - touchTime) <= ANIMATION_SCALE_TIME; 142443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 142543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (!isDragging && animating) { 142643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller stillAnimating = updateNextPosition(currentTime); 142743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 142843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 142943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller lastTime = currentTime; 14305ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 14315ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller cullCards(); 14325ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1433420b44b8b11ec1c309ea130e69a6876325dbfef9Jim Miller updateCardResources(currentTime); 14345ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 1435bfc5ce2da9e0d8d0ec2535c465624574d98418d7Jim Shuma // Draw cards opaque only if requested, and always draw detail textures with blending. 1436fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller if (forceBlendCardsWithZ) { 1437fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller rsgBindProgramStore(programStoreAlphaZ); 1438bfc5ce2da9e0d8d0ec2535c465624574d98418d7Jim Shuma } else { 1439fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller rsgBindProgramStore(programStoreAlphaNoZ); 1440bfc5ce2da9e0d8d0ec2535c465624574d98418d7Jim Shuma } 1441bf39450b962d91ec78af53db39826d55ddb39902Jim Shuma stillAnimating |= drawCards(currentTime); 1442fb179e7afd8f02be63061b478b0283e3085fc25fJim Miller rsgBindProgramStore(programStoreAlphaNoZ); 1443bf39450b962d91ec78af53db39826d55ddb39902Jim Shuma stillAnimating |= drawDetails(currentTime); 14445ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 144543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (stillAnimating != animating) { 144643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (stillAnimating) { 144743471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // we just started animating 144843471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller sendAnimationStarted(); 144943471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } else { 145043471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller // we were animating but stopped animating just now 145143471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller sendAnimationFinished(); 145243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 145343471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller animating = stillAnimating; 145443471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller } 145543471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller 145643471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller if (debugRays) { 14575ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller renderWithRays(); 14585ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller } 14595ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 14605ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller //rsSendToClient(CMD_PING); 14615ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller 146243471a7e84593d0dd855ec5c66d70891a6fd4c81Jim Miller return animating ? 1 : 0; 14635ce730797a8a7278dfe19dac8a9460b25675fed0Jim Miller} 1464