Космический корабль
Опубликовано 19 декабря, 2024 - 12:50 пользователем Савинцев Александр
Лекция:
3D модель космического корабля. Модель состоит из вершин, которые образуют полигоны. (Для удобства моделька Корабля была создана в Blender 3D для получения координат вершин Корабля и набора вершин для полигонов)
Вершины хранятся в памяти как объекты класса Vector3D:
class Vector3D{ // Конструктор для создания нового обьекта типа Vector3D constructor(x, y, z){ this.x = x; this.y = y; this.z = z; } // Функция класса для получения экранной проекции getProjection(obj, d){ let x = this.x*d/(d+this.z+obj.origin.z); let y = this.y*d/(d+this.z+obj.origin.z); return new Point(x + obj.origin.x, y + obj.origin.y); } // Функция класса для изменения размера scale(scaleX, scaleY, scaleZ){ this.x = scaleX * this.x; this.y = scaleY * this.y; this.z = scaleZ * this.z; } // Функция класса для перемещения обьекта move(obj, newX, newY, newZ){ this.x = this.x + newX; this.y = this.y + newY; this.z = this.z + newZ; } // Функция класса для вращения обьекта // вызывает функции для вращения вокруг определенной оси XY, YZ, XZ rotate(origin, angleXY, angleYZ, angleXZ){ if (angleXY != 0) { this.rotateXY(origin, angleXY); } if (angleYZ != 0) { this.rotateYZ(origin, angleYZ); } if (angleXZ != 0) { this.rotateXZ(origin, angleXZ); } } rotateXY(origin, angle){ const cos = Math.cos(angle); const sin = Math.sin(angle); const newX = (this.x - origin.x) * cos - (this.y - origin.y) * sin; const newY = (this.x - origin.x) * sin + (this.y - origin.y) * cos; const newZ = this.z; this.x = newX + origin.x; this.y = newY + origin.y; this.z = newZ; } rotateYZ(origin, angle){ const cos = Math.cos(angle); const sin = Math.sin(angle); const newX = this.x const newY = (this.y - origin.y) * cos - (this.z -origin.z) * sin; const newZ = (this.y - origin.y) * sin + (this.z -origin.z) * cos; this.x = newX; this.y = newY + origin.y; this.z = newZ + origin.z; } rotateXZ(origin, angle){ const cos = Math.cos(angle); const sin = Math.sin(angle); const newX = (this.x - origin.x) * cos - (this.z - origin.z) * sin; const newY = this.y; const newZ = (this.x - origin.x) * sin + (this.z - origin.z) * cos; this.x = newX + origin.x; this.y = newY; this.z = newZ + origin.z; } }
Вершины хранятся в классе SpaceShip, который является дочерним для Mash. То есть SpaceShip наследует все переменные и методы класса Mash
// Родительский класс для всех именованных мешей (Куб, Сфера, Корабль) class Mash{ constructor(color='white'){ this.origin = { x: 0, y: 0, z: 0 }; this.color = color; this.barycenter = 0; this.axisSet = []; } // Функция для расчета барицентра меша calcBarycenter(){ let sumX = 0; let sumY = 0; let sumZ = 0; this.vectorSet.forEach(V => { sumX += V.x; sumY += V.y; sumZ += V.z; }); let len = this.vectorSet.length; this.barycenter = new Vector3D(sumX/len, sumY/len, sumZ/len); } // Функция для вращения меша rotateMash(angleXY = 0, angleYZ = 0, angleXZ = 0, origin=this.barycenter){ this.vectorSet.forEach(V => { V.rotate(origin, angleXY, angleYZ, angleXZ); }); this.calcBarycenter(); } // Функция для перемещения меша moveMash(dX = 0, dY = 0, dZ = 0){ this.origin.x += dX; this.origin.y += dY; this.origin.z += dZ; this.vectorSet.forEach(V => { V.move(this, dX, dY, dZ); }); this.calcBarycenter(); } } class SpaceShip extends Mash{ constructor(factor, color){ // Задание цвета для объекта класса SpaceShip, так как переменная для цвета определенна // в родительском классе, нужно вызывать метод super(<переменная>); super(color); // Фактор определяющий размер корабля this.f = factor; this.vectorSet = [ // Набор вершин. К примеру две вершины определяющие плоскость //с координатами {(1,1,1),(1,-1,1),(-1,-1,1),(-1,1,1)}: new Vector3D( 1.00*factor, 1.00*factor, 1.00*factor), // 1 new Vector3D( 1.00*factor, -1.00*factor, 1.00*factor), // 2 new Vector3D( -1.00*factor, -1.00*factor, 1.00*factor), // 3 new Vector3D( -1.00*factor, 1.00*factor, 1.00*factor) // 4 // Реальный набор вершин корабля содержит 359 вершин ]; this.sideSet = [ // Набор полигонов заданные вершинами. // К примеру четыре вершины определяющие полигон //с координатами {(1,1,1),(1,-1,1),(-1,-1,1),(-1,1,1)}: [1, 2, 3, 4] // Реальный набор полигонов корабля содержит 360 полигонов ]; this.calcBarycenter(); } }
В коде есть класс scene3D, который облегчает работу со сценой. Класс содержит методы для:
- Создания именованного меша (makeCube, makeSphere, makeSpaceShip)
- Отрисовки всех объектов сцены
- Перемещения всей сцены, поворот всей сцены
- Дополнительные средства отображения
Основная работа происходит в функции main()
function main() { // Высота и Ширина экрана obj.width = window.innerWidth; obj.height = window.innerHeight; const W = obj.width; const H = obj.height; // Установка origin - обьект который задает центральную точку отрисовки //(по умолчанию это всегда начало координат (0,0,0)) // У обьекта Mash так же есть origin, это позволяет найти локальную //центральную точку для меша относительно глобальной центральной точки const ORIGIN = { x: Math.round(W/2), y: Math.round(H/2), z: 0 }; ctx.fillStyle = 'black'; ctx.fillRect(0,0,W,H); // Создание обьекта типа scene3D с аргументами origin=ORIGIN и дальность до наблюдателя d=500 const SCENE = new scene3D(ORIGIN, 500); SCENE.isDrawOrigin = false; SCENE.isDrawBarycenter = false; SCENE.isDrawAxis = false; SCENE.onliVisible = false; SCENE.isFill = false; // Создание переменной a типа SpaceShip. Для создания использовался класс Scene3D и его обьект SCENE // Здесь SCENE.makeSpaceShip( //<начальная координата X>, //<начальная координата Y>, //<начальная координата Z>, //<Фактор определяющий размер>, //<Цвет в виде строки>); let a = SCENE.makeSpaceShip(0, 0, 0, 50, 'red'); SCENE.drawScene(); SCENE.rotateScene(0, Math.PI/2, 0); // Функция play() реализующая вращение Корабля function play() { ctx.fillStyle = 'black'; ctx.fillRect(0,0,W,H); a.rotateMash(0.01, 0.01, 0.01); SCENE.drawScene(); requestAnimationFrame(play); } play(); }
Направление: