Власний движок 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
Деталізація — 35
 image
Деталізація — 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;  /* (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>

    
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.