1a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang/*
2a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * Copyright (C) 2011 The Android Open Source Project
3a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang *
4a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * Licensed under the Apache License, Version 2.0 (the "License");
5a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * you may not use this file except in compliance with the License.
6a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * You may obtain a copy of the License at
7a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang *
8a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang *      http://www.apache.org/licenses/LICENSE-2.0
9a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang *
10a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * Unless required by applicable law or agreed to in writing, software
11a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * distributed under the License is distributed on an "AS IS" BASIS,
12a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * See the License for the specific language governing permissions and
14a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * limitations under the License.
15a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang */
16a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
17a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang/** @file rs_quaternion.rsh
18a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang *  \brief Quaternion routines
19a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang *
20a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang *
21a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang */
22a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
23a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang#ifndef __RS_QUATERNION_RSH__
24a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang#define __RS_QUATERNION_RSH__
25a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
26a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
27a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang/**
28a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * Set the quaternion components
29a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param w component
30a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param x component
31a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param y component
32a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param z component
33a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang */
34a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wangstatic void __attribute__((overloadable))
35a6720149b0039b3a5e5a3183c124d500f0830d38Ying WangrsQuaternionSet(rs_quaternion *q, float w, float x, float y, float z) {
36a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->w = w;
37a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->x = x;
38a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->y = y;
39a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->z = z;
40a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang}
41a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
42a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang/**
43a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * Set the quaternion from another quaternion
44a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param q destination quaternion
45a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param rhs source quaternion
46a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang */
47a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wangstatic void __attribute__((overloadable))
48a6720149b0039b3a5e5a3183c124d500f0830d38Ying WangrsQuaternionSet(rs_quaternion *q, const rs_quaternion *rhs) {
49a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->w = rhs->w;
50a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->x = rhs->x;
51a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->y = rhs->y;
52a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->z = rhs->z;
53a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang}
54a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
55a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang/**
56a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * Multiply quaternion by a scalar
57a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param q quaternion to multiply
58a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param s scalar
59a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang */
60a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wangstatic void __attribute__((overloadable))
61a6720149b0039b3a5e5a3183c124d500f0830d38Ying WangrsQuaternionMultiply(rs_quaternion *q, float s) {
62a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->w *= s;
63a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->x *= s;
64a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->y *= s;
65a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->z *= s;
66a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang}
67a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
68a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang/**
69a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * Add two quaternions
70a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param q destination quaternion to add to
71a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param rsh right hand side quaternion to add
72a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang */
73a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wangstatic void
74a6720149b0039b3a5e5a3183c124d500f0830d38Ying WangrsQuaternionAdd(rs_quaternion *q, const rs_quaternion *rhs) {
75a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->w *= rhs->w;
76a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->x *= rhs->x;
77a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->y *= rhs->y;
78a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->z *= rhs->z;
79a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang}
80a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
81a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang/**
82a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * Loads a quaternion that represents a rotation about an arbitrary unit vector
83a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param q quaternion to set
84a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param rot angle to rotate by
85a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param x component of a vector
86a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param y component of a vector
87a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param x component of a vector
88a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang */
89a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wangstatic void
90a6720149b0039b3a5e5a3183c124d500f0830d38Ying WangrsQuaternionLoadRotateUnit(rs_quaternion *q, float rot, float x, float y, float z) {
91a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    rot *= (float)(M_PI / 180.0f) * 0.5f;
92a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    float c = cos(rot);
93a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    float s = sin(rot);
94a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
95a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->w = c;
96a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->x = x * s;
97a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->y = y * s;
98a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->z = z * s;
99a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang}
100a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
101a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang/**
102a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * Loads a quaternion that represents a rotation about an arbitrary vector
103a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * (doesn't have to be unit)
104a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param q quaternion to set
105a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param rot angle to rotate by
106a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param x component of a vector
107a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param y component of a vector
108a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param x component of a vector
109a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang */
110a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wangstatic void
111a6720149b0039b3a5e5a3183c124d500f0830d38Ying WangrsQuaternionLoadRotate(rs_quaternion *q, float rot, float x, float y, float z) {
112a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    const float len = x*x + y*y + z*z;
113a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    if (len != 1) {
114a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        const float recipLen = 1.f / sqrt(len);
115a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        x *= recipLen;
116a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        y *= recipLen;
117a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        z *= recipLen;
118a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    }
119a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    rsQuaternionLoadRotateUnit(q, rot, x, y, z);
120a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang}
121a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
122a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang/**
123a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * Conjugates the quaternion
124a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param q quaternion to conjugate
125a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang */
126a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wangstatic void
127a6720149b0039b3a5e5a3183c124d500f0830d38Ying WangrsQuaternionConjugate(rs_quaternion *q) {
128a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->x = -q->x;
129a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->y = -q->y;
130a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->z = -q->z;
131a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang}
132a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
133a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang/**
134a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * Dot product of two quaternions
135a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param q0 first quaternion
136a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param q1 second quaternion
137a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @return dot product between q0 and q1
138a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang */
139a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wangstatic float
140a6720149b0039b3a5e5a3183c124d500f0830d38Ying WangrsQuaternionDot(const rs_quaternion *q0, const rs_quaternion *q1) {
141a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    return q0->w*q1->w + q0->x*q1->x + q0->y*q1->y + q0->z*q1->z;
142a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang}
143a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
144a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang/**
145a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * Normalizes the quaternion
146a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param q quaternion to normalize
147a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang */
148a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wangstatic void
149a6720149b0039b3a5e5a3183c124d500f0830d38Ying WangrsQuaternionNormalize(rs_quaternion *q) {
150a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    const float len = rsQuaternionDot(q, q);
151a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    if (len != 1) {
152a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        const float recipLen = 1.f / sqrt(len);
153a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        rsQuaternionMultiply(q, recipLen);
154a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    }
155a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang}
156a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
157a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang/**
158a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * Multiply quaternion by another quaternion
159a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param q destination quaternion
160a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param rhs right hand side quaternion to multiply by
161a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang */
162a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wangstatic void __attribute__((overloadable))
163a6720149b0039b3a5e5a3183c124d500f0830d38Ying WangrsQuaternionMultiply(rs_quaternion *q, const rs_quaternion *rhs) {
164a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    rs_quaternion qtmp;
165a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    rsQuaternionSet(&qtmp, q);
166a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
167a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->w = qtmp.w*rhs->w - qtmp.x*rhs->x - qtmp.y*rhs->y - qtmp.z*rhs->z;
168a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->x = qtmp.w*rhs->x + qtmp.x*rhs->w + qtmp.y*rhs->z - qtmp.z*rhs->y;
169a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->y = qtmp.w*rhs->y + qtmp.y*rhs->w + qtmp.z*rhs->x - qtmp.x*rhs->z;
170a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    q->z = qtmp.w*rhs->z + qtmp.z*rhs->w + qtmp.x*rhs->y - qtmp.y*rhs->x;
171a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    rsQuaternionNormalize(q);
172a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang}
173a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
174a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang/**
175a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * Performs spherical linear interpolation between two quaternions
176a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param q result quaternion from interpolation
177a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param q0 first param
178a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param q1 second param
179a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param t how much to interpolate by
180a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang */
181a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wangstatic void
182a6720149b0039b3a5e5a3183c124d500f0830d38Ying WangrsQuaternionSlerp(rs_quaternion *q, const rs_quaternion *q0, const rs_quaternion *q1, float t) {
183a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    if (t <= 0.0f) {
184a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        rsQuaternionSet(q, q0);
185a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        return;
186a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    }
187a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    if (t >= 1.0f) {
188a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        rsQuaternionSet(q, q1);
189a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        return;
190a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    }
191a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
192a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    rs_quaternion tempq0, tempq1;
193a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    rsQuaternionSet(&tempq0, q0);
194a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    rsQuaternionSet(&tempq1, q1);
195a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
196a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    float angle = rsQuaternionDot(q0, q1);
197a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    if (angle < 0) {
198a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        rsQuaternionMultiply(&tempq0, -1.0f);
199a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        angle *= -1.0f;
200a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    }
201a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
202a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    float scale, invScale;
203a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    if (angle + 1.0f > 0.05f) {
204a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        if (1.0f - angle >= 0.05f) {
205a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang            float theta = acos(angle);
206a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang            float invSinTheta = 1.0f / sin(theta);
207a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang            scale = sin(theta * (1.0f - t)) * invSinTheta;
208a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang            invScale = sin(theta * t) * invSinTheta;
209a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        } else {
210a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang            scale = 1.0f - t;
211a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang            invScale = t;
212a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        }
213a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    } else {
214a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        rsQuaternionSet(&tempq1, tempq0.z, -tempq0.y, tempq0.x, -tempq0.w);
215a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        scale = sin(M_PI * (0.5f - t));
216a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang        invScale = sin(M_PI * t);
217a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    }
218a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
219a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    rsQuaternionSet(q, tempq0.w*scale + tempq1.w*invScale, tempq0.x*scale + tempq1.x*invScale,
220a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang                        tempq0.y*scale + tempq1.y*invScale, tempq0.z*scale + tempq1.z*invScale);
221a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang}
222a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
223a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang/**
224a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * Computes rotation matrix from the normalized quaternion
225a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param m resulting matrix
226a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang * @param p normalized quaternion
227a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang */
228a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wangstatic void rsQuaternionGetMatrixUnit(rs_matrix4x4 *m, const rs_quaternion *q) {
229a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    float xx = q->x * q->x;
230a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    float xy = q->x * q->y;
231a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    float xz = q->x * q->z;
232a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    float xw = q->x * q->w;
233a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    float yy = q->y * q->y;
234a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    float yz = q->y * q->z;
235a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    float yw = q->y * q->w;
236a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    float zz = q->z * q->z;
237a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    float zw = q->z * q->w;
238a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
239a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    m->m[0]  = 1.0f - 2.0f * ( yy + zz );
240a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    m->m[4]  =        2.0f * ( xy - zw );
241a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    m->m[8]  =        2.0f * ( xz + yw );
242a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    m->m[1]  =        2.0f * ( xy + zw );
243a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    m->m[5]  = 1.0f - 2.0f * ( xx + zz );
244a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    m->m[9]  =        2.0f * ( yz - xw );
245a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    m->m[2]  =        2.0f * ( xz - yw );
246a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    m->m[6]  =        2.0f * ( yz + xw );
247a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    m->m[10] = 1.0f - 2.0f * ( xx + yy );
248a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    m->m[3]  = m->m[7] = m->m[11] = m->m[12] = m->m[13] = m->m[14] = 0.0f;
249a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang    m->m[15] = 1.0f;
250a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang}
251a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
252a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang#endif
253a6720149b0039b3a5e5a3183c124d500f0830d38Ying Wang
254