var lib_matrix4= { get_I4: function() { return [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]}, set_I4: function(m) { m[0]=1; m[1]=0; m[2]=0; m[3]=0; m[4]=0; m[5]=1; m[6]=0; m[7]=0; m[8]=0; m[9]=0; m[10]=1; m[11]=0; m[12]=0; m[13]=0; m[14]=0; m[15]=1; }, copyNew: function(m) { return [m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15]]; }, mult:function(a,b,m) { m[0]=a[0]*b[0]+a[1]*b[4]+a[2]*b[8]+a[3]*b[12]; m[1]=a[0]*b[1]+a[1]*b[5]+a[2]*b[9]+a[3]*b[13]; m[2]=a[0]*b[2]+a[1]*b[6]+a[2]*b[10]+a[3]*b[14]; m[3]=a[0]*b[3]+a[1]*b[7]+a[2]*b[11]+a[3]*b[15]; m[4]=a[4]*b[0]+a[5]*b[4]+a[6]*b[8]+a[7]*b[12]; m[5]=a[4]*b[1]+a[5]*b[5]+a[6]*b[9]+a[7]*b[13]; m[6]=a[4]*b[2]+a[5]*b[6]+a[6]*b[10]+a[7]*b[14]; m[7]=a[4]*b[3]+a[5]*b[7]+a[6]*b[11]+a[7]*b[15]; m[8]=a[8]*b[0]+a[9]*b[4]+a[10]*b[8]+a[11]*b[12]; m[9]=a[8]*b[1]+a[9]*b[5]+a[10]*b[9]+a[11]*b[13]; m[10]=a[8]*b[2]+a[9]*b[6]+a[10]*b[10]+a[11]*b[14]; m[11]=a[8]*b[3]+a[9]*b[7]+a[10]*b[11]+a[11]*b[15]; m[12]=a[12]*b[0]+a[13]*b[4]+a[14]*b[8]+a[15]*b[12]; m[13]=a[12]*b[1]+a[13]*b[5]+a[14]*b[9]+a[15]*b[13]; m[14]=a[12]*b[2]+a[13]*b[6]+a[14]*b[10]+a[15]*b[14]; m[15]=a[12]*b[3]+a[13]*b[7]+a[14]*b[11]+a[15]*b[15]; } }; var lib_matrix_projection={ get: function(angle, a, zMin, zMax) { var tan=Math.tan(lib_maths.degToRad(0.5*angle)), A=-(zMax+zMin)/(zMax-zMin), B=(-2*zMax*zMin)/(zMax-zMin); return [ .5/tan, 0 , 0, 0, 0, .5*a/tan, 0, 0, 0, 0, A, -1, 0, 0, B, 0 ]; }, getOrtho: function(largeur, a, zMin, zMax) { var hauteur=a*largeur; return [ 1/largeur, 0 , 0, 0, 0, 1/hauteur, 0, 0, 0, 0, -2/(zMax-zMin),0, 0, 0, -(zMin+zMax)/(zMax-zMin), 1 ]; } }; var lib_matrix_rot4={ rotateX: function(m, phi) { var c=Math.cos(phi); var s=Math.sin(phi); var mv1=m[1], mv5=m[5], mv9=m[9]; m[1]=m[1]*c-m[2]*s; m[5]=m[5]*c-m[6]*s; m[9]=m[9]*c-m[10]*s; m[2]=m[2]*c+mv1*s; m[6]=m[6]*c+mv5*s; m[10]=m[10]*c+mv9*s; }, rotateY: function(m, phi) { var c=Math.cos(phi); var s=Math.sin(phi); var mv0=m[0], mv4=m[4], mv8=m[8]; m[0]=c*m[0]+s*m[2]; m[4]=c*m[4]+s*m[6]; m[8]=c*m[8]+s*m[10]; m[2]=c*m[2]-s*mv0; m[6]=c*m[6]-s*mv4; m[10]=c*m[10]-s*mv8; }, rotateZ: function(m, phi) { var c=Math.cos(phi); var s=Math.sin(phi); var mv0=m[0], mv4=m[4], mv8=m[8]; m[0]=c*m[0]-s*m[1]; m[4]=c*m[4]-s*m[5]; m[8]=c*m[8]-s*m[9]; m[1]=c*m[1]+s*mv0; m[5]=c*m[5]+s*mv4; m[9]=c*m[9]+s*mv8; } };var lib_matrix_mv={ translateRot: function(m,v) { m[12]+=v[0]*m[0]+v[1]*m[4]+v[2]*m[8]; m[13]+=v[0]*m[1]+v[1]*m[5]+v[2]*m[9]; m[14]+=v[0]*m[2]+v[1]*m[6]+v[2]*m[10]; }, setPos: function(m,v) { m[12]=v[0]; m[13]=v[1]; m[14]=v[2]; }, /* * effectue la rotation inverse à celle de m pour le vecteur v */ do_inv_rot: function(m,v) { var v0=v[0], v1=v[1]; v[0]=v0*m[0]+v1*m[1]+v[2]*m[2]; v[1]=v0*m[4]+v1*m[5]+v[2]*m[6]; v[2]=v0*m[8]+v1*m[9]+v[2]*m[10]; }, do_inv_rotNew: function(m,v) { return [v[0]*m[0]+v[1]*m[1]+v[2]*m[2], v[0]*m[4]+v[1]*m[5]+v[2]*m[6], v[0]*m[8]+v[1]*m[9]+v[2]*m[10]]; }, /* * effectue le mouvement inverse à m : */ do_inv_mvNew: function(m, v) { return [m[0]*v[0]+m[1]*v[1]+m[2]*v[2]-m[12]*m[0]-m[13]*m[1]-m[14]*m[2], m[4]*v[0]+m[5]*v[1]+m[6]*v[2]-m[12]*m[4]-m[13]*m[5]-m[14]*m[6], m[8]*v[0]+m[9]*v[1]+m[10]*v[2]-m[12]*m[8]-m[13]*m[9]-m[14]*m[10]]; }, get_position: function(m) { return [m[12], m[13], m[14]]; } };var lib_vector={ size: function(v) { return Math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]); }, add: function(u,v) { u[0]+=v[0]; u[1]+=v[1]; u[2]+=v[2]; }, addNew: function(u,v) { return [u[0]+v[0], u[1]+v[1], u[2]+v[2]]; }, normalize: function(v) { var n=this.size(v); v[0]/=n; v[1]/=n; v[2]/=n; }, //produit vectoriel prodVect: function(u,v) { return [u[1]*v[2]-v[1]*u[2], u[2]*v[0]-u[0]*v[2], u[0]*v[1]-u[1]*v[0]]; }, to_spherical: function(u) { var r=this.size(u); var phi=Math.acos(u[2]/r); var q=Math.acos(u[0]/Math.sqrt(u[0]*u[0]+u[1]*u[1])); var theta=(u[1]>=0)?q:2*Math.PI-q; return [r,theta, phi]; }, //produit scalaire dot: function(u, v) { return u[0]*v[0]+u[1]*v[1]+u[2]*v[2]; }, subNew: function(u,v) { return ([u[0]-v[0], u[1]-v[1], u[2]-v[2]]); }, halfNew: function(u) { return [u[0]*0.5, u[1]*0.5, u[2]*0.5]; }, copy: function(src, dst) { dst[0]=src[0]; dst[1]=src[1]; dst[2]=src[2]; }, copyNew: function(u) { return [u[0], u[1], u[2]]; }, addHalfNew: function(u,v) { return [u[0]+0.5*v[0], u[1]+0.5*v[1], u[2]+0.5*v[2]]; }, //déterminant det: function(u,v,w) { return u[0]*(v[1]*w[2]-v[2]*w[1])-u[1]*(v[0]*w[2]-v[2]*w[0])+u[2]*(v[0]*w[1]-v[1]*w[0]); }, fmaNew: function(u,v,a) { return [u[0]+a*v[0], u[1]+a*v[1], u[2]+a*v[2]]; } };var lib_maths={ degToRad: function(angle) { return angle*Math.PI/180; }, //passage en co sphériques to_spheriques: function(v) { var r=lib_vector.size(v); var phi=-Math.PI/2+Math.acos(v[2]/r); var theta=((v[1]>0)?1:-1)*Math.acos(v[0]/Math.sqrt(v[0]*v[0]+v[1]*v[1])); return {phi: phi, theta: theta}; }, piterval: function(a) { var b=a; while(b<-Math.PI) b+=2*Math.PI; while(b>Math.PI) b-=2*Math.PI; return b; } };if ( !window.requestAnimationFrame ) { window.requestAnimationFrame = ( function() { return window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame; } )(); }; if ( !window.cancelRequestAnimationFrame ) { window.cancelRequestAnimationFrame = ( function() { return window.webkitCancelRequestAnimationFrame || window.mozCancelRequestAnimationFrame || window.oCancelRequestAnimationFrame || window.msCancelRequestAnimationFrame; } )(); }; var lib_ajax={ get: function(url, func) { var xmlHttp = new XMLHttpRequest(); xmlHttp.open("GET", url, true); xmlHttp.onreadystatechange = function() { if (xmlHttp.readyState == 4 && xmlHttp.status==200) { func(xmlHttp.responseText); // la fonction de prise en charge } } xmlHttp.send(); } }; var lib_intersect={ /* * Calcule les coordonnées de plucker pour un segment [A,B] */ plucker_vecteur: function(A,B) { return [ A[0]*B[1]-A[1]*B[0], A[0]*B[2]-A[2]*B[0], A[0]-B[0], A[1]*B[2]-A[2]*B[1], A[2]-B[2], B[1]-A[1] ]; }, /* * Calcule les coordonnées de plucker pour un axe d'origine P et de vecteur u */ plucker_axe: function(P,u) { return [ P[0]*u[1]-P[1]*u[0], P[0]*u[2]-P[2]*u[0], -u[0], P[1]*u[2]-P[2]*u[1], -u[2], u[1] ] }, /* * fonction side() utilisant les coordonnées de plucker * L correspond au segment orienté R à l'axe */ side: function(L,R) { return R[2]*L[3]+R[5]*L[1]+R[4]*L[0]+R[1]*L[5]+R[0]*L[4]+R[3]*L[2]; }, plucker_tri: function(A,B,C) { return [this.plucker_vecteur(A,B), this.plucker_vecteur(B,C), this.plucker_vecteur(C,A) ] }, /* * intersection entre un triangle représenté par un tableau de coordonnées de plucker de ses arrètes, pluck_arretes * avec un rayon représenté par ses coordonnées de plucker, pluck_R */ intersect_ray_tri: function(pluck_R, pluck_arretes) { var side0=this.side(pluck_arretes[0], pluck_R); if (side0*this.side(pluck_arretes[1], pluck_R)<0) return false; if (side0*this.side(pluck_arretes[2], pluck_R)<0) return false; return true; }, /* * retourne les coordonnées de plucker des arrêtes d'un quad * prend en argument les 4 sommets du quad */ plucker_quad: function(A,B,C,D) { return [this.plucker_vecteur(A,B), this.plucker_vecteur(B,C), this.plucker_vecteur(C,D), this.plucker_vecteur(D,A) ] }, /* * retourne les coordonnées de plucker des arrêtes des 4 quads à tester pour effecter le teste d'intersection boite-rayon * c est le centre de la boite et d ses dimensions */ plucker_boite: function(c, d) { //coordonnées des points de la boite var A=[c[0]+0.5*d[0], c[1]-0.5*d[1], c[2]-0.5*d[2]], B=[c[0]+0.5*d[0], c[1]+0.5*d[1], c[2]-0.5*d[2]], C=[c[0]+0.5*d[0], c[1]+0.5*d[1], c[2]+0.5*d[2]], D=[c[0]+0.5*d[0], c[1]-0.5*d[1], c[2]+0.5*d[2]], E=[c[0]-0.5*d[0], c[1]-0.5*d[1], c[2]-0.5*d[2]], F=[c[0]-0.5*d[0], c[1]+0.5*d[1], c[2]-0.5*d[2]], G=[c[0]-0.5*d[0], c[1]+0.5*d[1], c[2]+0.5*d[2]], H=[c[0]-0.5*d[0], c[1]-0.5*d[1], c[2]+0.5*d[2]]; return [ this.plucker_quad(B,C,H,E), this.plucker_quad(A,D,G,F), this.plucker_quad(A,B,F,E), this.plucker_quad(D,C,G,H) ] }, /* *retourne une liste d'arrêtes du triangle T, spécifié par une liste de 3 points */ get_tri_arretes: function(T) { return [lib_vector.subNew(T[1], T[0]), lib_vector.subNew(T[2], T[1]), lib_vector.subNew(T[0], T[2])]; }, /* * retourne la normale au triangle T * tri est un tableau de 3 points */ normale_triangle: function(tri) { var AB=lib_vector.subNew(tri[1],tri[0]), AC=lib_vector.subNew(tri[2], tri[0]); var N=lib_vector.prodVect(AB, AC); lib_vector.normalize(N); return N; }, /* *projette le point P sur l'axe défini par un vecteur directeur unitaire u et un point O *retourne son abscisse sur l'axe par rapport au point O */ abscisse_point_axis: function(O, u, P) { var OP=lib_vector.subNew(P,O); return lib_vector.dot(OP, u); }, /* * fonction de coincidence * A et B sont des listes de points * Ru est le vecteur unitaire de l'axe R * RP est un point de l'axe R * retourne vrai si ça coincide */ SAT: function(A,B,Ru,RP) { var xA=[], xB=[], i, r=true; for (i=0; ixB[xB.length-1]) || (xA[xA.length-1]max[c]) max[c]=points[p][c]; } } var dim=lib_vector.subNew(max, min); return {dim : dim, centre: lib_vector.addHalfNew(min, dim)}; }, intersection_point_ray_tri: function(T,P,u,face) { var AB=lib_vector.subNew(T[1], T[0]), AC=lib_vector.subNew(T[2], T[0]), AP=lib_vector.subNew(P, T[0]); var k_num=lib_vector.det(AP,AB,AC), k_denom=lib_vector.det(u,AB,AC); if (k_denom==0) return false; var k=k_num/k_denom; if (k<0) return false; return {I: lib_vector.fmaNew(P, u, -k), d: k, T:T, face:face}; }, /* * intersection entre une boite, représentée par un tableau contenant les coordonnées de plucker des 4 quads à tester * avec un rayon représenté par ses coordonnées de plucker, pluck_R */ intersect_ray_boite: function(pluck_R, quads) { if (this.intersect_ray_quad(pluck_R, quads[0])) return true; if (this.intersect_ray_quad(pluck_R, quads[1])) return true; if (this.intersect_ray_quad(pluck_R, quads[2])) return true; if (this.intersect_ray_quad(pluck_R, quads[3])) return true; return false; }, /* * intersection entre un quad représenté par un tableau de coordonnées * de plucker de ses arrètes, pluck_arretes avec un rayon représenté * par ses coordonnées de plucker, pluck_R */ intersect_ray_quad: function(pluck_R, pluck_arretes) { var side0=this.side(pluck_arretes[0], pluck_R); if (side0*this.side(pluck_arretes[1], pluck_R)<0) return false; if (side0*this.side(pluck_arretes[2], pluck_R)<0) return false; if (side0*this.side(pluck_arretes[3], pluck_R)<0) return false; return true; } }; /* * spec.canvas_id : id of the canvas */ var GL, CV; var Contexte=(function() { return { instance: function(spec) { var canvas=document.getElementById(spec.canvas_id); canvas.width=window.innerWidth; canvas.height=window.innerHeight; CV=canvas; try { GL = canvas.getContext("experimental-webgl", {antialias: true}); } catch (e) { alert("Vous n'êtes pas compatibles webgl !") ; return false; } ; var scene=Scene.instance({}); var shaders=Shaders.instance({}); var vue=Vue.instance({camera: [0,0,-20], theta: 0, phi: 0, angle: 60, zMin: 1, zMax: 30000}); vue.set_rayon(); window.VUE=vue; var avionTexture=Texture.instance({url: "models/textureAO.png"}); var avion=BlenderObjet.instance({url: "models/2_avion.json", texture: avionTexture, lighting: true, onloaded: function(self) { scene.add_objet(self); NAVIGATION.set_avion(self) } }); var navigation=Navigation.instance({vue: vue, avion: avion}); navigation.set(); var terrainTexture=Texture.instance({url: "terrain/grenoble30.jpg"}); var terrain=Terrain.instance({url: "terrain/grenoble480.txt", texture: terrainTexture, pas: 480, offset: 2000, echelle: 0.1, lighting: true, onloaded: function(self) { scene.add_objet(self); RAYON.set_objet(self); RAYONAVION.set_objet(self); } }); var skydomeTexture=Texture.instance({url: "images/skydome.png"}); var skydome=Skydome.instance({texture: skydomeTexture, rayon: 10000, lighting: false}); scene.add_objet(skydome); var clouTexture=Texture.instance({url: "models/clou.png"}); var clou=BlenderObjet.instance({url: "models/clou.json", texture: clouTexture, lighting: true, onloaded: function(self) { scene.add_objet(self); window.CLOU=self; self.set_position([0,0,999999]);} }); var lumiere=LumiereDirectionnelle.instance({direction: [1,1,1], shadowMap: {largeur: 512, hauteur: 512, largeurVue: 30, zMin: 0. , zMax: 30., position: [0,0,-20]}}); //ZOU window.LUMIERE=lumiere; var textureSprite=Texture.instance({url: "images/sprite.png"}); var sprites=Sprites.instance({texture: textureSprite, N:32}); window.SPRITES=sprites; window.CRASH=false; var crash_son=Audio.instance({url: "sounds/crash.ogg", boucle: false}); window.CRASH_SON=crash_son; scene.draw(); var timer_physics=setInterval("SCENE.drawPhysics()", 16); return true; } } })(); /* * spec.vertices : tableau js des vertex * spec.indices : tableau js des indices * spec.octree : true/false suivant qu'il y a un octree */ var Maillage=(function () { return { instance: function(spec) { var vbo=VBO.instance({tableau_js: spec.vertices}); var vbo_indices=VBO_indices.instance({tableau_js: spec.indices}); var octree=false; if (spec.octree) octree=Octree.instance({indices: spec.indices, vertices: spec.vertices, vTaille: 8}); return { draw: function() { vbo.draw(); vbo_indices.draw(); }, drawDepth: function() { vbo.drawDepth(); vbo_indices.draw(); }, drawSprite: function() { vbo.drawSprite(); vbo_indices.draw(); }, pick: function(C,u) { return octree.intersect(C,u); } } } } }()); /* * spec.texture : texture || false * spec.maillage : maillage de l'objet (type Maillage) * spec.matrix: matrice de mouvement (defaut : I4) * spec.lighting: true/false */ var Objet=(function() { return { instance: function(spec) { spec.matrix=spec.matrix || lib_matrix4.get_I4(); var matrix=lib_matrix4.copyNew(spec.matrix); var that={ drawDepth: function() { SHADERS.set_matriceObjet_depth(matrix); spec.maillage.drawDepth(); }, draw: function() { if (spec.texture) spec.texture.draw(); SHADERS.set_matriceObjet(matrix); SHADERS.set_lighting(spec.lighting); spec.maillage.draw(); }, rotateX: function(dTheta) { lib_matrix_rot4.rotateX(matrix, dTheta); }, rotateY: function(dTheta) { lib_matrix_rot4.rotateY(matrix, dTheta); }, rotateZ: function(dTheta) { lib_matrix_rot4.rotateZ(matrix, dTheta); }, set_position: function(p) { lib_matrix_mv.setPos(matrix, p); }, set_mouvement: function(position, roulis, tangage, lacet) { lib_matrix4.set_I4(matrix); lib_matrix_rot4.rotateX(matrix, roulis); lib_matrix_rot4.rotateY(matrix, tangage); lib_matrix_rot4.rotateZ(matrix, lacet); lib_matrix_mv.setPos(matrix, position); }, drawPhysics: false, set_physics: function(phyFunc) { that.drawPhysics=phyFunc; }, pick:function(C, u) { var u_objet=lib_matrix_mv.do_inv_rotNew(matrix, u), C_objet=lib_matrix_mv.do_inv_mvNew(matrix, C); var intersection=spec.maillage.pick(C_objet,u_objet); if (!intersection) return false; NAVIGATION.go_to(intersection.I); }, crash: function(C,u) { var u_objet=lib_matrix_mv.do_inv_rotNew(matrix, u), C_objet=lib_matrix_mv.do_inv_mvNew(matrix, C); var intersection=spec.maillage.pick(C_objet,u_objet); if (!intersection) return false; if (intersection.d>5) return false; //active la transparence : GL.enable(GL.BLEND); GL.blendFunc(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA); CRASH=true; CRASH_SON.play(); } }; return that; } } })(); /* * spec.camera : position de la caméra. defaut : [0,0,0] * spec.theta : angle horizontal en radians. defaut : 0 * spec.phi : angle vertical en radians. defaut : 0 * spec.angle : angle de vue en degrés. Utilisé pour la matrice de projection. defaut : 45 * spec.zMin : zMin de la matrice de projection. defaut : 1 * spec. zMax : zMax de la matrice de projection. defaut : 10 * spec.a : rapport L/H du canvas. defaut : CV.width/CV.height * spec.ortho : true if ortho view */ var VUE; var Vue=(function() { return { instance: function(spec) { spec.camera=spec.camera || [0,0,0]; spec.theta=spec.theta || 0; spec.phi=spec.phi ||0; spec.angle=spec.angle || 45; spec.zMin=spec.zMin || 1; spec.zMax=spec.zMax || 10; spec.a=spec.a || CV.width/CV.height; var matrice_vue=lib_matrix4.get_I4(); var matrice_projection=(spec.ortho)?lib_matrix_projection.getOrtho(spec.largeur, spec.a, spec.zMin, spec.zMax):lib_matrix_projection.get(spec.angle, spec.a, spec.zMin, spec.zMax); var matrice_shadowMap=lib_matrix4.get_I4(); var calcule_matrice=function() { lib_matrix4.set_I4(matrice_vue); lib_matrix_rot4.rotateZ(matrice_vue, spec.theta); lib_matrix_rot4.rotateY(matrice_vue, spec.phi); lib_matrix_mv.translateRot(matrice_vue, spec.camera); lib_matrix4.mult(matrice_vue, matrice_projection, matrice_shadowMap); } var rayon=false; var that={ set_camera: function(cam) { spec.camera=cam; calcule_matrice(); }, set_rayon: function() { rayon=Rayon.instance({}); rayon.set(matrice_projection, CV.width, CV.height); window.RAYON=rayon; }, pick: function(X,Y) { rayon.lance(X,Y,matrice_vue); }, draw: function() { SHADERS.set_matriceVue(matrice_vue); SHADERS.set_matriceProjection(matrice_projection); }, draw_matriceShadowMap: function() { SHADERS.set_matriceShadowMap(matrice_shadowMap); }, drawPhysics: function() { }, drawShadowMap: function() { SHADERS.set_matrices_depth(matrice_vue, matrice_projection); }, set_mouvement: function(pos, L,H, roulis, tangage, lacet) { lib_matrix4.set_I4(matrice_vue); lib_matrix_rot4.rotateZ(matrice_vue,roulis); lib_matrix_rot4.rotateX(matrice_vue,-Math.PI/2); lib_matrix_rot4.rotateY(matrice_vue,Math.PI/2-lacet); lib_matrix_rot4.rotateX(matrice_vue, tangage+0.3); lib_matrix_mv.translateRot(matrice_vue, [-pos[0]+Math.cos(lacet)*(L*Math.cos(tangage)-H*Math.sin(tangage)), -pos[1]+Math.sin(lacet)*(L*Math.cos(tangage)-H*Math.sin(tangage)), -pos[2]-L*Math.sin(tangage)-H*Math.cos(tangage)]); } } calcule_matrice(); return that; } }; })(); /* * spec: empty */ var SCENE; var Scene=(function () { return { instance: function(spec) { var objets=[]; var drawObjet=function(objet) { objet.draw(); } var drawObjet_shadowMap=function(objet) { objet.drawDepth(); } var drawObjetPhysics=function(objet) { if (objet.drawPhysics) objet.drawPhysics(objet); } GL.enable(GL.DEPTH_TEST); GL.depthFunc(GL.LEQUAL); GL.clearColor(1.0, 1.0, 1.0, 1.0); GL.clearDepth(1.0); var that={ draw: function(timestamp) { LUMIERE.draw(); //draw the shadow map GL.viewport(0.0, 0.0, CV.width, CV.height); GL.clear(GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT); VUE.draw(); objets.map(drawObjet); if(CRASH) { SPRITES.draw(); } GL.flush(); window.requestAnimationFrame(SCENE.draw); }, draw_objets_shadowMap: function() { objets.map(drawObjet_shadowMap); }, drawPhysics: function() { VUE.drawPhysics(); NAVIGATION.drawPhysics(); objets.map(drawObjetPhysics); if(CRASH) { SPRITES.drawPhysics(); } }, add_objet: function(objet) { objets.push(objet); } }; SCENE=that; return that; } }; })(); /* * spec: empty */ var SHADERS; var Shaders=(function (){ var get_shader=function(source, type) { var shader = GL.createShader(type); GL.shaderSource(shader, source); GL.compileShader(shader); if (!GL.getShaderParameter(shader, GL.COMPILE_STATUS)) { alert(GL.getShaderInfoLog(shader)); return false; } return shader; }; var shader_vertex_source="attribute vec3 position, normale;\n\ attribute vec2 UV;\n\ uniform mat4 matrice_vue, matrice_projection, matrice_objet;\n\ varying vec2 vUV;\n\ varying vec3 vN, vPosition; //vecteur normal dans le référentiel scène\n\ varying float vFog;\n\ const float Dmin=20.;\n\ const float Dmax=2000.;\n\ \n\ void main(void) {\n\ gl_Position = matrice_projection * matrice_vue * matrice_objet * vec4(position, 1.0);\n\ vN=vec3(matrice_objet * vec4(normale, 0.0));\n\ vUV=UV;\n\ vPosition=vec3(matrice_objet * vec4(position, 1.0)); //position dans le référentiel scène\n\ \n\ float D=-(matrice_vue * matrice_objet * vec4(position, 1.0)).z;\n\ vFog=smoothstep(Dmin, Dmax, D);\n\ }", shader_fragment_source="\n\ precision mediump float;\n\ \n\ uniform sampler2D sampler, samplerDepth;\n\ uniform float enableLighting;\n\ uniform vec3 L;\n\ uniform mat4 matrice_shadowMap;\n\ uniform vec2 shadowMapSize;\n\ \n\ varying vec2 vUV;\n\ varying vec3 vN;\n\ varying vec3 vPosition;\n\ varying float vFog;\n\ \n\ const vec4 FOG_COULEUR=vec4(0.4,0.58,0.7,1.);\n\ \n\ //paramètres d'éclairage :\n\ const float Ia=0.3; //illumination ambiante\n\ \n\ //materiau de l'avion :\n\ const float ka=0.1; //coeff d'illumination ambiante\n\ const float kd=2.2; //coeff d'illumination diffuse\n\ \n\ void main(void) { \n\ float Id=max(0., dot(normalize(vN), L)); //illumination diffuse\n\ float Iphong=mix(1.,Ia*ka+Id*kd, enableLighting);//illumination de phong\n\ float I=Iphong;\n\ \n\ //SHADOW MAPPING\n\ vec4 position_lightRefClip=matrice_shadowMap*vec4(vPosition,1.); //position du point dans le référentiel de la shadowMap en coordonnées de clip\n\ position_lightRefClip.xyz/=position_lightRefClip.w; //position du point dans le viewPort de la shadowMap\n\ vec2 uv_shadowMap=(vec2(1., 1.)+vec2(position_lightRefClip))/2.; //coordonnées UV du point dans la shadowMap\n\ float z=(1.+position_lightRefClip.z)/2.; //z du point rendu dans le référentiel de la shadowMap, entre 0 et 1\n\ \n\ //vec4 shadowMapZ=texture2D(samplerDepth, uv_shadowMap);\n\ \n\ //Percentage Close Filtering\n\ float sum=0.;\n\ for (float pcf_x=-1.5; pcf_x<=1.5; pcf_x+=1.){\n\ for (float pcf_y=-1.5; pcf_y<=1.5; pcf_y+=1.){\n\ sum+=texture2D(samplerDepth, uv_shadowMap+vec2(pcf_x/shadowMapSize.x, pcf_y/shadowMapSize.y)).r;\n\ }\n\ }\n\ float shadowMapZ=sum/16.;\n\ float ombre_coeff=smoothstep(0.,0.05,z-shadowMapZ);\n\ \n\ if (uv_shadowMap.x>0. && uv_shadowMap.y>0. && uv_shadowMap.x<1. && uv_shadowMap.y<1. && shadowMapZ0 && y>0) { indices.push( //ajoute un quad (=2 faces) x+y*lignes.length, x-1+y*lignes.length, x-1+(y-1)*lignes.length, x+y*lignes.length, x-1+(y-1)*lignes.length, x+(y-1)*lignes.length ); } } } that=Objet.instance({ texture: spec.texture, lighting: spec.lighting, maillage: Maillage.instance({ octree: true, vertices: vertices, indices: indices })}); spec.onloaded(that); }) return that; } } })(); /* * spec.vue: vue associée à la navigation */ var NAVIGATION; var Navigation=(function () { return { instance: function(spec) { var dlacet=3, //sensibilité sur le lacet dtangage=3; var vitesse=2000, //vitesse initiale de l'avion position=[0,0,0],//position initiale de l'avion' dt=0.0016; //pas du moteur physique en secondes var roulis=0, //roulis de l'avion en radians tangage=0, //tangage de l'avion lacet=0.01; //lacet de l'avion var vTangage=0, //vitesse angulaire de tangage vLacet=0; //vitesse angulaire de lacet var offset_Z=340; //pour la navigation au clic var cible_tangage=0, //tangage menant à la cible cible_lacet=0, //lacet menant à la cible cible=false; var rayonAvion=Rayon.instance({}); window.RAYONAVION=rayonAvion; var dP=[0,0,0]; var camera_H=15, //hauteur entre l'avion et la camera camera_L=30; //distance horizontale entre l'avion et la camera var avion=false; var touches=[], eventHandlers=[], clic=false, mouseX0=0, mouseY0=0; var add_eventHandler=function(type, handler) { window.addEventListener(type, handler, false); eventHandlers.push({type: type, handler: handler}); } var keyDown=function(e) { e.preventDefault(); if (touches.indexOf(e.keyCode)!=-1) return false; touches.push(e.keyCode); e.preventDefault(); return false; } var keyUp=function(e) { var position=touches.indexOf(e.keyCode); if (position!=-1) touches.splice(position, 1); return false; } var manage_touche=function(touche) { cible=false; switch(touche) { case 37: //fleche de gauche vLacet+=dlacet; break; case 39: //fleche de droite vLacet-=dlacet; break; case 38: //fleche du haut vTangage+=dtangage; break; case 40: //fleche du bas vTangage-=dtangage; break; } } var mouseClick=function(e) { spec.vue.pick(e.pageX, e.pageY); } var that={ drawPhysics: function() { if (CRASH) return false; touches.map(manage_touche); if (cible) { var direction=lib_vector.subNew(cible, position); if (lib_vector.size(direction)<20) cible=false; var angles=lib_maths.to_spheriques(direction); cible_lacet=angles.theta; cible_tangage=angles.phi; vTangage=lib_maths.piterval(cible_tangage-tangage)*10; vLacet+=lib_maths.piterval(cible_lacet-lacet)*10; } vLacet*=0.9; vTangage*=0.9; tangage+=dt*vTangage; lacet+=dt*vLacet; roulis=-vLacet*0.05; //variation de la position : dP[0]=Math.cos(tangage)*Math.cos(lacet); dP[1]=Math.cos(tangage)*Math.sin(lacet); dP[2]=-Math.sin(tangage); //lance le rayon de crashtest rayonAvion.lance_refScene(position, dP); //calcule la position de l'avion position[0]+=vitesse*dt*dP[0]; position[1]+=vitesse*dt*dP[1]; position[2]+=vitesse*dt*dP[2]; if (avion) avion.set_mouvement(position, roulis, tangage+vTangage*0.01, lacet+vLacet*0.01); spec.vue.set_mouvement(position, camera_L,camera_H, 0.05*roulis, tangage, lacet); }, //navigue vers le point destination go_to: function(destination) { //calcul du roulis, du tangage et du lacet à obtenir pour aller de position à intersection.I+offset_Z : CLOU.set_position(destination); var newDest=[0,0,offset_Z]; lib_vector.add(newDest, destination); cible=newDest; }, set_avion: function(a) { avion=a; }, get_position: function() { return position; }, set: function() { add_eventHandler("click", mouseClick); add_eventHandler("keyup", keyUp); add_eventHandler("keydown", keyDown); }, unset: function() { eventHandlers.map(function(e) { window.removeEventListener(e.type, e.handler, false);}) eventHandlers=[]; } }; NAVIGATION=that; return that; } }; })(); /* * spec.centre : position du centre du cube. defaut : [0,0,0] * spec.rayon : rayon de la sphere. defaut : 1, * bandes : nombre de bandes. defaut : 16 * couronnes : nombre de couronnes. defaut : 16 * texture: texture */ var Skydome=(function() { return { instance: function(spec) { var centre=spec.centre || [0,0,0], rayon=spec.rayon || 1, bandes=spec.bandes ||16, couronnes=spec.couronnes || 16, vertices=[], indices=[]; var c, b, theta, phi; for (c=0; c<=couronnes; c++) { phi=((c/couronnes)-0.5)*Math.PI; for (b=0; b<=bandes; b++) { theta=(b/bandes)*2*Math.PI; vertices.push(centre[0]+Math.cos(theta)*Math.cos(phi)*rayon, //X centre[1]+Math.sin(theta)*Math.cos(phi)*rayon, //Y centre[2]+Math.sin(phi)*rayon, //Z Math.cos(theta)*Math.cos(phi), //Nx Math.sin(theta)*Math.cos(phi)*rayon, //Ny Math.sin(phi)*rayon, //Nz theta/(2*Math.PI), //U (phi+Math.PI/2)/Math.PI //V ); if (c!=0 ) { if (c!=couronnes) indices.push(c*(bandes+1)+b, c*(bandes+1)+b-1, (c-1)*(bandes+1)+b); if (c!=1) indices.push(c*(bandes+1)+b-1, (c-1)*(bandes+1)+b, (c-1)*(bandes+1)+b-1); } } if (c==0 || c==couronnes) continue; //premiere ou derniere couronne } var that=Objet.instance({ texture: spec.texture, maillage: Maillage.instance({ vertices: vertices, indices: indices })}); that.rotateZ(Math.PI/2); return that; } } })(); /* * spec.texture : texture des sprites * spec.N : nombre d'image dans le sprite */ var Sprites=(function() { return { instance: function(spec) { var vertices=[ -1,1, -1,-1, 1,-1, 1,1 ], indices=[0,1,2, 0,2,3]; var maillage=Maillage.instance({ octree: false, vertices: vertices, indices: indices}); var that=Objet.instance({ texture: spec.texture, lighting: false, maillage: maillage }); var u=0; that.draw=function() { if (u>spec.N) return; SHADERS.set_sprite((Math.floor(u)%spec.N)/spec.N, 1/spec.N); if (spec.texture.is_loaded()) spec.texture.draw(); maillage.drawSprite(); SHADERS.unset_sprite(); } that.drawPhysics=function(self) { u+=0.15; }; return that; } } })(); /* * spec.url: url du fichier audio * spec.boucle : true si le son est joué en boucle */ var Audio=(function () { return { instance: function(spec) { var audioElement=document.createElement("audio"); var sourceMusic=document.createElement("source"); sourceMusic.src=spec.url; audioElement.loop=spec.boucle; audioElement.preload=true; audioElement.autoplay=false; audioElement.appendChild(sourceMusic); return { play: function() { audioElement.play(); } } } } }()); /* * une boite est un parallépipède rectangle contenant : * - soit un tableau de faces * - soit 8 autres boites * Elle est exclusivement utilisée par la classe Octree * * paramètres : * spec.C : centre de la boite * spec.dim : dimensions de la boite * spec.faces : tableau des faces * spec.maxFaces : nombre maximal de faces dans une boite */ var Boite=(function() { return { instance: function(spec) { var pluck_boite=lib_intersect.plucker_boite(spec.C, spec.dim); var i,F=[], sous_boites=[]; for (var i=0; ispec.maxFaces); if (!feuille) { //subdivise la boite en 8 boites filles var x,y,z; var dim_sousBoite=lib_vector.halfNew(spec.dim); for (x=-0.5; x<=0.5; x++) { for (y=-0.5; y<=0.5; y++) { for (z=-0.5; z<=0.5; z++) { sous_boites.push(Boite.instance({ C: [ spec.C[0]+x*spec.dim[0]/2, spec.C[1]+y*spec.dim[1]/2, spec.C[2]+z*spec.dim[2]/2 ], dim: dim_sousBoite, faces: F, maxFaces: spec.maxFaces })); } } } } else { //calcule les coordonnées de plucker des arrêtes des faces var F_plucker=[]; for (i=0; ivue lance: function(Xp, Yp, M) { var cam=lib_matrix_mv.get_position(M); lib_matrix_mv.do_inv_rot(M,cam); //calcule dans le référentiel vue Ps[0]=(coeff_Xva*Xp+coeff_Xvb); Ps[1]=(coeff_Yva*Yp+coeff_Yvb); Ps[2]=1; lib_vector.normalize(Ps); //passe au référentiel scène lib_matrix_mv.do_inv_rot(M, Ps); camera[0]=-cam[0]; camera[1]=-cam[1]; camera[2]=-cam[2]; spec.objet.pick(camera, Ps); }, //lance le rayon dans le ref scene lance_refScene: function(origine, direction) { if (spec.objet) spec.objet.crash(origine, direction); } } return that; } } })(); /* * spec.direction: direction * spec.shadowMap: objet {largeur: largeur en px, hauteur: hauteur en px, zMin: zMin, zMax: zMax, angle: angle d'ouverture, position: position} */ var LumiereDirectionnelle=(function () { return { instance: function(spec) { var direction=spec.direction || [0,0,1], shadowMapEnabled=spec.shadowMap?true:false, shadowMap=false; lib_vector.normalize(direction); var that={ loaded: true, draw: function() { if (shadowMapEnabled) shadowMap.draw(); SHADERS.set_lumiereDirection(direction); return true; }, get_direction: function() { return direction; } }; if (shadowMapEnabled) { spec.shadowMap.lumiere=that; shadowMap=ShadowMap.instance(spec.shadowMap); } return that; } }; })(); /* * spec.lumiere: Lumiere * spec.largeur: largeur en pixels. doit être POT * spec.hauteur: hauteur en pixels. doit être POT * spec.position: position de la vue de la shadowMap * spec.zMin: zNear * spec.zMax: zFar * spec.largeurVue: largeur de la matrice de projection orthogonale */ var ShadowMap=(function () { return { instance: function(spec) { spec.lumiere=spec.lumiere || false; spec.largeur=spec.largeur || 512; spec.hauteur=spec.hauteur || 512; spec.largeurVue=spec.largeurVue || 5; spec.zMin=spec.zMin || 0.1; spec.zMax=spec.zMax || 10; var depthFrameBuffer, depthTexture, depthRenderBuffer; depthFrameBuffer=GL.createFramebuffer(); GL.bindFramebuffer(GL.FRAMEBUFFER, depthFrameBuffer); depthTexture=GL.createTexture(); GL.bindTexture(GL.TEXTURE_2D, depthTexture); GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.LINEAR); GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.LINEAR); GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE); GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE); GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, spec.largeur, spec.hauteur, 0, GL.RGBA, GL.UNSIGNED_BYTE, null); depthRenderBuffer=GL.createRenderbuffer(); GL.bindRenderbuffer(GL.RENDERBUFFER, depthRenderBuffer); GL.renderbufferStorage(GL.RENDERBUFFER, GL.DEPTH_COMPONENT16, spec.largeur, spec.hauteur); GL.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, GL.TEXTURE_2D, depthTexture, 0); GL.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.RENDERBUFFER, depthRenderBuffer); GL.bindTexture(GL.TEXTURE_2D, null); GL.bindRenderbuffer(GL.RENDERBUFFER, null); GL.bindFramebuffer(GL.FRAMEBUFFER, null); var spheriques=lib_vector.to_spherical(spec.lumiere.get_direction()); var vue=Vue.instance({camera: spec.position, theta: spheriques[1], phi: spheriques[2], ortho: true, zMin: spec.zMin, zMax: spec.zMax, a: spec.largeur/spec.hauteur, largeur: spec.largeurVue}); var that={ draw: function() { SHADERS.set_shadowMap(); GL.bindFramebuffer(GL.FRAMEBUFFER, depthFrameBuffer); GL.bindRenderbuffer(GL.RENDERBUFFER, depthRenderBuffer); GL.bindTexture(GL.TEXTURE_2D, depthTexture); GL.clearColor(1.0, 1.0, 1.0, 1.0); GL.clearDepth(1.0); GL.viewport(0.0, 0.0, spec.largeur, spec.hauteur); GL.clear(GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT); var pos=NAVIGATION.get_position(); vue.set_camera([-pos[0], -pos[1], -pos[2]-20]); vue.drawShadowMap(); SCENE.draw_objets_shadowMap(); GL.bindTexture(GL.TEXTURE_2D, null); GL.bindFramebuffer(GL.FRAMEBUFFER, null); GL.bindRenderbuffer(GL.RENDERBUFFER, null); SHADERS.unset_shadowMap(); GL.activeTexture(GL.TEXTURE1); GL.bindTexture(GL.TEXTURE_2D, depthTexture); GL.activeTexture(GL.TEXTURE0); vue.draw_matriceShadowMap(); SHADERS.set_shadowMapSize(spec.largeur, spec.hauteur); } }; //window.VUE=vue; return that; } }; })();var main=function() { var isContexte=Contexte.instance({canvas_id: "mon_canvas"}); };