From dde098328c2b313150ae78d1ccf6e753a0da48ef Mon Sep 17 00:00:00 2001 From: Syndamia Date: Sun, 20 Nov 2022 22:10:36 +0200 Subject: [WGL/H06] Started work on homework --- .../Homework 06 - The knot/Homework.html | 253 ++++ .../Homework 06 - The knot/webgl-fmi.js | 1562 ++++++++++++++++++++ 2 files changed, 1815 insertions(+) create mode 100644 Year 2/Computer Graphics with WebGL/Homework 06 - The knot/Homework.html create mode 100644 Year 2/Computer Graphics with WebGL/Homework 06 - The knot/webgl-fmi.js diff --git a/Year 2/Computer Graphics with WebGL/Homework 06 - The knot/Homework.html b/Year 2/Computer Graphics with WebGL/Homework 06 - The knot/Homework.html new file mode 100644 index 0000000..bbf9808 --- /dev/null +++ b/Year 2/Computer Graphics with WebGL/Homework 06 - The knot/Homework.html @@ -0,0 +1,253 @@ + + + + + Възелът + + + + + + + + + + + +

Възелът

+ + + + + Искаме canvas, но няма! + + diff --git a/Year 2/Computer Graphics with WebGL/Homework 06 - The knot/webgl-fmi.js b/Year 2/Computer Graphics with WebGL/Homework 06 - The knot/webgl-fmi.js new file mode 100644 index 0000000..5de1ad9 --- /dev/null +++ b/Year 2/Computer Graphics with WebGL/Homework 06 - The knot/webgl-fmi.js @@ -0,0 +1,1562 @@ +// Библиотека WebGL-FMI v0.12.11 +// +// Работа с контексти и шейдъри +// getContext(id) +// getShader(id,type) +// getProgram(idv,idf) +// +// Математически функции +// random(a,b) +// radians(degrees) +// +// Работа с вектори и матрици +// unitVector(x) +// vectorProduct(x,y) +// scalarProduct(x,y) +// vectorPoints(x,y) +// multiplyMatrix(a,b) +// calculateNormalMatrix(a) +// +// Трансформации с матрици +// unitMatrix(); +// translateMatrix(v) +// scaleMatrix(v) +// xRotateMatrix(a) +// yRotateMatrix(a) +// zRotateMatrix(a) +// rotateMatrix(a,v) +// +// Трансформации със съставни матрици +// identity() +// translate(v) +// scale(v) +// xRotate(a) +// yRotate(a) +// zRotate(a) +// useMatrix() +// pushMatrix() +// popMatrix() +// cloneMatrix() +// +// Проектиране с матрици +// viewMatrix (eye, focus, up) +// orthoMatrix(width, height, near, far) +// perspMatrix(angle, aspect, near, far) +// lookAt(eye, focus, up) +// perspective(angle, aspect, near, far) +// +// Графични обекти (класове) +// Cube(center,size) +// Cuboid(center,size) +// Pyramid(center,size,height,n) +// Cone(center,size,height) +// Prism(center,size,height,n) +// Cylinder(center,size,height) +// Sphere(center,size) +// Spheroid(center,size) +// Icosahedron(center,size) +// GeodesicSphere(center,size) +// RotationalSolid(center,size,f) +// Torus(center,size,R,r) +// +// Константи +// CONE_SIDES = 32; +// CYLINDER_SIDES = 32; +// SPHERE_SIDES = 32; +// GEODESIC_SIDES = 3; +// TORUS_MAJOR_SIDES = 50; +// TORUS_MINOR_SIDES = 25; + + + +var gl; // глобален WebGL контекст +var glprog; // глобална GLSL програма +var glmat; // глобална матрица на модела +var glmatnew; // true, ако матрицата е променена, но не е подадена на шейдъра +var glvmat; // глобална матрица на гледната точка +var glstack = []; // стек от матрици на модела + + +// брой байтове в един WebGL FLOAT (трябва да са 4 байта) +var FLOATS = Float32Array.BYTES_PER_ELEMENT; + + +// връща WebGL контекст, свързан с HTML елемент с даден id +function getContext(id) +{ + var canvas = document.getElementById(id); + if (!canvas) + { + alert("Искаме canvas с id=\""+id+"\", а няма!"); + return null; + } + + var context = canvas.getContext("webgl"); + if (!context) + { + context = canvas.getContext("experimental-webgl"); + } + + if (!context) + { + alert("Искаме WebGL контекст, а няма!"); + } + + return context; +} + + +// връща компилиран шейдър +function getShader(id,type) +{ + var source = document.getElementById(id).text; + var shader = gl.createShader(type); + + gl.shaderSource(shader,source); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader,gl.COMPILE_STATUS)) + { + alert(gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +} + + +// връща готова програма +function getProgram(idv,idf) +{ + var vShader = getShader(idv,gl.VERTEX_SHADER); + var fShader = getShader(idf,gl.FRAGMENT_SHADER); + + if (!vShader || !fShader) {return null;} + + var shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram,vShader); + gl.attachShader(shaderProgram,fShader); + gl.linkProgram(shaderProgram); + + if (!gl.getProgramParameter(shaderProgram,gl.LINK_STATUS)) + { + alert(gl.getProgramInfoLog(shaderProgram)); + return null; + } + + gl.useProgram(shaderProgram); + return shaderProgram; +} + + +// случайно дробно число в интервал +function random(a,b) +{ + return a+(b-a)*Math.random(); +} + + +// преобразува градуси в радиани +function radians(degrees) +{ + return degrees*Math.PI/180; +} + + +// създава матрица за ортографска проекция +function orthoMatrix(width, height, near, far) +{ + var matrix = [ + 2.0/width, 0, 0, 0, + 0, 2.0/height, 0, 0, + 0, 0, 2.0/(near-far), 0, + 0, 0, (far+near)/(near-far), 1]; + return new Float32Array(matrix); +} + + +// създава матрица за перспективна проекция +// ъгълът на зрителното поле е в градуси +function perspMatrix(angle, aspect, near, far) +{ + var fov = 1/Math.tan(radians(angle)/2); + var matrix = [ + fov/aspect, 0, 0, 0, + 0, fov, 0, 0, + 0, 0, (far+near)/(near-far), -1, + 0, 0, 2.0*near*far/(near-far), 0]; + return new Float32Array(matrix); +} + + +// установява перспективна проекция +function perspective(angle,aspect,near,far) +{ + var proj = perspMatrix(angle,aspect,near,far); + gl.uniformMatrix4fv(uProjectionMatrix,false,proj); +} + + +// единичен вектор +function unitVector(x) +{ + var len = 1/Math.sqrt( x[0]*x[0]+x[1]*x[1]+x[2]*x[2] ); + return [ len*x[0], len*x[1], len*x[2] ]; +} + + +// векторно произведение на два вектора +function vectorProduct(x,y) +{ + return [ + x[1]*y[2]-x[2]*y[1], + x[2]*y[0]-x[0]*y[2], + x[0]*y[1]-x[1]*y[0] ]; +} + + +// скаларно произведение на два вектора +function scalarProduct(x,y) +{ + return x[0]*y[0] + x[1]*y[1] + x[2]*y[2]; +} + + +// вектор между две точки +function vectorPoints(x,y) +{ + return [ x[0]-y[0], x[1]-y[1], x[2]-y[2] ]; +} + + +// генерира матрица за гледна точка, параметрите са масиви с по 3 елемента +function viewMatrix (eye, focus, up) +{ + // единичен вектор Z' по посоката на гледане + var z = unitVector(vectorPoints(eye,focus)); + + // единичен вектор X', перпендикулярен на Z' и на посоката нагоре + var x = unitVector(vectorProduct(up,z)); + + // единичен вектор Y', перпендикулярен на X' и Z' + var y = unitVector(vectorProduct(z,x)); + + // резултатът е тези три вектора + транслация + var matrix = [ + x[0], y[0], z[0], 0, + x[1], y[1], z[1], 0, + x[2], y[2], z[2], 0, + -scalarProduct(x,eye), + -scalarProduct(y,eye), + -scalarProduct(z,eye), 1 ]; + return new Float32Array(matrix); +}; + + +// умножение на матрици +function multiplyMatrix(a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + var out=[]; + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; +}; + +// създаване на матрица за нормалните вектори, +// чрез реципрочна стойност и транспозиция +function calculateNormalMatrix(a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // детерминанта + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + det = 1.0 / det; + + var out=[]; + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[3] = 0; + + out[4] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[7] = 0; + + out[8] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[9] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = 0; + + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + + return out; +}; + + +// установява гледна точка +function lookAt(eye,target,up) +{ + glvmat = viewMatrix(eye,target,up); + gl.uniformMatrix4fv(uViewMatrix,false,glvmat); +} + + +// единична матрица +function unitMatrix() +{ + var matrix = [ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1]; + return new Float32Array(matrix); +} + +// матрица за транслация по вектор V +function translateMatrix(v) +{ + var matrix = [ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + v[0], v[1], v[2], 1]; + return new Float32Array(matrix); +} + + +// матрица за мащабиране с вектор V +function scaleMatrix(v) +{ + var matrix = [ + v[0], 0, 0, 0, + 0, v[1], 0, 0, + 0, 0, v[2], 0, + 0, 0, 0, 1]; + return new Float32Array(matrix); +} + + +// матрица за въртене от X към Y около Z на ъгъл A +function zRotateMatrix(a) +{ + a = radians(a); + var c = Math.cos(a); + var s = Math.sin(a); + var matrix = [ + c, s, 0, 0, + -s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1]; + return new Float32Array(matrix); +} + + +// матрица за въртене от Y към Z около X на ъгъл A +function xRotateMatrix(a) +{ + a = radians(a); + var c = Math.cos(a); + var s = Math.sin(a); + var matrix = [ + 1, 0, 0, 0, + 0, c, s, 0, + 0,-s, c, 0, + 0, 0, 0, 1]; + return new Float32Array(matrix); +} + + +// матрица за въртене от X към Z около Y на ъгъл A +function yRotateMatrix(a) +{ + a = radians(a); + var c = Math.cos(a); + var s = Math.sin(a); + var matrix = [ + c, 0, s, 0, + 0, 1, 0, 0, + -s, 0, c, 0, + 0, 0, 0, 1]; + return new Float32Array(matrix); +} + + +// матрица за въртене около вектор V на ъгъл A +function rotateMatrix(a, v) +{ + a = radians(a); + v = unitVector(v); + + var c = Math.cos(a); + var s = Math.sin(a); + + var xx = v[0]*v[0]*(1-c); + var xy = v[0]*v[1]*(1-c); + var xz = v[0]*v[2]*(1-c); + var yy = v[1]*v[1]*(1-c); + var yz = v[1]*v[2]*(1-c); + var zz = v[2]*v[2]*(1-c); + + var matrix = [ + xx+c, xy-v[2]*s, xz+v[1]*s, 0, + xy+v[2]*s, yy+c, yz-v[0]*s, 0, + xz-v[1]*s, yz+v[0]*s, zz+c, 0, + 0, 0, 0, 1]; + return new Float32Array(matrix); +} + + +// зарежда единичната матрица в матрицата на модела +function identity() +{ + glmatnew = true; + glmat = new Float32Array( [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1] ); +} + + +// добавя транслация към матрицата на модела +function translate(v) +{ + glmatnew = true; + glmat[12] += glmat[0]*v[0]+glmat[4]*v[1]+glmat[8]*v[2]; + glmat[13] += glmat[1]*v[0]+glmat[5]*v[1]+glmat[9]*v[2]; + glmat[14] += glmat[2]*v[0]+glmat[6]*v[1]+glmat[10]*v[2]; +} + + +// добавя мащабиране към матрицата на модела +function scale(v) +{ + glmatnew = true; + glmat[0] *= v[0]; + glmat[1] *= v[0]; + glmat[2] *= v[0]; + + glmat[4] *= v[1]; + glmat[5] *= v[1]; + glmat[6] *= v[1]; + + glmat[8] *= v[2]; + glmat[9] *= v[2]; + glmat[10] *= v[2]; +} + + +// ако матрицата на модела е променена, изпраща я към шейдъра +function useMatrix() +{ + if (glmatnew) + { + glmatnew = false; + gl.uniformMatrix4fv(uModelMatrix,false,glmat); + } +} + + +// добавя въртене около X към матрицата на модела +function xRotate(a) +{ + glmatnew = true; + + a = radians(a); + var s = Math.sin(a); + var c = Math.cos(a); + + a = glmat[4]*s+glmat[8]*c; + glmat[4]=glmat[4]*c-glmat[8]*s; + glmat[8]=a; + + a = glmat[5]*s+glmat[9]*c; + glmat[5]=glmat[5]*c-glmat[9]*s; + glmat[9]=a; + + a = glmat[6]*s+glmat[10]*c; + glmat[6]=glmat[6]*c-glmat[10]*s; + glmat[10]=a; +} + + +// добавя въртене около Y към матрицата на модела +function yRotate(a) +{ + glmatnew = true; + + a = radians(a); + var s = Math.sin(a); + var c = Math.cos(a); + + a = glmat[0]*s+glmat[8]*c; + glmat[0]=glmat[0]*c-glmat[8]*s; + glmat[8]=a; + + a = glmat[1]*s+glmat[9]*c; + glmat[1]=glmat[1]*c-glmat[9]*s; + glmat[9]=a; + + a = glmat[2]*s+glmat[10]*c; + glmat[2]=glmat[2]*c-glmat[10]*s; + glmat[10]=a; +} + + +// добавя въртене около Z към матрицата на модела +function zRotate(a) +{ + glmatnew = true; + + a = radians(a); + var s = Math.sin(a); + var c = Math.cos(a); + + a = glmat[0]*s+glmat[4]*c; + glmat[0]=glmat[0]*c-glmat[4]*s; + glmat[4]=a; + + a = glmat[1]*s+glmat[5]*c; + glmat[1]=glmat[1]*c-glmat[5]*s; + glmat[5]=a; + + a = glmat[2]*s+glmat[6]*c; + glmat[2]=glmat[2]*c-glmat[6]*s; + glmat[6]=a; +} + + +// добавя текущата матрица на модела в стека за матрици +function pushMatrix() +{ + var mat = new Float32Array(glmat.length); + mat.set(glmat); + glstack.push(mat); +} + + +// извлича матрица на модела от стека за матрици +// при празен стек връща единичната матрица +function popMatrix() +{ + glmatnew = true; + if (glstack.length) + glmat = glstack.pop(); + else + identity(); +} + + +// клонира матрица като копира всичките ѝ стойности +function cloneMatrix(a) +{ + var b = new Float32Array(a.length); + b.set(a); + return b; +} + + + +// каноничен куб - конструктор +CanonicalCube = function() +{ + // върхове + var v = [ [+0.5,-0.5,-0.5], [+0.5,+0.5,-0.5], + [-0.5,+0.5,-0.5], [-0.5,-0.5,-0.5], + [+0.5,-0.5,+0.5], [+0.5,+0.5,+0.5], + [-0.5,+0.5,+0.5], [-0.5,-0.5,+0.5] ]; + // нормални вектори + var n = [ [1,0,0], [-1,0,0], + [0,1,0], [0,-1,0], + [0,0,1], [0,0,-1] ]; + // общ списък връх-нормала + var data = [].concat( + v[0],n[0],v[1],n[0],v[4],n[0], + v[4],n[0],v[1],n[0],v[5],n[0], + v[6],n[1],v[2],n[1],v[7],n[1], + v[7],n[1],v[2],n[1],v[3],n[1], + v[5],n[2],v[1],n[2],v[6],n[2], + v[6],n[2],v[1],n[2],v[2],n[2], + v[4],n[3],v[7],n[3],v[0],n[3], + v[0],n[3],v[7],n[3],v[3],n[3], + v[4],n[4],v[5],n[4],v[7],n[4], + v[7],n[4],v[5],n[4],v[6],n[4], + v[0],n[5],v[3],n[5],v[1],n[5], + v[1],n[5],v[3],n[5],v[2],n[5] ); + // локална променлива за инстанцията с WebGL буфер + var buf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER,buf); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW); + // запомняме буфера в текущата инстанция + this.buf = buf; +} + +// каноничен куб - метод за рисуване +CanonicalCube.prototype.draw = function() +{ + // активираме буфера, създаден от конструктора + gl.bindBuffer(gl.ARRAY_BUFFER,this.buf); + // казваме къде са координатите + gl.enableVertexAttribArray(aXYZ); + gl.vertexAttribPointer(aXYZ,3,gl.FLOAT,false,6*FLOATS,0*FLOATS); + // казваме къде са нормалите + gl.enableVertexAttribArray(aNormal); + gl.vertexAttribPointer(aNormal,3,gl.FLOAT,false,6*FLOATS,3*FLOATS); + // рисуваме + gl.drawArrays(gl.TRIANGLES,0,36); +} + +var canonicalCube; + +// куб - конструктор с параметри център и размер +Cube = function(center,size) +{ + // съхраняваме центъра и размера на куба + this.center = center; + this.size = size; + this.color = [1,0.75,0]; + // създаваме еднократно канонична инстанция + if (!canonicalCube) + canonicalCube = new CanonicalCube(); +} + +// куб - рисуване +Cube.prototype.draw = function() +{ + pushMatrix(); // запомняме матрицата + gl.vertexAttrib3fv(aColor,this.color); // подаваме цвета + translate(this.center); // мястото + scale([this.size,this.size,this.size]); // и размера + useMatrix(); + canonicalCube.draw(); // самото рисуване + popMatrix(); // възстановяваме матрицата +} + + + +// кубоид - конструктор с параметри център и размер +Cuboid = function(center,size) +{ + // съхраняваме центъра и размера на куба + this.center = center; + this.size = size; + this.color = [1,0.75,0]; + // създаваме еднократно канонична инстанция + if (!canonicalCube) + canonicalCube = new CanonicalCube(); +} + +// кубоид - рисуване +Cuboid.prototype.draw = function() +{ + pushMatrix(); // запомняме матрицата + gl.vertexAttrib3fv(aColor,this.color); // подаваме цвета + translate(this.center); // мястото + scale(this.size); // и размера + useMatrix(); + canonicalCube.draw(); // самото рисуване + popMatrix(); // възстановяваме матрицата +} + + + +// канонична пирамида - конструктор +CanonicalPyramid = function(n) +{ + // текущ ъгъл и ъглова разлика + var a = 0, dA = 2*Math.PI/n; + + // генериране на основата като ветрило + var data = [0,0,0, 0,0,-1]; + for (var i=0; i<=n; i++) + { + data.push(Math.cos(a),Math.sin(a),0,0,0,-1); + a += dA; + } + + // генериране на околните стени + a = 0; + var nZ = Math.cos(Math.PI/n); // височина на нормалния вектор + for (var i=0; i<=n; i++) + { + // нормален вектор (няма нужда да е единичен, в щейдъра се нормализира) + var N = [Math.cos(a+dA/2),Math.sin(a+dA/2),0/*nZ*/]; + data.push(0,0,1,N[0],N[1],nZ); + data.push(Math.cos(a),Math.sin(a),0,N[0],N[1],0); + data.push(Math.cos(a+dA),Math.sin(a+dA),0,N[0],N[1],0); + a += dA; + } + + var buf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER,buf); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW); + + // запомняме n и буфера + this.n = n; + this.buf = buf; +} + +// канонична пирамида - метод за рисуване +CanonicalPyramid.prototype.draw = function() +{ + gl.bindBuffer(gl.ARRAY_BUFFER,this.buf); + // върхове + gl.enableVertexAttribArray(aXYZ); + gl.vertexAttribPointer(aXYZ,3,gl.FLOAT,false,6*FLOATS,0*FLOATS); + // нормали + gl.enableVertexAttribArray(aNormal); + gl.vertexAttribPointer(aNormal,3,gl.FLOAT,false,6*FLOATS,3*FLOATS); + // рисуваме основата + gl.drawArrays(gl.TRIANGLE_FAN,0,this.n+2); + // рисуваме околните стени + gl.drawArrays(gl.TRIANGLES,this.n+2,3*this.n); +} + +// масив от канонични пирамиди +var canonicalPyramid = []; + +// пирамида - конструктор с параметри център, размер на основата, височина и брой стени +Pyramid = function(center,size,height,n) +{ + this.center = center; + this.size = size; + this.height = height; + this.n = n; + this.color = [1,0.75,0]; + // създаваме еднократно канонична пирамида + if (!canonicalPyramid[n]) + canonicalPyramid[n] = new CanonicalPyramid(n); +} + +// пирамида - рисуване +Pyramid.prototype.draw = function() +{ + pushMatrix(); + gl.vertexAttrib3fv(aColor,this.color); + translate(this.center); + scale([this.size,this.size,this.height]); + useMatrix(); + canonicalPyramid[this.n].draw(); + popMatrix(); +} + + +// каноничен конус - конструктор +CanonicalCone = function(n) +{ + // текущ ъгъл и ъглова разлика + var a = 0, dA = 2*Math.PI/n; + + // генериране на основата като ветрило + var data = [0,0,0, 0,0,-1]; + for (var i=0; i<=n; i++) + { + data.push(Math.cos(a),Math.sin(a),0,0,0,-1); + a += dA; + } + + // генериране на околните стени + a = 0; + //var nZ = Math.cos(Math.PI/n); // височина на нормалния вектор + for (var i=0; i<=n; i++) + { + // нормален вектор (няма нужда да е единичен, в щейдъра се нормализира) + data.push(0,0,1,0,0,1/*N[0],N[1],N[2]*/); + data.push(Math.cos(a),Math.sin(a),0,Math.cos(a),Math.sin(a),0/*Nz*/); + a += dA; + data.push(Math.cos(a),Math.sin(a),0,Math.cos(a),Math.sin(a),0/*Nz*/); + } + + var buf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER,buf); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW); + + // запомняме n и буфера + this.n = n; + this.buf = buf; +} + +// канонична пирамида - метод за рисуване +CanonicalCone.prototype.draw = function() +{ + gl.bindBuffer(gl.ARRAY_BUFFER,this.buf); + // върхове + gl.enableVertexAttribArray(aXYZ); + gl.vertexAttribPointer(aXYZ,3,gl.FLOAT,false,6*FLOATS,0*FLOATS); + // нормали + gl.enableVertexAttribArray(aNormal); + gl.vertexAttribPointer(aNormal,3,gl.FLOAT,false,6*FLOATS,3*FLOATS); + // рисуваме основата + gl.drawArrays(gl.TRIANGLE_FAN,0,this.n+2); + // рисуваме околните стени + gl.drawArrays(gl.TRIANGLES,this.n+2,3*this.n); +} + +// масив от канонични конуси +var canonicalCone = []; + +// конус - конструктор с параметри център, размер на основата, височина +var CONE_SIDES = 32; +Cone = function(center,size,height) +{ + this.center = center; + this.size = size; + this.height = height; + this.n = CONE_SIDES; + this.color = [1,0.75,0]; + // създаваме еднократно канонична пирамида + if (!canonicalCone[this.n]) + canonicalCone[this.n] = new CanonicalCone(this.n); +} + +// конус - рисуване +Cone.prototype.draw = function() +{ + pushMatrix(); + gl.vertexAttrib3fv(aColor,this.color); + translate(this.center); + scale([this.size,this.size,this.height]); + useMatrix(); + canonicalCone[this.n].draw(); + popMatrix(); +} + + + +// канонична призма - конструктор +CanonicalPrism = function(n) +{ + // текущ ъгъл и ъглова разлика + var a = 0, dA = 2*Math.PI/n; + + // генериране на долната основа като ветрило + var data = [0,0,0, 0,0,-1]; + for (var i=0; i<=n; i++) + { + data.push(Math.cos(a),Math.sin(a),0,0,0,-1); + a += dA; + } + + // генериране на горната основа като ветрило + data.push(0,0,1, 0,0,1); + for (var i=0; i<=n; i++) + { + data.push(Math.cos(a),Math.sin(a),1,0,0,1); + a += dA; + } + + // генериране на околните стени + a = 0; + var nZ = Math.cos(Math.PI/n); // височина на нормалния вектор + for (var i=0; i<=n; i++) + { + var N = [Math.cos(a+dA/2),Math.sin(a+dA/2)]; + data.push(Math.cos(a),Math.sin(a),1,N[0],N[1],0); + data.push(Math.cos(a),Math.sin(a),0,N[0],N[1],0); + data.push(Math.cos(a+dA),Math.sin(a+dA),0,N[0],N[1],0); + data.push(Math.cos(a+dA),Math.sin(a+dA),1,N[0],N[1],0); + data.push(Math.cos(a+dA),Math.sin(a+dA),0,N[0],N[1],0); + data.push(Math.cos(a),Math.sin(a),1,N[0],N[1],0); + a += dA; + } + + var buf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER,buf); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW); + + // запомняме n и буфера + this.n = n; + this.buf = buf; +} + +// канонична призма - метод за рисуване +CanonicalPrism.prototype.draw = function() +{ + gl.bindBuffer(gl.ARRAY_BUFFER,this.buf); + // върхове + gl.enableVertexAttribArray(aXYZ); + gl.vertexAttribPointer(aXYZ,3,gl.FLOAT,false,6*FLOATS,0*FLOATS); + // нормали + gl.enableVertexAttribArray(aNormal); + gl.vertexAttribPointer(aNormal,3,gl.FLOAT,false,6*FLOATS,3*FLOATS); + // рисуваме долната и горната основа + gl.drawArrays(gl.TRIANGLE_FAN,0,this.n+2); + gl.drawArrays(gl.TRIANGLE_FAN,this.n+2,this.n+2); + // рисуваме околните стени + gl.drawArrays(gl.TRIANGLES,2*this.n+4,6*this.n); +} + +// масив от канонични призми +var canonicalPrism = []; + +// призма - конструктор с параметри център, размер на основата, височина и брой стени +Prism = function(center,size,height,n) +{ + this.center = center; + this.size = size; + this.height = height; + this.n = n; + this.color = [1,0.75,0]; + // създаваме еднократно канонична призма + if (!canonicalPrism[n]) + canonicalPrism[n] = new CanonicalPrism(n); +} + +// призма - рисуване +Prism.prototype.draw = function() +{ + pushMatrix(); + gl.vertexAttrib3fv(aColor,this.color); + translate(this.center); + scale([this.size,this.size,this.height]); + useMatrix(); + canonicalPrism[this.n].draw(); + popMatrix(); +} + + + +// каноничен цилиндър - конструктор +CanonicalCylinder = function(n) +{ + // текущ ъгъл и ъглова разлика + var a = 0, dA = 2*Math.PI/n; + + // генериране на долната основа като ветрило + var data = [0,0,0, 0,0,-1]; + for (var i=0; i<=n; i++) + { + data.push(Math.cos(a),Math.sin(a),0,0,0,-1); + a += dA; + } + + // генериране на горната основа като ветрило + data.push(0,0,1, 0,0,1); + for (var i=0; i<=n; i++) + { + data.push(Math.cos(a),Math.sin(a),1,0,0,1); + a += dA; + } + + // генериране на околните стени + a = 0; + var nZ = Math.cos(Math.PI/n); // височина на нормалния вектор + for (var i=0; i<=n; i++) + { + var N = [Math.cos(a),Math.sin(a)]; // нормала към един отвес + var M = [Math.cos(a+dA),Math.sin(a+dA)]; // нормала към следващия отвес + data.push(Math.cos(a),Math.sin(a),1,N[0],N[1],0); + data.push(Math.cos(a),Math.sin(a),0,N[0],N[1],0); + data.push(Math.cos(a+dA),Math.sin(a+dA),0,M[0],M[1],0); + data.push(Math.cos(a+dA),Math.sin(a+dA),1,M[0],M[1],0); + data.push(Math.cos(a+dA),Math.sin(a+dA),0,M[0],M[1],0); + data.push(Math.cos(a),Math.sin(a),1,N[0],N[1],0); + a += dA; + } + + var buf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER,buf); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW); + + // запомняме n и буфера + this.n = n; + this.buf = buf; +} + +// каноничен цилиндър - метод за рисуване +CanonicalCylinder.prototype.draw = function() +{ + gl.bindBuffer(gl.ARRAY_BUFFER,this.buf); + // върхове + gl.enableVertexAttribArray(aXYZ); + gl.vertexAttribPointer(aXYZ,3,gl.FLOAT,false,6*FLOATS,0*FLOATS); + // нормали + gl.enableVertexAttribArray(aNormal); + gl.vertexAttribPointer(aNormal,3,gl.FLOAT,false,6*FLOATS,3*FLOATS); + // рисуваме долната и горната основа + gl.drawArrays(gl.TRIANGLE_FAN,0,this.n+2); + gl.drawArrays(gl.TRIANGLE_FAN,this.n+2,this.n+2); + // рисуваме околните стени + gl.drawArrays(gl.TRIANGLES,2*this.n+4,6*this.n); +} + +// масив от канонични цилиндри +var canonicalCylinder = []; + +// цилиндър - конструктор с параметри център, размер на основата, височина и брой стени +var CYLINDER_SIDES = 32; +Cylinder = function(center,size,height) +{ + this.center = center; + this.size = size; + this.height = height; + this.n = CYLINDER_SIDES; + this.color = [1,0.75,0]; + // създаваме еднократно канонична призма + if (!canonicalCylinder[this.n]) + canonicalCylinder[this.n] = new CanonicalCylinder(this.n); +} + +// цилиндър - рисуване +Cylinder.prototype.draw = function() +{ + pushMatrix(); + gl.vertexAttrib3fv(aColor,this.color); + translate(this.center); + scale([this.size,this.size,this.height]); + useMatrix(); + canonicalCylinder[this.n].draw(); + popMatrix(); +} + +// канонична сфера - конструктор +CanonicalSphere = function(n) +{ + n = 2*Math.floor(n/2); + function dataPush(a,b) + { // координати на точка и нормален вектор + data.push( + Math.cos(a)*Math.cos(b), + Math.sin(a)*Math.cos(b), + Math.sin(b) ); + } + + var data = []; + + // генериране на хоризонтални ленти + var b = -Math.PI/2, dB = 2*Math.PI/n; + for (var bi=0; bi