Собственный движок WebGL. Статья №3. Примитивы

В продолжении статьиВ первой статье уже использовался самый первый примитив, который можно назвать просто «произвольная форма».

Перед описанием примитивов-объектов еще раз повторю два основных требования-замечания от нашей системы:

Каждый примитив должен содержать вектор вершин и вектор индексов (vertex, indices). Строится каждый примитив по индексам через треугольники (TRIANGLE), то есть каждые 3 точки образуют независимый треугольник. По мимо требований к каждому примитиву мы можем подключить матрицу. После подключения можно с легкостью производить следующие манипуляции:

Перемещение по любой из осей. Перемещение на определенное кол-во единиц или перемещение к любой точки в пространстве Поворот вокруг любой точки. При подключении матрицы нам становится известен центр, соответственно, мы можем поворачивать примитив вокруг любой точки указав точку или же вокруг собственного центра. Сами примитивы можно разделить:

Простые. Состоят только из одного примитива. Сложные-составные. Состоят из нескольких примитивов Простые примитивыPlain Описание Данный примитив из себя представляет плоский прямоугольник. Для построения достаточно 2 треугольника. Необходимый минимум входных данных, который мы должны получить — это центр нашей фигуры, его ширина и высота. На выходе мы должны получить как минимум 2 массива: массив вершин, массив индексов. Каждую вершину рассчитываем через центр, ширину и высоту, так верхняя левая вершина — это точка, у которой x смещен от центра на половину ширины влево, а y на половину высоты вверх, z не смещается — фигура плоская. Таким же образом находим и все остальные вершины. [this.frontCenter[0] — width / 2, this.frontCenter[1] + height / 2, this.frontCenter[2], /*верхняя левая точка — 0*/ this.frontCenter[0] + width / 2, this.frontCenter[1] + height / 2, this.frontCenter[2], /*верхняя правая точка — 1*/ this.frontCenter[0] + width / 2, this.frontCenter[1] — height / 2, this.frontCenter[2], /*нижняя правая точка — 2*/ this.frontCenter[0] — width / 2, this.frontCenter[1] — height / 2, this.frontCenter[2]]; /*нижняя левая точка — 3*/ В массиве индексов мы определяем, как наши точки будут объединяться. В данном примитиве 2 треугольникаВерхняя левая точка, верхняя правая точка, нижняя левая точка. В массиве вершин это элементы — 0,1,3Верхняя правая точка, нижняя правая точка, нижняя левая точка. В массиве вершин это элементы — 1,2,3Соответственно, массив индексов выглядит следующим образом:[0,1,3,1,2,3,]; Порядок индексов меняться не будет, а вот с вершинами могут быть некие изменения. Для того, чтобы легко было совершать манипуляции с нашим примитивом, переведем массив вершин в матрицу. this.matrix = new botuMatrix (this.vertex,3); Операции с примитивом При манипуляции с матрицей будет изменяться массив, переданный как входящий параметр, в данном случае массив вершин. При описании матрицы были указаны возможные манипуляции с матрицей. Подключим данные манипуляции к нашему примитиву. moveByX: function (value){ this.matrix.move (value,0); }, moveByY: function (value){ this.matrix.move (value,1); }, moveByZ: function (value){ this.matrix.move (value,2); },

testToPoint: function (value){ this.matrix.toPoint (value); }, rotateAroundCenter: function (angle, xyzType) { this.matrix.rotate (angle, this.matrix.center, xyzType); },

