Строим 3D проекты – Шаг 11

К данному шагу наша программа получила интерфейс (ссылка на программу). Очевидна нехватка информации о том, на какие углы повернут объект в данный момент. Также желательно придать объекту форму, по которой будет в любой момент понятно, где перед и верх фигуры. Сместим фигуру в центр координат, заменим текстуру, и получим такой результат (ссылка на обновленную программу):

Рассмотрим процесс внесения этих изменений.
Контроль кнопками
По аналогии с управлением скоростью, введем кнопки и ползунок для управления углом поворота. Например, для поворота относительно оси Х:
<input class='handonbutton' type='submit' id='clXangleDownID11pr' value=' - ' onclick='clXangleDown11pr()'> Angle-x: <span id="x-angle-label11pr">0.000</span> <input class='handonbutton' type='submit' id='clXangleUpID11pr' value='+' onclick='clXangleUp11pr()'> <input type='range' step='1.0' id='x-angle-range11pr' name='x-angle-range11pr' value='0.000' min='-360.000' max='360.000'/>
Максимальный угол поворота в каждую сторону – 360 градусов; точность поворота – 1 градус.
Пропишем новые функции для увеличеня/уменьшения угла, перемещения ползунка:
//--X angle--------------------------------------------------------------------------------
$(document).ready(function()
{
$("#x-angle-range11pr").change(function()
{
var range = $(this);
var value = parseFloat(range.val());
var angleXoutRad = -(value*PI/180);
angleX11pr = angleXoutRad;
$("#x-angle-label11pr").html(value);
});
});
function clXangleDown11pr()
{
var angleXoutDegr = -(angleX11pr*180/PI).toFixed(0) - 1;
outAngleX(angleXoutDegr)
}
function clXangleUp11pr()
{
var angleXoutDegr = -(angleX11pr*180/PI).toFixed(0) + 1;
outAngleX(angleXoutDegr);
}
function outAngleX(angleXoutDegr)
{
//Keep in [-2pi,2pi].
if(angleXoutDegr > 360)
angleXoutDegr = -359;
if(angleXoutDegr < -359)
angleXoutDegr = 360;
var angleXoutRad = -(angleXoutDegr*PI/180);
angleX11pr = angleXoutRad;
$("#x-angle-label11pr").html(angleXoutDegr);
document.getElementById("x-angle-range11pr").value = angleXoutDegr;
}
В выражении
angleXoutRad = -(value*PI/180); (var PI=3.141592653589793238462643383;)
мы преобразуем значение в градусах (используемые нами в интерфейсе) в радианы (используемые нами в вычислении модели). Знак минус введен для изменения направления вращения. И еще, мы следим, чтобы угол не вырастал больше 360 градусов.
Задаем в файле обработчиков кнопок еще одну функцию, которая будет обновлять значение угла в интерфейсе при автоматическом вращении, либо при вращении мышью:
//Common out.
function OutToLabelAndRange()
{
//X
if(document.getElementById( "clXbackID11pr" ).value == " | | " ||
document.getElementById( "clXforwID11pr" ).value == " | | " ||
capture11pr
)
{
//Keep in [-2pi,2pi].
if(angleX11pr > 2*Math.PI) //360
angleX11pr = -2*Math.PI; //-359
if(angleX11pr < -2*Math.PI )//+ 1*Math.PI/180) //-359
angleX11pr = 2*Math.PI; //360
var angleXoutDegr = -(angleX11pr*180/PI).toFixed(0);
$( "#x-angle-label11pr" ).html(angleXoutDegr);
document.getElementById( "x-angle-range11pr" ).value = angleXoutDegr;
}
//Y
…
//Z
…
}
Здесь мы наоборот, преобразуем радианы в градусы.
Также значительно изменится функция сброса настроек в начальные:
function SetInitialSettings11pr()
{
StopRotation();
SetEqualSpeeds11pr();
angleX11pr = initXrotView;
var angleXoutDegr = -(angleX11pr*180/PI).toFixed(0);
$("#x-angle-label11pr").html(angleXoutDegr);
document.getElementById("x-angle-range11pr").value = angleXoutDegr;
//Y
…
//Z
…
}
Теперь начальные углы поворота сцены хранятся в новых глобальных переменных:
//Start angles in Rad. var initXrotView = 0.50; var initYrotView =-0.70; var initZrotView = 0.00;
Приготовления закончены, теперь – изменяем главный алгоритм в функции setupWebGL11pr ():
//Initial scene rotation.
//Set position of cam.
mat4.translate(mvMatrix11pr, [0.0, 0.0, -4.0]); //Zoom.
//Zoom view.
mat4.scale(mvMatrix11pr, [zoom11pr, zoom11pr, zoom11pr]);
if(startProg)
{
startProg = !startProg;
SetInitialSettings11pr();
}
//Plus scene rotation this frame.
mat4.rotate(mvMatrix11pr, angleX11pr, [1.0, 0.0, 0.0]);
mat4.rotate(mvMatrix11pr, angleY11pr, [0.0, 1.0, 0.0]);
mat4.rotate(mvMatrix11pr, angleZ11pr, [0.0, 0.0, 1.0]);
//mat4.rotate(mvMatrix11pr, angle11pr, [0.0, 1.0, 0.0]);
if( !paused11pr )
{
angleX11pr += ASDirectionX11pr * ASspeedX11pr;
angleY11pr += ASDirectionY11pr * ASspeedY11pr;
angleZ11pr += ASDirectionZ11pr * ASspeedZ11pr;
//Out to label and range if auto-rotation.
OutToLabelAndRange();
}
Изменение формы и места объекта
Теперь, к самому интересному. Призму можно преобразовать в похожий на самолет объект смещением некоторых точек, как на схеме ниже:

