summaryrefslogtreecommitdiffstats
path: root/carousel/java/com/android/ex/carousel/carousel.rs
diff options
context:
space:
mode:
Diffstat (limited to 'carousel/java/com/android/ex/carousel/carousel.rs')
-rw-r--r--carousel/java/com/android/ex/carousel/carousel.rs1884
1 files changed, 0 insertions, 1884 deletions
diff --git a/carousel/java/com/android/ex/carousel/carousel.rs b/carousel/java/com/android/ex/carousel/carousel.rs
deleted file mode 100644
index e4dcc65..0000000
--- a/carousel/java/com/android/ex/carousel/carousel.rs
+++ /dev/null
@@ -1,1884 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma version(1)
-#pragma rs java_package_name(com.android.ex.carousel);
-#pragma rs set_reflect_license()
-
-#include "rs_graphics.rsh"
-
-typedef struct __attribute__((aligned(4))) Card {
- // *** Update initCard if you add/remove fields here.
- rs_allocation texture; // basic card texture
- rs_allocation detailTexture; // screen-aligned detail texture
- float2 detailTextureOffset; // offset to add, in screen coordinates
- float2 detailLineOffset; // offset to add to detail line, in screen coordinates
- float2 detailTexturePosition[2]; // screen coordinates of detail texture, computed at draw time
- rs_mesh geometry;
- rs_matrix4x4 matrix; // custom transform for this card/geometry
- int textureState; // whether or not the primary card texture is loaded.
- int detailTextureState; // whether or not the detail for the card is loaded.
- int geometryState; // whether or not geometry is loaded
- int cardVisible; // not bool because of packing bug?
- int detailVisible; // not bool because of packing bug?
- int shouldPrefetch; // not bool because of packing bug?
- int64_t textureTimeStamp; // time when this texture was last updated, in ms
- int64_t detailTextureTimeStamp; // time when this texture was last updated, in ms
- int64_t geometryTimeStamp; // time when the card itself was last updated, in ms
-} Card_t;
-
-typedef struct Ray_s {
- float3 position;
- float3 direction;
-} Ray;
-
-typedef struct Plane_s {
- float3 point;
- float3 normal;
- float constant;
-} Plane;
-
-typedef struct Cylinder_s {
- float3 center; // center of a y-axis-aligned infinite cylinder
- float radius;
-} Cylinder;
-
-typedef struct PerspectiveCamera_s {
- float3 from;
- float3 at;
- float3 up;
- float fov;
- float aspect;
- float near;
- float far;
-} PerspectiveCamera;
-
-typedef struct ProgramStore_s {
- rs_program_store programStore;
-} ProgramStore_t;
-
-typedef struct FragmentShaderConstants_s {
- float fadeAmount;
- float overallAlpha;
-} FragmentShaderConstants;
-
-// Request states. Used for loading 3D object properties from the Java client.
-// Typical properties: texture, geometry and matrices.
-enum {
- STATE_INVALID = 0, // item hasn't been loaded
- STATE_LOADING, // we've requested an item but are waiting for it to load
- STATE_STALE, // we have an old item, but should request an update
- STATE_UPDATING, // we've requested an update, and will display the old one in the meantime
- STATE_LOADED // item was delivered
-};
-
-// Interpolation modes ** THIS LIST MUST MATCH THOSE IN CarouselView.java ***
-enum {
- INTERPOLATION_LINEAR = 0,
- INTERPOLATION_DECELERATE_QUADRATIC = 1,
- INTERPOLATION_ACCELERATE_DECELERATE_CUBIC = 2,
-};
-
-// Detail texture alignments ** THIS LIST MUST MATCH THOSE IN CarouselView.java ***
-enum {
- /** Detail is centered vertically with respect to the card **/
- CENTER_VERTICAL = 1,
- /** Detail is aligned with the top edge of the carousel view **/
- VIEW_TOP = 1 << 1,
- /** Detail is aligned with the bottom edge of the carousel view (not yet implemented) **/
- VIEW_BOTTOM = 1 << 2,
- /** Detail is positioned above the card (not yet implemented) **/
- ABOVE = 1 << 3,
- /** Detail is positioned below the card **/
- BELOW = 1 << 4,
- /** Mask that selects those bits that control vertical alignment **/
- VERTICAL_ALIGNMENT_MASK = 0xff,
-
- /**
- * Detail is centered horizontally with respect to either the top or bottom
- * extent of the card, depending on whether the detail is above or below the card.
- */
- CENTER_HORIZONTAL = 1 << 8,
- /**
- * Detail is aligned with the left edge of either the top or the bottom of
- * the card, depending on whether the detail is above or below the card.
- */
- LEFT = 1 << 9,
- /**
- * Detail is aligned with the right edge of either the top or the bottom of
- * the card, depending on whether the detail is above or below the card.
- * (not yet implemented)
- */
- RIGHT = 1 << 10,
- /** Mask that selects those bits that control horizontal alignment **/
- HORIZONTAL_ALIGNMENT_MASK = 0xff00,
-};
-
-// Client messages *** THIS LIST MUST MATCH THOSE IN CarouselRS.java. ***
-static const int CMD_CARD_SELECTED = 100;
-static const int CMD_DETAIL_SELECTED = 105;
-static const int CMD_CARD_LONGPRESS = 110;
-static const int CMD_REQUEST_TEXTURE = 200;
-static const int CMD_INVALIDATE_TEXTURE = 210;
-static const int CMD_REQUEST_GEOMETRY = 300;
-static const int CMD_INVALIDATE_GEOMETRY = 310;
-static const int CMD_ANIMATION_STARTED = 400;
-static const int CMD_ANIMATION_FINISHED = 500;
-static const int CMD_REQUEST_DETAIL_TEXTURE = 600;
-static const int CMD_INVALIDATE_DETAIL_TEXTURE = 610;
-static const int CMD_PING = 1000;
-
-// Drag model *** THIS LIST MUST MATCH THOSE IN CarouselRS.java. ***
-static const int DRAG_MODEL_SCREEN_DELTA = 0; // Drag relative to x coordinate of motion vector
-static const int DRAG_MODEL_PLANE = 1; // Drag relative to projected point on plane of carousel
-static const int DRAG_MODEL_CYLINDER_INSIDE = 2; // Drag relative to point on inside of cylinder
-static const int DRAG_MODEL_CYLINDER_OUTSIDE = 3; // Drag relative to point on outside of cylinder
-
-// Constants
-static const int ANIMATION_DELAY_TIME = 125; // hold off scale animation until this time
-static const int ANIMATION_SCALE_UP_TIME = 200; // Time it takes to animate selected card, in ms
-static const int ANIMATION_SCALE_DOWN_TIME = 200; // Time it takes to animate selected card, in ms
-static const float3 SELECTED_SCALE_FACTOR = { 0.1f, 0.1f, 0.1f }; // increase by this %
-static const int VELOCITY_HISTORY_MAX = 10; // # recent velocity samples used to calculate average
-static const int VISIBLE_SLOT_PADDING = 2; // # slots to draw on either side of visible slots
-
-// Constants affecting tilt overscroll. Some of these should be parameters.
-static const int TILT_SLOT_NUMBER = 5;
-static const float TILT_MIN_ANGLE = M_PI / 315.0f;
-static const float TILT_MAX_BIAS = M_PI / 8.0f;
-static const float TILT_MAX_ANGLE = M_PI / 8.0f;
-static const float MAX_DELTA_BIAS = 0.008f;
-
-// Debug flags
-const bool debugCamera = false; // dumps ray/camera coordinate stuff
-const bool debugSelection = false; // logs selection events
-const bool debugTextureLoading = false; // for debugging texture load/unload
-const bool debugGeometryLoading = false; // for debugging geometry load/unload
-const bool debugDetails = false; // for debugging detail texture geometry
-const bool debugRendering = false; // flashes display when the frame changes
-const bool debugRays = false; // shows visual depiction of hit tests, See renderWithRays().
-
-// Exported variables. These will be reflected to Java set_* variables.
-Card_t *cards; // array of cards to draw
-float startAngle; // position of initial card, in radians
-int slotCount; // number of positions where a card can be
-int cardCount; // number of cards in stack
-int programStoresCardCount; // number of program fragment stores
-int visibleSlotCount; // number of visible slots (for culling)
-int visibleDetailCount; // number of visible detail textures to show
-int prefetchCardCount; // how many cards to keep in memory
-int detailTextureAlignment; // How to align detail texture with respect to card
-bool drawRuler; // whether to draw a ruler from the card to the detail texture
-float radius; // carousel radius. Cards will be centered on a circle with this radius
-float cardRotation; // rotation of card in XY plane relative to Z=1
-bool cardsFaceTangent; // whether cards are rotated to face along a tangent to the circle
-float swaySensitivity; // how much to rotate cards in relation to the rotation velocity
-float frictionCoeff; // how much to slow down the carousel over time
-float dragFactor; // a scale factor for how sensitive the carousel is to user dragging
-int fadeInDuration; // amount of time (in ms) for smoothly switching out textures
-int cardCreationFadeDuration; // amount of time (in ms) to fade while initially showing a card
-float rezInCardCount; // this controls how rapidly distant card textures will be rez-ed in
-float detailFadeRate; // rate at which details fade as they move into the distance
-float4 backgroundColor;
-int rowCount; // number of rows of cards in a given slot, default 1
-float rowSpacing; // spacing between rows of cards
-bool firstCardTop; // set true for first card on top row when multiple rows used
-float overscrollSlots; // amount of allowed overscroll (in slots)
-
-int dragModel = DRAG_MODEL_SCREEN_DELTA;
-int fillDirection; // the order in which to lay out cards: +1 for CCW (default), -1 for CW
-ProgramStore_t *programStoresCard;
-rs_program_store programStoreBackground;
-rs_program_store programStoreDetail;
-rs_program_fragment singleTextureFragmentProgram;
-rs_program_fragment singleTextureBlendingFragmentProgram;
-rs_program_fragment multiTextureFragmentProgram;
-rs_program_fragment multiTextureBlendingFragmentProgram;
-rs_program_vertex vertexProgram;
-rs_program_raster rasterProgram;
-rs_allocation defaultTexture; // shown when no other texture is assigned
-rs_allocation loadingTexture; // progress texture (shown when app is fetching the texture)
-rs_allocation backgroundTexture; // drawn behind everything, if set
-rs_allocation detailLineTexture; // used to draw detail line (as a quad, of course)
-rs_allocation detailLoadingTexture; // used when detail texture is loading
-rs_mesh defaultGeometry; // shown when no geometry is loaded
-rs_mesh loadingGeometry; // shown when geometry is loading
-rs_matrix4x4 defaultCardMatrix;
-rs_matrix4x4 projectionMatrix;
-rs_matrix4x4 modelviewMatrix;
-FragmentShaderConstants* shaderConstants;
-rs_sampler linearClamp;
-
-// Local variables
-static float bias; // rotation bias, in radians. Used for animation and dragging.
-static float overscrollBias; // Track overscroll bias separately for tilt effect.
-static bool updateCamera; // force a recompute of projection and lookat matrices
-static const float FLT_MAX = 1.0e37;
-static int animatedSelection = -1;
-static int currentFirstCard = -1;
-static int64_t touchTime = -1; // time of first touch (see doStart())
-static int64_t releaseTime = 0L; // when touch was released
-static float touchBias = 0.0f; // bias on first touch
-static float2 touchPosition; // position of first touch, as defined by last call to doStart(x,y)
-static float velocity = 0.0f; // angular velocity in radians/s
-static bool isOverScrolling = false; // whether we're in the overscroll animation
-static bool isAutoScrolling = false; // whether we're in the autoscroll animation
-static bool isDragging = false; // true while the user is dragging the carousel
-static float selectionRadius = 50.0f; // movement greater than this will result in no selection
-static bool enableSelection = false; // enabled until the user drags outside of selectionRadius
-static float tiltAngle = 0.0f;
-
-// Default plane of the carousel. Used for angular motion estimation in view.
-static Plane carouselPlane = {
- { 0.0f, 0.0f, 0.0f }, // point
- { 0.0f, 1.0f, 0.0f }, // normal
- 0.0f // plane constant (= -dot(P, N))
-};
-
-static Cylinder carouselCylinder = {
- {0.0f, 0.0f, 0.0f }, // center
- 1.0f // radius - update with carousel radius.
-};
-
-// Because allocations can't have 0 dimensions, we have to track whether or not
-// cards and program stores are valid separately.
-// TODO: Remove this dependency once allocations can have a zero dimension.
-static bool cardAllocationValid = false;
-static bool programStoresAllocationValid = false;
-
-// Default geometry when card.geometry is not set.
-static const float3 cardVertices[4] = {
- { -1.0, -1.0, 0.0 },
- { 1.0, -1.0, 0.0 },
- { 1.0, 1.0, 0.0 },
- {-1.0, 1.0, 0.0 }
-};
-
-// Default camera
-static PerspectiveCamera camera = {
- {2,2,2}, // from
- {0,0,0}, // at
- {0,1,0}, // up
- 25.0f, // field of view
- 1.0f, // aspect
- 0.1f, // near
- 100.0f // far
-};
-
-// Forward references
-static int intersectGeometry(Ray* ray, float *bestTime);
-static int intersectDetailTexture(float x, float y, float2 *tapCoordinates);
-static bool __attribute__((overloadable))
- makeRayForPixelAt(Ray* ray, PerspectiveCamera* cam, float x, float y);
-static bool __attribute__((overloadable))
- makeRayForPixelAt(Ray* ray, rs_matrix4x4* model, rs_matrix4x4* proj, float x, float y);
-static float deltaTimeInSeconds(int64_t current);
-static bool rayPlaneIntersect(Ray* ray, Plane* plane, float* tout);
-static bool rayCylinderIntersect(Ray* ray, Cylinder* cylinder, float* tout);
-static void stopAutoscroll();
-static bool tiltOverscroll();
-
-void init() {
- // initializers currently have a problem when the variables are exported, so initialize
- // globals here.
- if (debugTextureLoading) rsDebug("Renderscript: init()", 0);
- startAngle = 0.0f;
- slotCount = 10;
- visibleSlotCount = 1;
- visibleDetailCount = 3;
- bias = 0.0f;
- overscrollBias = 0.0f;
- tiltAngle = 0.0f;
- radius = carouselCylinder.radius = 1.0f;
- cardRotation = 0.0f;
- cardsFaceTangent = false;
- updateCamera = true;
- backgroundColor = (float4) { 0.0f, 0.0f, 0.0f, 1.0f };
- cardAllocationValid = false;
- programStoresAllocationValid = false;
- cardCount = 0;
- rowCount = 1;
- rowSpacing = 0.0f;
- firstCardTop = false;
- fadeInDuration = 250;
- rezInCardCount = 0.0f; // alpha will ramp to 1.0f over this many cards (0.0f means disabled)
- detailFadeRate = 0.5f; // fade details over this many slot positions.
- rsMatrixLoadIdentity(&defaultCardMatrix);
-}
-
-static void updateAllocationVars()
-{
- // Cards
- rs_allocation cardAlloc;
- cardAlloc = rsGetAllocation(cards);
- cardCount = (cardAllocationValid && rsIsObject(cardAlloc)) ? rsAllocationGetDimX(cardAlloc) : 0;
-
- // Program stores
- rs_allocation psAlloc;
- psAlloc = rsGetAllocation(programStoresCard);
- programStoresCardCount = (programStoresAllocationValid && rsIsObject(psAlloc) ?
- rsAllocationGetDimX(psAlloc) : 0);
-}
-
-void setRadius(float rad)
-{
- radius = carouselCylinder.radius = rad;
-}
-
-static void initCard(Card_t* card)
-{
- // Object refs are always initilized cleared.
- static const float2 zero = {0.0f, 0.0f};
- card->detailTextureOffset = zero;
- card->detailLineOffset = zero;
- rsMatrixLoad(&card->matrix, &defaultCardMatrix);
- card->textureState = STATE_INVALID;
- card->detailTextureState = STATE_INVALID;
- card->geometryState = STATE_INVALID;
- card->cardVisible = false;
- card->detailVisible = false;
- card->shouldPrefetch = false;
- card->textureTimeStamp = 0;
- card->detailTextureTimeStamp = 0;
- card->geometryTimeStamp = rsUptimeMillis();
-}
-
-void createCards(int start, int total)
-{
- if (!cardAllocationValid) {
- // If the allocation is invalid, it contains a single place-holder
- // card that has not yet been initialized (see CarouselRS.createCards).
- // Here we ensure that it is initialized when growing the total.
- start = 0;
- }
- for (int k = start; k < total; k++) {
- initCard(cards + k);
- }
-
- // Since allocations can't have 0-size, we track validity ourselves based on the call to
- // this method.
- cardAllocationValid = total > 0;
-
- updateAllocationVars();
-}
-
-// Computes an alpha value for a card using elapsed time and constant fadeInDuration
-static float getAnimatedAlpha(int64_t startTime, int64_t currentTime, int64_t duration)
-{
- double timeElapsed = (double) (currentTime - startTime); // in ms
- double alpha = duration > 0 ? (double) timeElapsed / duration : 1.0;
- return min(1.0f, (float) alpha);
-}
-
-// Returns total angle for given number of slots
-static float wedgeAngle(float slots)
-{
- return slots * 2.0f * M_PI / slotCount;
-}
-
-// Return angle of slot in position p.
-static float slotPosition(int p)
-{
- return startAngle + wedgeAngle(p) * fillDirection;
-}
-
-// Return angle for card in position p.
-static float cardPosition(int p)
-{
- return bias + slotPosition(p / rowCount);
-}
-
-// Return the lowest possible bias value, based on the fill direction
-static float minimumBias()
-{
- const int totalSlots = (cardCount + rowCount - 1) / rowCount;
- return (fillDirection > 0) ?
- -max(0.0f, wedgeAngle(totalSlots - visibleDetailCount)) :
- wedgeAngle(0.0f);
-}
-
-// Return the highest possible bias value, based on the fill direction
-static float maximumBias()
-{
- const int totalSlots = (cardCount + rowCount - 1) / rowCount;
- return (fillDirection > 0) ?
- wedgeAngle(0.0f) :
- max(0.0f, wedgeAngle(totalSlots - visibleDetailCount));
-}
-
-
-// convert from carousel rotation angle (in card slot units) to radians.
-static float carouselRotationAngleToRadians(float carouselRotationAngle)
-{
- return -wedgeAngle(carouselRotationAngle);
-}
-
-// convert from radians to carousel rotation angle (in card slot units).
-static float radiansToCarouselRotationAngle(float angle)
-{
- return -angle * slotCount / ( 2.0f * M_PI );
-}
-
-// Set basic camera properties:
-// from - position of the camera in x,y,z
-// at - target we're looking at - used to compute view direction
-// up - a normalized vector indicating up (typically { 0, 1, 0})
-//
-// NOTE: the view direction and up vector cannot be parallel/antiparallel with each other
-void lookAt(float fromX, float fromY, float fromZ,
- float atX, float atY, float atZ,
- float upX, float upY, float upZ)
-{
- camera.from.x = fromX;
- camera.from.y = fromY;
- camera.from.z = fromZ;
- camera.at.x = atX;
- camera.at.y = atY;
- camera.at.z = atZ;
- camera.up.x = upX;
- camera.up.y = upY;
- camera.up.z = upZ;
- updateCamera = true;
-}
-
-// Load a projection matrix for the given parameters. This is equivalent to gluPerspective()
-static void loadPerspectiveMatrix(rs_matrix4x4* matrix, float fovy, float aspect, float near, float far)
-{
- rsMatrixLoadIdentity(matrix);
- float top = near * tan((float) (fovy * M_PI / 360.0f));
- float bottom = -top;
- float left = bottom * aspect;
- float right = top * aspect;
- rsMatrixLoadFrustum(matrix, left, right, bottom, top, near, far);
-}
-
-// Construct a matrix based on eye point, center and up direction. Based on the
-// man page for gluLookat(). Up must be normalized.
-static void loadLookatMatrix(rs_matrix4x4* matrix, float3 eye, float3 center, float3 up)
-{
- float3 f = normalize(center - eye);
- float3 s = normalize(cross(f, up));
- float3 u = cross(s, f);
- float m[16];
- m[0] = s.x;
- m[4] = s.y;
- m[8] = s.z;
- m[12] = 0.0f;
- m[1] = u.x;
- m[5] = u.y;
- m[9] = u.z;
- m[13] = 0.0f;
- m[2] = -f.x;
- m[6] = -f.y;
- m[10] = -f.z;
- m[14] = 0.0f;
- m[3] = m[7] = m[11] = 0.0f;
- m[15] = 1.0f;
- rsMatrixLoad(matrix, m);
- rsMatrixTranslate(matrix, -eye.x, -eye.y, -eye.z);
-}
-
-/*
- * Returns true if a state represents a texture that is loaded enough to draw
- */
-static bool textureEverLoaded(int state) {
- return (state == STATE_LOADED) || (state == STATE_STALE) || (state == STATE_UPDATING);
-}
-
-void setTexture(int n, rs_allocation texture)
-{
- if (n < 0 || n >= cardCount) return;
- cards[n].texture = texture;
- if (cards[n].textureState != STATE_STALE &&
- cards[n].textureState != STATE_UPDATING) {
- cards[n].textureTimeStamp = rsUptimeMillis();
- }
- cards[n].textureState = (texture.p != 0) ? STATE_LOADED : STATE_INVALID;
-}
-
-void setDetailTexture(int n, float offx, float offy, float loffx, float loffy, rs_allocation texture)
-{
- if (n < 0 || n >= cardCount) return;
- cards[n].detailTexture = texture;
- if (cards[n].detailTextureState != STATE_STALE &&
- cards[n].detailTextureState != STATE_UPDATING) {
- cards[n].detailTextureTimeStamp = rsUptimeMillis();
- }
- cards[n].detailTextureOffset.x = offx;
- cards[n].detailTextureOffset.y = offy;
- cards[n].detailLineOffset.x = loffx;
- cards[n].detailLineOffset.y = loffy;
- cards[n].detailTextureState = (texture.p != 0) ? STATE_LOADED : STATE_INVALID;
-}
-
-void invalidateTexture(int n, bool eraseCurrent)
-{
- if (n < 0 || n >= cardCount) return;
- if (eraseCurrent) {
- cards[n].textureState = STATE_INVALID;
- rsClearObject(&cards[n].texture);
- } else {
- cards[n].textureState =
- textureEverLoaded(cards[n].textureState) ? STATE_STALE : STATE_INVALID;
- }
-}
-
-void invalidateDetailTexture(int n, bool eraseCurrent)
-{
- if (n < 0 || n >= cardCount) return;
- if (eraseCurrent) {
- cards[n].detailTextureState = STATE_INVALID;
- rsClearObject(&cards[n].detailTexture);
- } else {
- cards[n].detailTextureState =
- textureEverLoaded(cards[n].detailTextureState) ? STATE_STALE : STATE_INVALID;
- }
-}
-
-void setGeometry(int n, rs_mesh geometry)
-{
- if (n < 0 || n >= cardCount) return;
- cards[n].geometry = geometry;
- if (cards[n].geometry.p != 0)
- cards[n].geometryState = STATE_LOADED;
- else
- cards[n].geometryState = STATE_INVALID;
- cards[n].geometryTimeStamp = rsUptimeMillis();
-}
-
-void setMatrix(int n, rs_matrix4x4 matrix) {
- if (n < 0 || n >= cardCount) return;
- cards[n].matrix = matrix;
-}
-
-void setProgramStoresCard(int n, rs_program_store programStore)
-{
- programStoresCard[n].programStore = programStore;
- programStoresAllocationValid = true;
-}
-
-void setCarouselRotationAngle(float carouselRotationAngle) {
- bias = carouselRotationAngleToRadians(carouselRotationAngle);
-}
-
-// Gets animated scale value for current selected card.
-// If card is currently being animated, returns true, otherwise returns false.
-static bool getAnimatedScaleForSelected(float3* scale)
-{
- static const float3 one = { 1.0f, 1.0f, 1.0f };
- static float fraction = 0.0f;
- bool stillAnimating = false;
- if (isDragging) {
- // "scale up" animation
- int64_t dt = rsUptimeMillis() - touchTime - ANIMATION_DELAY_TIME;
- if (dt > 0L && enableSelection) {
- float s = (float) dt / ANIMATION_SCALE_UP_TIME;
- s = min(s, 1.0f);
- fraction = max(s, fraction);
- }
- stillAnimating = dt < ANIMATION_SCALE_UP_TIME;
- } else {
- // "scale down" animation
- int64_t dt = rsUptimeMillis() - releaseTime;
- if (dt < ANIMATION_SCALE_DOWN_TIME) {
- float s = 1.0f - ((float) dt / ANIMATION_SCALE_DOWN_TIME);
- fraction = min(s, fraction);
- stillAnimating = true;
- } else {
- fraction = 0.0f;
- }
- }
- *scale = one + fraction * SELECTED_SCALE_FACTOR;
- return stillAnimating; // still animating;
-}
-
-// The Verhulst logistic function: http://en.wikipedia.org/wiki/Logistic_function
-// P(t) = 1 / (1 + e^(-t))
-// Parameter t: Any real number
-// Returns: A float in the range (0,1), with P(0.5)=0
-static float logistic(float t) {
- return 1.f / (1.f + exp(-t));
-}
-
-static float getSwayAngleForVelocity(float v, bool enableSway)
-{
- float sway = 0.0f;
-
- if (enableSway) {
- const float range = M_PI * 2./3.; // How far we can deviate from center, peak-to-peak
- sway = range * (logistic(-v * swaySensitivity) - 0.5f);
- }
-
- return sway;
-}
-
-static float getCardTiltAngle(int i) {
- i /= rowCount;
- int totalSlots = (cardCount + rowCount - 1) / rowCount;
- float tiltSlotNumber = TILT_SLOT_NUMBER;
- float deltaTilt = tiltAngle / tiltSlotNumber;
- float cardTiltAngle = 0;
- if (tiltAngle > 0 && i < tiltSlotNumber) {
- // Overscroll for the front cards.
- cardTiltAngle = deltaTilt * (tiltSlotNumber - i);
- } else if (tiltAngle < 0 && i > (totalSlots - tiltSlotNumber)) {
- cardTiltAngle = deltaTilt * (i - totalSlots + tiltSlotNumber + 1);
- }
- return cardTiltAngle;
-}
-
-// Returns the vertical offset for a card in its slot,
-// depending on the number of rows configured.
-static float getVerticalOffsetForCard(int i) {
- if (rowCount == 1) {
- // fast path
- return 0;
- }
- const float cardHeight = (cardVertices[3].y - cardVertices[0].y) *
- rsMatrixGet(&defaultCardMatrix, 1, 1);
- const float totalHeight = rowCount * (cardHeight + rowSpacing) - rowSpacing;
- if (firstCardTop)
- i = rowCount - (i % rowCount) - 1;
- else
- i = i % rowCount;
- const float rowOffset = i * (cardHeight + rowSpacing);
- return (cardHeight - totalHeight) / 2 + rowOffset;
-}
-
-/*
- * Composes a matrix for the given card.
- * matrix: The output matrix.
- * i: The card we're getting the matrix for.
- * enableSway: Whether to enable swaying. (We want it on for cards, and off for detail textures.)
- * enableCardMatrix: Whether to also consider the user-specified card matrix
- *
- * returns true if an animation is being applied to the given card
- */
-static bool getMatrixForCard(rs_matrix4x4* matrix, int i, bool enableSway, bool enableCardMatrix)
-{
- float theta = cardPosition(i);
- float swayAngle = getSwayAngleForVelocity(velocity, enableSway);
- rsMatrixRotate(matrix, degrees(theta), 0, 1, 0);
- rsMatrixTranslate(matrix, radius, getVerticalOffsetForCard(i), 0);
- float tiltAngle = getCardTiltAngle(i);
- float rotation = cardRotation + swayAngle + tiltAngle;
- if (!cardsFaceTangent) {
- rotation -= theta;
- }
- rsMatrixRotate(matrix, degrees(rotation), 0, 1, 0);
- bool stillAnimating = false;
- if (i == animatedSelection) {
- float3 scale;
- stillAnimating = getAnimatedScaleForSelected(&scale);
- rsMatrixScale(matrix, scale.x, scale.y, scale.z);
- }
- // TODO(jshuma): Instead of ignoring this matrix for the detail texture, use card bounding box
- if (enableCardMatrix) {
- rsMatrixLoadMultiply(matrix, matrix, &cards[i].matrix);
- }
- return stillAnimating;
-}
-
-/*
- * Draws the requested mesh, with the appropriate program store in effect.
- */
-static void drawMesh(rs_mesh mesh)
-{
- if (programStoresCardCount == 1) {
- // Draw the entire mesh, with the only available program store
- rsgBindProgramStore(programStoresCard[0].programStore);
- rsgDrawMesh(mesh);
- } else {
- // Draw each primitive in the mesh with the corresponding program store
- for (int i=0; i<programStoresCardCount; ++i) {
- if (programStoresCard[i].programStore.p != 0) {
- rsgBindProgramStore(programStoresCard[i].programStore);
- rsgDrawMesh(mesh, i);
- }
- }
- }
-}
-
-/*
- * Draws cards around the Carousel.
- * Returns true if we're still animating any property of the cards (e.g. fades).
- */
-static bool drawCards(int64_t currentTime)
-{
- const float wedgeAngle = 2.0f * M_PI / slotCount;
- const float endAngle = startAngle + visibleSlotCount * wedgeAngle;
- bool stillAnimating = false;
- for (int i = cardCount-1; i >= 0; i--) {
- if (cards[i].cardVisible) {
- // If this card was recently loaded, this will be < 1.0f until the animation completes
- float animatedAlpha = getAnimatedAlpha(cards[i].textureTimeStamp, currentTime,
- fadeInDuration);
- float overallAlpha = getAnimatedAlpha(cards[i].geometryTimeStamp, currentTime,
- cardCreationFadeDuration);
- if (animatedAlpha < 1.0f || overallAlpha < 1.0f) {
- stillAnimating = true;
- }
-
- // Compute fade out for cards in the distance
- float positionAlpha;
- if (rezInCardCount > 0.0f) {
- positionAlpha = (endAngle - cardPosition(i)) / wedgeAngle;
- positionAlpha = min(1.0f, positionAlpha / rezInCardCount);
- } else {
- positionAlpha = 1.0f;
- }
-
- // Set alpha for blending between the textures
- shaderConstants->fadeAmount = min(1.0f, animatedAlpha * positionAlpha);
- shaderConstants->overallAlpha = overallAlpha;
- rsgAllocationSyncAll(rsGetAllocation(shaderConstants));
-
- // Bind the appropriate shader network. If there's no alpha blend, then
- // switch to single shader for better performance.
- const int state = cards[i].textureState;
- bool loaded = textureEverLoaded(state) && rsIsObject(cards[i].texture);
- if (shaderConstants->fadeAmount == 1.0f || shaderConstants->fadeAmount < 0.01f) {
- if (overallAlpha < 1.0) {
- rsgBindProgramFragment(singleTextureBlendingFragmentProgram);
- rsgBindTexture(singleTextureBlendingFragmentProgram, 0,
- (loaded && shaderConstants->fadeAmount == 1.0f) ?
- cards[i].texture : loadingTexture);
- } else {
- rsgBindProgramFragment(singleTextureFragmentProgram);
- rsgBindTexture(singleTextureFragmentProgram, 0,
- (loaded && shaderConstants->fadeAmount == 1.0f) ?
- cards[i].texture : loadingTexture);
- }
- } else {
- if (overallAlpha < 1.0) {
- rsgBindProgramFragment(multiTextureBlendingFragmentProgram);
- rsgBindTexture(multiTextureBlendingFragmentProgram, 0, loadingTexture);
- rsgBindTexture(multiTextureBlendingFragmentProgram, 1, loaded ?
- cards[i].texture : loadingTexture);
- } else {
- rsgBindProgramFragment(multiTextureFragmentProgram);
- rsgBindTexture(multiTextureFragmentProgram, 0, loadingTexture);
- rsgBindTexture(multiTextureFragmentProgram, 1, loaded ?
- cards[i].texture : loadingTexture);
- }
- }
-
- // Draw geometry
- rs_matrix4x4 matrix = modelviewMatrix;
- stillAnimating |= getMatrixForCard(&matrix, i, true, true);
- rsgProgramVertexLoadModelMatrix(&matrix);
- if (cards[i].geometryState == STATE_LOADED && cards[i].geometry.p != 0) {
- drawMesh(cards[i].geometry);
- } else if (cards[i].geometryState == STATE_LOADING && loadingGeometry.p != 0) {
- drawMesh(loadingGeometry);
- } else if (defaultGeometry.p != 0) {
- drawMesh(defaultGeometry);
- } else {
- // Draw place-holder geometry
- rsgBindProgramStore(programStoresCard[0].programStore);
- rsgDrawQuad(
- cardVertices[0].x, cardVertices[0].y, cardVertices[0].z,
- cardVertices[1].x, cardVertices[1].y, cardVertices[1].z,
- cardVertices[2].x, cardVertices[2].y, cardVertices[2].z,
- cardVertices[3].x, cardVertices[3].y, cardVertices[3].z);
- }
- }
- }
- return stillAnimating;
-}
-
-/**
- * Convert projection from normalized coordinates to pixel coordinates.
- *
- * @return True on success, false on failure.
- */
-static bool convertNormalizedToPixelCoordinates(float4 *screenCoord, float width, float height) {
- // This is probably cheaper than pre-multiplying with another matrix.
- if (screenCoord->w == 0.0f) {
- rsDebug("Bad transform while converting from normalized to pixel coordinates: ",
- screenCoord);
- return false;
- }
- *screenCoord *= 1.0f / screenCoord->w;
- screenCoord->x += 1.0f;
- screenCoord->y += 1.0f;
- screenCoord->z += 1.0f;
- screenCoord->x = round(screenCoord->x * 0.5f * width);
- screenCoord->y = round(screenCoord->y * 0.5f * height);
- screenCoord->z = - 0.5f * screenCoord->z;
- return true;
-}
-
-/*
- * Draws a screen-aligned card with the exact dimensions from the detail texture.
- * This is used to display information about the object being displayed.
- * Returns true if we're still animating any property of the cards (e.g. fades).
- */
-static bool drawDetails(int64_t currentTime)
-{
- const float width = rsgGetWidth();
- const float height = rsgGetHeight();
-
- bool stillAnimating = false;
-
- // We'll be drawing in screen space, sampled on pixel centers
- rs_matrix4x4 projection, model;
- rsMatrixLoadOrtho(&projection, 0.0f, width, 0.0f, height, 0.0f, 1.0f);
- rsgProgramVertexLoadProjectionMatrix(&projection);
- rsMatrixLoadIdentity(&model);
- rsgProgramVertexLoadModelMatrix(&model);
- updateCamera = true; // we messed with the projection matrix. Reload on next pass...
-
- const float yPadding = 5.0f; // draw line this far (in pixels) away from top and geometry
-
- // This can be done once...
- rsgBindTexture(multiTextureFragmentProgram, 0, detailLoadingTexture);
-
- const float wedgeAngle = 2.0f * M_PI / slotCount;
- // Angle where details start fading from 1.0f
- const float startDetailFadeAngle = startAngle + (visibleDetailCount - 1) * wedgeAngle;
- // Angle where detail alpha is 0.0f
- const float endDetailFadeAngle = startDetailFadeAngle + detailFadeRate * wedgeAngle;
-
- for (int i = cardCount-1; i >= 0; --i) {
- if (cards[i].cardVisible) {
- const int state = cards[i].detailTextureState;
- const bool isLoaded = textureEverLoaded(state);
- if (isLoaded && cards[i].detailTexture.p != 0) {
- const float lineWidth = rsAllocationGetDimX(detailLineTexture);
-
- // Compute position in screen space of top corner or bottom corner of card
- rsMatrixLoad(&model, &modelviewMatrix);
- stillAnimating |= getMatrixForCard(&model, i, false, false);
- rs_matrix4x4 matrix;
- rsMatrixLoadMultiply(&matrix, &projectionMatrix, &model);
-
- int indexLeft, indexRight;
- float4 screenCoord;
- if (detailTextureAlignment & BELOW) {
- indexLeft = 0;
- indexRight = 1;
- } else {
- indexLeft = 3;
- indexRight = 2;
- }
- float4 screenCoordLeft = rsMatrixMultiply(&matrix, cardVertices[indexLeft]);
- float4 screenCoordRight = rsMatrixMultiply(&matrix, cardVertices[indexRight]);
- if (screenCoordLeft.w == 0.0f || screenCoordRight.w == 0.0f) {
- // this shouldn't happen
- rsDebug("Bad transform: ", screenCoord);
- continue;
- }
- if (detailTextureAlignment & CENTER_VERTICAL) {
- // If we're centering vertically, we'll need the other vertices too
- if (detailTextureAlignment & BELOW) {
- indexLeft = 3;
- indexRight = 2;
- } else {
- indexLeft = 0;
- indexRight = 1;
- }
- float4 otherScreenLeft = rsMatrixMultiply(&matrix, cardVertices[indexLeft]);
- float4 otherScreenRight = rsMatrixMultiply(&matrix, cardVertices[indexRight]);
- screenCoordRight.y = screenCoordLeft.y = (screenCoordLeft.y + screenCoordRight.y
- + otherScreenLeft.y + otherScreenRight.y) / 4.;
- }
- (void) convertNormalizedToPixelCoordinates(&screenCoordLeft, width, height);
- (void) convertNormalizedToPixelCoordinates(&screenCoordRight, width, height);
- if (debugDetails) {
- RS_DEBUG(screenCoordLeft);
- RS_DEBUG(screenCoordRight);
- }
- screenCoord = screenCoordLeft;
- if (detailTextureAlignment & BELOW) {
- screenCoord.y = min(screenCoordLeft.y, screenCoordRight.y);
- } else if (detailTextureAlignment & CENTER_VERTICAL) {
- screenCoord.y -= round(rsAllocationGetDimY(cards[i].detailTexture) / 2.0f);
- }
- if (detailTextureAlignment & CENTER_HORIZONTAL) {
- screenCoord.x += round((screenCoordRight.x - screenCoordLeft.x) / 2.0f -
- rsAllocationGetDimX(cards[i].detailTexture) / 2.0f);
- }
-
- // Compute alpha for gradually fading in details. Applied to both line and
- // detail texture. TODO: use a separate background texture for line.
- float animatedAlpha = getAnimatedAlpha(cards[i].detailTextureTimeStamp,
- currentTime, fadeInDuration);
- if (animatedAlpha < 1.0f) {
- stillAnimating = true;
- }
-
- // Compute alpha based on position. We fade cards quickly so they cannot overlap
- float positionAlpha = ((float)endDetailFadeAngle - cardPosition(i))
- / (endDetailFadeAngle - startDetailFadeAngle);
- positionAlpha = max(0.0f, positionAlpha);
- positionAlpha = min(1.0f, positionAlpha);
-
- const float blendedAlpha = min(1.0f, animatedAlpha * positionAlpha);
-
- if (blendedAlpha == 0.0f) {
- cards[i].detailVisible = false;
- continue; // nothing to draw
- } else {
- cards[i].detailVisible = true;
- }
- if (blendedAlpha == 1.0f) {
- rsgBindProgramFragment(singleTextureFragmentProgram);
- } else {
- rsgBindProgramFragment(multiTextureFragmentProgram);
- }
-
- // Set alpha for blending between the textures
- shaderConstants->fadeAmount = blendedAlpha;
- rsgAllocationSyncAll(rsGetAllocation(shaderConstants));
-
- // Draw line from the card to the detail texture.
- // The line is drawn from the top or bottom left of the card
- // to either the top of the screen or the top of the detail
- // texture, depending on detailTextureAlignment.
- if (drawRuler) {
- float rulerTop;
- float rulerBottom;
- if (detailTextureAlignment & BELOW) {
- rulerTop = screenCoord.y;
- rulerBottom = 0;
- } else {
- rulerTop = height;
- rulerBottom = screenCoord.y;
- }
- const float halfWidth = lineWidth * 0.5f;
- const float x0 = trunc(cards[i].detailLineOffset.x + screenCoord.x - halfWidth);
- const float x1 = x0 + lineWidth;
- const float y0 = rulerBottom + yPadding;
- const float y1 = rulerTop - yPadding - cards[i].detailLineOffset.y;
-
- if (blendedAlpha == 1.0f) {
- rsgBindTexture(singleTextureFragmentProgram, 0, detailLineTexture);
- } else {
- rsgBindTexture(multiTextureFragmentProgram, 1, detailLineTexture);
- }
- rsgDrawQuad(x0, y0, screenCoord.z, x1, y0, screenCoord.z,
- x1, y1, screenCoord.z, x0, y1, screenCoord.z);
- }
-
- // Draw the detail texture next to it using the offsets provided.
- const float textureWidth = rsAllocationGetDimX(cards[i].detailTexture);
- const float textureHeight = rsAllocationGetDimY(cards[i].detailTexture);
- const float offx = cards[i].detailTextureOffset.x;
- const float offy = -cards[i].detailTextureOffset.y;
- const float textureTop = (detailTextureAlignment & VIEW_TOP)
- ? height : screenCoord.y;
- const float x0 = cards[i].detailLineOffset.x + screenCoord.x + offx;
- const float x1 = cards[i].detailLineOffset.x + screenCoord.x + offx + textureWidth;
- const float y0 = textureTop + offy - textureHeight - cards[i].detailLineOffset.y;
- const float y1 = textureTop + offy - cards[i].detailLineOffset.y;
- cards[i].detailTexturePosition[0].x = x0;
- cards[i].detailTexturePosition[0].y = height - y1;
- cards[i].detailTexturePosition[1].x = x1;
- cards[i].detailTexturePosition[1].y = height - y0;
-
- if (blendedAlpha == 1.0f) {
- rsgBindTexture(singleTextureFragmentProgram, 0, cards[i].detailTexture);
- } else {
- rsgBindTexture(multiTextureFragmentProgram, 1, cards[i].detailTexture);
- }
- rsgDrawQuad(x0, y0, screenCoord.z, x1, y0, screenCoord.z,
- x1, y1, screenCoord.z, x0, y1, screenCoord.z);
- }
- }
- }
- return stillAnimating;
-}
-
-static void drawBackground()
-{
- static bool toggle;
- if (backgroundTexture.p != 0) {
- rsgClearDepth(1.0f);
- rs_matrix4x4 projection, model;
- rsMatrixLoadOrtho(&projection, -1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f);
- rsgProgramVertexLoadProjectionMatrix(&projection);
- rsMatrixLoadIdentity(&model);
- rsgProgramVertexLoadModelMatrix(&model);
- rsgBindTexture(singleTextureFragmentProgram, 0, backgroundTexture);
- float z = -0.9999f;
- rsgDrawQuad(
- cardVertices[0].x, cardVertices[0].y, z,
- cardVertices[1].x, cardVertices[1].y, z,
- cardVertices[2].x, cardVertices[2].y, z,
- cardVertices[3].x, cardVertices[3].y, z);
- updateCamera = true; // we mucked with the matrix.
- } else {
- rsgClearDepth(1.0f);
- if (debugRendering) { // for debugging - flash the screen so we know we're still rendering
- rsgClearColor(toggle ? backgroundColor.x : 1.0f,
- toggle ? backgroundColor.y : 0.0f,
- toggle ? backgroundColor.z : 0.0f,
- backgroundColor.w);
- toggle = !toggle;
- } else {
- rsgClearColor(backgroundColor.x, backgroundColor.y, backgroundColor.z,
- backgroundColor.w);
- }
- }
-}
-
-static void updateCameraMatrix(float width, float height)
-{
- float aspect = width / height;
- if (aspect != camera.aspect || updateCamera) {
- camera.aspect = aspect;
- loadPerspectiveMatrix(&projectionMatrix, camera.fov, camera.aspect, camera.near, camera.far);
- rsgProgramVertexLoadProjectionMatrix(&projectionMatrix);
-
- loadLookatMatrix(&modelviewMatrix, camera.from, camera.at, camera.up);
- rsgProgramVertexLoadModelMatrix(&modelviewMatrix);
- updateCamera = false;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Behavior/Physics
-////////////////////////////////////////////////////////////////////////////////////////////////////
-static int64_t lastTime = 0L; // keep track of how much time has passed between frames
-static float lastAngle = 0.0f;
-static float2 lastPosition;
-static bool animating = false;
-static float stopVelocity = 0.1f * M_PI / 180.0f; // slower than this: carousel stops
-static float selectionVelocity = 15.0f * M_PI / 180.0f; // faster than this: tap won't select
-static float velocityHistory[VELOCITY_HISTORY_MAX];
-static int velocityHistoryCount;
-static float mass = 5.0f; // kg
-
-static const float G = 9.80f; // gravity constant, in m/s
-static const float springConstant = 0.0f;
-
-// Computes a hit angle from the center of the carousel to a point on either a plane
-// or on a cylinder. If neither is hit, returns false.
-static bool hitAngle(float x, float y, float *angle)
-{
- Ray ray;
- makeRayForPixelAt(&ray, &camera, x, y);
- float t = FLT_MAX;
- if (dragModel == DRAG_MODEL_PLANE && rayPlaneIntersect(&ray, &carouselPlane, &t)) {
- const float3 point = (ray.position + t*ray.direction);
- const float3 direction = point - carouselPlane.point;
- *angle = atan2(direction.x, direction.z);
- if (debugSelection) rsDebug("Plane Angle = ", degrees(*angle));
- return true;
- } else if ((dragModel == DRAG_MODEL_CYLINDER_INSIDE || dragModel == DRAG_MODEL_CYLINDER_OUTSIDE)
- && rayCylinderIntersect(&ray, &carouselCylinder, &t)) {
- const float3 point = (ray.position + t*ray.direction);
- const float3 direction = point - carouselCylinder.center;
- *angle = atan2(direction.x, direction.z);
- if (debugSelection) rsDebug("Cylinder Angle = ", degrees(*angle));
- return true;
- }
- return false;
-}
-
-static float dragFunction(float x, float y)
-{
- float result;
- float angle;
- if (hitAngle(x, y, &angle)) {
- result = angle - lastAngle;
- // Handle singularity where atan2 switches between +- PI
- if (result < -M_PI) {
- result += 2.0f * M_PI;
- } else if (result > M_PI) {
- result -= 2.0f * M_PI;
- }
- lastAngle = angle;
- } else {
- // If we didn't hit anything or drag model wasn't plane or cylinder, we use screen delta
- result = dragFactor * ((x - lastPosition.x) / rsgGetWidth()) * M_PI;
- }
- return result;
-}
-
-static float deltaTimeInSeconds(int64_t current)
-{
- return (lastTime > 0L) ? (float) (current - lastTime) / 1000.0f : 0.0f;
-}
-
-static int doSelection(float x, float y)
-{
- Ray ray;
- if (makeRayForPixelAt(&ray, &camera, x, y)) {
- float bestTime = FLT_MAX;
- return intersectGeometry(&ray, &bestTime);
- }
- return -1;
-}
-
-static void sendAnimationStarted() {
- rsSendToClient(CMD_ANIMATION_STARTED);
-}
-
-static void sendAnimationFinished() {
- float data[1];
- data[0] = radiansToCarouselRotationAngle(bias);
- rsSendToClient(CMD_ANIMATION_FINISHED, (int*) data, sizeof(data));
-}
-
-void doStart(float x, float y, long eventTime)
-{
- touchPosition = lastPosition = (float2) { x, y };
- lastAngle = hitAngle(x,y, &lastAngle) ? lastAngle : 0.0f;
- enableSelection = fabs(velocity) < selectionVelocity;
- velocity = 0.0f;
- velocityHistory[0] = 0.0f;
- velocityHistoryCount = 0;
-
- releaseTime = lastTime; // used to disable scale down animation - any time in the past will do
- touchTime = lastTime = eventTime;
- touchBias = bias;
- isDragging = true;
- isOverScrolling = false;
- tiltAngle = 0;
- overscrollBias = bias;
-
- animatedSelection = doSelection(x, y); // used to provide visual feedback on touch
- stopAutoscroll();
-}
-
-static float computeAverageVelocityFromHistory()
-{
- if (velocityHistoryCount > 0) {
- const int count = min(VELOCITY_HISTORY_MAX, velocityHistoryCount);
- float vsum = 0.0f;
- for (int i = 0; i < count; i++) {
- vsum += velocityHistory[i];
- }
- return vsum / count;
- } else {
- return 0.0f;
- }
-}
-
-void doStop(float x, float y, long eventTime)
-{
- updateAllocationVars();
-
- releaseTime = rsUptimeMillis();
-
- if (enableSelection) {
- int data[3];
- int selection;
- float2 point;
-
- if ((selection = intersectDetailTexture(x, y, &point)) != -1) {
- if (debugSelection) rsDebug("Selected detail texture on doStop():", selection);
- data[0] = selection;
- data[1] = point.x;
- data[2] = point.y;
- rsSendToClientBlocking(CMD_DETAIL_SELECTED, data, sizeof(data));
- }
- else if ((selection = doSelection(x, y))!= -1) {
- if (debugSelection) rsDebug("Selected item on doStop():", selection);
- data[0] = selection;
- rsSendToClientBlocking(CMD_CARD_SELECTED, data, sizeof(data));
- }
- animating = false;
- } else {
- velocity = computeAverageVelocityFromHistory();
- if (fabs(velocity) > stopVelocity) {
- animating = true;
- }
- }
- enableSelection = false;
- lastTime = eventTime;
- isDragging = false;
-}
-
-void doLongPress()
-{
- int64_t currentTime = rsUptimeMillis();
- updateAllocationVars();
- // Selection happens for most recent position detected in doMotion()
- if (enableSelection && animatedSelection != -1) {
- if (debugSelection) rsDebug("doLongPress(), selection = ", animatedSelection);
- int data[7];
- data[0] = animatedSelection;
- data[1] = lastPosition.x;
- data[2] = lastPosition.y;
- data[3] = cards[animatedSelection].detailTexturePosition[0].x;
- data[4] = cards[animatedSelection].detailTexturePosition[0].y;
- data[5] = cards[animatedSelection].detailTexturePosition[1].x;
- data[6] = cards[animatedSelection].detailTexturePosition[1].y;
- rsSendToClientBlocking(CMD_CARD_LONGPRESS, data, sizeof(data));
- enableSelection = false;
- }
- lastTime = rsUptimeMillis();
-}
-
-void doMotion(float x, float y, long eventTime)
-{
- const float highBias = maximumBias();
- const float lowBias = minimumBias();
- float deltaOmega = dragFunction(x, y);
- overscrollBias += deltaOmega;
- overscrollBias = clamp(overscrollBias, lowBias - TILT_MAX_BIAS,
- highBias + TILT_MAX_BIAS);
- bias = clamp(overscrollBias, lowBias, highBias);
- isOverScrolling = tiltOverscroll();
-
- const float2 delta = (float2) { x, y } - touchPosition;
- float distance = sqrt(dot(delta, delta));
- bool inside = (distance < selectionRadius);
- enableSelection &= inside;
- lastPosition = (float2) { x, y };
- float dt = deltaTimeInSeconds(eventTime);
- if (dt > 0.0f) {
- float v = deltaOmega / dt;
- velocityHistory[velocityHistoryCount % VELOCITY_HISTORY_MAX] = v;
- velocityHistoryCount++;
- }
- velocity = computeAverageVelocityFromHistory();
- lastTime = eventTime;
-}
-
-bool tiltOverscroll() {
- if (overscrollBias == bias) {
- // No overscroll required.
- return false;
- }
-
- // How much we deviate from the maximum bias.
- float deltaBias = overscrollBias - bias;
- // We clamped, that means we need overscroll.
- tiltAngle = (deltaBias / TILT_MAX_BIAS)
- * TILT_MAX_ANGLE * fillDirection;
-
- return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Autoscroll Interpolation
-////////////////////////////////////////////////////////////////////////////////////////////////////
-static int64_t autoscrollStartTime = 0L; //tracks when we actually started interpolating
-static int64_t autoscrollDuration = 0L; //in milli seconds
-static int autoscrollInterpolationMode = INTERPOLATION_LINEAR;
-
-static float autoscrollStopAngle = 0.0f;
-static float autoscrollStartAngle = 0.0f;
-
-void setCarouselRotationAngle2(
- float endAngle,
- int milliseconds,
- int interpolationMode,
- float maxAnimatedArc)
-{
- float actualStart = radiansToCarouselRotationAngle(bias);
-
- if (maxAnimatedArc > 0) {
- //snap the current position to keep end - start under maxAnimatedArc
- if (actualStart <= endAngle) {
- if (actualStart < endAngle - maxAnimatedArc) {
- actualStart = endAngle - maxAnimatedArc;
- }
- }
- else {
- if (actualStart > endAngle + maxAnimatedArc) {
- actualStart = endAngle + maxAnimatedArc;
- }
- }
- }
-
- animating = true;
- isAutoScrolling = true;
- autoscrollDuration = milliseconds;
- autoscrollInterpolationMode = interpolationMode;
- autoscrollStartAngle = carouselRotationAngleToRadians(actualStart);
- autoscrollStopAngle = carouselRotationAngleToRadians(endAngle);
-
- //Make sure the start and stop angles are in the allowed range
- const float highBias = maximumBias();
- const float lowBias = minimumBias();
- autoscrollStartAngle = clamp(autoscrollStartAngle, lowBias, highBias);
- autoscrollStopAngle = clamp(autoscrollStopAngle, lowBias, highBias);
-
- //stop other animation kinds
- isOverScrolling = false;
- velocity = 0.0f;
-}
-
-static void stopAutoscroll()
-{
- isAutoScrolling = false;
- autoscrollStartTime = 0L; //reset for next time
-}
-
-// This method computes the position of all the cards by updating bias based on a
-// simple interpolation model. If the cards are still in motion, returns true.
-static bool doAutoscroll(float currentTime)
-{
- if (autoscrollDuration == 0L) {
- return false;
- }
-
- if (autoscrollStartTime == 0L) {
- autoscrollStartTime = currentTime;
- }
-
- const int64_t interpolationEndTime = autoscrollStartTime + autoscrollDuration;
-
- float timePos = (currentTime - autoscrollStartTime) / (float)autoscrollDuration;
- if (timePos > 1.0f) {
- timePos = 1.0f;
- }
-
- float lambda = timePos; //default to linear
- if (autoscrollInterpolationMode == INTERPOLATION_DECELERATE_QUADRATIC) {
- lambda = 1.0f - (1.0f - timePos) * (1.0f - timePos);
- }
- else if (autoscrollInterpolationMode == INTERPOLATION_ACCELERATE_DECELERATE_CUBIC) {
- lambda = timePos * timePos * (3 - 2 * timePos);
- }
-
- bias = lambda * autoscrollStopAngle + (1.0 - lambda) * autoscrollStartAngle;
-
- if (currentTime > interpolationEndTime) {
- stopAutoscroll();
- return false;
- }
- else {
- return true;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Hit detection using ray casting.
-////////////////////////////////////////////////////////////////////////////////////////////////////
-static const float EPSILON = 1.0e-6f;
-static const float tmin = 0.0f;
-
-static bool
-rayTriangleIntersect(Ray* ray, float3 p0, float3 p1, float3 p2, float* tout)
-{
- float3 e1 = p1 - p0;
- float3 e2 = p2 - p0;
- float3 s1 = cross(ray->direction, e2);
-
- float div = dot(s1, e1);
- if (div == 0.0f) return false; // ray is parallel to plane.
-
- float3 d = ray->position - p0;
- float invDiv = 1.0f / div;
-
- float u = dot(d, s1) * invDiv;
- if (u < 0.0f || u > 1.0f) return false;
-
- float3 s2 = cross(d, e1);
- float v = dot(ray->direction, s2) * invDiv;
- if ( v < 0.0f || (u+v) > 1.0f) return false;
-
- float t = dot(e2, s2) * invDiv;
- if (t < tmin || t > *tout)
- return false;
- *tout = t;
- return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Computes ray/plane intersection. Returns false if no intersection found.
-////////////////////////////////////////////////////////////////////////////////////////////////////
-static bool
-rayPlaneIntersect(Ray* ray, Plane* plane, float* tout)
-{
- float denom = dot(ray->direction, plane->normal);
- if (fabs(denom) > EPSILON) {
- float t = - (plane->constant + dot(ray->position, plane->normal)) / denom;
- if (t > tmin && t < *tout) {
- *tout = t;
- return true;
- }
- }
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Computes ray/cylindr intersection. There are 0, 1 or 2 hits.
-// Returns true and sets *tout to the closest point or
-// returns false if no intersection found.
-////////////////////////////////////////////////////////////////////////////////////////////////////
-static bool
-rayCylinderIntersect(Ray* ray, Cylinder* cylinder, float* tout)
-{
- const float A = ray->direction.x * ray->direction.x + ray->direction.z * ray->direction.z;
- if (A < EPSILON) return false; // ray misses
-
- // Compute quadratic equation coefficients
- const float B = 2.0f * (ray->direction.x * ray->position.x
- + ray->direction.z * ray->position.z);
- const float C = ray->position.x * ray->position.x
- + ray->position.z * ray->position.z
- - cylinder->radius * cylinder->radius;
- float disc = B*B - 4*A*C;
-
- if (disc < 0.0f) return false; // ray misses
- disc = sqrt(disc);
- const float denom = 2.0f * A;
-
- // Nearest point
- const float t1 = (-B - disc) / denom;
- if (dragModel == DRAG_MODEL_CYLINDER_OUTSIDE && t1 > tmin && t1 < *tout) {
- *tout = t1;
- return true;
- }
-
- // Far point
- const float t2 = (-B + disc) / denom;
- if (dragModel == DRAG_MODEL_CYLINDER_INSIDE && t2 > tmin && t2 < *tout) {
- *tout = t2;
- return true;
- }
- return false;
-}
-
-// Creates a ray for an Android pixel coordinate given a camera, ray and coordinates.
-// Note that the Y coordinate is opposite of GL rendering coordinates.
-static bool __attribute__((overloadable))
-makeRayForPixelAt(Ray* ray, PerspectiveCamera* cam, float x, float y)
-{
- if (debugCamera) {
- rsDebug("------ makeRay() -------", 0);
- rsDebug("Camera.from:", cam->from);
- rsDebug("Camera.at:", cam->at);
- rsDebug("Camera.dir:", normalize(cam->at - cam->from));
- }
-
- // Vector math. This has the potential to be much faster.
- // TODO: pre-compute lowerLeftRay, du, dv to eliminate most of this math.
- const float u = x / rsgGetWidth();
- const float v = 1.0f - (y / rsgGetHeight());
- const float aspect = (float) rsgGetWidth() / rsgGetHeight();
- const float tanfov2 = 2.0f * tan(radians(cam->fov / 2.0f));
- float3 dir = normalize(cam->at - cam->from);
- float3 du = tanfov2 * normalize(cross(dir, cam->up));
- float3 dv = tanfov2 * normalize(cross(du, dir));
- du *= aspect;
- float3 lowerLeftRay = dir - (0.5f * du) - (0.5f * dv);
- const float3 rayPoint = cam->from;
- const float3 rayDir = normalize(lowerLeftRay + u*du + v*dv);
- if (debugCamera) {
- rsDebug("Ray direction (vector math) = ", rayDir);
- }
-
- ray->position = rayPoint;
- ray->direction = rayDir;
- return true;
-}
-
-// Creates a ray for an Android pixel coordinate given a model view and projection matrix.
-// Note that the Y coordinate is opposite of GL rendering coordinates.
-static bool __attribute__((overloadable))
-makeRayForPixelAt(Ray* ray, rs_matrix4x4* model, rs_matrix4x4* proj, float x, float y)
-{
- rs_matrix4x4 pm = *model;
- rsMatrixLoadMultiply(&pm, proj, model);
- if (!rsMatrixInverse(&pm)) {
- rsDebug("ERROR: SINGULAR PM MATRIX", 0);
- return false;
- }
- const float width = rsgGetWidth();
- const float height = rsgGetHeight();
- const float winx = 2.0f * x / width - 1.0f;
- const float winy = 2.0f * y / height - 1.0f;
-
- float4 eye = { 0.0f, 0.0f, 0.0f, 1.0f };
- float4 at = { winx, winy, 1.0f, 1.0f };
-
- eye = rsMatrixMultiply(&pm, eye);
- eye *= 1.0f / eye.w;
-
- at = rsMatrixMultiply(&pm, at);
- at *= 1.0f / at.w;
-
- const float3 rayPoint = { eye.x, eye.y, eye.z };
- const float3 atPoint = { at.x, at.y, at.z };
- const float3 rayDir = normalize(atPoint - rayPoint);
- if (debugCamera) {
- rsDebug("winx: ", winx);
- rsDebug("winy: ", winy);
- rsDebug("Ray position (transformed) = ", eye);
- rsDebug("Ray direction (transformed) = ", rayDir);
- }
- ray->position = rayPoint;
- ray->direction = rayDir;
- return true;
-}
-
-static int intersectDetailTexture(float x, float y, float2 *tapCoordinates)
-{
- for (int id = 0; id < cardCount; id++) {
- if (cards[id].detailVisible) {
- const int x0 = cards[id].detailTexturePosition[0].x;
- const int y0 = cards[id].detailTexturePosition[0].y;
- const int x1 = cards[id].detailTexturePosition[1].x;
- const int y1 = cards[id].detailTexturePosition[1].y;
- if (x >= x0 && x <= x1 && y >= y0 && y <= y1) {
- float2 point = { x - x0, y - y0 };
- *tapCoordinates = point;
- return id;
- }
- }
- }
- return -1;
-}
-
-static int intersectGeometry(Ray* ray, float *bestTime)
-{
- int hit = -1;
- for (int id = 0; id < cardCount; id++) {
- if (cards[id].cardVisible) {
- rs_matrix4x4 matrix;
- float3 p[4];
-
- // Transform card vertices to world space
- rsMatrixLoadIdentity(&matrix);
- getMatrixForCard(&matrix, id, true, true);
- for (int vertex = 0; vertex < 4; vertex++) {
- float4 tmp = rsMatrixMultiply(&matrix, cardVertices[vertex]);
- if (tmp.w != 0.0f) {
- p[vertex].x = tmp.x;
- p[vertex].y = tmp.y;
- p[vertex].z = tmp.z;
- p[vertex] *= 1.0f / tmp.w;
- } else {
- rsDebug("Bad w coord: ", tmp);
- }
- }
-
- // Intersect card geometry
- if (rayTriangleIntersect(ray, p[0], p[1], p[2], bestTime)
- || rayTriangleIntersect(ray, p[2], p[3], p[0], bestTime)) {
- hit = id;
- }
- }
- }
- return hit;
-}
-
-// This method computes the position of all the cards by updating bias based on a
-// simple physics model. If the cards are still in motion, returns true.
-static bool doPhysics(float dt)
-{
- const float minStepTime = 1.0f / 300.0f; // ~5 steps per frame
- const int N = (dt > minStepTime) ? (1 + round(dt / minStepTime)) : 1;
- dt /= N;
- for (int i = 0; i < N; i++) {
- // Force friction - always opposes motion
- const float Ff = -frictionCoeff * velocity;
-
- // Restoring force to match cards with slots
- const float theta = startAngle + bias;
- const float dtheta = 2.0f * M_PI / slotCount;
- const float position = theta / dtheta;
- const float fraction = position - floor(position); // fractional position between slots
- float x;
- if (fraction > 0.5f) {
- x = - (1.0f - fraction);
- } else {
- x = fraction;
- }
- const float Fr = - springConstant * x;
-
- // compute velocity
- const float momentum = mass * velocity + (Ff + Fr)*dt;
- velocity = momentum / mass;
- bias += velocity * dt;
- }
- return fabs(velocity) > stopVelocity;
-}
-
-static float easeOut(float x)
-{
- return x;
-}
-
-// Computes the next value for bias using the current animation (physics/overscroll/autoscrolling)
-static bool updateNextPosition(int64_t currentTime)
-{
- static const float biasMin = 1e-4f; // close enough if we're within this margin of result
-
- float dt = deltaTimeInSeconds(currentTime);
-
- if (dt <= 0.0f) {
- if (debugRendering) rsDebug("Time delta was <= 0", dt);
- return true;
- }
-
- const float firstBias = maximumBias();
- const float lastBias = minimumBias();
- bool stillAnimating = false;
- if (isOverScrolling) {
- if (tiltAngle > TILT_MIN_ANGLE) {
- tiltAngle -= dt * TILT_MAX_ANGLE;
- stillAnimating = true;
- } else if (tiltAngle < -TILT_MIN_ANGLE) {
- tiltAngle += dt * TILT_MAX_ANGLE;
- stillAnimating = true;
- } else {
- isOverScrolling = false;
- tiltAngle = false;
- velocity = 0.0f;
- }
- } else if (isAutoScrolling) {
- stillAnimating = doAutoscroll(currentTime);
- } else {
- stillAnimating = doPhysics(dt);
- isOverScrolling = tiltAngle != 0;
- if (isOverScrolling) {
- velocity = 0.0f; // prevent bouncing due to v > 0 after overscroll animation.
- stillAnimating = true;
- }
- }
- bias = clamp(bias, lastBias, firstBias);
- return stillAnimating;
-}
-
-// Cull cards based on visibility and visibleSlotCount.
-// If visibleSlotCount is > 0, then only show those slots and cull the rest.
-// Otherwise, it should cull based on bounds of geometry.
-static void cullCards()
-{
- // Calculate the first and last angles of visible slots. We include
- // VISIBLE_SLOT_PADDING slots on either side of visibleSlotCount to allow
- // cards to slide in / out at either side, and rely on the view frustrum
- // for accurate clipping.
- const float visibleFirst = slotPosition(-VISIBLE_SLOT_PADDING);
- const float visibleLast = slotPosition(visibleSlotCount + VISIBLE_SLOT_PADDING);
-
- // We'll load but not draw prefetchCardCountPerSide cards
- // from either side of the visible slots.
- const int prefetchCardCountPerSide = max(prefetchCardCount / 2, VISIBLE_SLOT_PADDING);
- const float prefetchFirst = slotPosition(-prefetchCardCountPerSide);
- const float prefetchLast = slotPosition(visibleSlotCount + prefetchCardCountPerSide);
- for (int i = 0; i < cardCount; i++) {
- if (visibleSlotCount > 0) {
- // If visibleSlotCount is specified then only show cards between visibleFirst and visibleLast
- float p = cardPosition(i);
- if ((p >= prefetchFirst && p < prefetchLast)
- || (p <= prefetchFirst && p > prefetchLast)) {
- cards[i].shouldPrefetch = true;
- cards[i].cardVisible = (p >= visibleFirst && p < visibleLast)
- || (p <= visibleFirst && p > visibleLast);
- // cards[i].detailVisible will be set at draw time
- } else {
- cards[i].shouldPrefetch = false;
- cards[i].cardVisible = false;
- cards[i].detailVisible = false;
- }
- } else {
- // Cull the rest of the cards using bounding box of geometry.
- // TODO
- cards[i].cardVisible = true;
- // cards[i].detailVisible will be set at draw time
- }
- }
-}
-
-// Request missing texture/geometry for a single card
-static void requestCardResources(int i) {
- if (debugTextureLoading) rsDebug("*** Texture stamp: ", (int)cards[i].textureTimeStamp);
- int data[1] = { i };
-
- // request texture from client if not loaded
- if (cards[i].textureState == STATE_INVALID) {
- if (debugTextureLoading) rsDebug("Requesting card because state is STATE_INVALID", i);
- bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data));
- if (enqueued) {
- cards[i].textureState = STATE_LOADING;
- } else {
- if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_TEXTURE", i);
- }
- } else if (cards[i].textureState == STATE_STALE) {
- if (debugTextureLoading) rsDebug("Requesting card because state is STATE_STALE", i);
- bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data));
- if (enqueued) {
- cards[i].textureState = STATE_UPDATING;
- } else {
- if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_TEXTURE", i);
- }
- }
-
- // request detail texture from client if not loaded
- if (cards[i].detailTextureState == STATE_INVALID) {
- bool enqueued = rsSendToClient(CMD_REQUEST_DETAIL_TEXTURE, data, sizeof(data));
- if (enqueued) {
- cards[i].detailTextureState = STATE_LOADING;
- } else {
- if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_DETAIL_TEXTURE", i);
- }
- } else if (cards[i].detailTextureState == STATE_STALE) {
- bool enqueued = rsSendToClient(CMD_REQUEST_DETAIL_TEXTURE, data, sizeof(data));
- if (enqueued) {
- cards[i].detailTextureState = STATE_UPDATING;
- } else {
- if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_DETAIL_TEXTURE", i);
- }
- }
-
- // request geometry from client if not loaded
- if (cards[i].geometryState == STATE_INVALID) {
- bool enqueued = rsSendToClient(CMD_REQUEST_GEOMETRY, data, sizeof(data));
- if (enqueued) {
- cards[i].geometryState = STATE_LOADING;
- } else {
- if (debugGeometryLoading) rsDebug("Couldn't send CMD_REQUEST_GEOMETRY", i);
- }
- }
-}
-
-// Request texture/geometry for items that have come into view
-// or doesn't have a texture yet.
-static void updateCardResources(int64_t currentTime)
-{
- // First process any visible cards
- for (int i = cardCount-1; i >= 0; --i) {
- if (cards[i].cardVisible) {
- requestCardResources(i);
- }
- }
-
- // Then the rest
- for (int i = cardCount-1; i >= 0; --i) {
- if (cards[i].cardVisible) {
- // already requested above
- } else if (cards[i].shouldPrefetch) {
- requestCardResources(i);
- } else {
- // ask the host to remove the texture
- int data[1];
- if (cards[i].textureState != STATE_INVALID) {
- data[0] = i;
- bool enqueued = rsSendToClient(CMD_INVALIDATE_TEXTURE, data, sizeof(data));
- if (enqueued) {
- cards[i].textureState = STATE_INVALID;
- cards[i].textureTimeStamp = currentTime;
- } else {
- if (debugTextureLoading) rsDebug("Couldn't send CMD_INVALIDATE_TEXTURE", 0);
- }
- }
- // ask the host to remove the detail texture
- if (cards[i].detailTextureState != STATE_INVALID) {
- data[0] = i;
- bool enqueued = rsSendToClient(CMD_INVALIDATE_DETAIL_TEXTURE, data, sizeof(data));
- if (enqueued) {
- cards[i].detailTextureState = STATE_INVALID;
- cards[i].detailTextureTimeStamp = currentTime;
- } else {
- if (debugTextureLoading) rsDebug("Can't send CMD_INVALIDATE_DETAIL_TEXTURE", 0);
- }
- }
- // ask the host to remove the geometry
- if (cards[i].geometryState != STATE_INVALID) {
- data[0] = i;
- bool enqueued = rsSendToClient(CMD_INVALIDATE_GEOMETRY, data, sizeof(data));
- if (enqueued) {
- cards[i].geometryState = STATE_INVALID;
- } else {
- if (debugGeometryLoading) rsDebug("Couldn't send CMD_INVALIDATE_GEOMETRY", 0);
- }
- }
- }
- }
-}
-
-// Places dots on geometry to visually inspect that objects can be seen by rays.
-// NOTE: the color of the dot is somewhat random, as it depends on texture of previously-rendered
-// card.
-static void renderWithRays()
-{
- const float w = rsgGetWidth();
- const float h = rsgGetHeight();
- const int skip = 8;
-
- rsgProgramFragmentConstantColor(singleTextureFragmentProgram, 1.0f, 0.0f, 0.0f, 1.0f);
- for (int j = 0; j < (int) h; j+=skip) {
- float posY = (float) j;
- for (int i = 0; i < (int) w; i+=skip) {
- float posX = (float) i;
- Ray ray;
- if (makeRayForPixelAt(&ray, &camera, posX, posY)) {
- float bestTime = FLT_MAX;
- if (intersectGeometry(&ray, &bestTime) != -1) {
- rsgDrawSpriteScreenspace(posX, h - posY - 1, 0.0f, 2.0f, 2.0f);
- }
- }
- }
- }
-}
-
-int root() {
- int64_t currentTime = rsUptimeMillis();
-
- rsgBindProgramVertex(vertexProgram);
- rsgBindProgramRaster(rasterProgram);
- rsgBindSampler(singleTextureFragmentProgram, 0, linearClamp);
- rsgBindSampler(multiTextureFragmentProgram, 0, linearClamp);
- rsgBindSampler(multiTextureFragmentProgram, 1, linearClamp);
-
- updateAllocationVars();
-
- rsgBindProgramFragment(singleTextureFragmentProgram);
- // rsgClearDepth() currently follows the value of glDepthMask(), so it's disabled when
- // the mask is disabled. We may want to change the following to always draw w/o Z for
- // the background if we can guarantee the depth buffer will get cleared and
- // there's a performance advantage.
- rsgBindProgramStore(programStoreBackground);
- drawBackground();
-
- updateCameraMatrix(rsgGetWidth(), rsgGetHeight());
-
- bool stillAnimating = (currentTime - touchTime) <= ANIMATION_SCALE_UP_TIME;
-
- if (!isDragging && animating) {
- stillAnimating = updateNextPosition(currentTime);
- }
-
- lastTime = currentTime;
-
- cullCards();
-
- updateCardResources(currentTime);
-
- // Draw cards opaque only if requested, and always draw detail textures with blending.
- stillAnimating |= drawCards(currentTime);
- rsgBindProgramStore(programStoreDetail);
- stillAnimating |= drawDetails(currentTime);
-
- if (stillAnimating != animating) {
- if (stillAnimating) {
- // we just started animating
- sendAnimationStarted();
- } else {
- // we were animating but stopped animating just now
- sendAnimationFinished();
- }
- animating = stillAnimating;
- }
-
- if (debugRays) {
- renderWithRays();
- }
-
- //rsSendToClient(CMD_PING);
-
- return animating ? 1 : 0;
-}