rotateAroundMaxPoint: function (angle, xyzType) { this.matrix.rotate (angle, this.matrix.maxval, xyzType); }, rotateAroundPoint: function (angle, point, xyzType) { this.matrix.rotate (angle, point, xyzType); }, moveByX, moveByY, moveByZ — перемещение примитива по X, Y и Z, соответственно. Единственный входящий параметр — кол-во единиц. Пример, obj.moveByX (50) — перемещение obj на 50 единиц вправо. testToPoint — перемещаем примитив (ориентируясь на цент) к определенной точки. Входящий параметр — вектор-точка. Пример, obj.testToPoint ([0,50,10]); rotateAroundCenter, rotateAroundMaxPoint — разворот вокруг центра или вокруг максимальных координат. (в примере с прямоугольником, максимальные координаты совпадают с правой верхней точкой, однако максимальные координаты не всегда совпадают с какой либо точкой примитива. Если грубо каждый трехмерный объект упаковывать в куб, то максимальная координата — это верхняя дальняя точка этого куба.). Входящий параметр — угол разворота и ось по которой должен быть разворот. Пример: obj.rotateAroundCenter (45, «byX») — разворот вокруг оси X на 45 градусов. Угол указывается в градусах, оси — «byX», «byY», «byZ». rotateAroundPoint — разворот вокруг произвольной точки в пространстве. Пример, obj.rotateAroundPoint (45,[0,0,0], «byZ»); Данные операции не зависят от примитива, поэтому в дальнейшем будем их подключать без комментариев.Cub Данный примитив представляет из себя гексаэдр. Почти куб, только грани могут быть как квадраты, так и прямоугольники.Описание будет такое же, как и у прямоугольника, только добавим ещё один входящий параметр — глубину.У куба будет 8 вершин, как будто дальний и ближний прямоугольник. От простого прямоугольника, описанного выше, отличие будет заключаться в расчете координаты Z, которая в ближнем прямоугольнике будет уменьшаться на половину глубины, а в дальнем увеличиваться также на половину глубины.Для этого просто возьмем два центра this.frontCenter=[centerPoint[0], centerPoint[1], centerPoint[2] — depth / 2]; this.backCenter=[centerPoint[0], centerPoint[1], centerPoint[2] + depth / 2]; И в массиве будем создавать 2 прямоугольника, первый с центром frontCenter, второй с центром backCenter. /*ближниий прямоугольник*/ this.frontCenter[0] — width / 2, this.frontCenter[1] + height / 2, this.frontCenter[2], /*индекс — 0*/ this.frontCenter[0] + width / 2, this.frontCenter[1] + height / 2, this.frontCenter[2],/*индекс — 1*/ this.frontCenter[0] + width / 2, this.frontCenter[1] — height / 2, this.frontCenter[2],/*индекс — 2*/ this.frontCenter[0] — width / 2, this.frontCenter[1] — height / 2, this.frontCenter[2],/*индекс — 3*/

/*дальний прямоугольник*/ this.backCenter[0] — width / 2, this.backCenter[1] + height / 2, this.backCenter[2],/*индекс — 4*/ this.backCenter[0] + width / 2, this.backCenter[1] + height / 2, this.backCenter[2],/*индекс — 5*/ this.backCenter[0] + width / 2, this.backCenter[1] — height / 2, this.backCenter[2],/*индекс — 6*/ this.backCenter[0] — width / 2, this.backCenter[1] — height / 2, this.backCenter[2]/*индекс — 7*/ По поводу вершин индексов. В кубе 6 граней, каждая из который состоит из 2-х треугольников./*ближайший к нам прямоугольник, единственный, который мы видим, до манипуляций с кубом*/0,1,3,1,2,3,

/*левая грань*/0,4,7,0,3,7,

/*нижняя грань*/3,7,6,6,3,2,

/*правая грань*/2,6,1,1,5,6,

/*верхняя грань*/0,4,5,0,5,1,

/*задняя грань*/7,4,5,7,5,6

Сложные-составные примитивы Простые примитивы. которые мы создали состоят из треугольников, и перед тем как создать данный примитив мы мысленно разбивали его на треугольники. Сложные примитивы будут состоять из любой другой геометрической, двухмерной фигуры. В данной статье будет рассмотрен единственный «сложный примитив» — шар. Который будет состоять из прямоугольников.Шар Что необходимо знать, чтобы нарисовать шар — координаты и радиус? Да. Но я добавлю ещё 1 маленький параметр — детализация.Здесь один и тот же круг, с одним и тем же радиусом, только разной детализацией. О том, что в описании примитива будет пониматься под детализацией — чуть позже.imageДетализация — 35imageДетализация — 10Алгоритм: Строим прямоугольник по касательной, то есть центр прямоугольника — это центр круга смещенный по оси Z на величину радиуса. Высота и ширина прямоугольника — длина окружности, которая рассчитывается через радиус, разделенная на детализацию. Чем больше детализация, чем меньше высота-ширина прямоугольников, тем больше самих прямоугольников. Сверху добавляем ещё один прямоугольник, развернутый на угол, равный 360* / (кол-во прямоугольников, которое было найдено на предыдущем шаге). Повторяю предыдущий этап n-раз. Где n — кол-во прямоугольников. В результате получаем колесо. image Делаем копию данного колеса с разворотом по оси Y на угол, равный 360* / (длинна круга, деленная на ширину). Повторяем предыдущую операцию n-раз, где n — это длина круга, деленная на ширину. Для реализации данного алгоритма Создаем объект к которому можно подсоединять другие объекты. С точки зрения кода, в массивы вершин и индексов добавляются массивы вершин и индексов разных объектов. function botuGroupedObject (){ this.vertex = []; this.indices = []; } botuGroupedObject.prototype = { addObject: function (obj){ this.vertex.size = obj.vertex.size; var next = Math.max (this.vertex.length / this.vertex.size,0); this.vertex = this.vertex.concat (obj.vertex); this.indices = this.indices.concat (obj.indices.map (function (i){return i + next})); this.vertex.size = obj.vertex.size; } } Создаем вспомогательную функцию создания копии объекта в новом объекте. function makeCopy (object, type){ var vertex = object.vertex.slice (0); var indices = object.indices.slice (0); return new type (vertex, indices); } К примитиву Plain добавляем метод connectUP: function (anotherPlain){ var downLeftPoint = anotherPlain.getDownLeft (); var dvPoint = downLeftPoint.map (function (value, index){return value — this.getUpperLeft ()[index]}, this); this.testToPoint (dvPoint); } , которые строит примитив — plain вплотную к верхней границе другого примитива plain. getDownLeft () — это нижняя левая точка, то есть элемент из массива вершин с индексом 3. (см. выше в описании примитива Plain). getUpperLeft () — это верхняя левая точка, то есть элемент из массива вершин с индексом 0. Полный код первых 3-х статей function makeCopy (object, type){ var vertex = object.vertex.slice (0); var indices = object.indices.slice (0); return new type (vertex, indices); }

function vectorByMatrix (vector, matrix) { var resultVector = []; if (vector.length == matrix.rowNumbers) { var columnCount = 0; while (columnCount < matrix.columnNumbers){ var rowCount = 0; var value = 0; while(rowCount < matrix.rowNumbers) {

value += vector[rowCount] * matrix.column[columnCount][rowCount]; rowCount++; } resultVector.push (value); columnCount++; } }

return resultVector; }

function botuMatrix (source, columns) { this.source = source; this.columnNumbers = columns; this.rowNumbers = source.length / columns; this.rows = []; this.minval = []; this.maxval = []; this.radius = []; this.center = []; this.column = [];

if (source.length > 0) { var count = 0;

while (count < source.length) { var currentRow = this.source.slice(count,count + this.columnNumbers); this.rows.push(currentRow); var columnCount = 0; while(columnCount <= this.columnNumbers) { if (!this.column[columnCount]) { this.column[columnCount] = []; } this.column[columnCount].push(currentRow[columnCount]); columnCount += 1; } count = count + this.columnNumbers; } this.rowNumbers = this.rows.length; if (this.rows.length > 0) { count = 0; while (count < this.rows.length) { var tempRow = this.rows[count].slice(0); if (count == 0 ) { this.minval = tempRow.slice(0); this.maxval = tempRow.slice(0); this.radius = tempRow.slice(0); this.center = tempRow.slice(0); } if (count > 0) { var rowcount = 0; while (rowcount < tempRow.length) { this.minval.splice(rowcount,1,Math.min(this.minval[rowcount],tempRow[rowcount])); this.maxval.splice(rowcount,1,Math.max(this.maxval[rowcount],tempRow[rowcount])); this.radius.splice(rowcount,1,(this.maxval[rowcount] - this.minval[rowcount]) / 2); this.center.splice(rowcount,1,this.maxval[rowcount] - this.radius[rowcount]); rowcount = rowcount + 1; } } tempRow = undefined; count = count + 1; } tempRow = undefined; } }

}

