-
-
Notifications
You must be signed in to change notification settings - Fork 0
Integration with 3D Rendering Libraries
This section explores how NAS6LIB can be effectively integrated with popular web-based 3D rendering libraries
like X3DOM and Three.js. It demonstrates how NAS6LIB's robust mathematical and physics capabilities
can drive 3D scene transformations, camera controls, and object movements within these rendering environments.
A simple page and file set that allows you to test the basic operations of NAS6LIB in the console.
It also includes minimal integration samples with 3D rendering libraries such as Three.js and X3DOM.
Please enable comments and add test code to make use of it.
TestPage ZipFile (External Link)
testpage000 DEMO (External Link)
test3JS000 DEMO (External Link)
testX3DOM000 DEMO (External Link)
Please scroll up a little to see the project template.
NAS6LIB has a unique placement rule with a leading w,
but it can also be accessed with general placement rules.
//Accessor Test
var v = new N6LVector().Create([1,2,3,4], true);
var ary = v.Get("array");
var len = v.Get("length");
var acs = v.GetAccessor("x");
//Zero fill x→z//acs acts directly as an iterator
for(; acs < len; acs++) ary[acs] = 0.0;
console.log(ary); // Result: [4, 0, 0, 0]
var m = new N6LMatrix().Create([
[1,0,0,1],
[0,1,0,2],
[0,0,1,3],
[0,0,0,4]]);
var arym = m.Get("array");
var vv = m.Get("v0");//x axis
var aryv = vv.Get("array");
var lenv = vv.Get("length");
var acsv = vv.GetAccessor("x");
//Zero fill x→z//acs acts as a direct iterator
for(; acsv < lenv; acsv++) aryv[acsv] = 0.0;
m.Set("m23", 1);
console.log(arym); // Result: [[4,0,0,0], [1,0,0,0], [2,0,1,0], [1,0,0,1]]
var mm = new N6LMatrix().Create([
1,0,0,1,
0,1,0,2,
0,0,1,3,
0,0,0,4],4,4);
var arymm = mm.Get("array");
console.log(arymm); // Result: [[4,0,0,0], [1,1,0,0], [2,0,1,0], [3,0,0,1]]
var q = new N6LQuaternion().Create([1,2,3,4]);
var aryq = q.Get("array");
var lenq = q.Get("length");
var acsq = q.GetAccessor("x");
//Zero fill x→z//acs acts directly as an iterator
for(; acsq < lenq; acsq++) aryq[acsq] = 0.0;
console.log(aryq); // Result: [4, 0, 0, 0]
var l = new N6LLnQuaternion().Create([1,2,3]);
var aryl = l.Get("array");
var lenl = l.Get("length");
var acsl = l.GetAccessor("y");
//Zero fill y→z//acs acts directly as an iterator
for(; acsl < lenl; acsl++) aryl[acsl] = 0.0;
console.log(aryl); // Result: [1, 0, 0]This part details the integration of NAS6LIB with X3DOM, a declarative 3D framework for HTML5.
It showcases how NAS6LIB can control X3DOM elements to perform complex transformations and animations.
testpoly.htm Demo (External Link)
testpoly.zip ZipFile (External Link)
TestPage ZipFile (External Link)
testpage000 DEMO (External Link)
test3JS000 DEMO (External Link)
testX3DOM000 DEMO (External Link)
This example demonstrates a basic setup for integrating NAS6LIB with X3DOM to animate objects.
It illustrates how to define a 3D scene in HTML and use NAS6LIB's JavaScript to apply transformations.
Page Structure:
The necessary libraries are loaded and styles are set within the <head> section of the HTML.
The 3D scene is defined using <x3d> tags within the <body> section.
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
...
<script src="./javascripts/x3dom/jquery-2.1.4.min.js" ></script>
<script src='./javascripts/x3dom/x3dom.js'> </script>
<link rel='stylesheet' type='text/css' href='./javascripts/x3dom/x3dom.css'>
<script src="./javascripts/nas6lib/timer.js"></script>
<script src="./javascripts/nas6lib/vector.js"></script>
<script src="./javascripts/nas6lib/matrix.js"></script>
<script src="./javascripts/nas6lib/quaternion.js"></script>
<script src="./javascripts/nas6/help.js"></script>
<script src="./javascripts/nas6/testpoly.js"></script>
<style>
article, aside, dialog, figure, footer, header,
hgroup, menu, nav, section { display: block; }
#x3dabs{
position: absolute;
float: left;
top: 60px;
left: 20px;
background:#8080b0;
border: 2px #000000 solid;
}
</style>
</head>
<body text="black" link="#3333cc" vlink="#663399" alink="#cc0000" bgcolor="#faebf1" background="./img/kumausagineko.jpg">
...
<div style = 'width:500px; height:250px; border: 0px; overflow:hidden;'>
<x3d id='x3dabs' width='500px' height='250px'>
<scene>
<Viewpoint id='viewp001' position='0 3 20' orientation='0 1 0 0' description='camera'></Viewpoint>
<!--
<Viewpoint id='viewp001' position='0 0 200' orientation='0 1 0 0' description='camera'></Viewpoint>
<navigationInfo type='"none"' id="navType"></navigationInfo>
//-->
<directionalLight id="directional" direction='1 -1 1' on ="TRUE" intensity='1.0' shadowIntensity='0.0'></directionalLight>
<Transform center='0 0 0' translation='0 0 8' id='box0' DEF='box0'>
<Transform rotation='0 1 0 0' id='box1' DEF='box1'>
<!--
<shape>
<appearance>
<ImageTexture url="./img/koala.jpg"></ImageTexture>
<material diffuseColor='0.8 0.8 0.8'></material>
</appearance>
<Box size = '5 5 5'></Box>
</shape>
//-->
<Shape DEF='Front'>
<IndexedFaceSet coordIndex='0 4 5 1' texCoordIndex='0 1 2 3'>
<Coordinate DEF='Points' point='2.5 2.5 2.5 2.5 -2.5 2.5 2.5 -2.5 -2.5 2.5 2.5 -2.5 -2.5 2.5 2.5 -2.5 -2.5 2.5 -2.5 -2.5 -2.5 -2.5 2.5 -2.5'></Coordinate>
<TextureCoordinate DEF='DefaultTextureCoordinate' point='0 0 1 0 1 1 0 1'></TextureCoordinate>
</IndexedFaceSet>
<Appearance>
<ImageTexture url="./img/koalaF.jpg"></ImageTexture>
<TextureTransform DEF='Rotate180' rotation='3.14'></TextureTransform>
<material diffuseColor='0.8 0.8 0.8'></material>
</Appearance>
</Shape>
<Shape DEF='Back'>
<IndexedFaceSet coordIndex='3 2 6 7' texCoordIndex='0 1 2 3'>
<Coordinate USE='Points'></Coordinate>
<TextureCoordinate USE='DefaultTextureCoordinate'></TextureCoordinate>
</IndexedFaceSet>
<Appearance>
<ImageTexture url="./img/koala.jpg"></ImageTexture>
<TextureTransform DEF='RotateRight' rotation='-1.57'></TextureTransform>
<material diffuseColor='0.8 0.8 0.8'></material>
</Appearance>
</Shape>
<Shape DEF='Left'>
<IndexedFaceSet coordIndex='7 6 5 4' texCoordIndex='0 1 2 3'>
<Coordinate USE='Points'></Coordinate>
<TextureCoordinate USE='DefaultTextureCoordinate'></TextureCoordinate>
</IndexedFaceSet>
<Appearance>
<ImageTexture url="./img/koala.jpg"></ImageTexture>
<TextureTransform USE='RotateRight'></TextureTransform>
<material diffuseColor='0.8 0.8 0.8'></material>
</Appearance>
</Shape>
<Shape DEF='Right'>
<IndexedFaceSet coordIndex='0 1 2 3' texCoordIndex='0 1 2 3'>
<Coordinate USE='Points'></Coordinate>
<TextureCoordinate USE='DefaultTextureCoordinate'></TextureCoordinate>
</IndexedFaceSet>
<Appearance>
<ImageTexture url="./img/koala.jpg"></ImageTexture>
<TextureTransform USE='RotateRight'></TextureTransform>
<material diffuseColor='0.8 0.8 0.8'></material>
</Appearance>
</Shape>
<Shape DEF='Top'>
<IndexedFaceSet coordIndex='0 3 7 4' texCoordIndex='0 1 2 3'>
<Coordinate USE='Points'></Coordinate>
<TextureCoordinate USE='DefaultTextureCoordinate'></TextureCoordinate>
</IndexedFaceSet>
<Appearance>
<ImageTexture url="./img/koala.jpg"></ImageTexture>
<TextureTransform DEF='RotateLeft' rotation='1.57'></TextureTransform>
<material diffuseColor='0.8 0.8 0.8'></material>
</Appearance>
</Shape>
<Shape DEF='Bottom'>
<IndexedFaceSet coordIndex='1 5 6 2' texCoordIndex='0 1 2 3'>
<Coordinate USE='Points'></Coordinate>
<TextureCoordinate USE='DefaultTextureCoordinate'></TextureCoordinate>
</IndexedFaceSet>
<Appearance>
<ImageTexture url="./img/koala.jpg"></ImageTexture>
<TextureTransform USE='Rotate180'></TextureTransform>
<material diffuseColor='0.8 0.8 0.8'></material>
</Appearance>
</Shape>
</Transform>
</Transform>
</scene>
</x3d>
</div>
...
This JavaScript file contains the core logic for animating objects in the X3DOM scene using NAS6LIB.
JavaScript
var chk = false;
var TMan = new N6LTimerMan(); //timer manager//タイマーマネージャー
var TimerID = -1;
var x3domRuntime;
jQuery(document).ready(function(){
TimerID = TMan.add();
GLoop(TimerID); //set main loop//メインループセット
});
var bx = new N6LVector(new Array('1','0','0','8'), true); //pos Box//Box座標
var bm = new N6LMatrix(4).UnitMat(); //mat Box//Box回転行列
var bp = new N6LVector([1, 0, 0, 8, 1, 0, 0, 0], true); //pos Box//Box座標
//main loop//メインループ
function GLoop(id){
//x3domRuntimeを保存
if(x3domRuntime == undefined) x3domRuntime = document.getElementById('x3dabs').runtime;
//lib test//ライブラリ検証
//N6LVectorTest(); //N6LVector.Sphere4D()???
//N6LMatrixTest(); //N6LMatrix.Diagonal & N6LMatrix.DiagonalMat???
//N6LQuaternionTest();
//普通に行列
// var MatWK = new N6LMatrix(4).UnitMat();
// var v = new N6LVector(4, false);
// //unit vector//単位ベクトル
// var ax = new N6LVector(4, true).UnitVec(1);
// var ay = new N6LVector(4, true).UnitVec(2);
// var az = new N6LVector(4, true).UnitVec(3);
//
// //rot mov obj//物体回転移動
// MatWK = MatWK.RotAxis(ay, 1.0 * Math.PI / 180.0); //around y axis rotate 1 degree//y軸回りに1度回転する回転行列を乗算
// //test//ライブラリ行列変換検証
// //ay.x[0] = 1.0 * Math.PI / 180.0;
// //MatWK = MatWK.RotAxisVec(ay); //around y axis rotate 1 degree//y軸回りに1度回転する回転行列を乗算
// bx = MatWK.Mul(bx); //pos Box multiply matrix//Box座標に回転行列を乗算して更新
//
// //unit vector//単位ベクトル
// ax = ax.UnitVec(1);
// ay = ay.UnitVec(2);
// az = az.UnitVec(3);
// //rot obj//物体回転
// MatWK = new N6LMatrix(bm); //mat Box//Box回転行列
// MatWK = MatWK.RotAxis(az, 3.0 * Math.PI / 180.0); //around z axis rotate 3 degree//z軸回りに3度回転する回転行列を乗算
// MatWK = MatWK.RotAxis(ay, 2.0 * Math.PI / 180.0); //around y axis rotate 2 degree//y軸回りに2度回転する回転行列を乗算
// bm = MatWK.RotAxis(ax, 1.0 * Math.PI / 180.0); //around x axis rotate 1 degree//x軸回りに1度回転する回転行列を乗算
//
// //bm = bm.Quaternion().Matrix().Vector().Matrix(); //test//ライブラリ行列相互変換検証
// //var dt = new Array();
// //bm = bm.InverseMat(dt).InverseMat(dt); //test//ライブラリ行列変換検証
//
// v = bm.Vector(); //rot vector//回転行列から回転ベクトルを取得
//
// var angle = bm.EulerAngle(3, 2, 1); //rotate order ZYX //回転順番 ZYX
// var tbx = new N6LVector(bx);
//姿勢ベクトル演算
var VecWK = new N6LVector([1,0,0,0,1,0,0,0],true);
var v = new N6LVector(4, false);
//unit vector//単位ベクトル
var ax = new N6LVector(4, true).UnitVec(1);
var ay = new N6LVector(4, true).UnitVec(2);
var az = new N6LVector(4, true).UnitVec(3);
//rot mov obj//物体回転移動
var q = new N6LQuaternion().UnitQuat().RotAxisQuat(ay, 1.0 * Math.PI / 180.0);
var pv = new N6LVector([1,0,0,0,q.q.x[0],q.q.x[1],q.q.x[2],q.q.x[3]], true)
var pvb = new N6LVector([bp.x[0],bp.x[1],bp.x[2],bp.x[3],1,0,0,0], true);
VecWK = pv.PosVecMul(pvb); //around y axis rotate 1 degree//y軸回りに1度回転する回転行列を乗算
//unit vector//単位ベクトル
ax = ax.UnitVec(1);
ay = ay.UnitVec(2);
az = az.UnitVec(3);
//rot obj//物体回転
q = new N6LQuaternion().UnitQuat().RotAxisQuat(az, 3.0 * Math.PI / 180.0);
pv = new N6LVector([1,0,0,0,q.q.x[0],q.q.x[1],q.q.x[2],q.q.x[3]], true)
pvb = new N6LVector([VecWK.x[0],VecWK.x[1],VecWK.x[2],VecWK.x[3],bp.x[4],bp.x[5],bp.x[6],bp.x[7]], true);
VecWK = pvb.PosVecMul(pv); //around z axis rotate 3 degree//z軸回りに3度回転する回転行列を乗算
q = new N6LQuaternion().UnitQuat().RotAxisQuat(ay, 2.0 * Math.PI / 180.0);
pv = new N6LVector([1,0,0,0,q.q.x[0],q.q.x[1],q.q.x[2],q.q.x[3]], true)
VecWK = VecWK.PosVecMul(pv); //around z axis rotate 3 degree//z軸回りに3度回転する回転行列を乗算
q = new N6LQuaternion().UnitQuat().RotAxisQuat(ax, 1.0 * Math.PI / 180.0);
pv = new N6LVector([1,0,0,0,q.q.x[0],q.q.x[1],q.q.x[2],q.q.x[3]], true)
bp = VecWK.PosVecMul(pv); //around z axis rotate 3 degree//z軸回りに3度回転する回転行列を乗算
var tbm = bp.PosVecMatrix();
var tbx = tbm.Pos();
var v = tbm.Vector(); //rot vector//回転行列から回転ベクトルを取得
var angle = tbm.EulerAngle(3, 2, 1); //rotate order ZYX //回転順番 ZYX
//viewp(); //lookat//注視
//moveobj(a, pyr);
//apply x3dom
var pos = tbx.ToX3DOM(true);
var elm = document.getElementById('box0');
elm.setAttribute('translation', pos.toString());
var rot = v.ToX3DOM();
elm = document.getElementById('box1');
elm.setAttribute('rotation', rot.toString());
//debug//デバッグ用
elm = document.getElementById('debug');
elm.innerText =
'EulerAngle(rotate per degree z(3)_y(2)_x(1))\n' + angle.x[0] + ' ' + Math.floor(angle.x[1] * 180.0 / Math.PI) + ' ' + Math.floor(angle.x[2] * 180.0 / Math.PI) + ' ' + Math.floor(angle.x[3] * 180.0 / Math.PI);
//elm.innerText =
//'テンキーで操縦\npyr = [' + pyr.x[1] + ', ' +pyr.x[2] + ', ' +pyr.x[3] + ']\nV = ' + V +' a = ' + a;
//elm = document.getElementById('debug');
//elm.innerText =
//bx.x[0] + ' ' + bx.x[1] + ' ' + bx.x[2] + ' ' + bx.x[3] + '\n\n' +
//bm.x[0].x[0] + ' ' + bm.x[0].x[1] + ' ' + bm.x[0].x[2] + ' ' + bm.x[0].x[3] + '\n' +
//bm.x[1].x[0] + ' ' + bm.x[1].x[1] + ' ' + bm.x[1].x[2] + ' ' + bm.x[1].x[3] + '\n' +
//bm.x[2].x[0] + ' ' + bm.x[2].x[1] + ' ' + bm.x[2].x[2] + ' ' + bm.x[2].x[3] + '\n' +
//bm.x[3].x[0] + ' ' + bm.x[3].x[1] + ' ' + bm.x[3].x[2] + ' ' + bm.x[3].x[3];
TMan.timer[id].setalerm(function() { GLoop(id); }, 50); //reset main loop//メインループ再セット
}
//################################################################
//test code//以下、ライブラリテストコード
//################################################################
...
Transformation Approaches in GLoop:
The GLoop JavaScript code showcases different methods for object transformation. It includes examples of:
-
Direct Matrix Operations: An approach (currently commented out but conceptually similar to the active method)
where transformations are applied directly using 4x4 matrices. -
Orientation Vector Operations: The currently active method, which achieves similar results using orientation vectors,
often derived from matrices or quaternions.
This section highlights the creation of unit matrices and unit vectors for each axis as foundational elements for transformations.
JavaScript
//普通に行列
// var MatWK = new N6LMatrix(4).UnitMat();
// var v = new N6LVector(4, false);
// //unit vector//単位ベクトル
// var ax = new N6LVector(4, true).UnitVec(1);
// var ay = new N6LVector(4, true).UnitVec(2);
// var az = new N6LVector(4, true).UnitVec(3);Moving the Object in a Circle:
This code snippet illustrates how to apply a rotational transformation to an object's position, causing it to move in a circular path.
It involves multiplying the object's position vector by a rotation matrix, typically rotating around a specific axis
(e.g., the Y-axis by 1 degree per frame) to simulate orbital motion.
JavaScript
// //rot mov obj//物体回転移動
// MatWK = MatWK.RotAxis(ay, 1.0 * Math.PI / 180.0); //around y axis rotate 1 degree//y軸回りに1度回転する回転行列を乗算
...
// bx = MatWK.Mul(bx); //pos Box multiply matrix//Box座標に回転行列を乗算して更新Rotating an Object In Place:
This part of the code demonstrates how to apply rotations to an object's local orientation, making it spin around its own axes
(X, Y, and Z). It involves composing multiple rotation matrices to achieve complex rotational animations.
JavaScript
// //unit vector//単位ベクトル
// ax = ax.UnitVec(1);
// ay = ay.UnitVec(2);
// az = az.UnitVec(3);
// //rot obj//物体回転
// MatWK = new N6LMatrix(bm); //mat Box//Box回転行列
// MatWK = MatWK.RotAxis(az, 3.0 * Math.PI / 180.0); //around z axis rotate 3 degree//z軸回りに3度回転する回転行列を乗算
// MatWK = MatWK.RotAxis(ay, 2.0 * Math.PI / 180.0); //around y axis rotate 2 degree//y軸回りに2度回転する回転行列を乗算
// bm = MatWK.RotAxis(ax, 1.0 * Math.PI / 180.0); //around x axis rotate 1 degree//x軸回りに1度回転する回転行列を乗算Get rotation vector etc.
This section explains how to extract rotation information from a transformation matrix. It shows how to obtain
an orientation vector (often a quaternion or axis-angle representation) from a rotation matrix and how to
calculate Euler angles (here, using ZYX order) from a matrix, which can be useful for debugging or specific control.
JavaScript
// v = bm.Vector(); //rot vector//回転行列から回転ベクトルを取得
//
// var angle = bm.EulerAngle(3, 2, 1); //rotate order ZYX //回転順番 ZYX
// var tbx = new N6LVector(bx);Applying Changes to X3DOM and Resetting the Main Loop:
Finally, the calculated positions (pos) and rotations (rot) from NAS6LIB are converted into X3DOM's SFVec3f
and SFRotation formats, respectively, and applied to the translation and rotation attributes of
the corresponding X3D elements (e.g., 'box0', 'box1'). The GLoop function then uses
TMan.timer[id].setalerm() to schedule its next execution, ensuring continuous animation.
JavaScript
//apply x3dom
var pos = tbx.ToX3DOM(true);
var elm = document.getElementById('box0');
elm.setAttribute('translation', pos.toString());
var rot = v.ToX3DOM();
elm = document.getElementById('box1');
elm.setAttribute('rotation', rot.toString());
...
TMan.timer[id].setalerm(function() { GLoop(id); }, 50); //reset main loop//メインループ再セット
Back to Table of contents
rottest.htm Demo (External Link)
rottestWithX3DOMAndThreeJS.zip ZipFile (External Link)
This example focuses on demonstrating object rotation within an X3DOM scene, emphasizing visual setup with a skybox and scene elements.
Scene Creation with X3D:
This HTML snippet shows how an X3D scene is set up, including a Viewpoint (camera), a Background (skybox),
and a DirectionalLight. A notable tip is provided regarding the skybox images: they must be flipped horizontally,
and their Z-axis positions might need to be inverted for correct display.
HTML
...
<div style = 'width:500px; height:500px; border: 0px; overflow:hidden;'>
<x3d id='x3dabs' width='500px' height='500px'>
<scene>
<Viewpoint id='viewp001' position='0 0 0' orientation='0 1 0 0' description='camera'></Viewpoint>
<background skyColor='0.4 0.4 0.5' backUrl='"./img/MirrorSkyBoxPZ.png"' bottomUrl='"./img/MirrorSkyBoxNY.png"' frontUrl='"./img/MirrorSkyBoxNZ.png"' leftUrl='"./img/MirrorSkyBoxNX.png"' rightUrl='"./img/MirrorSkyBoxPX.png"' topUrl='"./img/MirrorSkyBoxPY.png"'></background>
<directionalLight id="directional" direction='1 -1 1' on ="TRUE" intensity='1.0' shadowIntensity='0.0'></directionalLight>
</scene>
</x3d>
</div>
...
The Main Loop: The rottest.js code contains the main animation loop (GLoop) that drives the rotation of objects within this X3D scene.
JavaScript
...
/**
* The main loop function.
* Gets the X3DOM view matrix, updates the rotation information based on it, and reflects it in the display field.
* It also resets the timer to run continuously.
* @param {number} id - timer ID
*/
function GLoop(id){
// If X3DOM runtime is not yet initialized, wait until it is initialized
if(x3domRuntime == undefined) {
x3domRuntime = document.getElementById('x3dabs').runtime; // Get X3DOM runtime object
TMan.timer[id].setalerm(function() { GLoop(id); }, 50); // Reset main loop after 50 ms
return;
}
var elm = document.getElementById('viewp001'); // Get 'viewp001' element
// Get inverse of X3DOM view matrix (world rotation matrix)
var SWM = x3domRuntime.viewMatrix().inverse();
// Convert X3DOM matrix to N6LMatrix object
A = new N6LMatrix().FromX3DOM(SWM);
// Get the radio button list named 'INV'
var radioList = document.getElementsByName("INV");
var dt = []; // Temporary variable that may be used when calculating the inverse matrix
var rot = A.Vector(); // Calculate the rotation vector from matrix A
// Set matrix A based on the radio button selection state
if(radioList[0].checked) {
// If the first radio button is checked (probably inverse matrix mode)
A = rot.Matrix(); // Generate a matrix from the rotation vector
A = A.InverseMat(dt); // Calculate the inverse of the generated matrix
} else {
// Otherwise (normal mode)
A = rot.Matrix(); // Generate a matrix from the rotation vector
}
// Calculate the Euler angles from matrix A
var ea = A.EulerAngle(1,2,3);
// Set the 'rotEA' element to the calculated Euler angles
var elm = document.getElementById('rotEA');
// Convert Euler angles to degrees, round with fr, and convert to string
var str = String(Math.floor(ea.x[1]*(180.0/Math.PI)*fr)/fr)+','+String(Math.floor(ea.x[2]*(180.0/Math.PI)*fr)/fr)+','+String(Math.floor(ea.x[3]*(180.0/Math.PI)*fr)/fr);
elm.value = str;
// Recalculate rotation vector from updated matrix A
rot = A.Vector();
// Set calculated rotation vector to 'rot' element
elm = document.getElementById('rot');
// Round rotation vector components with fr, and convert to string
str = 'true,4,'+String(Math.floor(rot.x[0]*fr)/fr)+','+String(Math.floor(rot.x[1]*fr)/fr)+','+String(Math.floor(rot.x[2]*fr)/fr)+','+String(Math.floor(rot.x[3]*fr)/fr);
elm.value = str;
// Calculate quaternion from matrix A
var qt = A.Quaternion();
// Set the calculated quaternion to the 'rotQT' element
elm = document.getElementById('rotQT');
// Round the quaternion components by fr and convert them to strings
str = 'true,4,'+String(Math.floor(qt.q.x[0]*fr)/fr)+','+String(Math.floor(qt.q.x[1]*fr)/fr)+','+String(Math.floor(qt.q.x[2]*fr)/fr)+','+String(Math.floor(qt.q.x[3]*fr)/fr);
elm.value = str;
// Set the calculated matrix to the 'rotMT' element
elm = document.getElementById('rotMT');
// Round each element of the matrix by fr and convert it to a string including a newline code
str = 'true,4\ntrue,4,'+String(Math.floor(A.x[0].x[0]*fr)/fr)+','+String(Math.floor(A.x[0].x[1]* fr)/fr)+','+String(Math.floor(A.x[0].x[2]*fr)/fr)+','+String(Math.floor(A.x[0].x[3]*fr)/fr) + '\ntrue,4,'+String(Math.floor(A.x[1].x[0]*fr)/fr)+','+String(Math.floor(A.x[1].x[1]*fr) /fr)+','+String(Math.floor(A.x[1].x[2]*fr)/fr)+','+String(Math.floor(A.x[1].x[3]*fr)/fr) + '\ntrue,4,'+String(Math.floor(A.x[2].x[0]*fr)/fr)+','+String(Math.floor(A.x[2].x[1]*fr) /fr)+','+String(Math.floor(A.x[2].x[2]*fr)/fr)+','+String(Math.floor(A.x[2].x[3]*fr)/fr) + '\ntrue,4,'+String(Math.floor(A.x[3].x[0]*fr)/fr)+','+String(Math.floor(A.x[3].x[1]*fr)/fr)+','+String(Math.floor(A.x[3].x[2]*fr)/fr)+','+String(Math.floor(A.x[3].x[3]*fr)/fr);
elm.innerText = str; // Set innerText as well
elm.value = str; // Set value as well
// Check radio button selection (nothing done here, but perhaps a remnant of previous logic)
if(radioList[0].checked) {
;
} else {
A = A.InverseMat(dt);
}
TMan.timer[id].setalerm(function() { GLoop(id); }, 50); // Reset the main loop after 50 milliseconds.
}
...
This section outlines how NAS6LIB can be used in conjunction with Three.js, a popular and powerful 3D JavaScript library,
to manage transformations and animations.
A journey through homogeneous coordinates and the rendering pipeline
Welcome to the world of 3D graphics using NAS6LIB and Three.js!
Here, we will explain in an easy-to-understand way the important roles of "homogeneous coordinates" and "rendering pipelines"
that lie behind the magical journey of a 3D model to be displayed on the screen.
1. 3D models are a collection of "points"
First, imagine it. 3D models do not directly exist in a computer as "three-dimensional objects" as we think of them. In fact,
they are represented as a collection of many "points".
These points are called "vertices", and each has a position in space (coordinates).
If you connect these vertices with lines, you get a wireframe, and if you fill them with faces, you get the three-dimensional models we usually see.
2. Coordinates that identify the "location"
Each vertex represents its position in three-dimensional space with three numbers: (x, y, z). These are called "coordinates".
For example, to move an object along the y axis, you might think that you just need to add the same value to the y coordinates of all vertices.
3. Making calculations smarter: the introduction of "homogeneous coordinates"
However, in the 3D world, three basic types of transformations are frequently performed: "translation (translation)",
"rotation (rotation transformation)", and ****"scaling (scaling transformation)".
Rotation and scaling can be expressed by multiplying coordinates (matrix multiplication).
However, translation (translation) requires adding to coordinates.
This mixes up the types of calculations, which is very inconvenient. If we could uniformly express all transformations as "multiplication",
calculations would be much easier, right?
That's where "homogeneous coordinates" come in!
We express a 3D point (x, y, z) as a 4D point of (x, y, z, w) by adding another component w to the 3D point (x, y, z, w).
Usually, in 3D graphics, w=1 is used.
A point (x, y, z) is (x, y, z, 1) in homogeneous coordinates.
A directional vector (x, y, z) (which does not move, so the w component is 0) is (x, y, z, 0) in homogeneous coordinates.
By adding this w component, all transformations (translation, rotation, scaling) can be expressed with just one "matrix multiplication".
4. Chain of transformations: the core of the rendering pipeline
Before a 3D model is displayed on the screen, it is transformed in various coordinate systems in succession.
This is called the rendering pipeline.
By using homogeneous coordinates, all of these transformations can be performed
with a unified calculation of multiplying matrices one after another.
The main transformations are as follows.
Model transformation:
Determines the position, rotation, and size of the model itself. For example, when you change the position, rotation,
or scale of an object in Three.js,
this model transformation matrix is calculated internally.
The role of NAS6LIB: NAS6LIB's N6LVector, N6LMatrix, and N6LQuaternion classes can be used to efficiently calculate and manipulate
this model transformation matrix.For example, it is possible to manage rotation with quaternions and apply them to Three.js matrices.
View Transformation:
Determines where in the scene the camera is facing and in what direction. It's as if
the camera is moving and rotating to capture the entire scene.
In Three.js, it is controlled by Camera.lookAt() and Camera.position, etc.
Projection Transformation:
Projects a 3D scene onto a 2D screen. Distant objects can appear smaller (perspective projection) or be projected parallel
without any sense of perspective (parallel projection).
In Three.js, PerspectiveCamera and OrthographicCamera take on this role.
All these transformations are applied in sequence by matrix multiplication using homogeneous coordinates.
5. Integration between NAS6LIB and Three.js
NAS6LIB provides powerful mathematical classes (such as N6LMatrix, N6LVector, and N6LQuaternion) to handle these homogeneous coordinates.
Three.js has its own Matrix4 and Vector3 classes, but by using NAS6LIB, it is possible to perform more complex custom transformations
and specific mathematical calculations using the NAS6LIB API and then apply the results to Three.js objects.
For example, you can use NAS6LIB to create specific physics simulations or complex animation logic, and then reflect
the final transformation results in a Three.js model.
Summary
Homogeneous coordinates are a clever mathematical trick that allows all transformations in 3D graphics, such as
"movement," "rotation," and "scaling," to be handled in a unified format, as "multiplication with a 4x4 matrix."
The rendering pipeline is a "series of calculations" that chains together these matrix multiplications using homogeneous coordinates,
and ultimately accurately renders a 3D model on a 2D screen.
NAS6LIB is a tool for efficiently performing these matrix and vector calculations,
and in combination with the powerful rendering functions of Three.js, it expands the possibilities of your 3D graphics expression.
A simple page and file set that allows you to test the basic operations of NAS6LIB in the console.
It also includes minimal integration samples with 3D rendering libraries such as Three.js and X3DOM.
Please note that upgrading these libraries (especially Three.js beyond R148) may introduce compatibility issues.
Please enable comments and add test code to make use of it.
TestPage ZipFile (External Link)
testpage000 DEMO (External Link)
test3JS000 DEMO (External Link)
testX3DOM000 DEMO (External Link)
Here are some notes and workarounds for upgrading:
FAQ Regarding Three.js Version Upgrades Beyond R148
test3js.htm Demo (External Link)
test3js.zip ZipFile (External Link)
This example demonstrates the basic integration of NAS6LIB with Three.js
for animating objects, highlighting the setup of a Three.js canvas.
Page Structure:
The <head> section includes NAS6LIB's timer.js and Three.js itself. The 3D rendering takes place within a <canvas> element,
which is styled and positioned in the <body> section.
HTML
<head>
...
<script src="./javascripts/nas6lib/timer.js"></script>
<script src="./javascripts/threejs/three.js"></script>
<style>
article, aside, dialog, figure, footer, header,
hgroup, menu, nav, section { display: block; }
#cnv0{
position: absolute;
float: left;
top: 120px;
left: 20px;
background:#8080b0;
border: 2px #000000 solid;
}
</style>
...
</head>
<body text="black" link="#3333cc" vlink="#663399" alink="#cc0000" bgcolor="#faebf1" background="./img/kumausagineko.jpg">
...
<div style = 'width:500px; height:250px; border: 0px; overflow:hidden;'>
<canvas id="cnv0" name="cnv0" width="500" height="250"></canvas>
</div>
...
</body>
...
The Init() function sets up the Three.js scene, adds a timer from N6LTimerMan, and then calls Loop() to begin the main animation loop.
The Loop() function directly manipulates the rotation of Three.js meshes. Unlike typical Three.js animations that often rely on
requestAnimationFrame for browser-synced updates, this example uses N6LTimerMan to drive the rendering loop at
a fixed interval (50 milliseconds). After rendering the scene, Loop() is reset via TMan.timer[id].setalerm()
to continue the animation at the specified interval.
JavaScript
window.addEventListener("DOMContentLoaded", init);//After DOM loading is complete init()
var TMan = new N6LTimerMan(); //Timer manager
var TimerID = -1;
function init() {
const width = 500;
const height = 250;
const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector("#cnv0")
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45,width / height,1,10000);
camera.position.set(0, 0, 20);
//Create a box
const geometry = new THREE.BoxGeometry(5, 5, 5);
const loader = new THREE.TextureLoader();
const texture = loader.load('./img/koala.jpg');
const textureF = loader.load('./img/koalaF.jpg');
// Set texture to material
const materials = [
new THREE.MeshStandardMaterial({map: textureF}),
new THREE.MeshStandardMaterial({map: texture}),
new THREE.MeshStandardMaterial({map: texture}),
new THREE.MeshStandardMaterial({map: texture}),
new THREE.MeshStandardMaterial({map: texture})
];
// Create mesh
const box = new THREE.Mesh(geometry, materials);
box.position.set(0, 0, 8);
scene.add(box);
// Directional light
const light = new THREE.DirectionalLight(0xffffff);
light.intensity = 2; // Double the light intensity
light.position.set(1, 1, 1);
// Add to scene
scene.add(light);
TimerID = TMan.add();
Loop(TimerID); // Set main loop
function Loop(id) {
box.rotation.x += 1.0 * Math.PI / 180.0; // Rotate 1 degree around x-axis
box.rotation.y += 2.0 * Math.PI / 180.0; // Rotate 2 degrees around y-axis
box.rotation.z += 3.0 * Math.PI / 180.0; // Rotate 3 degrees around z-axis
// Render
renderer.render(scene, camera);
TMan.timer[id].setalerm(function() { Loop(id); }, 50); // Set main loop
}
<!--
/*
// First execution
Loop();
function Loop() {
requestAnimationFrame(Loop);
box.rotation.x += 1.0 * Math.PI / 180.0;//Rotate 1 degree around the x axis
box.rotation.y += 2.0 * Math.PI / 180.0;//Rotate 2 degrees around the y axis
box.rotation.z += 3.0 * Math.PI / 180.0;//Rotate 3 degrees around the z axis
renderer.render(scene, camera);
}
*/
//-->
}Back to Table of contents
rottest3js.htm Demo (External Link)
rottestWithX3DOMAndThreeJS.zip ZipFile (External Link)
This example further explores object rotation, specifically within a Three.js environment, building upon the test3js.htm example.
Initialization and Main Loop:
This section indicates that the init() and Loop() functions in rottest3js.js handle the setup and animation logic
for the Three.js scene, similar to test3js.htm but tailored for rotation-specific demonstrations.
JavaScript
var chk = false;
var TMan = new N6LTimerMan(); //timer manager//Timer manager
var TimerID = -1;
var fr = 1000;
var A = new N6LMatrix(4).UnitMat(); //mat view // Holds the pure rotation matrix before converting it to a Three.js view matrix (mainly for UI display)
var B = new N6LMatrix(4).UnitMat(); //mat view // Camera world transformation matrix (including position and rotation). Continuously updated and applied to the Three.js camera.
var la = new N6LVector(4, true).UnitVec(3); // lookAt vector (direction the camera is facing)
var up = new N6LVector(4, true).UnitVec(2); // up vector (upward direction of the camera)
var cr = new N6LVector(4, true).UnitVec(1); // right vector (right direction of the camera)
var tr = new N6LVector(4, true).UnitVec(0); // translation vector (position of the camera)
var pos = new N6LVector(4, true).UnitVec(0); // Vector for saving the current position of the camera (mainly used for restoration processing)
var pyr = new N6LVector([1, 0, 0, 0], true); // Accumulated angles of pitch (x), yaw (y), and roll (z) (for keyboard operation)
// Hit init when DOM loading is complete
window.addEventListener("DOMContentLoaded", init);
var renderer;
var camera;
var scene;
//Entry point
function init() {
const width = 500;
const height = 500;
renderer = new THREE.WebGLRenderer({
canvas: document.querySelector("#cnv0")
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45,width / height,1,10000);
camera.position.set(0, 0, 20);
// camera.projectionMatrix is ??the Three.js projection matrix. It is converted to NAS6LIB format, but is not used directly in this demo.
// Mainly for the purpose of initializing B as the camera's world transformation matrix.
// Ideally, you should initialize B to match camera.position.set(0, 0, 20);.
// Alternatively, you can initialize B to a pure identity matrix and construct B separately based on camera.position.
var Proj = camera.projectionMatrix;
B = B.From3JS(Proj); // Convert Three.js matrix to NAS6LIB format
// B = B.SetHomo(true); // Set W component of homogeneous coordinate to 1 and normalize
const geometry = new THREE.BoxGeometry(5, 5, 5);
const loader = new THREE.TextureLoader();
path = "./img/skybox";
format = '2.jpg';
urls = [
path + 'px' + format, path + 'nx' + format,
path + 'py' + format, path + 'ny' + format,
path + 'pz' + format, path + 'nz' + format
];
tCube = new THREE.CubeTextureLoader().load( urls );
scene.background = tCube;
// Directional light source
const light = new THREE.DirectionalLight(0xffffff);
light.intensity = 2; // Double the light intensity
light.position.set(1, 1, 1);
// Add to the scene
scene.add(light);
const alight = new THREE.AmbientLight( '#808080' );
scene.add( alight );
// Get the canvas element that creates the Three.js renderer
// Example: document.getElementById('myCanvas') or renderer.domElement
const canvasMS = renderer.domElement;
let mouseX = 0;
let mouseY = 0;
let isDragging = false;
let previousMouseX = 0;
let previousMouseY = 0;
canvasMS.addEventListener('mousedown', (event) => {
if (event.button === 0) { // Left click (button === 0)
isDragging = true;
const rect = canvasMS.getBoundingClientRect();
previousMouseX = event.pageX - rect.left;
previousMouseY = event.pageY - rect.top;
}
});
canvasMS.addEventListener('mousemove', (event) => {
if (isDragging) {
const rect = canvasMS.getBoundingClientRect();
const currentMouseX = event.pageX - rect.left;
const currentMouseY = event.pageY - rect.top;
// Camera yaw and pitch sensitivity adjustment. Radians per 1px
const rotationSpeed = 0.005;
// Mouse horizontal movement deltaX corresponds to camera yaw (around Y axis) rotation,
// Mouse vertical movement deltaY corresponds to camera pitch (around X axis) rotation.
// Sign is adjusted to match Three.js coordinate system and mouse operation direction.
// Mouse delta (movement)
const deltaX = currentMouseX - previousMouseX;
const deltaY = currentMouseY - previousMouseY;
// Calculate yaw and pitch angles
// In Three.js, the Y axis is upwards, so pay attention to the sign of deltaY to match the up and down movement of the mouse with the pitch direction.
const yawAngle = -deltaX * rotationSpeed; // Rotate around the Y axis (upward) when moving left and right
const pitchAngle = -deltaY * rotationSpeed; // Rotate around the X axis (horizontal) when moving up and down
// Temporarily copy the camera's world matrix (B),
// Treat it as a view matrix (camera perspective) or world matrix (object perspective) by taking the inverse matrix as necessary
//Camera world matrix
var matWK = new N6LMatrix(B);
var radioList = document.getElementsByName("INV"); // Inverts depending on the state of the "INV" radio button
var dt = []; // Dummy variable for the InverseMat function (can be deleted if not used)
if(radioList[0].checked) {
;// If radioList[0] is checked, matWK is treated as the camera's world matrix.
} else {
// If radioList[0] is not checked, convert matWK to the view matrix (inverse matrix).
matWK = matWK.InverseMat(dt);
}
// Get the camera's current local axes (X, Y, Z) from matWK.
// These axes are based on the camera's current orientation, allowing accurate rotation at any pose.
// Note the correspondence between N6LMatrix indexes and axes (e.g. x[3] is the Z axis, x[2] is the Y axis).
// Three.js uses a column-major matrix (mat.elements[0] is the X component of the X axis).
// Get the axis assuming that NAS6LIB's N6LMatrix.x[index] returns a column vector.
// az = new N6LVector(matWK.x[2]); // Camera Z axis (forward) - Three.js Z axis (col 2)
// ay = new N6LVector(matWK.x[1]); // Camera Y axis (up) - Three.js Y axis (col 1)
// ax = ay.Cross(az); // Camera X axis (right) - In a right-handed system, the cross product of the Y and Z axes is the X axis
// The order is different between 3JS and NAS6LIB as shown above
var az = new N6LVector(matWK.x[3]); // Camera Z axis (forward)
az = az.SetHomo(true);
var ay = new N6LVector(matWK.x[2]); // Camera Y axis (up)
ay = ay.SetHomo(true);
var ax = az.Cross(ay); // Camera X axis (right). Right-handed system: (forward x up) = left, (up x forward) = Right. Matches NAS6LIB's Cross implementation.
ax = ax.SetHomo(true);
// Apply yaw rotation (around Y axis)
// matWK uses quaternions internally, avoiding gimbal lock issues
matWK = matWK.RotAxis(ay, yawAngle * 1.0);
// ★IMPORTANT: Since matWK has been updated by the yaw rotation, recalculate the local axis as well.
// This ensures that the next pitch rotation is around the correct X axis of the current camera.
// (Not necessary for small angles, but required to avoid glitches (twisting) at large angles or with continuous operations)
az = new N6LVector(matWK.x[3]);
az = az.SetHomo(true);
ay = new N6LVector(matWK.x[2]);
ay = ay.SetHomo(true);
ax = az.Cross(ay);
ax = ax.SetHomo(true);
// Apply pitch rotation (around X axis)
matWK = matWK.RotAxis(ax, pitchAngle * -1.0);
matWK = matWK.NormalMat(); // Normalize matrix (to avoid scale/shear accumulation)
//update
if(radioList[0].checked) {
;
}
else {
matWK = matWK.InverseMat(dt);
}
// matWK = matWK.SetCol(0,new N6LVector([1,1,1,1])); // This line may not be needed for some purposes
matWK = matWK.SetCol(0,new N6LVector([1,0,0,0])); // This line may not be needed for some purposes
// matWK = matWK.SetHomo(true); // Normalize W component
// Save final camera world matrix in B
B = new N6LMatrix(matWK);
previousMouseX = currentMouseX;
previousMouseY = currentMouseY;
}
});
canvasMS.addEventListener('mouseup', (event) => {
if (event.button === 0) {
isDragging = false;
}
});
// It's more robust if you also add a mouseup event to the window in case you release the mouse outside the canvas
window.addEventListener('mouseup', () => {
isDragging = false;
});
TimerID = TMan.add();
Loop(TimerID);
//main loop//Main loop
function Loop(id){
//Camera world matrix
var matWK = new N6LMatrix(B);
var radioList = document.getElementsByName("INV");
var dt = [];
if(radioList[0].checked) {
;
}
else {
matWK = matWK.InverseMat(dt);
}
var az = new N6LVector(matWK.x[3]); //Camera Z-axis row
az = az.SetHomo(true);
var ay = new N6LVector(matWK.x[2]); //Camera Y-axis row
ay = ay.SetHomo(true);
var ax = az.Cross(ay); //Camera X-axis row
ax = ax.SetHomo(true);
// ... (Axis acquisition and rotation processing almost the same as in the mousemove event) ...
// Yaw pitch roll rotation (rotation based on input value from text box)
// The loop function applies the accumulated value of pyr as a small angle every frame,
// Since omitting to reacquire the axis after each rotation is unlikely to cause any problems (in appearance).
// However, strictly speaking, it is most accurate to update the axis sequentially, as with mousemove.
// matWK = matWK.RotAxis(az, pyr.x[3] * -1.0); // Roll (around Z axis)
matWK = matWK.RotAxis(ay, pyr.x[2] * -1.0); // Yaw (around Y axis)
matWK = matWK.RotAxis(ax, pyr.x[1] * -1.0); // Pitch (around X axis)
matWK = matWK.NormalMat();
//update
if(radioList[0].checked) {
;
} else {
matWK = matWK.InverseMat(dt);
}
// matWK = matWK.SetCol(0,new N6LVector([1,1,1,1]));
matWK = matWK.SetCol(0,new N6LVector([1,0,0,0]));
// matWK = matWK.SetHomo(true);
B = new N6LMatrix(matWK);
tr = new N6LVector(matWK.x[0]);
tr = tr.SetHomo(true);
up = new N6LVector(matWK.x[2]);
up = up.SetHomo(true);
la = new N6LVector(matWK.x[3]);
la = la.SetHomo(true);
A = matWK.TransposedMat().TranslatedMat(tr.Mul(-1)).ScaleMat(new N6LVector([1,1,1,-1],true)).TransposedMat();
//This is for updating the output text box, so it may be better to insert the homogeneous element w=1
// A = A.SetCol(0,new N6LVector([1,1,1,1]));
A = A.SetCol(0,new N6LVector([1,0,0,0]));
// A = A.SetHomo(true);
var rot = matWK.Vector();
//reset
pyr = new N6LVector([1, 0, 0, 0], true);
//Get rotation information and update text box
var ea = A.EulerAngle(1,2,3);
var elm = document.getElementById('rotEA');
var str = String(Math.floor(ea.x[1]*(180.0/Math.PI)*fr)/fr)+','+String(Math.floor(ea.x[2]*(180.0/Math.PI)*fr)/fr)+','+String(Math.floor(ea.x[3]*(180.0/Math.PI)*fr)/fr);
elm.value = str;
rot = A.Vector();
elm = document.getElementById('rot');
str = 'true,4,'+String(Math.floor(rot.x[0]*fr)/fr)+','+String(Math.floor(rot.x[1]*fr)/fr)+','+String(Math.floor(rot.x[2]*fr)/fr)+','+String(Math.floor(rot.x[3]*fr)/fr);
elm.value = str;
var qt = A.Quaternion();
elm = document.getElementById('rotQT');
str = 'true,4,'+String(Math.floor(qt.q.x[0]*fr)/fr)+','+String(Math.floor(qt.q.x[1]*fr)/fr)+','+String(Math.floor(qt.q.x[2]*fr)/fr)+','+String(Math.floor(qt.q.x[3]*fr)/fr);
elm.value = str;
elm = document.getElementById('rotMT');
str = 'true,4\ntrue,4,'+String(Math.floor(A.x[0].x[0]*fr)/fr)+','+String(Math.floor(A.x[0].x[1]* fr)/fr)+','+String(Math.floor(A.x[0].x[2]*fr)/fr)+','+String(Math.floor(A.x[0].x[3]*fr)/fr) + '\ntrue,4,'+String(Math.floor(A.x[1].x[0]*fr)/fr)+','+String(Math.floor(A.x[1].x[1]*fr) /fr)+','+String(Math.floor(A.x[1].x[2]*fr)/fr)+','+String(Math.floor(A.x[1].x[3]*fr)/fr) + '\ntrue,4,'+String(Math.floor(A.x[2].x[0]*fr)/fr)+','+String(Math.floor(A.x[2].x[1]*fr) /fr)+','+String(Math.floor(A.x[2].x[2]*fr)/fr)+','+String(Math.floor(A.x[2].x[3]*fr)/fr) + '\ntrue,4,'+String(Math.floor(A.x[3].x[0]*fr)/fr)+','+String(Math.floor(A.x[3].x[1]*fr)/fr)+','+String(Math.floor(A.x[3].x[2]*fr)/fr)+','+String(Math.floor(A.x[3].x[3]*fr)/fr);
elm.innerText = str;
elm.value = str;
// A is a pure rotation matrix for UI display, and only the rotation part is extracted from B (including position information)
// Extract the information required by Three.js from the NAS6LIB matrix to set it to the Three.js camera
// (tr: position, up: up vector, la: focus point (lookAt) vector)
camera.position.set(tr.x[1], tr.x[2], tr.x[3]); // The order of NAS6L vectors is W, X, Y, Z, so it maps to Three.js's X, Y, Z
camera.up.set(up.x[1], up.x[2], up.x[3]); // The order of NAS6L vectors is W, X, Y, Z, so it maps to Three.js's X, Y, Z
camera.lookAt(la.x[1], la.x[2], la.x[3]); // The order of NAS6L vectors is W, X, Y, Z, so it maps to Three.js's X, Y, Z
// Render
renderer.render(scene, camera);
TMan.timer[id].setalerm(function() { Loop(id); }, 50); // Main loop set
}
}
...Back to Table of contents
Back to NAS6LIB Repository [Links outside the wiki]