1. Переміщення спостерігача за допомогою клавіш.
2. Бібліотека для роботи з фігурами.
1. Переміщення спостерігача за допомогою клавіш.
У попередніх лекціях спостерігач стояв на місці, а всі об'єкти розташовувалися прямо перед ним, з усіх рухів ми використовували тільки обертання навколо центру об'єкта. Тепер розглянемо більш складний випадок:
Розглянемо приклад анімації (рис.8.1), керованої клавішами-стрілками. Клавіші «вгору» і «вниз» переміщують спостерігача вперед і назад, клавіші «вліво» і «вправо» повертають його у відповідному напрямку. Якщо утримувати клавішу Shift, клавіші «вгору» і «вниз» змінюють висоту очей спостерігача. Використання бібліотек для роботи з фігурами.
2. Бібліотека для роботи з фігурами.
Для того, щоб побудувати такий фільм, ми спочатку створимо невелику бібліотеку функцій для роботи з тривимірною графікою. Для опису фігури будемо використовувати структуру, показану на рисунку. Вона містить три поля (властивості):
Як бачимо, це ті ж дані, які використовувалися для опису піраміди в попередній лекції, тільки вони об'єднані в одну структуру. Спочатку напишемо функцію moveBy, яка переміщує фігуру на вектор (a, b, c). Це означає, що до x-координатами всіх вершин потрібно додати a, до y-координат додати b і до z-координат додати c. При цьому початок координат переміщається в точку (a, b, c). Для такого переміщення потрібно зробити цикл по масиву вершин vert і для кожної з них виконати код:
vert [i] .x + = a;
vert [i] .y + = b;
vert [i] .z + = c;
Для зручності три координати вектора переміщення записуються в одну структуру, яка може бути побудована за допомогою функції pt3D (її код знаходиться в файлі 3D.as). Створимо новий документ типу ActionScript File та запишемо в нього код функції переміщення:
function moveBy (fig, way) {
var i; for (i = 0; i
with (fig.vert [i]) {
x + = way.x; y + = way.y; z + = way.z;
}
}
}
Збережемо файл під ім'ям figures3D.as. У цій функції два параметри – посилання на фігуру (вірніше, на область, де зберігаються її дані), і вектор переміщення way. Далі створимо функцію, яка будує прямокутний паралелепіпед (блок, «цеглина»). Так можна, наприклад, моделювати будинок. Ця функція повинна приймати в параметрах розміри блоку, масив з 6 елементів, що задає кольори граней, і початкову точку. В якості точки прив'язки виберемо лівий нижній кут ближній до нас межі. Якщо початкове положення не задано, ця точка поміщається в початок координат. Додайте в файл figures3D.fla код функції:
function brick (dx, dy, dz, color, place) {
var a = 0, b = 0, c = 0;
if () {
a = place.x; b = place.y; c = place.z;
}
rect.vert = [pt3D (0, 0, dz), pt3D (0, -dy, dz),
pt3D (dx, -dy, dz), pt3D (dx, 0, dz),
pt3D (0, 0, 0), pt3D (0, -dy, 0),
pt3D (dx, -dy, 0), pt3D (dx, 0, 0)];
rect.faucet = [[0, 1, 2, 3], [2, 3, 7, 6],
[5, 6, 7, 4], [1, 5, 4, 0],
[1, 2, 6, 5], [0, 3, 7, 4]];
rect.color = color; moveBy (rect, pt3D (a, b, c));
return rect;
У функції створюється новий об'єкт rect (рис.8.2), що має поля vert, faucet і color. Параметри dx, dy і dz задають його розміри, масив color – кольору граней, а додатковий параметр place – це координати точки, куди потрібно перемістити точку прив'язки з початку координат.
Масив точок і вершин будується так само, як і раніше, для переміщення використовується тільки що написана функція moveBy. Зверніть увагу, що в наведеному варіанті функції brick масив color не буде копіюватися, а просто в поле rect.color записується посилання на нього (адреса). Це означає наступне: якщо ми створюємо два блоки, використовуючи один і той же масив color, і потім змінимо цей масив, то зміняться кольори обох блоків. Щоб цього не сталося, можна створювати масив заново і копіювати значення, передані через параметр:
rect.color = new Array ();
for (i = 0; i
rect.color [i] = color [i];
У цій програмі ми будемо працювати з декількома об'єктами, які можуть перекривати один одного. Щоб враховувати це, потрібно зібрати всі грані разом, в одному об'єкті, і застосувати єдине Z-сортування. Наступна функція додає до об'єкта-фігури fig нову фігуру newFig того ж типу, яка передається як другий параметр. Фактично при цьому зливаються їх масиви vert, faucet і color, в результаті будується один об'єкт, що описує всі грані обох вихідних фігур. Для злиття масивів використовується метод concat (concatenation – з'єднання, зчіпка). Наприклад, у результаті виконання коду:
a = [1, 2, 3];
b = [4, 5];
c = a.concat (b);
створюється новий масив c з елементами [1,2,3,4,5]. Додайте в файл figures3D.fla код функції:
function addFigure (fig, newFig) {
var nPrev = fig.vert.length;
var i, k, n;
fig.vert = fig.vert.concat (newFig.vert);
for (i = 0; i
n = newFig.faucet [i] .length;
for (k = 0; k
}
fig.faucet = fig.faucet.concat (newFig.faucet);
fig.color = fig.color.concat (newFig.color);
}
При злитті масивів важливо не забути перетворити масив faucet, який описує межі другої фігури, що приєднується. У ньому до злиття вказані номери вершин саме для другої фігури, а після злиття там повинні бути номери тих же вершин, але в загальній нумерації. Наприклад, якщо перша фігура включала 5 вершин, номери вершин другої фігури в загальному списку будуть починатися з 5. Отже, перед злиттям з масиві faucet фігури newFig потрібно збільшити номери всіх вершин на 5. Ось як це зроблено у функції:
var nPrev = fig.vert.length;
for (i = 0; i
n = newFig.faucet [i] .length;
for (k = 0; k
}
Спочатку викликається функція perspective (з файлу 3D.as), яка розраховує ефект перспективи для заданого фокуса. Потім за допомогою функції midPointZ для всіх граней обчислюється z-координата середньої точки, за якою виконується сортування. Нарешті в циклі для кожної грані викликається функція малювання drawFaucet. Додайте в файл figures3D.fla код функції:
function midPointsZ (fig) {
var i, j, k, nPoints, sumZ;
var mid = new Array ();
for (i = 0; i
sumZ = 0; nPoints = fig.faucet [i] .length;
for (j = 0;
k = fig.faucet [i] [j]; (5)
sumZ + = ptScreen [k] .z; );
}
sumZ / = nPoints;
mid [i] = {id: i, z: sumZ};
}
return mid;
}
Наступна функція, showFigures, призначена для малювання всіх граней в потрібному порядку. Додайте в файл figures3D.fla код функції:
function showFigures (fig) {
var i, midScreen;
ptScreen = perspective (fig.vert, focalLength);
midScreen = midPointsZ (fig);
midScreen.sortOn ( "z", Array.DESCENDING + Array.NUMERIC);
scene.clear ();
for (i = 0; i
}
Спочатку викликається функція perspective (з файлу 3D.as), яка розраховує ефект перспективи для заданого фокуса. Потім за допомогою функції midPointZ для всіх граней обчислюється z-координата середньої точки, за якою виконується сортування. Нарешті в циклі для кожної грані викликається функція малювання drawFaucet. Додайте в файл figures3D.fla код функції:
function midPointsZ (fig) {
var i, j, k, nPoints, sumZ;
var mid = new Array ();
for (i = 0; i
sumZ = 0; nPoints = fig.faucet [i] .length;
for (j = 0; j
k = fig.faucet [i] [j];
sumZ + = ptScreen [k] .
Наприклад, створимо напівпрозору пірамідку (рис. 7.4), що обертається за допомогою клавіш-стрілок. Дамо новому документу ім'я pyramid.fla. Додамо в кадр 1 код:
}
sumZ / = nPoints;
mid [i] = {id: i, z: sumZ};
}
return mid;
}
У порівнянні з попереднім варіантом (див. лекція 8) функція midPointsZ працює для будь-якого числа вершин (а не тільки для трьох). Єдине, що потрібно – вершини в масиві faucet повинні бути перераховані в порядку їх обходу при побудові контуру. Нова версія функції drawFaucet також працює для будь-якого числа вершин, вони перебираються в циклі. Добавте у файл figures3D.fla код функції функції:
drawFaucet (рис, midPoint) {
якщо (midPoint.z
var id = midPoint.id; з (сцена) {
beginFill (fig.color [id], 100);
var k0 = fig.faucet [id] [0];
moveTo (ptScreen [k0]. x, ptScreen [k0]. y);
для (var i = 1; i
var k = fig.faucet [id] [i];
lineTo (ptScreen [k] .x, ptScreen [k] .y);
}
lineTo (ptScreen [k0] .x, ptScreen [k0]. y);
endFill ();
}
}
Збережемо остаточний варіант бібліотек функцій. На початку цієї функції добавлена стрічка (midPoint.z за допомогою якої здійснюється поворот. Ми вважаємо, що спостерігач знаходиться в точці з координатами (0,0, -focalLength), тому всі грані, що опинилися позаду нього (що мають меншу z-координату, ніж –focalLength) не малюються, оскільки відбувається вихід із функції по оператору return.