botuMatrix.prototype = { move: function (value, xyzw){ this.column[xyzw] = this.column[xyzw].map (function (i){return i+value;}) this.updateByColumn (); }, updateByColumn: function (){ var columnCount = 0; while (columnCount < this.columnNumbers) { var rowCount = 0; while(rowCount < this.rowNumbers) { this.rows[rowCount][columnCount] = this.column[columnCount][rowCount]; this.source[columnCount + rowCount * this.columnNumbers] = this.column[columnCount][rowCount]; rowCount++; } columnCount++; } }, updateByRow:function(){ var rowCount = 0; while(rowCount < this.rowNumbers) { var columnCount = 0; while(columnCount < this.columnNumbers) { this.column[columnCount][rowCount] = this.rows[rowCount][columnCount]; this.source[columnCount + rowCount * this.columnNumbers] = this.column[columnCount][rowCount]; columnCount++; } columnCount = undefined; rowCount++; } columnCount = undefined; rowCount = undefined; }, toPoint:function(point){ if (point) { if(point.length == this.columnNumbers) { this.rows = this.rows.map(function(rowArray){ return rowArray.map(function(rowElement,index) { return rowElement + point[index]; } ) }); this.updateByRow(); } } }, byPoint:function(point){ if (point) { if(point.length == this.columnNumbers) { this.rows = this.rows.map(function(rowArray){ return rowArray.map(function(rowElement,index) { return rowElement * point[index]; } ) }); this.updateByRow(); } } }, rotate:function(angle,point,xyzType){ function multPointByValue(point,value){ return point.map(function(val){return value * val}); } this.toPoint(multPointByValue(point,-1)); var rotateSource = []; var radians = angle * Math.PI / 180.0;

switch (xyzType){ case «byX»: rotateSource = [1,0,0, 0, Math.cos (radians), Math.sin (radians), 0,-1 * Math.sin (radians), Math.cos (radians) ]; break; case «byY»: rotateSource = [Math.cos (radians),0,-1 * Math.sin (radians), 0,1,0, Math.sin (radians),0, Math.cos (radians) ]; break; case «byZ»: rotateSource = [Math.cos (radians), Math.sin (radians),0, -1 * Math.sin (radians), Math.cos (radians),0, 0,0,1]; break;

} var rotateMatrix = new botuMatrix (rotateSource,3); this.rows = this.rows.map (function (irow){ return vectorByMatrix (irow, rotateMatrix); }); rotateMatrix = null; rotateSource = null; this.updateByRow (); this.toPoint (point); }

}

function Scene (canvasID) { this.backgroundColor = {red:1.0, green:1.0, blue:1.0, alpha:1.0}; this.canvas = document.getElementById (canvasID); this.getContext (); this.indicBuffer = »; this.vecVertex = []; this.vecIndices = [];

}

Scene.prototype = {

clear: function (){ this.indicBuffer = »; this.vecVertex = []; this.vecIndices = []; }, getContext: function (){ var names = [«webgl», «experimental-webgl», «webkit-3d», «moz-webgl»]; this.gl = null; for (var ii = 0; ii < names.length; ++ii) { try { this.gl = this.canvas.getContext(names[ii]); } catch(e) {} if (this.gl) { break; } } }, initBuffers: function (vertex, indices) { this.vertexBuffer = this.gl.createBuffer(); this.vertexBuffer.size = vertex.size; this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer);

this.program.botuPositionAttr = this.gl.getAttribLocation (this.program, «botuPosition»); this.gl.enableVertexAttribArray (this.program.botuPositionAttr);

this.gl.bufferData (this.gl.ARRAY_BUFFER, new Float32Array (vertex), this.gl.STATIC_DRAW); if (indices) { this.indicBuffer = this.gl.createBuffer (); this.indicBuffer.numberOfItems = indices.length; this.gl.bindBuffer (this.gl.ELEMENT_ARRAY_BUFFER, this.indicBuffer); this.gl.bufferData (this.gl.ELEMENT_ARRAY_BUFFER, new Uint16Array (indices), this.gl.STATIC_DRAW); }

},

initProgram: function (vxShaderDom, frShaderDom) { var vxShader = document.getElementById (vxShaderDom).textContent; var frShader = document.getElementById (frShaderDom).textContent;

this.program = createProgram (this.gl, vxShader, frShader); this.gl.useProgram (this.program); this.program.botuPositionAttr = this.gl.getAttribLocation (this.program, «botuPosition»); this.gl.enableVertexAttribArray (this.program.botuPositionAttr);

function createProgram (context, vxs, frs) { var prg = context.createProgram (); var VertexShader = createShader (context, context.VERTEX_SHADER, vxs); var FragmentShader = createShader (context, context.FRAGMENT_SHADER, frs); context.attachShader (prg, VertexShader); context.attachShader (prg, FragmentShader); context.linkProgram (prg); if (! context.getProgramParameter (prg, context.LINK_STATUS)) { alert (context.getProgramInfoLog (prg)); } return prg; }

function createShader (context, type, shader) { var sh = context.createShader (type); context.shaderSource (sh, shader); context.compileShader (sh); if (! context.getShaderParameter (sh, context.COMPILE_STATUS)) { alert (context.getShaderInfoLog (sh)); } return sh; }

},