Положение такого объекта в пространстве легче определять визуально, то есть, видно, где у него нос, крылья, днище.
Сразу после задания начальных точек призмы, сместим их:
//Move object into center of coords.
for(var i=0; i<triangleVerticesOriginal.length; i+=3)
{
triangleVerticesOriginal[i] -= 1;
triangleVerticesOriginal[i+1] -= 1;
triangleVerticesOriginal[i+2] += 1;
}
//Make air plane, moving points of prism.
var j; //Num of vertice.
var i; //Num of coord X of vertice j.
//Forward.
j = 1; i = j*3;
triangleVerticesOriginal[i+2] += 3;
//Right wing.
j = 6; i = j*3;
triangleVerticesOriginal[i] -= 2;
triangleVerticesOriginal[i+1] += 1;
//Left wing.
j = 8; i = j*3;
triangleVerticesOriginal[i] += 2;
triangleVerticesOriginal[i+1] += 1;
//Roof.
j = 5; i = j*3;
triangleVerticesOriginal[i+1] -= 0.5;
triangleVerticesOriginal[i+2] -= 1;
j = 11; i = j*3;
triangleVerticesOriginal[i+1] -= 0.5;
triangleVerticesOriginal[i+2] -= 1.5;
//Back.
j = 7; i = j*3;
triangleVerticesOriginal[i+2] -= 1;
j = 9; i = j*3;
triangleVerticesOriginal[i+2] -= 1;
j = 10; i = j*3;
triangleVerticesOriginal[i+2] -= 1;

Для симметрии днища мы меняем треугольники на 0-7-1 и 0-6-7. А также добавим треугольник 1-0-2.
VertexIndices = [ … /* //For prism. 0,6,1, //tr17 1,6,7, //tr18 1,7,2, //tr19 2,7,8, //tr20 */ //For airplane. 1,0,2, 0,6,7, 2,7,8, 0,7,2, ];
Вуаля! Остается только задать другие текстуры.
Контроль мышью
Управление мышью пока не изменится, только добавим вывод результатов вращений:
$("#my-canvas11pr").mousemove(function(e)
{
if(capture11pr)
{
…
//Out nums.
OutToLabelAndRange();
}
});
Тесты
Напоследок, рассмотрим проблему, которую теперь можно легко смоделироавать в нашей программе – шарнирный замок. Изначально, еще до начальных поворотов, наш объект повернут носом вдоль оси Z:

Если мы повернем модель, например, относительно оси Y на 90 градусов, то ось Z совпадет с начальной осью X. Теперь вращение вокруг осей X и Z совпадает, и если мы запустим по ним вращение в разные стороны, то объект будет неподвижен, хотя углы поворота будут меняться:

Решение для данной проблемы давно найдено, и скоро мы его применим.

