В продолжении статьи
В первой статье уже использовался самый первый примитив, который можно назвать просто «произвольная форма».
Перед описанием примитивов-объектов еще раз повторю два основных требования-замечания от нашей системы:
- Каждый примитив должен содержать вектор вершин и вектор индексов (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 маленький параметр — детализация.
Здесь один и тот же круг, с одним и тем же радиусом, только разной детализацией. О том, что в описании примитива будет пониматься под детализацией — чуть позже.
Детализация — 35
Детализация — 10
Алгоритм:
- Строим прямоугольник по касательной, то есть центр прямоугольника — это центр круга смещенный по оси Z на величину радиуса. Высота и ширина прямоугольника — длина окружности, которая рассчитывается через радиус, разделенная на детализацию. Чем больше детализация, чем меньше высота-ширина прямоугольников, тем больше самих прямоугольников.
- Сверху добавляем ещё один прямоугольник, развернутый на угол, равный 360* / (кол-во прямоугольников, которое было найдено на предыдущем шаге).
- Повторяю предыдущий этап n-раз. Где n — кол-во прямоугольников. В результате получаем колесо.
- Делаем копию данного колеса с разворотом по оси 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; /* (1/2 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 файл
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="default.js"></script>
<script type="x-shader" id="vertexShader">
attribute vec3 botuPosition;
varying vec4 colorPos;
void main(){
colorPos = vec4(max(botuPosition.x,(-1.0) * botuPosition.x) / 200.0, max(botuPosition.y,(-1.0) * botuPosition.y) / 200.0, max(botuPosition.z,(-1.0) * botuPosition.z) / 200.0,1.0);
gl_Position = vec4(botuPosition,200);
}
</script>
<script type="x-shader" id="fragmentShader">
precision highp float;
varying vec4 colorPos;
void main(){
gl_FragColor = colorPos;
}
</script>
</head>
<body class="phone">
<canvas id="webgl" width="300" height="300"> </canvas>
<p id="debugAngle"></p>
</body>
</html>
Автор: Botu