attributeSetup: function (attribName, attribSize) { var attrib = this.gl.getAttribLocation (this.program, attribName); this.gl.enableVertexAttribArray (attrib); this.gl.vertexAttribPointer (attrib, attribSize, this.gl.FLOAT, false, 0, 0); return attrib; },

setViewPort: function (width, height){ this.gl.viewportWidth = width; this.gl.viewportHeight = height; }, setBackgroundColor: function (colorVec){ if (colorVec){ if (colorVec.length > 0) { this.backgroundColor.red = colorVec[0]; } if (colorVec.length > 1) { this.backgroundColor.green = colorVec[1]; } if (colorVec.length > 2) { this.backgroundColor.blue = colorVec[2]; } if (colorVec.alpha > 3) { this.backgroundColor.red = colorVec[3]; } } }, AddObject: function (botuObj){ this.vecVertex.size = botuObj.vertex.size; var next = Math.max (this.vecVertex.length / this.vecVertex.size,0); this.vecVertex = this.vecVertex.concat (botuObj.vertex); this.vecIndices = this.vecIndices.concat (botuObj.indices.map (function (i){return i + next})); this.vecVertex.size = botuObj.vertex.size; },

draw: function () { this.initProgram («vertexShader», «fragmentShader»); this.initBuffers (this.vecVertex, this.vecIndices);

this.gl.viewport (0, 0, this.gl.viewportWidth, this.gl.viewportHeight); this.gl.clearColor (this.backgroundColor.red, this.backgroundColor.green, this.backgroundColor.blue, this.backgroundColor.alpha); this.gl.clear (this.gl.COLOR_BUFFER_BIT); this.gl.vertexAttribPointer (this.program.botuPositionAttr, this.vertexBuffer.size, this.gl.FLOAT, false,0,0); this.gl.enable (this.gl.DEPTH_TEST); this.gl.drawElements (this.gl.TRIANGLES, this.indicBuffer.numberOfItems, this.gl.UNSIGNED_SHORT, 0); }

}

function botuObject (vertex, indices){ this.vertex = []; this.indices = indices; this.matrix = new botuMatrix (vertex,3);

this.vertex = this.matrix.source; this.vertex.size = 3; }

botuObject.prototype = { moveByX: function (value){ this.matrix.move (value,0); }, moveByY: function (value){ this.matrix.move (value,1); }, moveByZ: function (value){ this.matrix.move (value,2); },

testToPoint: function (value){ this.matrix.toPoint (value); }, rotateAroundCenter: function (angle, xyzType) { this.matrix.rotate (angle, this.matrix.center, xyzType); },

rotateAroundMaxPoint: function (angle, xyzType) { this.matrix.rotate (angle, this.matrix.maxval, xyzType); }, rotateAroundPoint: function (angle, point, xyzType) { this.matrix.rotate (angle, point, xyzType); } }

function botuGroupedObject (){ this.vertex = []; this.indices = []; } botuGroupedObject.prototype = { addObject: function (obj){ this.vertex.size = obj.vertex.size; var next = Math.max (this.vertex.length / this.vertex.size,0); this.vertex = this.vertex.concat (obj.vertex); this.indices = this.indices.concat (obj.indices.map (function (i){return i + next})); this.vertex.size = obj.vertex.size; } }

function cub (centerPoint, width, height, depth) { this.frontCenter=[centerPoint[0], centerPoint[1], centerPoint[2] + depth / 2]; this.backCenter=[centerPoint[0], centerPoint[1], centerPoint[2] — depth / 2];

this.matrix = »; this.vertex=[]; this.indices=[0,1,3, 1,2,3,

0,4,7, 0,3,7,

3,7,6, 6,3,2, 2,6,1, 1,5,6, 0,4,5, 0,5,1, 7,4,5, 7,5,6 ]; this.init (width, height); }

