Exemplo n.º 1
2
void sol_body_p(float p[3],
                const struct s_vary *vary,
                const struct v_body *bp,
                float dt)
{
    float v[3];

    if (bp->mi >= 0)
    {
        const struct v_move *mp = vary->mv + bp->mi;

        const struct b_path *pp = vary->base->pv + mp->pi;
        const struct b_path *pq = vary->base->pv + pp->pi;

        float s;

        if (pp->t == 0.f)
            s = 1.0e+6f;
        else if (vary->pv[mp->pi].f)
            s = (mp->t + dt) / pp->t;
        else
            s = mp->t / pp->t;

        v_sub(v, pq->p, pp->p);
        v_mad(p, pp->p, v, pp->s ? erp(s) : s);

        return;
    }

    p[0] = 0.0f;
    p[1] = 0.0f;
    p[2] = 0.0f;
}
Exemplo n.º 2
0
/*
 * Compute the new  linear and angular velocities of  a bouncing ball.
 * Q  gives the  position  of the  point  of impact  and  W gives  the
 * velocity of the object being impacted.
 */
static float sol_bounce(struct v_ball *up,
                        const float q[3],
                        const float w[3], float dt)
{
    float n[3], r[3], d[3], vn, wn;
    float *p = up->p;
    float *v = up->v;

    /* Find the normal of the impact. */

    v_sub(r, p, q);
    v_sub(d, v, w);
    v_nrm(n, r);

    /* Find the new angular velocity. */

    v_crs(up->w, d, r);
    v_scl(up->w, up->w, -1.0f / (up->r * up->r));

    /* Find the new linear velocity. */

    vn = v_dot(v, n);
    wn = v_dot(w, n);

    v_mad(v, v, n, 1.7 * (wn - vn));

    v_mad(p, q, n, up->r);

    /* Return the "energy" of the impact, to determine the sound amplitude. */

    return fabsf(v_dot(n, d));
}
Exemplo n.º 3
0
static int sol_test_back(float dt,
                         const struct v_ball *up,
                         const struct b_side *sp,
                         const float o[3],
                         const float w[3])
{
    float q[3], d;

    /* If the ball is not in front of the plane, the test passes. */

    v_sub(q, up->p, o);
    d = sp->d;

    if (v_dot(q, sp->n) - d - up->r <= 0)
        return 1;

    /* If it's not in front of the plane after DT seconds, the test passes. */

    v_mad(q, q, up->v, dt);
    d += v_dot(w, sp->n) * dt;

    if (v_dot(q, sp->n) - d - up->r <= 0)
        return 1;

    /* Else, test fails. */

    return 0;
}
Exemplo n.º 4
0
/*
 * Compute the new angular velocity and orientation of a ball pendulum.
 * A gives the accelleration of the ball.  G gives the gravity vector.
 */
void sol_pendulum(struct v_ball *up,
                  const float a[3],
                  const float g[3], float dt)
{
    float v[3], A[3], F[3], r[3], Y[3], T[3] = { 0.0f, 0.0f, 0.0f };

    const float m  = 5.000f;
    const float ka = 0.500f;
    const float kd = 0.995f;

    /* Find the total force over DT. */

    v_scl(A, a,     ka);
    v_mad(A, A, g, -dt);

    /* Find the force. */

    v_scl(F, A, m / dt);

    /* Find the position of the pendulum. */

    v_scl(r, up->E[1], -up->r);

    /* Find the torque on the pendulum. */

    if (fabsf(v_dot(r, F)) > 0.0f)
        v_crs(T, F, r);

    /* Apply the torque and dampen the angular velocity. */

    v_mad(up->W, up->W, T, dt);
    v_scl(up->W, up->W,    kd);

    /* Apply the angular velocity to the pendulum basis. */

    sol_rotate(up->E, up->W, dt);

    /* Apply a torque turning the pendulum toward the ball velocity. */

    v_mad(v, up->v, up->E[1], v_dot(up->v, up->E[1]));
    v_crs(Y, v, up->E[2]);
    v_scl(Y, up->E[1], 2 * v_dot(Y, up->E[1]));

    sol_rotate(up->E, Y, dt);
}
Exemplo n.º 5
0
/*
 * Compute the positions of all balls after DT seconds have passed.
 */
void sol_ball_step(struct s_vary *vary, float dt)
{
    int i;

    for (i = 0; i < vary->uc; i++)
    {
        struct v_ball *up = vary->uv + i;

        v_mad(up->p, up->p, up->v, dt);

        sol_rotate(up->e, up->w, dt);
    }
}
Exemplo n.º 6
0
/*
 * Compute  the earliest  time and  position of  the intersection  of a
 * sphere and a plane.
 *
 * The sphere has radius R and moves along vector V from point P.  The
 * plane  moves  along  vector  W.   The  plane has  normal  N  and  is
 * positioned at distance D from the origin O along that normal.
 */
static float v_side(float Q[3],
                    const float o[3],
                    const float w[3],
                    const float n[3], float d,
                    const float p[3],
                    const float v[3], float r)
{
    float vn = v_dot(v, n);
    float wn = v_dot(w, n);
    float t  = LARGE;
    
    if (vn - wn < 0.0f)
    {
        float on = v_dot(o, n);
        float pn = v_dot(p, n);

        float u = (r + d + on - pn) / (vn - wn);
        float a = (    d + on - pn) / (vn - wn);

        if (0.0f <= u)
        {
            t = u;

            v_mad(Q, p, v, +t);
            v_mad(Q, Q, n, -r);
        }
        else if (0.0f <= a)
        {
            t = 0;

            v_mad(Q, p, v, +t);
            v_mad(Q, Q, n, -r);
        }
    }
    return t;
}
Exemplo n.º 7
0
/*
 * Compute the  earliest time  and position of  the intersection  of a
 * sphere and a vertex.
 *
 * The sphere has radius R and moves along vector V from point P.  The
 * vertex moves  along vector  W from point  Q in a  coordinate system
 * based at O.
 */
