PageRenderTime 63ms CodeModel.GetById 9ms app.highlight 48ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llmath/m3math.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 614 lines | 415 code | 106 blank | 93 comment | 25 complexity | 0359b68362c8a14692a15a3a5a383739 MD5 | raw file
  1/** 
  2 * @file m3math.cpp
  3 * @brief LLMatrix3 class implementation.
  4 *
  5 * $LicenseInfo:firstyear=2000&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "linden_common.h"
 28
 29//#include "vmath.h"
 30#include "v3math.h"
 31#include "v3dmath.h"
 32#include "v4math.h"
 33#include "m4math.h"
 34#include "m3math.h"
 35#include "llquaternion.h"
 36
 37// LLMatrix3
 38
 39//              ji  
 40// LLMatrix3 = |00 01 02 |
 41//             |10 11 12 |
 42//             |20 21 22 |
 43
 44// LLMatrix3 = |fx fy fz |  forward-axis
 45//             |lx ly lz |  left-axis
 46//             |ux uy uz |  up-axis
 47
 48
 49// Constructors
 50
 51
 52LLMatrix3::LLMatrix3(const LLQuaternion &q)
 53{
 54	setRot(q);
 55}
 56
 57
 58LLMatrix3::LLMatrix3(const F32 angle, const LLVector3 &vec)
 59{
 60	LLQuaternion	quat(angle, vec);
 61	setRot(quat);
 62}
 63
 64LLMatrix3::LLMatrix3(const F32 angle, const LLVector3d &vec)
 65{
 66	LLVector3 vec_f;
 67	vec_f.setVec(vec);
 68	LLQuaternion	quat(angle, vec_f);
 69	setRot(quat);
 70}
 71
 72LLMatrix3::LLMatrix3(const F32 angle, const LLVector4 &vec)
 73{
 74	LLQuaternion	quat(angle, vec);
 75	setRot(quat);
 76}
 77
 78LLMatrix3::LLMatrix3(const F32 angle, const F32 x, const F32 y, const F32 z)
 79{
 80	LLVector3 vec(x, y, z);
 81	LLQuaternion	quat(angle, vec);
 82	setRot(quat);
 83}
 84
 85LLMatrix3::LLMatrix3(const F32 roll, const F32 pitch, const F32 yaw)
 86{
 87	setRot(roll,pitch,yaw);
 88}
 89
 90// From Matrix and Quaternion FAQ
 91void LLMatrix3::getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const
 92{
 93	F64 angle_x, angle_y, angle_z;
 94	F64 cx, cy, cz;					// cosine of angle_x, angle_y, angle_z
 95	F64 sx,     sz;					// sine of angle_x, angle_y, angle_z
 96
 97	angle_y = asin(llclamp(mMatrix[2][0], -1.f, 1.f));
 98	cy = cos(angle_y);
 99
100	if (fabs(cy) > 0.005)		// non-zero
101	{
102		// no gimbal lock
103		cx = mMatrix[2][2] / cy;
104		sx = - mMatrix[2][1] / cy;
105
106		angle_x = (F32) atan2(sx, cx);
107
108		cz = mMatrix[0][0] / cy;
109		sz = - mMatrix[1][0] / cy;
110
111		angle_z = (F32) atan2(sz, cz);
112	}
113	else
114	{
115		// yup, gimbal lock
116		angle_x = 0;
117
118		// some tricky math thereby avoided, see article
119
120		cz = mMatrix[1][1];
121		sz = mMatrix[0][1];
122
123		angle_z = atan2(sz, cz);
124	}
125
126	*roll = (F32)angle_x;
127	*pitch = (F32)angle_y;
128	*yaw = (F32)angle_z;
129}
130	
131
132// Clear and Assignment Functions
133
134const LLMatrix3&	LLMatrix3::setIdentity()
135{
136	mMatrix[0][0] = 1.f;
137	mMatrix[0][1] = 0.f;
138	mMatrix[0][2] = 0.f;
139
140	mMatrix[1][0] = 0.f;
141	mMatrix[1][1] = 1.f;
142	mMatrix[1][2] = 0.f;
143
144	mMatrix[2][0] = 0.f;
145	mMatrix[2][1] = 0.f;
146	mMatrix[2][2] = 1.f;
147	return (*this);
148}
149
150const LLMatrix3&	LLMatrix3::clear()
151{
152	mMatrix[0][0] = 0.f;
153	mMatrix[0][1] = 0.f;
154	mMatrix[0][2] = 0.f;
155
156	mMatrix[1][0] = 0.f;
157	mMatrix[1][1] = 0.f;
158	mMatrix[1][2] = 0.f;
159
160	mMatrix[2][0] = 0.f;
161	mMatrix[2][1] = 0.f;
162	mMatrix[2][2] = 0.f;
163	return (*this);
164}
165
166const LLMatrix3&	LLMatrix3::setZero()
167{
168	mMatrix[0][0] = 0.f;
169	mMatrix[0][1] = 0.f;
170	mMatrix[0][2] = 0.f;
171
172	mMatrix[1][0] = 0.f;
173	mMatrix[1][1] = 0.f;
174	mMatrix[1][2] = 0.f;
175
176	mMatrix[2][0] = 0.f;
177	mMatrix[2][1] = 0.f;
178	mMatrix[2][2] = 0.f;
179	return (*this);
180}
181
182// various useful mMatrix functions
183
184const LLMatrix3&	LLMatrix3::transpose() 
185{
186	// transpose the matrix
187	F32 temp;
188	temp = mMatrix[VX][VY]; mMatrix[VX][VY] = mMatrix[VY][VX]; mMatrix[VY][VX] = temp;
189	temp = mMatrix[VX][VZ]; mMatrix[VX][VZ] = mMatrix[VZ][VX]; mMatrix[VZ][VX] = temp;
190	temp = mMatrix[VY][VZ]; mMatrix[VY][VZ] = mMatrix[VZ][VY]; mMatrix[VZ][VY] = temp;
191	return *this;
192}
193
194
195F32		LLMatrix3::determinant() const
196{
197	// Is this a useful method when we assume the matrices are valid rotation
198	// matrices throughout this implementation?
199	return	mMatrix[0][0] * (mMatrix[1][1] * mMatrix[2][2] - mMatrix[1][2] * mMatrix[2][1]) +
200		  	mMatrix[0][1] * (mMatrix[1][2] * mMatrix[2][0] - mMatrix[1][0] * mMatrix[2][2]) +
201		  	mMatrix[0][2] * (mMatrix[1][0] * mMatrix[2][1] - mMatrix[1][1] * mMatrix[2][0]); 
202}
203
204// inverts this matrix
205void LLMatrix3::invert()
206{
207	// fails silently if determinant is zero too small
208	F32 det = determinant();
209	const F32 VERY_SMALL_DETERMINANT = 0.000001f;
210	if (fabs(det) > VERY_SMALL_DETERMINANT)
211	{
212		// invertiable
213		LLMatrix3 t(*this);
214		mMatrix[VX][VX] = ( t.mMatrix[VY][VY] * t.mMatrix[VZ][VZ] - t.mMatrix[VY][VZ] * t.mMatrix[VZ][VY] ) / det;
215		mMatrix[VY][VX] = ( t.mMatrix[VY][VZ] * t.mMatrix[VZ][VX] - t.mMatrix[VY][VX] * t.mMatrix[VZ][VZ] ) / det;
216		mMatrix[VZ][VX] = ( t.mMatrix[VY][VX] * t.mMatrix[VZ][VY] - t.mMatrix[VY][VY] * t.mMatrix[VZ][VX] ) / det;
217		mMatrix[VX][VY] = ( t.mMatrix[VZ][VY] * t.mMatrix[VX][VZ] - t.mMatrix[VZ][VZ] * t.mMatrix[VX][VY] ) / det;
218		mMatrix[VY][VY] = ( t.mMatrix[VZ][VZ] * t.mMatrix[VX][VX] - t.mMatrix[VZ][VX] * t.mMatrix[VX][VZ] ) / det;
219		mMatrix[VZ][VY] = ( t.mMatrix[VZ][VX] * t.mMatrix[VX][VY] - t.mMatrix[VZ][VY] * t.mMatrix[VX][VX] ) / det;
220		mMatrix[VX][VZ] = ( t.mMatrix[VX][VY] * t.mMatrix[VY][VZ] - t.mMatrix[VX][VZ] * t.mMatrix[VY][VY] ) / det;
221		mMatrix[VY][VZ] = ( t.mMatrix[VX][VZ] * t.mMatrix[VY][VX] - t.mMatrix[VX][VX] * t.mMatrix[VY][VZ] ) / det;
222		mMatrix[VZ][VZ] = ( t.mMatrix[VX][VX] * t.mMatrix[VY][VY] - t.mMatrix[VX][VY] * t.mMatrix[VY][VX] ) / det;
223	}
224}
225
226// does not assume a rotation matrix, and does not divide by determinant, assuming results will be renormalized
227const LLMatrix3&	LLMatrix3::adjointTranspose()
228{
229	LLMatrix3 adjoint_transpose;
230	adjoint_transpose.mMatrix[VX][VX] = mMatrix[VY][VY] * mMatrix[VZ][VZ] - mMatrix[VY][VZ] * mMatrix[VZ][VY] ;
231	adjoint_transpose.mMatrix[VY][VX] = mMatrix[VY][VZ] * mMatrix[VZ][VX] - mMatrix[VY][VX] * mMatrix[VZ][VZ] ;
232	adjoint_transpose.mMatrix[VZ][VX] = mMatrix[VY][VX] * mMatrix[VZ][VY] - mMatrix[VY][VY] * mMatrix[VZ][VX] ;
233	adjoint_transpose.mMatrix[VX][VY] = mMatrix[VZ][VY] * mMatrix[VX][VZ] - mMatrix[VZ][VZ] * mMatrix[VX][VY] ;
234	adjoint_transpose.mMatrix[VY][VY] = mMatrix[VZ][VZ] * mMatrix[VX][VX] - mMatrix[VZ][VX] * mMatrix[VX][VZ] ;
235	adjoint_transpose.mMatrix[VZ][VY] = mMatrix[VZ][VX] * mMatrix[VX][VY] - mMatrix[VZ][VY] * mMatrix[VX][VX] ;
236	adjoint_transpose.mMatrix[VX][VZ] = mMatrix[VX][VY] * mMatrix[VY][VZ] - mMatrix[VX][VZ] * mMatrix[VY][VY] ;
237	adjoint_transpose.mMatrix[VY][VZ] = mMatrix[VX][VZ] * mMatrix[VY][VX] - mMatrix[VX][VX] * mMatrix[VY][VZ] ;
238	adjoint_transpose.mMatrix[VZ][VZ] = mMatrix[VX][VX] * mMatrix[VY][VY] - mMatrix[VX][VY] * mMatrix[VY][VX] ;
239
240	*this = adjoint_transpose;
241	return *this;
242}
243
244// SJB: This code is correct for a logicly stored (non-transposed) matrix;
245//		Our matrices are stored transposed, OpenGL style, so this generates the
246//		INVERSE quaternion (-x, -y, -z, w)!
247//		Because we use similar logic in LLQuaternion::getMatrix3,
248//		we are internally consistant so everything works OK :)
249LLQuaternion	LLMatrix3::quaternion() const
250{
251	LLQuaternion	quat;
252	F32		tr, s, q[4];
253	U32		i, j, k;
254	U32		nxt[3] = {1, 2, 0};
255
256	tr = mMatrix[0][0] + mMatrix[1][1] + mMatrix[2][2];
257
258	// check the diagonal
259	if (tr > 0.f) 
260	{
261		s = (F32)sqrt (tr + 1.f);
262		quat.mQ[VS] = s / 2.f;
263		s = 0.5f / s;
264		quat.mQ[VX] = (mMatrix[1][2] - mMatrix[2][1]) * s;
265		quat.mQ[VY] = (mMatrix[2][0] - mMatrix[0][2]) * s;
266		quat.mQ[VZ] = (mMatrix[0][1] - mMatrix[1][0]) * s;
267	} 
268	else
269	{		
270		// diagonal is negative
271		i = 0;
272		if (mMatrix[1][1] > mMatrix[0][0]) 
273			i = 1;
274		if (mMatrix[2][2] > mMatrix[i][i]) 
275			i = 2;
276
277		j = nxt[i];
278		k = nxt[j];
279
280
281		s = (F32)sqrt ((mMatrix[i][i] - (mMatrix[j][j] + mMatrix[k][k])) + 1.f);
282
283		q[i] = s * 0.5f;
284
285		if (s != 0.f) 
286			s = 0.5f / s;
287
288		q[3] = (mMatrix[j][k] - mMatrix[k][j]) * s;
289		q[j] = (mMatrix[i][j] + mMatrix[j][i]) * s;
290		q[k] = (mMatrix[i][k] + mMatrix[k][i]) * s;
291
292		quat.setQuat(q);
293	}
294	return quat;
295}
296
297
298// These functions take Rotation arguments
299const LLMatrix3&	LLMatrix3::setRot(const F32 angle, const F32 x, const F32 y, const F32 z)
300{
301	setRot(LLQuaternion(angle,x,y,z));
302	return *this;
303}
304
305const LLMatrix3&	LLMatrix3::setRot(const F32 angle, const LLVector3 &vec)
306{
307	setRot(LLQuaternion(angle, vec));
308	return *this;
309}
310
311const LLMatrix3&	LLMatrix3::setRot(const F32 roll, const F32 pitch, const F32 yaw)
312{
313	// Rotates RH about x-axis by 'roll'  then
314	// rotates RH about the old y-axis by 'pitch' then
315	// rotates RH about the original z-axis by 'yaw'.
316	//                .
317	//               /|\ yaw axis
318	//                |     __.
319	//   ._        ___|      /| pitch axis
320	//  _||\       \\ |-.   /
321	//  \|| \_______\_|__\_/_______
322	//   | _ _   o o o_o_o_o o   /_\_  ________\ roll axis
323	//   //  /_______/    /__________>         /   
324	//  /_,-'       //   /
325	//             /__,-'
326
327	F32		cx, sx, cy, sy, cz, sz;
328	F32		cxsy, sxsy;
329
330    cx = (F32)cos(roll); //A
331    sx = (F32)sin(roll); //B
332    cy = (F32)cos(pitch); //C
333    sy = (F32)sin(pitch); //D
334    cz = (F32)cos(yaw); //E
335    sz = (F32)sin(yaw); //F
336
337    cxsy = cx * sy; //AD
338    sxsy = sx * sy; //BD 
339
340    mMatrix[0][0] =  cy * cz;
341    mMatrix[1][0] = -cy * sz;
342    mMatrix[2][0] = sy;
343    mMatrix[0][1] = sxsy * cz + cx * sz;
344    mMatrix[1][1] = -sxsy * sz + cx * cz;
345    mMatrix[2][1] = -sx * cy;
346    mMatrix[0][2] =  -cxsy * cz + sx * sz;
347    mMatrix[1][2] =  cxsy * sz + sx * cz;
348    mMatrix[2][2] =  cx * cy;
349	return *this;
350}
351
352
353const LLMatrix3&	LLMatrix3::setRot(const LLQuaternion &q)
354{
355	*this = q.getMatrix3();
356	return *this;
357}
358
359const LLMatrix3&	LLMatrix3::setRows(const LLVector3 &fwd, const LLVector3 &left, const LLVector3 &up)
360{
361	mMatrix[0][0] = fwd.mV[0];
362	mMatrix[0][1] = fwd.mV[1];
363	mMatrix[0][2] = fwd.mV[2];
364
365	mMatrix[1][0] = left.mV[0];
366	mMatrix[1][1] = left.mV[1];
367	mMatrix[1][2] = left.mV[2];
368
369	mMatrix[2][0] = up.mV[0];
370	mMatrix[2][1] = up.mV[1];
371	mMatrix[2][2] = up.mV[2];
372
373	return *this;
374}
375
376const LLMatrix3& LLMatrix3::setRow( U32 rowIndex, const LLVector3& row )
377{
378	llassert( rowIndex >= 0 && rowIndex < NUM_VALUES_IN_MAT3 );
379
380	mMatrix[rowIndex][0] = row[0];
381	mMatrix[rowIndex][1] = row[1];
382	mMatrix[rowIndex][2] = row[2];
383
384	return *this;
385}
386
387const LLMatrix3& LLMatrix3::setCol( U32 colIndex, const LLVector3& col )
388{
389	llassert( colIndex >= 0 && colIndex < NUM_VALUES_IN_MAT3 );
390
391	mMatrix[0][colIndex] = col[0];
392	mMatrix[1][colIndex] = col[1];
393	mMatrix[2][colIndex] = col[2];
394
395	return *this;
396}
397		
398// Rotate exisitng mMatrix
399const LLMatrix3&	LLMatrix3::rotate(const F32 angle, const F32 x, const F32 y, const F32 z)
400{
401	LLMatrix3	mat(angle, x, y, z);
402	*this *= mat;
403	return *this;
404}
405
406
407const LLMatrix3&	LLMatrix3::rotate(const F32 angle, const LLVector3 &vec)
408{
409	LLMatrix3	mat(angle, vec);
410	*this *= mat;
411	return *this;
412}
413
414
415const LLMatrix3&	LLMatrix3::rotate(const F32 roll, const F32 pitch, const F32 yaw)
416{
417	LLMatrix3	mat(roll, pitch, yaw); 
418	*this *= mat;
419	return *this;
420}
421
422
423const LLMatrix3&	LLMatrix3::rotate(const LLQuaternion &q)
424{
425	LLMatrix3	mat(q);
426	*this *= mat;
427	return *this;
428}
429
430void LLMatrix3::add(const LLMatrix3& other_matrix)
431{
432	for (S32 i = 0; i < 3; ++i)
433	{
434		for (S32 j = 0; j < 3; ++j)
435		{
436			mMatrix[i][j] += other_matrix.mMatrix[i][j];
437		}
438	}
439}
440
441LLVector3	LLMatrix3::getFwdRow() const
442{
443	return LLVector3(mMatrix[VX]);
444}
445
446LLVector3	LLMatrix3::getLeftRow() const
447{
448	return LLVector3(mMatrix[VY]);
449}
450
451LLVector3	LLMatrix3::getUpRow() const
452{
453	return LLVector3(mMatrix[VZ]);
454}
455
456
457
458const LLMatrix3&	LLMatrix3::orthogonalize()
459{
460	LLVector3 x_axis(mMatrix[VX]);
461	LLVector3 y_axis(mMatrix[VY]);
462	LLVector3 z_axis(mMatrix[VZ]);
463
464	x_axis.normVec();
465	y_axis -= x_axis * (x_axis * y_axis);
466	y_axis.normVec();
467	z_axis = x_axis % y_axis;
468	setRows(x_axis, y_axis, z_axis);
469	return (*this);
470}
471
472
473// LLMatrix3 Operators
474
475LLMatrix3 operator*(const LLMatrix3 &a, const LLMatrix3 &b)
476{
477	U32		i, j;
478	LLMatrix3	mat;
479	for (i = 0; i < NUM_VALUES_IN_MAT3; i++)
480	{
481		for (j = 0; j < NUM_VALUES_IN_MAT3; j++)
482		{
483			mat.mMatrix[j][i] = a.mMatrix[j][0] * b.mMatrix[0][i] + 
484							    a.mMatrix[j][1] * b.mMatrix[1][i] + 
485							    a.mMatrix[j][2] * b.mMatrix[2][i];
486		}
487	}
488	return mat;
489}
490
491/* Not implemented to help enforce code consistency with the syntax of 
492   row-major notation.  This is a Good Thing.
493LLVector3 operator*(const LLMatrix3 &a, const LLVector3 &b)
494{
495	LLVector3	vec;
496	// matrix operates "from the left" on column vector
497	vec.mV[VX] = a.mMatrix[VX][VX] * b.mV[VX] + 
498				 a.mMatrix[VX][VY] * b.mV[VY] + 
499				 a.mMatrix[VX][VZ] * b.mV[VZ];
500	
501	vec.mV[VY] = a.mMatrix[VY][VX] * b.mV[VX] + 
502				 a.mMatrix[VY][VY] * b.mV[VY] + 
503				 a.mMatrix[VY][VZ] * b.mV[VZ];
504	
505	vec.mV[VZ] = a.mMatrix[VZ][VX] * b.mV[VX] + 
506				 a.mMatrix[VZ][VY] * b.mV[VY] + 
507				 a.mMatrix[VZ][VZ] * b.mV[VZ];
508	return vec;
509}
510*/
511
512
513LLVector3 operator*(const LLVector3 &a, const LLMatrix3 &b)
514{
515	// matrix operates "from the right" on row vector
516	return LLVector3(
517				a.mV[VX] * b.mMatrix[VX][VX] + 
518				a.mV[VY] * b.mMatrix[VY][VX] + 
519				a.mV[VZ] * b.mMatrix[VZ][VX],
520	
521				a.mV[VX] * b.mMatrix[VX][VY] + 
522				a.mV[VY] * b.mMatrix[VY][VY] + 
523				a.mV[VZ] * b.mMatrix[VZ][VY],
524	
525				a.mV[VX] * b.mMatrix[VX][VZ] + 
526				a.mV[VY] * b.mMatrix[VY][VZ] + 
527				a.mV[VZ] * b.mMatrix[VZ][VZ] );
528}
529
530LLVector3d operator*(const LLVector3d &a, const LLMatrix3 &b)
531{
532	// matrix operates "from the right" on row vector
533	return LLVector3d(
534				a.mdV[VX] * b.mMatrix[VX][VX] + 
535				a.mdV[VY] * b.mMatrix[VY][VX] + 
536				a.mdV[VZ] * b.mMatrix[VZ][VX],
537	
538				a.mdV[VX] * b.mMatrix[VX][VY] + 
539				a.mdV[VY] * b.mMatrix[VY][VY] + 
540				a.mdV[VZ] * b.mMatrix[VZ][VY],
541	
542				a.mdV[VX] * b.mMatrix[VX][VZ] + 
543				a.mdV[VY] * b.mMatrix[VY][VZ] + 
544				a.mdV[VZ] * b.mMatrix[VZ][VZ] );
545}
546
547bool operator==(const LLMatrix3 &a, const LLMatrix3 &b)
548{
549	U32		i, j;
550	for (i = 0; i < NUM_VALUES_IN_MAT3; i++)
551	{
552		for (j = 0; j < NUM_VALUES_IN_MAT3; j++)
553		{
554			if (a.mMatrix[j][i] != b.mMatrix[j][i])
555				return FALSE;
556		}
557	}
558	return TRUE;
559}
560
561bool operator!=(const LLMatrix3 &a, const LLMatrix3 &b)
562{
563	U32		i, j;
564	for (i = 0; i < NUM_VALUES_IN_MAT3; i++)
565	{
566		for (j = 0; j < NUM_VALUES_IN_MAT3; j++)
567		{
568			if (a.mMatrix[j][i] != b.mMatrix[j][i])
569				return TRUE;
570		}
571	}
572	return FALSE;
573}
574
575const LLMatrix3& operator*=(LLMatrix3 &a, const LLMatrix3 &b)
576{
577	U32		i, j;
578	LLMatrix3	mat;
579	for (i = 0; i < NUM_VALUES_IN_MAT3; i++)
580	{
581		for (j = 0; j < NUM_VALUES_IN_MAT3; j++)
582		{
583			mat.mMatrix[j][i] = a.mMatrix[j][0] * b.mMatrix[0][i] + 
584							    a.mMatrix[j][1] * b.mMatrix[1][i] + 
585							    a.mMatrix[j][2] * b.mMatrix[2][i];
586		}
587	}
588	a = mat;
589	return a;
590}
591
592const LLMatrix3& operator*=(LLMatrix3 &a, F32 scalar )
593{
594	for( U32 i = 0; i < NUM_VALUES_IN_MAT3; ++i )
595	{
596		for( U32 j = 0; j < NUM_VALUES_IN_MAT3; ++j )
597		{
598			a.mMatrix[i][j] *= scalar;
599		}
600	}
601
602	return a;
603}
604
605std::ostream& operator<<(std::ostream& s, const LLMatrix3 &a) 
606{
607	s << "{ " 
608		<< a.mMatrix[VX][VX] << ", " << a.mMatrix[VX][VY] << ", " << a.mMatrix[VX][VZ] << "; "
609		<< a.mMatrix[VY][VX] << ", " << a.mMatrix[VY][VY] << ", " << a.mMatrix[VY][VZ] << "; "
610		<< a.mMatrix[VZ][VX] << ", " << a.mMatrix[VZ][VY] << ", " << a.mMatrix[VZ][VZ] 
611	  << " }";
612	return s;
613}
614