cub.prototype = { init: function (width, height){ this.matrix = new botuMatrix ([ this.frontCenter[0] — width / 2, this.frontCenter[1] + height / 2, this.frontCenter[2], this.frontCenter[0] + width / 2, this.frontCenter[1] + height / 2, this.frontCenter[2], this.frontCenter[0] + width / 2, this.frontCenter[1] — height / 2, this.frontCenter[2], this.frontCenter[0] — width / 2, this.frontCenter[1] — height / 2, this.frontCenter[2],

this.backCenter[0] — width / 2, this.backCenter[1] + height / 2, this.backCenter[2], this.backCenter[0] + width / 2, this.backCenter[1] + height / 2, this.backCenter[2], this.backCenter[0] + width / 2, this.backCenter[1] — height / 2, this.backCenter[2], this.backCenter[0] — width / 2, this.backCenter[1] — height / 2, this.backCenter[2], ],3); this.vertex = this.matrix.source; this.vertex.size = 3; }, moveByX: function (value){ this.matrix.move (value,0); }, moveByY: function (value){ this.matrix.move (value,1); }, moveByZ: function (value){ this.matrix.move (value,2); },

testToPoint: function (value){ this.matrix.toPoint (value); }, rotateAroundCenter: function (angle, xyzType) { this.matrix.rotate (angle, this.matrix.center, xyzType); },

rotateAroundMaxPoint: function (angle, xyzType) { this.matrix.rotate (angle, this.matrix.maxval, xyzType); }, rotateAroundPoint: function (angle, point, xyzType) { this.matrix.rotate (angle, point, xyzType); }

}

function plainClass (centerPoint, width, height) { this.frontCenter=centerPoint;

this.matrix = »; this.vertex=[]; this.indices=[0,1,3, 1,2,3, ]; this.init (width, height); }

plainClass.prototype = { init: function (width, height){ this.vertex = [ this.frontCenter[0] — width / 2, this.frontCenter[1] + height / 2, this.frontCenter[2], this.frontCenter[0] + width / 2, this.frontCenter[1] + height / 2, this.frontCenter[2], this.frontCenter[0] + width / 2, this.frontCenter[1] — height / 2, this.frontCenter[2], this.frontCenter[0] — width / 2, this.frontCenter[1] — height / 2, this.frontCenter[2]]; this.matrix = new botuMatrix (this.vertex,3); this.vertex.size = 3; }, moveByX: function (value){ this.matrix.move (value,0); }, moveByY: function (value){ this.matrix.move (value,1); }, moveByZ: function (value){ this.matrix.move (value,2); },

testToPoint: function (value){ this.matrix.toPoint (value); }, rotateAroundCenter: function (angle, xyzType) { this.matrix.rotate (angle, this.matrix.center, xyzType); },

rotateAroundMaxPoint: function (angle, xyzType) { this.matrix.rotate (angle, this.matrix.maxval, xyzType); }, rotateAroundPoint: function (angle, point, xyzType) { this.matrix.rotate (angle, point, xyzType); },

getUpperLeft: function (){ return this.matrix.rows[0]; }, getDownLeft: function (){ return this.matrix.rows[3]; }, getDownRight: function (){ return this.matrix.rows[2]; }, connect: function (anotherPlain){ var downLeftPoint = anotherPlain.getUpperLeft (); var dvPoint = downLeftPoint.map (function (value, index){return value — this.getUpperLeft ()[index]}, this); this.testToPoint (dvPoint); }, connectUP: function (anotherPlain){ var downLeftPoint = anotherPlain.getDownLeft (); var dvPoint = downLeftPoint.map (function (value, index){return value — this.getUpperLeft ()[index]}, this); this.testToPoint (dvPoint); }, connectLeft: function (anotherPlain){ var downRightPoint = anotherPlain.getDownRight (); var dvPoint = downRightPoint.map (function (value, index){return value — this.getDownLeft ()[index]}, this); this.testToPoint (dvPoint); }

}

function circle (centerPoint, radius, numOfPlains){ var plainWidth = 2 * Math.PI * radius / numOfPlains; var globalAngle = 45 * plainWidth / radius; /* (½ width * 90) */ var numOfIterations = 360 / globalAngle; var plainCenter = [centerPoint[0], centerPoint[1], centerPoint[2] + radius]; this.angle = 360 / numOfPlains; this.plainObjects = []; var plain = »; var oldplain = »; var grp = new botuGroupedObject (); for (var i = 1; i<=numOfPlains;i++) { var ang = (i-1) * this.angle plain = new plainClass(plainCenter,plainWidth,plainWidth); plain.rotateAroundCenter(ang,"byX"); if (i > 1){ plain.connectUP (oldplain); } oldplain = plain; grp.addObject (plain); } plain = null; oldplain = null; var itt = 0; var tempAngle = 0; var temp = makeCopy (grp, botuObject); while (tempAngle < 360) { itt++; tempAngle = itt * globalAngle; var grp2 = makeCopy(temp,botuObject); grp2.rotateAroundCenter(tempAngle,"byY"); grp.addObject(grp2); }

/* var plain = »; var oldplain = »; for (var i = 1; i<=numOfPlains;i++) { var ang = (i-1) * this.angle plain = new plainClass(plainCenter,plainWidth,plainWidth); plain.rotateAroundCenter(ang,"byY"); // alert(plain + " " + oldplain + " " + plainCenter + " " + plainWidth); if (i > 1){ // alert (plain + » » + oldplain + » » + i); plain.connectLeft (oldplain); } oldplain = plain; // this.plainObjects.push (plain); grp.addObject (plain); }

*/ var grpobj = new botuObject (grp.vertex, grp.indices); this.vertex = grp.vertex; this.indices = grp.indices; this.matrix = grpobj.matrix;

}

circle.prototype = { init: function (width, height){ this.matrix = new botuMatrix ([ this.frontCenter[0] — width / 2, this.frontCenter[1] + height / 2, this.frontCenter[2], this.frontCenter[0] + width / 2, this.frontCenter[1] + height / 2, this.frontCenter[2], this.frontCenter[0] + width / 2, this.frontCenter[1] — height / 2, this.frontCenter[2], this.frontCenter[0] — width / 2, this.frontCenter[1] — height / 2, this.frontCenter[2],

this.backCenter[0] — width / 2, this.backCenter[1] + height / 2, this.backCenter[2], this.backCenter[0] + width / 2, this.backCenter[1] + height / 2, this.backCenter[2], this.backCenter[0] + width / 2, this.backCenter[1] — height / 2, this.backCenter[2], this.backCenter[0] — width / 2, this.backCenter[1] — height / 2, this.backCenter[2], ],3); this.vertex = this.matrix.source; this.vertex.size = 3; }, moveByX: function (value){ this.matrix.move (value,0); }, moveByY: function (value){ this.matrix.move (value,1); }, moveByZ: function (value){ this.matrix.move (value,2); },

testToPoint: function (value){ this.matrix.toPoint (value); }, rotateAroundCenter: function (angle, xyzType) { this.matrix.rotate (angle, this.matrix.center, xyzType); },

rotateAroundMaxPoint: function (angle, xyzType) { this.matrix.rotate (angle, this.matrix.maxval, xyzType); }, rotateAroundMinPoint: function (angle, xyzType) { this.matrix.rotate (angle, this.matrix.minval, xyzType); }, rotateAroundPoint: function (angle, point, xyzType) { this.matrix.rotate (angle, point, xyzType); }

}

window.onload = function () { var scene = new Scene («webgl»); scene.setBackgroundColor ([0.1,0.5,0.6,0.2]); scene.setViewPort (300, 300); var angle = 0; var debugAngle = document.getElementById («debugAngle»); var speed = 0;

var cube = new cub ([-100,105,0],90,90,90); var circ = new circle ([-120,-70,0],60,30); var plain = new plainClass ([100,10,10],120,130); /* scene.AddObject (cube); scene.AddObject (circ); scene.AddObject (plain); scene.draw ();*/

(function animloop (){

if (angle >=90){ speed = -0.01; }

if (angle <= 0){ speed = 0.01; }

angle+=speed; debugAngle.innerHTML = angle + » » + speed; scene.clear (); cube.rotateAroundCenter (angle, «byY»); cube.rotateAroundCenter (angle, «byX»); cube.rotateAroundCenter (angle, «byZ»); plain.rotateAroundCenter (angle, «byX»); circ.rotateAroundPoint (angle,[-100,-50,20], «byZ»);

scene.AddObject (cube); scene.AddObject (circ); scene.AddObject (plain); scene.draw (); requestAnimFrame (animloop, scene.canvas); })();

}

window.requestAnimFrame = (function (){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback, element) { return window.setTimeout (callback, 1000/100); }; })(); HTML файл

© Habrahabr.ru