static float v_vert(float Q[3],
                    const float o[3],
                    const float q[3],
                    const float w[3],
                    const float p[3],
                    const float v[3], float r)
{
    float O[3], P[3], V[3];
    float t = LARGE;

    v_add(O, o, q);
    v_sub(P, p, O);
    v_sub(V, v, w);

    if (v_dot(P, V) < 0.0f)
    {
        t = v_sol(P, V, r);

        if (t < LARGE)
            v_mad(Q, O, w, t);
    }
    return t;
}
Exemplo n.º 8
0
Arquivo: game.c Projeto: rlk/neverball
void game_set_fly(float k)
{
    struct s_vary *fp = &file.vary;

    float  x[3] = { 1.f, 0.f, 0.f };
    float  y[3] = { 0.f, 1.f, 0.f };
    float  z[3] = { 0.f, 0.f, 1.f };
    float c0[3] = { 0.f, 0.f, 0.f };
    float p0[3] = { 0.f, 0.f, 0.f };
    float c1[3] = { 0.f, 0.f, 0.f };
    float p1[3] = { 0.f, 0.f, 0.f };
    float  v[3];

    if (!state)
        return;

    v_cpy(view_e[0], x);
    v_cpy(view_e[1], y);

    if (fp->base->zc > 0)
        v_sub(view_e[2], fp->uv[ball].p, fp->base->zv[0].p);
    else
        v_cpy(view_e[2], z);

    if (fabs(v_dot(view_e[1], view_e[2])) > 0.999)
        v_cpy(view_e[2], z);

    v_crs(view_e[0], view_e[1], view_e[2]);
    v_crs(view_e[2], view_e[0], view_e[1]);

    v_nrm(view_e[0], view_e[0]);
    v_nrm(view_e[2], view_e[2]);

    /* k = 0.0 view is at the ball. */

    if (fp->uc > 0)
    {
        v_cpy(c0, fp->uv[ball].p);
        v_cpy(p0, fp->uv[ball].p);
    }

    v_mad(p0, p0, view_e[1], view_dy);
    v_mad(p0, p0, view_e[2], view_dz);

    /* k = +1.0 view is s_view 0 */

    if (k >= 0 && fp->base->wc > 0)
    {
        v_cpy(p1, fp->base->wv[0].p);
        v_cpy(c1, fp->base->wv[0].q);
    }

    /* k = -1.0 view is s_view 1 */

    if (k <= 0 && fp->base->wc > 1)
    {
        v_cpy(p1, fp->base->wv[1].p);
        v_cpy(c1, fp->base->wv[1].q);
    }

    /* Interpolate the views. */

    v_sub(v, p1, p0);
    v_mad(view_p, p0, v, k * k);

    v_sub(v, c1, c0);
    v_mad(view_c, c0, v, k * k);

    /* Orthonormalize the view basis. */

    v_sub(view_e[2], view_p, view_c);
    v_crs(view_e[0], view_e[1], view_e[2]);
    v_crs(view_e[2], view_e[0], view_e[1]);
    v_nrm(view_e[0], view_e[0]);
    v_nrm(view_e[2], view_e[2]);

    view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
}
Exemplo n.º 9
0
Arquivo: game.c Projeto: rlk/neverball
void game_update_view(float dt)
{
    const float y[3] = { 0.f, 1.f, 0.f };

    float dy;
    float dz;
    float k;
    float e[3];
    float d[3];
    float s = 2.f * dt;

    if (!state)
        return;

    /* Center the view about the ball. */

    v_cpy(view_c, file.vary.uv[ball].p);
    v_inv(view_v, file.vary.uv[ball].v);

    switch (config_get_d(CONFIG_CAMERA))
    {
    case 2:
        /* Camera 2: View vector is given by view angle. */

        view_e[2][0] = fsinf(V_RAD(view_a));
        view_e[2][1] = 0.f;
        view_e[2][2] = fcosf(V_RAD(view_a));

        s = 1.f;
        break;

    default:
        /* View vector approaches the ball velocity vector. */

        v_mad(e, view_v, y, v_dot(view_v, y));
        v_inv(e, e);

        k = v_dot(view_v, view_v);

        v_sub(view_e[2], view_p, view_c);
        v_mad(view_e[2], view_e[2], view_v, k * dt * 0.1f);
    }

    /* Orthonormalize the basis of the view in its new position. */

    v_crs(view_e[0], view_e[1], view_e[2]);
    v_crs(view_e[2], view_e[0], view_e[1]);
    v_nrm(view_e[0], view_e[0]);
    v_nrm(view_e[2], view_e[2]);

    /* The current view (dy, dz) approaches the ideal (view_dy, view_dz). */

    v_sub(d, view_p, view_c);

    dy = v_dot(view_e[1], d);
    dz = v_dot(view_e[2], d);

    dy += (view_dy - dy) * s;
    dz += (view_dz - dz) * s;

    /* Compute the new view position. */

    view_p[0] = view_p[1] = view_p[2] = 0.f;

    v_mad(view_p, view_c, view_e[1], dy);
    v_mad(view_p, view_p, view_e[2], dz);

    view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
}
Exemplo n.º 10
0
float sol_step(struct s_vary *vary, const float *g, float dt, int ui, int *m)
{
    float P[3], V[3], v[3], r[3], a[3], d, e, nt, b = 0.0f, tt = dt, t;
    int c;

    union cmd cmd;

    if (ui < vary->uc)
    {
        struct v_ball *up = vary->uv + ui;

        /* If the ball is in contact with a surface, apply friction. */

        v_cpy(a, up->v);
        v_cpy(v, up->v);
        v_cpy(up->v, g);

        if (m && sol_test_file(tt, P, V, up, vary) < 0.0005f)
        {
            v_cpy(up->v, v);
            v_sub(r, P, up->p);

            t = v_len(r) * v_len(g);
            if (t == 0.f)
            {
              t = SMALL;
            }
            
            if ((d = v_dot(r, g) / t) > 0.999f)
            {
                if ((e = (v_len(up->v) - dt)) > 0.0f)
                {
                    /* Scale the linear velocity. */

                    v_nrm(up->v, up->v);
                    v_scl(up->v, up->v, e);

                    /* Scale the angular velocity. */

                    v_sub(v, V, up->v);
                    v_crs(up->w, v, r);
                    v_scl(up->w, up->w, -1.0f / (up->r * up->r));
                }
                else
                {
                    /* Friction has brought the ball to a stop. */

                    up->v[0] = 0.0f;
                    up->v[1] = 0.0f;
                    up->v[2] = 0.0f;

                    (*m)++;
                }
            }
            else v_mad(up->v, v, g, tt);
        }
        else v_mad(up->v, v, g, tt);

        /* Test for collision. */

        for (c = 16; c > 0 && tt > 0; c--)
        {
            float st;
            int mi, ms;

            /* HACK: avoid stepping across path changes. */

            st = tt;

            for (mi = 0; mi < vary->mc; mi++)
            {
                struct v_move *mp = vary->mv + mi;
                struct v_path *pp = vary->pv + mp->pi;

                if (!pp->f)
                    continue;

                if (mp->tm + ms_peek(st) > pp->base->tm)
                    st = MS_TO_TIME(pp->base->tm - mp->tm);
            }

            /* Miss collisions if we reach the iteration limit. */

            if (c > 1)
                nt = sol_test_file(st, P, V, up, vary);
            else
                nt = tt;

            cmd.type       = CMD_STEP_SIMULATION;
            cmd.stepsim.dt = nt;
            sol_cmd_enq(&cmd);

            ms = ms_step(nt);

            sol_move_step(vary, nt, ms);
            sol_swch_step(vary, nt, ms);
            sol_ball_step(vary, nt);

            if (nt < st)
                if (b < (d = sol_bounce(up, P, V, nt)))
                    b = d;

            tt -= nt;
        }

        v_sub(a, up->v, a);

        sol_pendulum(up, a, g, dt);
    }

    return b;
}
Exemplo n.º 11
0
static float sol_test_body(float dt,
                           float T[3], float V[3],
                           const struct v_ball *up,
                           const struct s_vary *vary,
                           const struct v_body *bp)
{
    float U[3], O[3], E[4], W[3], u;

    const struct b_node *np = vary->base->nv + bp->base->ni;

    sol_body_p(O, vary, bp, 0.0f);
    sol_body_v(W, vary, bp, dt);
    sol_body_e(E, vary, bp, 0.0f);

    /*
     * For rotating bodies, rather than rotate every normal and vertex
     * of the body, we temporarily pretend the ball is rotating and
     * moving about a static body.
     */

    /*
     * Linear velocity of a point rotating about the origin:
     * v = w x p
     */

    if (E[0] != 1.0f || sol_body_w(vary, bp))
    {
        /* The body has a non-identity orientation or it is rotating. */

        struct v_ball ball;
        float e[4], p0[3], p1[3];
        const float z[3] = { 0 };

        /* First, calculate position at start and end of time interval. */

        v_sub(p0, up->p, O);
        v_cpy(p1, p0);
        q_conj(e, E);
        q_rot(p0, e, p0);

        v_mad(p1, p1, up->v, dt);
        v_mad(p1, p1, W, -dt);
        sol_body_e(e, vary, bp, dt);
        q_conj(e, e);
        q_rot(p1, e, p1);

        /* Set up ball struct with values relative to body. */

        ball = *up;

        v_cpy(ball.p, p0);

        /* Calculate velocity from start/end positions and time. */

        v_sub(ball.v, p1, p0);
        v_scl(ball.v, ball.v, 1.0f / dt);

        if ((u = sol_test_node(dt, U, &ball, vary->base, np, z, z)) < dt)
        {
            /* Compute the final orientation. */

            sol_body_e(e, vary, bp, u);

            /* Return world space coordinates. */

            q_rot(T, e, U);
            v_add(T, O, T);

            /* Move forward. */

            v_mad(T, T, W, u);

            /* Express "non-ball" velocity. */

            q_rot(V, e, ball.v);
            v_sub(V, up->v, V);

            dt = u;
        }
    }
    else
    {
        if ((u = sol_test_node(dt, U, up, vary->base, np, O, W)) < dt)
        {
            v_cpy(T, U);
            v_cpy(V, W);
            dt = u;
        }
    }
    return dt;
}
Exemplo n.º 12
0
/*
 * Compute the  earliest time  and position of  the intersection  of a
 * sphere and an edge.
 *
 * The sphere has radius R and moves along vector V from point P.  The
 * edge moves along vector W from point Q in a coordinate system based
 * at O.  The edge extends along the length of vector U.
 */
