PageRenderTime 58ms CodeModel.GetById 2ms app.highlight 50ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llmath/llcamera.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 671 lines | 470 code | 105 blank | 96 comment | 57 complexity | 9bbbffca49cd922b50745a52b517d300 MD5 | raw file
  1/** 
  2 * @file llcamera.cpp
  3 * @brief Implementation of the LLCamera class.
  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 "llmath.h"
 30#include "llcamera.h"
 31
 32// ---------------- Constructors and destructors ----------------
 33
 34LLCamera::LLCamera() :
 35	LLCoordFrame(),
 36	mView(DEFAULT_FIELD_OF_VIEW),
 37	mAspect(DEFAULT_ASPECT_RATIO),
 38	mViewHeightInPixels( -1 ),			// invalid height
 39	mNearPlane(DEFAULT_NEAR_PLANE),
 40	mFarPlane(DEFAULT_FAR_PLANE),
 41	mFixedDistance(-1.f),
 42	mPlaneCount(6),
 43	mFrustumCornerDist(0.f)
 44{
 45	calculateFrustumPlanes();
 46} 
 47
 48LLCamera::LLCamera(F32 vertical_fov_rads, F32 aspect_ratio, S32 view_height_in_pixels, F32 near_plane, F32 far_plane) :
 49	LLCoordFrame(),
 50	mViewHeightInPixels(view_height_in_pixels),
 51	mFixedDistance(-1.f),
 52	mPlaneCount(6),
 53	mFrustumCornerDist(0.f)
 54{
 55	mAspect = llclamp(aspect_ratio, MIN_ASPECT_RATIO, MAX_ASPECT_RATIO);
 56	mNearPlane = llclamp(near_plane, MIN_NEAR_PLANE, MAX_NEAR_PLANE);
 57	if(far_plane < 0) far_plane = DEFAULT_FAR_PLANE;
 58	mFarPlane = llclamp(far_plane, MIN_FAR_PLANE, MAX_FAR_PLANE);
 59
 60	setView(vertical_fov_rads);
 61} 
 62
 63LLCamera::~LLCamera()
 64{
 65
 66}
 67
 68// ---------------- LLCamera::getFoo() member functions ----------------
 69
 70F32 LLCamera::getMinView() const 
 71{
 72	// minimum vertical fov needs to be constrained in narrow windows.
 73	return mAspect > 1
 74		? MIN_FIELD_OF_VIEW // wide views
 75		: MIN_FIELD_OF_VIEW * 1/mAspect; // clamps minimum width in narrow views
 76}
 77
 78F32 LLCamera::getMaxView() const 
 79{
 80	// maximum vertical fov needs to be constrained in wide windows.
 81	return mAspect > 1 
 82		? MAX_FIELD_OF_VIEW / mAspect  // clamps maximum width in wide views
 83		: MAX_FIELD_OF_VIEW; // narrow views
 84}
 85
 86// ---------------- LLCamera::setFoo() member functions ----------------
 87
 88void LLCamera::setUserClipPlane(LLPlane& plane)
 89{
 90	mPlaneCount = 7;
 91	mAgentPlanes[6] = plane;
 92	mPlaneMask[6] = plane.calcPlaneMask();
 93}
 94
 95void LLCamera::disableUserClipPlane()
 96{
 97	mPlaneCount = 6;
 98}
 99
100void LLCamera::setView(F32 vertical_fov_rads) 
101{
102	mView = llclamp(vertical_fov_rads, MIN_FIELD_OF_VIEW, MAX_FIELD_OF_VIEW);
103	calculateFrustumPlanes();
104}
105
106void LLCamera::setViewHeightInPixels(S32 height)
107{
108	mViewHeightInPixels = height;
109
110	// Don't really need to do this, but update the pixel meter ratio with it.
111	calculateFrustumPlanes();
112}
113
114void LLCamera::setAspect(F32 aspect_ratio) 
115{
116	mAspect = llclamp(aspect_ratio, MIN_ASPECT_RATIO, MAX_ASPECT_RATIO);
117	calculateFrustumPlanes();
118}
119
120
121void LLCamera::setNear(F32 near_plane) 
122{
123	mNearPlane = llclamp(near_plane, MIN_NEAR_PLANE, MAX_NEAR_PLANE);
124	calculateFrustumPlanes();
125}
126
127
128void LLCamera::setFar(F32 far_plane) 
129{
130	mFarPlane = llclamp(far_plane, MIN_FAR_PLANE, MAX_FAR_PLANE);
131	calculateFrustumPlanes();
132}
133
134
135// ---------------- read/write to buffer ---------------- 
136
137size_t LLCamera::writeFrustumToBuffer(char *buffer) const
138{
139	memcpy(buffer, &mView, sizeof(F32));		/* Flawfinder: ignore */		
140	buffer += sizeof(F32);
141	memcpy(buffer, &mAspect, sizeof(F32));		/* Flawfinder: ignore */
142	buffer += sizeof(F32);
143	memcpy(buffer, &mNearPlane, sizeof(F32));	/* Flawfinder: ignore */
144	buffer += sizeof(F32);
145	memcpy(buffer, &mFarPlane, sizeof(F32));		/* Flawfinder: ignore */
146	return 4*sizeof(F32);
147}
148
149size_t LLCamera::readFrustumFromBuffer(const char *buffer)
150{
151	memcpy(&mView, buffer, sizeof(F32));		/* Flawfinder: ignore */
152	buffer += sizeof(F32);
153	memcpy(&mAspect, buffer, sizeof(F32));		/* Flawfinder: ignore */
154	buffer += sizeof(F32);
155	memcpy(&mNearPlane, buffer, sizeof(F32));	/* Flawfinder: ignore */
156	buffer += sizeof(F32);
157	memcpy(&mFarPlane, buffer, sizeof(F32));		/* Flawfinder: ignore */
158	return 4*sizeof(F32);
159}
160
161
162// ---------------- test methods  ---------------- 
163
164S32 LLCamera::AABBInFrustum(const LLVector4a &center, const LLVector4a& radius) 
165{
166	static const LLVector4a scaler[] = {
167		LLVector4a(-1,-1,-1),
168		LLVector4a( 1,-1,-1),
169		LLVector4a(-1, 1,-1),
170		LLVector4a( 1, 1,-1),
171		LLVector4a(-1,-1, 1),
172		LLVector4a( 1,-1, 1),
173		LLVector4a(-1, 1, 1),
174		LLVector4a( 1, 1, 1)
175	};
176
177	U8 mask = 0;
178	bool result = false;
179	LLVector4a rscale, maxp, minp;
180	LLSimdScalar d;
181	for (U32 i = 0; i < mPlaneCount; i++)
182	{
183		mask = mPlaneMask[i];
184		if (mask != 0xff)
185		{
186			const LLPlane& p(mAgentPlanes[i]);
187			p.getAt<3>(d);
188			rscale.setMul(radius, scaler[mask]);
189			minp.setSub(center, rscale);
190			d = -d;
191			if (p.dot3(minp).getF32() > d) 
192			{
193				return 0;
194			}
195			
196			if(!result)
197			{
198				maxp.setAdd(center, rscale);
199				result = (p.dot3(maxp).getF32() > d);
200			}
201		}
202	}
203
204	return result?1:2;
205}
206
207
208S32 LLCamera::AABBInFrustumNoFarClip(const LLVector4a& center, const LLVector4a& radius) 
209{
210	static const LLVector4a scaler[] = {
211		LLVector4a(-1,-1,-1),
212		LLVector4a( 1,-1,-1),
213		LLVector4a(-1, 1,-1),
214		LLVector4a( 1, 1,-1),
215		LLVector4a(-1,-1, 1),
216		LLVector4a( 1,-1, 1),
217		LLVector4a(-1, 1, 1),
218		LLVector4a( 1, 1, 1)
219	};
220
221	U8 mask = 0;
222	bool result = false;
223	LLVector4a rscale, maxp, minp;
224	LLSimdScalar d;
225	for (U32 i = 0; i < mPlaneCount; i++)
226	{
227		mask = mPlaneMask[i];
228		if ((i != 5) && (mask != 0xff))
229		{
230			const LLPlane& p(mAgentPlanes[i]);
231			p.getAt<3>(d);
232			rscale.setMul(radius, scaler[mask]);
233			minp.setSub(center, rscale);
234			d = -d;
235			if (p.dot3(minp).getF32() > d) 
236			{
237				return 0;
238			}
239			
240			if(!result)
241			{
242				maxp.setAdd(center, rscale);
243				result = (p.dot3(maxp).getF32() > d);
244			}
245		}
246	}
247
248	return result?1:2;
249}
250
251int LLCamera::sphereInFrustumQuick(const LLVector3 &sphere_center, const F32 radius) 
252{
253	LLVector3 dist = sphere_center-mFrustCenter;
254	float dsq = dist * dist;
255	float rsq = mFarPlane*0.5f + radius;
256	rsq *= rsq;
257
258	if (dsq < rsq) 
259	{
260		return 1;
261	}
262	
263	return 0;	
264}
265
266// HACK: This version is still around because the version below doesn't work
267// unless the agent planes are initialized.
268// Return 1 if sphere is in frustum, 2 if fully in frustum, otherwise 0.
269// NOTE: 'center' is in absolute frame.
270int LLCamera::sphereInFrustumOld(const LLVector3 &sphere_center, const F32 radius) const 
271{
272	// Returns 1 if sphere is in frustum, 0 if not.
273	// modified so that default view frust is along X with Z vertical
274	F32 x, y, z, rightDist, leftDist, topDist, bottomDist;
275
276	// Subtract the view position 
277	//LLVector3 relative_center;
278	//relative_center = sphere_center - getOrigin();
279	LLVector3 rel_center(sphere_center);
280	rel_center -= mOrigin;
281
282	bool all_in = TRUE;
283
284	// Transform relative_center.x to camera frame
285	x = mXAxis * rel_center;
286	if (x < MIN_NEAR_PLANE - radius)
287	{
288		return 0;
289	}
290	else if (x < MIN_NEAR_PLANE + radius)
291	{
292		all_in = FALSE;
293	}
294
295	if (x > mFarPlane + radius)
296	{
297		return 0;
298	}
299	else if (x > mFarPlane - radius)
300	{
301		all_in = FALSE;
302	}
303
304	// Transform relative_center.y to camera frame
305	y = mYAxis * rel_center;
306
307	// distance to plane is the dot product of (x, y, 0) * plane_normal
308	rightDist = x * mLocalPlanes[PLANE_RIGHT][VX] + y * mLocalPlanes[PLANE_RIGHT][VY];
309	if (rightDist < -radius)
310	{
311		return 0;
312	}
313	else if (rightDist < radius)
314	{
315		all_in = FALSE;
316	}
317
318	leftDist = x * mLocalPlanes[PLANE_LEFT][VX] + y * mLocalPlanes[PLANE_LEFT][VY];
319	if (leftDist < -radius)
320	{
321		return 0;
322	}
323	else if (leftDist < radius)
324	{
325		all_in = FALSE;
326	}
327
328	// Transform relative_center.y to camera frame
329	z = mZAxis * rel_center;
330
331	topDist = x * mLocalPlanes[PLANE_TOP][VX] + z * mLocalPlanes[PLANE_TOP][VZ];
332	if (topDist < -radius)
333	{
334		return 0;
335	}
336	else if (topDist < radius)
337	{
338		all_in = FALSE;
339	}
340
341	bottomDist = x * mLocalPlanes[PLANE_BOTTOM][VX] + z * mLocalPlanes[PLANE_BOTTOM][VZ];
342	if (bottomDist < -radius)
343	{
344		return 0;
345	}
346	else if (bottomDist < radius)
347	{
348		all_in = FALSE;
349	}
350
351	if (all_in)
352	{
353		return 2;
354	}
355
356	return 1;
357}
358
359
360// HACK: This (presumably faster) version only currently works if you set up the
361// frustum planes using GL.  At some point we should get those planes through another
362// mechanism, and then we can get rid of the "old" version above.
363
364// Return 1 if sphere is in frustum, 2 if fully in frustum, otherwise 0.
365// NOTE: 'center' is in absolute frame.
366int LLCamera::sphereInFrustum(const LLVector3 &sphere_center, const F32 radius) const 
367{
368	// Returns 1 if sphere is in frustum, 0 if not.
369	bool res = false;
370	for (int i = 0; i < 6; i++)
371	{
372		if (mPlaneMask[i] != 0xff)
373		{
374			float d = mAgentPlanes[i].dist(sphere_center);
375
376			if (d > radius) 
377			{
378				return 0;
379			}
380			res = res || (d > -radius);
381		}
382	}
383
384	return res?1:2;
385}
386
387
388// return height of a sphere of given radius, located at center, in pixels
389F32 LLCamera::heightInPixels(const LLVector3 &center, F32 radius ) const
390{
391	if (radius == 0.f) return 0.f;
392
393	// If height initialized
394	if (mViewHeightInPixels > -1)
395	{
396		// Convert sphere to coord system with 0,0,0 at camera
397		LLVector3 vec = center - mOrigin;
398
399		// Compute distance to sphere
400		F32 dist = vec.magVec();
401
402		// Calculate angle of whole object
403		F32 angle = 2.0f * (F32) atan2(radius, dist);
404
405		// Calculate fraction of field of view
406		F32 fraction_of_fov = angle / mView;
407
408		// Compute number of pixels tall, based on vertical field of view
409		return (fraction_of_fov * mViewHeightInPixels);
410	}
411	else
412	{
413		// return invalid height
414		return -1.0f;
415	}
416}
417
418// If pos is visible, return the distance from pos to the camera.
419// Use fudge distance to scale rad against top/bot/left/right planes
420// Otherwise, return -distance
421F32 LLCamera::visibleDistance(const LLVector3 &pos, F32 rad, F32 fudgedist, U32 planemask) const
422{
423	if (mFixedDistance > 0)
424	{
425		return mFixedDistance;
426	}
427	LLVector3 dvec = pos - mOrigin;
428	// Check visibility
429	F32 dist = dvec.magVec();
430	if (dist > rad)
431	{
432 		F32 dp,tdist;
433 		dp = dvec * mXAxis;
434  		if (dp < -rad)
435  			return -dist;
436
437		rad *= fudgedist;
438		LLVector3 tvec(pos);
439		for (int p=0; p<PLANE_NUM; p++)
440		{
441			if (!(planemask & (1<<p)))
442				continue;
443			tdist = -(mWorldPlanes[p].dist(tvec));
444			if (tdist > rad)
445				return -dist;
446		}
447	}
448	return dist;
449}
450
451// Like visibleDistance, except uses mHorizPlanes[], which are left and right
452//  planes perpindicular to (0,0,1) in world space
453F32 LLCamera::visibleHorizDistance(const LLVector3 &pos, F32 rad, F32 fudgedist, U32 planemask) const
454{
455	if (mFixedDistance > 0)
456	{
457		return mFixedDistance;
458	}
459	LLVector3 dvec = pos - mOrigin;
460	// Check visibility
461	F32 dist = dvec.magVec();
462	if (dist > rad)
463	{
464		rad *= fudgedist;
465		LLVector3 tvec(pos);
466		for (int p=0; p<HORIZ_PLANE_NUM; p++)
467		{
468			if (!(planemask & (1<<p)))
469				continue;
470			F32 tdist = -(mHorizPlanes[p].dist(tvec));
471			if (tdist > rad)
472				return -dist;
473		}
474	}
475	return dist;
476}
477
478// ---------------- friends and operators ----------------  
479
480std::ostream& operator<<(std::ostream &s, const LLCamera &C) 
481{
482	s << "{ \n";
483	s << "  Center = " << C.getOrigin() << "\n";
484	s << "  AtAxis = " << C.getXAxis() << "\n";
485	s << "  LeftAxis = " << C.getYAxis() << "\n";
486	s << "  UpAxis = " << C.getZAxis() << "\n";
487	s << "  View = " << C.getView() << "\n";
488	s << "  Aspect = " << C.getAspect() << "\n";
489	s << "  NearPlane   = " << C.mNearPlane << "\n";
490	s << "  FarPlane    = " << C.mFarPlane << "\n";
491	s << "  TopPlane    = " << C.mLocalPlanes[LLCamera::PLANE_TOP][VX] << "  " 
492							<< C.mLocalPlanes[LLCamera::PLANE_TOP][VY] << "  " 
493							<< C.mLocalPlanes[LLCamera::PLANE_TOP][VZ] << "\n";
494	s << "  BottomPlane = " << C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VX] << "  " 
495							<< C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VY] << "  " 
496							<< C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VZ] << "\n";
497	s << "  LeftPlane   = " << C.mLocalPlanes[LLCamera::PLANE_LEFT][VX] << "  " 
498							<< C.mLocalPlanes[LLCamera::PLANE_LEFT][VY] << "  " 
499							<< C.mLocalPlanes[LLCamera::PLANE_LEFT][VZ] << "\n";
500	s << "  RightPlane  = " << C.mLocalPlanes[LLCamera::PLANE_RIGHT][VX] << "  " 
501							<< C.mLocalPlanes[LLCamera::PLANE_RIGHT][VY] << "  " 
502							<< C.mLocalPlanes[LLCamera::PLANE_RIGHT][VZ] << "\n";
503	s << "}";
504	return s;
505}
506
507
508
509// ----------------  private member functions ----------------
510
511void LLCamera::calculateFrustumPlanes() 
512{
513	// The planes only change when any of the frustum descriptions change.
514	// They are not affected by changes of the position of the Frustum
515	// because they are known in the view frame and the position merely
516	// provides information on how to get from the absolute frame to the 
517	// view frame.
518
519	F32 left,right,top,bottom;
520	top = mFarPlane * (F32)tanf(0.5f * mView);
521	bottom = -top;
522	left = top * mAspect;
523	right = -left;
524
525	calculateFrustumPlanes(left, right, top, bottom);
526}
527
528LLPlane planeFromPoints(LLVector3 p1, LLVector3 p2, LLVector3 p3)
529{
530	LLVector3 n = ((p2-p1)%(p3-p1));
531	n.normVec();
532
533	return LLPlane(p1, n);
534}
535
536
537void LLCamera::ignoreAgentFrustumPlane(S32 idx)
538{
539	if (idx < 0 || idx > (S32) mPlaneCount)
540	{
541		return;
542	}
543
544	mPlaneMask[idx] = 0xff;
545	mAgentPlanes[idx].clear();
546}
547
548void LLCamera::calcAgentFrustumPlanes(LLVector3* frust)
549{
550	
551	for (int i = 0; i < 8; i++)
552	{
553		mAgentFrustum[i] = frust[i];
554	}
555
556	mFrustumCornerDist = (frust[5] - getOrigin()).magVec();
557
558	//frust contains the 8 points of the frustum, calculate 6 planes
559
560	//order of planes is important, keep most likely to fail in the front of the list
561
562	//near - frust[0], frust[1], frust[2]
563	mAgentPlanes[2] = planeFromPoints(frust[0], frust[1], frust[2]);
564
565	//far  
566	mAgentPlanes[5] = planeFromPoints(frust[5], frust[4], frust[6]);
567
568	//left  
569	mAgentPlanes[0] = planeFromPoints(frust[4], frust[0], frust[7]);
570
571	//right  
572	mAgentPlanes[1] = planeFromPoints(frust[1], frust[5], frust[6]);
573
574	//top  
575	mAgentPlanes[4] = planeFromPoints(frust[3], frust[2], frust[6]);
576
577	//bottom  
578	mAgentPlanes[3] = planeFromPoints(frust[1], frust[0], frust[4]);
579
580	//cache plane octant facing mask for use in AABBInFrustum
581	for (U32 i = 0; i < mPlaneCount; i++)
582	{
583		mPlaneMask[i] = mAgentPlanes[i].calcPlaneMask();
584	}
585}
586
587void LLCamera::calculateFrustumPlanes(F32 left, F32 right, F32 top, F32 bottom)
588{
589	LLVector3 a, b, c;
590
591	// For each plane we need to define 3 points (LLVector3's) in camera view space.  
592	// The order in which we pass the points to planeFromPoints() matters, because the 
593	// plane normal has a degeneracy of 2; we want it pointing _into_ the frustum. 
594
595	a.setVec(0.0f, 0.0f, 0.0f);
596	b.setVec(mFarPlane, right, top);
597	c.setVec(mFarPlane, right, bottom);
598	mLocalPlanes[PLANE_RIGHT].setVec(a, b, c);
599
600	c.setVec(mFarPlane, left, top);
601	mLocalPlanes[PLANE_TOP].setVec(a, c, b);
602
603	b.setVec(mFarPlane, left, bottom);
604	mLocalPlanes[PLANE_LEFT].setVec(a, b, c);
605
606	c.setVec(mFarPlane, right, bottom);
607	mLocalPlanes[PLANE_BOTTOM].setVec( a, c, b); 
608
609	//calculate center and radius squared of frustum in world absolute coordinates
610	static LLVector3 const X_AXIS(1.f, 0.f, 0.f);
611	mFrustCenter = X_AXIS*mFarPlane*0.5f;
612	mFrustCenter = transformToAbsolute(mFrustCenter);
613	mFrustRadiusSquared = mFarPlane*0.5f;
614	mFrustRadiusSquared *= mFrustRadiusSquared * 1.05f; //pad radius squared by 5%
615}
616
617// x and y are in WINDOW space, so x = Y-Axis (left/right), y= Z-Axis(Up/Down)
618void LLCamera::calculateFrustumPlanesFromWindow(F32 x1, F32 y1, F32 x2, F32 y2)
619{
620	F32 bottom, top, left, right;
621	F32 view_height = (F32)tanf(0.5f * mView) * mFarPlane;
622	F32 view_width = view_height * mAspect;
623	
624	left = 	 x1 * -2.f * view_width;
625	right =  x2 * -2.f * view_width;
626	bottom = y1 * 2.f * view_height; 
627	top = 	 y2 * 2.f * view_height;
628
629	calculateFrustumPlanes(left, right, top, bottom);
630}
631
632void LLCamera::calculateWorldFrustumPlanes() 
633{
634	F32 d;
635	LLVector3 center = mOrigin - mXAxis*mNearPlane;
636	mWorldPlanePos = center;
637	LLVector3 pnorm;	
638	for (int p=0; p<4; p++)
639	{
640		mLocalPlanes[p].getVector3(pnorm);
641		LLVector3 norm = rotateToAbsolute(pnorm);
642		norm.normVec();
643		d = -(center * norm);
644		mWorldPlanes[p] = LLPlane(norm, d);
645	}
646	// horizontal planes, perpindicular to (0,0,1);
647	LLVector3 zaxis(0, 0, 1.0f);
648	F32 yaw = getYaw();
649	{
650		LLVector3 tnorm;
651		mLocalPlanes[PLANE_LEFT].getVector3(tnorm);
652		tnorm.rotVec(yaw, zaxis);
653		d = -(mOrigin * tnorm);
654		mHorizPlanes[HORIZ_PLANE_LEFT] = LLPlane(tnorm, d);
655	}
656	{
657		LLVector3 tnorm;
658		mLocalPlanes[PLANE_RIGHT].getVector3(tnorm);
659		tnorm.rotVec(yaw, zaxis);
660		d = -(mOrigin * tnorm);
661		mHorizPlanes[HORIZ_PLANE_RIGHT] = LLPlane(tnorm, d);
662	}
663}
664
665// NOTE: this is the OpenGL matrix that will transform the default OpenGL view 
666// (-Z=at, Y=up) to the default view of the LLCamera class (X=at, Z=up):
667// 
668// F32 cfr_transform =  {  0.f,  0.f, -1.f,  0.f,   // -Z becomes X
669// 						  -1.f,  0.f,  0.f,  0.f,   // -X becomes Y
670//  					   0.f,  1.f,  0.f,  0.f,   //  Y becomes Z
671// 						   0.f,  0.f,  0.f,  1.f };