Строим 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 совпадает, и если мы запустим по ним вращение в разные стороны, то объект будет неподвижен, хотя углы поворота будут меняться:
Решение для данной проблемы давно найдено, и скоро мы его применим.