static float v_edge(float Q[3],
                    const float o[3],
                    const float q[3],
                    const float u[3],
                    const float w[3],
                    const float p[3],
                    const float v[3], float r)
{
    float d[3], e[3];
    float P[3], V[3];
    float du, eu, uu, s, t;

    v_sub(d, p, o);
    v_sub(d, d, q);
    v_sub(e, v, w);

    /*
     * Think projections.  Vectors D, extending from the edge vertex Q
     * to the sphere,  and E, the relative velocity  of sphere wrt the
     * edge, are  made orthogonal to  the edge vector U.   Division of
     * the  dot products  is required  to obtain  the  true projection
     * ratios since U does not have unit length.
     */

    du = v_dot(d, u);
    eu = v_dot(e, u);
    uu = v_dot(u, u);

    v_mad(P, d, u, -du / uu);

    /* First, test for intersection. */

    if (v_dot(P, P) < r * r)
    {
        /* The sphere already intersects the line of the edge. */

        if (du < 0 || du > uu)
        {
            /*
             * The sphere is behind the endpoints of the edge, and
             * can't hit the edge without hitting the vertices first.
             */
            return LARGE;
        }

        /* The sphere already intersects the edge. */

        if (v_dot(P, e) >= 0)
        {
            /* Moving apart. */
            return LARGE;
        }

        v_nrm(P, P);
        v_mad(Q, p, P, -r);

        return 0;
    }

    v_mad(V, e, u, -eu / uu);

    t = v_sol(P, V, r);
    s = (du + eu * t) / uu; /* Projection of D + E * t on U. */

    if (0.0f <= t && t < LARGE && 0.0f < s && s < 1.0f)
    {
        v_mad(d, o, w, t);
        v_mad(e, q, u, s);
        v_add(Q, e, d);
    }
    else
        t = LARGE;

    return t;
}
Exemplo n.º 13
0
void game_set_fly(float k)
{
    struct s_file *fp = &file;

    float  x[3] = { 1.f, 0.f, 0.f };
    float  y[3] = { 0.f, 1.f, 0.f };
    float  z[3] = { 0.f, 0.f, 1.f };
    float c0[3] = { 0.f, 0.f, 0.f };
    float p0[3] = { 0.f, 0.f, 0.f };
    float c1[3] = { 0.f, 0.f, 0.f };
    float p1[3] = { 0.f, 0.f, 0.f };
    float  v[3];

    v_cpy(view_e[0], x);
    v_cpy(view_e[1], y);
    v_cpy(view_e[2], z);

    /* k = 0.0 view is at the ball. */

    if (fp->uc > 0)
    {
        v_cpy(c0, fp->uv[0].p);
        v_cpy(p0, fp->uv[0].p);
    }

    v_mad(p0, p0, y, view_dp);
    v_mad(p0, p0, z, view_dz);
    v_mad(c0, c0, y, view_dc);

    /* k = +1.0 view is s_view 0 */

    if (k >= 0 && fp->wc > 0)
    {
        v_cpy(p1, fp->wv[0].p);
        v_cpy(c1, fp->wv[0].q);
    }

    /* k = -1.0 view is s_view 1 */

    if (k <= 0 && fp->wc > 1)
    {
        v_cpy(p1, fp->wv[1].p);
        v_cpy(c1, fp->wv[1].q);
    }

    /* Interpolate the views. */

    v_sub(v, p1, p0);
    v_mad(view_p, p0, v, k * k);

    v_sub(v, c1, c0);
    v_mad(view_c, c0, v, k * k);

    /* Orthonormalize the view basis. */

    v_sub(view_e[2], view_p, view_c);
    v_crs(view_e[0], view_e[1], view_e[2]);
    v_crs(view_e[2], view_e[0], view_e[1]);
    v_nrm(view_e[0], view_e[0]);
    v_nrm(view_e[2], view_e[2]);
}
Exemplo n.º 14
0
static void game_update_view(float dt)
{
    float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
    float dx = view_ry * dt * 5.0f;
    float k;

    view_a += view_ry * dt * 90.f;

    /* Center the view about the ball. */

    v_cpy(view_c, file.uv->p);
    v_inv(view_v, file.uv->v);

    switch (config_get_d(CONFIG_CAMERA))
    {
    case 1: /* Camera 1:  Viewpoint chases the ball position. */

        v_sub(view_e[2], view_p, view_c);
        break;

    case 2: /* Camera 2: View vector is given by view angle. */

        view_e[2][0] = fsinf(V_RAD(view_a));
        view_e[2][1] = 0.f;
        view_e[2][2] = fcosf(V_RAD(view_a));

        dx = 0.0f;

        break;

    default: /* Default: View vector approaches the ball velocity vector. */

        k = v_dot(view_v, view_v);

        v_sub(view_e[2], view_p, view_c);
        v_mad(view_e[2], view_e[2], view_v, k * dt / 4);

        break;
    }

    /* Orthonormalize the basis of the view in its new position. */

    v_crs(view_e[0], view_e[1], view_e[2]);
    v_crs(view_e[2], view_e[0], view_e[1]);
    v_nrm(view_e[0], view_e[0]);
    v_nrm(view_e[2], view_e[2]);

    /* Compute the new view position. */

    k = 1.0f + v_dot(view_e[2], view_v) / 10.0f;

    view_k = view_k + (k - view_k) * dt;

    if (view_k < 0.5) view_k = 0.5;

    v_cpy(view_p, file.uv->p);
    v_mad(view_p, view_p, view_e[0], dx      * view_k);
    v_mad(view_p, view_p, view_e[1], view_dp * view_k);
    v_mad(view_p, view_p, view_e[2], view_dz * view_k);

    /* Compute the new view center. */

    v_cpy(view_c, file.uv->p);
    v_mad(view_c, view_c, view_e[1], dc);

    /* Note the current view angle. */

    view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
}
Exemplo n.º 15
-1
static void game_update_view(float dt)
{
    float dc = view.dc * (jump_b > 0 ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
    float da = input_get_r() * dt * 90.0f;
    float k;

    float M[16], v[3], Y[3] = { 0.0f, 1.0f, 0.0f };
    float view_v[3];

    float spd = (float) cam_speed(input_get_c()) / 1000.0f;

    /* Track manual rotation time. */

    if (da == 0.0f)
    {
        if (view_time < 0.0f)
        {
            /* Transition time is influenced by activity time. */

            view_fade = CLAMP(VIEW_FADE_MIN, -view_time, VIEW_FADE_MAX);
            view_time = 0.0f;
        }

        /* Inactivity. */

        view_time += dt;
    }
    else
    {
        if (view_time > 0.0f)
        {
            view_fade = 0.0f;
            view_time = 0.0f;
        }

        /* Activity (yes, this is negative). */

        view_time -= dt;
    }

    /* Center the view about the ball. */

    v_cpy(view.c, vary.uv->p);

    view_v[0] = -vary.uv->v[0];
    view_v[1] =  0.0f;
    view_v[2] = -vary.uv->v[2];

    /* Compute view vector. */

    if (spd >= 0.0f)
    {
        /* Viewpoint chases ball position. */

        if (da == 0.0f)
        {
            float s;

            v_sub(view.e[2], view.p, view.c);
            v_nrm(view.e[2], view.e[2]);

            /* Gradually restore view vector convergence rate. */

            s = fpowf(view_time, 3.0f) / fpowf(view_fade, 3.0f);
            s = CLAMP(0.0f, s, 1.0f);

            v_mad(view.e[2], view.e[2], view_v, v_len(view_v) * spd * s * dt);
        }
    }
    else
    {
        /* View vector is given by view angle. */

        view.e[2][0] = fsinf(V_RAD(view.a));
        view.e[2][1] = 0.0;
        view.e[2][2] = fcosf(V_RAD(view.a));
    }

    /* Apply manual rotation. */

    if (da != 0.0f)
    {
        m_rot(M, Y, V_RAD(da));
        m_vxfm(v, M, view.e[2]);
        v_cpy(view.e[2], v);
    }

    /* Orthonormalize the new view reference frame. */

    v_crs(view.e[0], view.e[1], view.e[2]);
    v_crs(view.e[2], view.e[0], view.e[1]);
    v_nrm(view.e[0], view.e[0]);
    v_nrm(view.e[2], view.e[2]);

    /* Compute the new view position. */

    k = 1.0f + v_dot(view.e[2], view_v) / 10.0f;

    view_k = view_k + (k - view_k) * dt;

    if (view_k < 0.5) view_k = 0.5;

    v_scl(v,    view.e[1], view.dp * view_k);
    v_mad(v, v, view.e[2], view.dz * view_k);
    v_add(view.p, v, vary.uv->p);

    /* Compute the new view center. */

    v_cpy(view.c, vary.uv->p);
    v_mad(view.c, view.c, view.e[1], dc);

    /* Note the current view angle. */

    view.a = V_DEG(fatan2f(view.e[2][0], view.e[2][2]));

    game_cmd_updview();
}